# Team Roster

Canonical: https://socra.design/landing/team-roster

The approved members of one Account, people and AI teammates side by side.

Show the team itself as proof: each member is the identity card, a portrait at the navigation size, the name and a Human or AI teammate chip, with an optional role. The page passes who is on the team; the roster owns the geometry and never counts anyone, so adding a member changes no sentence.

Status: ready. Shared team roster (landing.TeamRoster — identity cards from the Web UI Avatar at its lg size, emphasized Body name, quiet secondary sm Chip and optional secondary role; names alone take socra.com’s hero roster, two per row on phones, three from the 600px step and one row of six from 1180px, and names with roles take socra.com’s team chapter, two then three columns with 32px gaps; portraits lazy by default and eager with high priority in a hero) lives in @socra/ui-web-landing.

Library: @socra/ui-web-landing

## Anatomy

- Intent, states, feedback, hierarchy, and access requirements are independent of any renderer.
- Every shared visual relationship resolves from semantic design roles without changing their meaning between platforms.
- Each platform uses its native mechanics and provides live conformance evidence without changing the guideline.

## States

- **Rest:** Shows the stable, enabled component without implied activity.
- **Hover:** Every enabled clickable target shows a pointing hand. Keep the shared visual hover treatment independent, without moving content or changing meaning.
- **Focus:** Shows the shared visible focus treatment without depending on hover.
- **Pressed:** Acknowledges active input immediately and returns cleanly on release.
- **Selected:** Uses a neutral surface plus native selected semantics when selection applies.
- **Disabled:** Remains legible, unavailable, and absent from misleading interaction feedback.
- **Loading:** Preserves context while honestly identifying work that has not completed.
- **Error:** Places the failure and recovery path beside the action or content that failed.

## Motion

Stable state changes respond immediately. Appearance, expansion, and morphing begin at the triggering origin and use an interruptible spring that can retarget from its live position and velocity. When spatial motion is not meaningful, the component changes without decorative travel.

## Usage

- Use the owning platform implementation first. Each renderer improves once and every product on that platform inherits the fix.
- Name the user job before the component. A component earns a place only when it helps the user decide, act, recover, or understand.
- Promote missing behavior into the owning platform domain. Product-local UI creates drift; platform contracts preserve shared meaning and native quality.
- Raise luminance to bring a surface forward. Depth reads as light. A surface that comes toward the reader catches more of it, so a raised panel, a selected tab, a menu, a popover resolves LIGHTER than what it sits on — in dark mode away from the canvas, in light mode toward the white it cannot exceed. The ladder runs page, resting card, elevated, and never doubles back.

## Avoid

- Do not build a product-local replacement. A local implementation splits behavior, access requirements, and future migrations.
- Do not copy theme values. Semantic values are shared system roles and are consumed by meaning.
- Do not add decoration as a substitute for structure. If the hierarchy is weak, fix the hierarchy.
- Do not darken a surface to lift it. Darkening in a dark theme pushes a surface INTO the page — it reads as a hole cut in the canvas rather than a panel resting on it. Sinking a surface to make it stand out is the most common way a dark interface loses its depth.

## Tokens

- `designTokens.color[mode].color.brand.primary`: Primary action role.
- `designTokens.color[mode].color.surface.secondary`: Working surface role.
- `designTokens.color[mode].color.content.primary`: Primary content role.
- `designTokens.spacing.role.contentGap`: Spacing relationship.
- `designTokens.shape.role.control`: Shape relationship.

## Properties

[Complete public API, including inherited properties and event signatures](https://socra.design/landing/team-roster/api.md)

### Setup

- Use the host-selected compatible Socra theme provider and published component styles. Keep one compatible provider instance across shared packages.

### TeamRoster

Import: `import { TeamRoster } from '@socra/ui-web-landing';`

The approved members of one Account as identity cards: portrait, name, Human or AI teammate chip and optional role, in the geometry socra.com’s hero roster renders.

| Property | Type or permitted values | Default / omission | Required | Meaning and behavior | Inspector |
| --- | --- | --- | --- | --- | --- |
| `label` | `string` | Required input | yes | The accessible name of the list of members. Required: provide a meaningful nonempty name such as the people and AI teammates in an Account. Applied to the ul as aria-label. The catalog’s fallback belongs to its specimen adapter. | Direct API |
| `members` | `readonly TeamRosterMember[]` | Required input | yes | The approved members in the order the page shows them. Each TeamRosterMember has a stable id, a name, a kind of human or ai-teammate, an optional portrait URL and an optional role line. Use unique stable string ids. Pass right-sized portrait copies the page serves itself; a member without a portrait shows the initial of its name. Every member renders as one list item: the shared Avatar at its lg size (40 CSS pixels at the default text size), the name on emphasized Body, a secondary sm Chip reading Human or AI teammate and, when given, the role on secondary Body with its separators glued so no line ends on a lone dot. The geometry follows the content: names alone take socra.com’s hero roster (two per row on phones, three from 600px, six from 1180px, 24 by 16px gaps); once any member carries a role the roster takes socra.com’s team chapter (two per row on phones, three from 600px, 32px gaps, 720px wide from 600px and 1024px from 1180px, 16px between portrait and words). The roster never renders a count. | Direct API |
| `portraitLoading` | `"eager"`, `"lazy"` | `"lazy"` | no | How the portraits load. Pass eager only for a roster on the first screen. lazy sets loading="lazy" on each portrait; eager sets fetchPriority="high" so the photos lead the page, as socra.com’s hero does. | Portrait loading |

### Band

Import: `import { Band } from '@socra/ui-web-landing';`

| Property | Type or permitted values | Default / omission | Required | Meaning and behavior | Inspector |
| --- | --- | --- | --- | --- | --- |
| `ariaLabelledby` | `string \| undefined` | Omitted; no declared default | no | id of the band's heading — keeps the <section> named for screen readers. | Direct API |
| `bleed` | `false`, `true` | `false` | no | Band owns its full width (carousels): children render edge-to-edge with no content column. The child then re-creates the column where it needs one. | Direct API |
| `children` | `ReactNode` | Required input | yes |  | Direct API |
| `colorMode` | `"dark"`, `"ambient"` | `"ambient"` | no | whether the band is fixed dark or follows the ambient page theme. | Direct API |
| `contentMaxWidth` | `string \| undefined` | `"1120px"` | no | content column width; the production landing column. | Direct API |
| `divider` | `false`, `true` | `false` | no | The divider at a standalone band's leading edge. Inside a marketing page the band sequence puts the divider on every boundary, so pages do not set it. | Direct API |
| `id` | `string \| undefined` | Omitted; no declared default | no | anchor id for in-page navigation. | Direct API |
| `spacing` | `"none"`, `"compact"`, `"screen"`, `"regular"` | `"regular"` | no | the band's vertical rhythm. | Direct API |
| `tone` | `"base"`, `"surface"`, `"raised"` | `"base"` | no | A standalone band's surface step. Inside a marketing page the band sequence alternates base and surface by position, so pages do not set it. | Direct API |

### BandIntro

Import: `import { BandIntro } from '@socra/ui-web-landing';`

| Property | Type or permitted values | Default / omission | Required | Meaning and behavior | Inspector |
| --- | --- | --- | --- | --- | --- |
| `align` | `"center"`, `"responsive"` | `"responsive"` | no | whether the column centers at every width or only from \`md\`. | Direct API |
| `children` | `ReactNode` | Omitted; no declared default | no | extra intro content below the description (a second paragraph, a CTA). | Direct API |
| `description` | `string \| undefined` | Omitted; no declared default | no | optional standfirst under the heading (\`subhead\` role). | Direct API |
| `headingScale` | `"section"`, `"statement"` | `'section'` | no | the heading scale. | Direct API |
| `headingTreatment` | `"solid"`, `"gradient"` | `'gradient'` | no | the heading paint treatment. | Direct API |
| `id` | `string \| undefined` | Omitted; no declared default | no | heading id — the target of the owning band's \`ariaLabelledby\`. | Direct API |
| `level` | `"h1"`, `"h2"` | `'h2'` | no | the heading level; the page's first band passes 'h1'. | Direct API |
| `title` | `ReactNode` | Required input | yes | the band heading, rendered through <BandHeader/>. | Direct API |

### Behavior and constraints

- TeamRosterMember, TeamRosterProps and TeammateKind are public type exports. The kind drives the chip’s words; the roster renders no other kind-specific treatment, so people and AI teammates read as members of one team.
- Every fixture member shows the initial of its name, as a member without a photo does in the product: a real portrait appears only under its own name, so fictional identities take none. Product hosts pass their own approved roster and portraits.
- The roster states no number of members anywhere in its markup or accessible name, so a growing team changes no sentence.

### Inspector mappings

| Setting | Initial preview value | Consumed code |
| --- | --- | --- |
| Accessible name | `People and AI teammates in the Account` | composition: Passes String(configuration.label ?? 'People and AI teammates in the Account').trim() \|\| that default to TeamRoster.label, so a blank inspector value retains a named preview list. The public label itself remains required without a default. |
| Members | `6` | composition: Converts the selected string count to a number and slices the six neutral fixture members to construct TeamRoster.members. It does not supply a count property; every retained member keeps its stable id, kind and role. |
| Roles | `true` | composition: Keeps each fixture member’s role when on and drops it when off, so the specimen shows both geometries the roster derives from its content. It is not a public property: the roster reads the roles on its members. |
| Portrait loading | `lazy` | TeamRoster.portraitLoading |
