Klarhimmel UI

App, member list

App, member list preview

Install

npx shadcn add @klarhimmel/app-team-members

Usage

import { Theme } from '@/components/klarhimmel/theme/style';
import { BlockSurface } from '@/components/klarhimmel/blocks/surface';
import AppTeamMembers from '@/components/klarhimmel/blocks/app-team-members';

export default function Section() {
  return (
    <Theme theme="brand">
      <BlockSurface>
        <AppTeamMembers props={{
          "eyebrow": "Team",
          "title": "People at Exempelhotellet",
          "description": "Everyone who can sign in. Owners can change roles.",
          "summaryWord": "members",
          "roles": [
            "Owner",
            "Admin",
            "Member",
            "Viewer"
          ],
          "roleLabel": "Role for",
          "members": [
            {
              "name": "Maja Lindqvist",
              "email": "maja@northlight.example",
              "sample": "Owner",
              "locked": true
            },
            {
              "name": "Nils Berg",
              "email": "nils@northlight.example",
              "sample": "Admin",
              "locked": false
            },
            {
              "name": "Ada Ek",
              "email": "ada@northlight.example",
              "sample": "Member",
              "locked": false
            },
            {
              "name": "Oskar Holm",
              "email": "oskar@northlight.example",
              "sample": "Viewer",
              "locked": false
            }
          ],
          "remove": {
            "label": "Remove",
            "question": "Remove this person?",
            "yes": "Yes, remove",
            "no": "Keep"
          },
          "notices": {
            "roleNow": "is now",
            "removed": "removed"
          },
          "state": "filled",
          "messages": {
            "emptyTitle": "Only you here so far",
            "emptyBody": "Invite a colleague and they appear in this list."
          }
        }} />
      </BlockSurface>
    </Theme>
  );
}

Install

Import

import Block from '@/components/klarhimmel/blocks/app-team-members';
Also called
  • app/team-members
About
The people on a team: avatar, name, email and a role select per member, a locked owner row, Remove with a confirmation that keeps focus in the list, role changes and removals announced by name, a count that follows, and an empty state.
Category
Interface sections for signed-in products.

Notes

When to use it

A list of the people on a team, each with a role select and a Remove button. Use `app-team-pending` for invitations that are not accepted yet and `app-team-invite` for adding someone; this one is for people who already have access.

Writing for it

- roles (2 to 5 names, 20 characters) feeds every row's select, so list all of them, including Owner if one row is locked. - Each member's sample must be one of the roles. Set locked: true on the owner so that row shows a badge and has no Remove button. - roleLabel (30) is spoken before the name, so end it with a word like 'Role for'. Write notices.roleNow and notices.removed so they read after the person's name.

Watch out for

Removing a member is a confirm step in the list, but the block removes the row only on screen. With up to 8 members the count in the heading follows the rows that are left, and when it reaches zero the empty message takes over.

Usage

The block in a page, with its default content (long lists shortened). Every prop is in the table below.

section.tsx
import { Theme } from '@/components/klarhimmel/theme/style';
import { BlockSurface } from '@/components/klarhimmel/blocks/surface';
import AppTeamMembers from '@/components/klarhimmel/blocks/app-team-members';

export default function Section() {
  return (
    <Theme theme="brand">
      <BlockSurface>
        <AppTeamMembers props={{
          "eyebrow": "Team",
          "title": "People at Exempelhotellet",
          "description": "Everyone who can sign in. Owners can change roles.",
          "summaryWord": "members",
          "roles": [
            "Owner",
            "Admin",
            "Member"
          ],
          "roleLabel": "Role for",
          "members": [
            {
              "name": "Maja Lindqvist",
              "email": "maja@northlight.example",
              "sample": "Owner",
              "locked": true
            },
            {
              "name": "Nils Berg",
              "email": "nils@northlight.example",
              "sample": "Admin",
              "locked": false
            },
            {
              "name": "Ada Ek",
              "email": "ada@northlight.example",
              "sample": "Member",
              "locked": false
            }
          ],
          "remove": {
            "label": "Remove",
            "question": "Remove this person?",
            "yes": "Yes, remove",
            "no": "Keep"
          },
          "notices": {
            "roleNow": "is now",
            "removed": "removed"
          },
          "state": "filled",
          "messages": {
            "emptyTitle": "Only you here so far",
            "emptyBody": "Invite a colleague and they appear in this list."
          }
        }} />
      </BlockSurface>
    </Theme>
  );
}

Props

The type, default and text limit of every prop. On a real page, over-limit text is kept and checkSections flags it (Installation, Check a page before you ship); this live preview shows the default until it fits. The panel above edits all props.

PropTypeDefaultDescription
eyebrowstring'Team'Text, up to 60 characters. Optional; empty is not rendered.
titlestring'People at Exempelhotellet'Text, up to 120 characters.
descriptionstring'Everyone who can sign in. Owners ca…'Text, up to 300 characters. Optional; empty is not rendered.
summaryWordstring'members'Text, up to 20 characters.
rolesstring[]4 itemsList of 2 to 5.
roleLabelstring'Role for'Text, up to 30 characters.
members{ name, email, sample, locked }[]4 itemsList of 1 to 8. Each item: name, email, sample, locked.
remove{ label, question, yes, no }{…}Fields: label, question, yes, no.
notices{ roleNow, removed }{…}Fields: roleNow, removed.
state'filled' | 'empty''filled'One of 'filled', 'empty'.
messages{ emptyTitle, emptyBody }{…}Fields: emptyTitle, emptyBody.
headingLevel'1' | '2'-One of '1', '2'. Optional; empty is not rendered.