Organism · beta · v1.0.0 · owner: Site system lead · contract 19 of 20 points (open: figma)
Do and don't
Do: A system owner’s captured home
Rendered by roles-picker.tsOpen example: What changes for each role · Do: A system owner’s captured home Use the full-page example to follow page links.
Do: Pick a role
Rendered by roles-picker.tsOpen example: What changes for each role · Do: Pick a role Use the full-page example to follow page links.
Do: Sample records
Rendered by roles-picker.tsOpen example: What changes for each role · Do: Sample records Use the full-page example to follow page links.
Don't: Real client data
Rendered by roles-picker.tsOpen example: What changes for each role · Don't: Real client data Use the full-page example to follow page links.
Don't: Uncaptioned
Rendered by roles-picker.tsOpen example: What changes for each role · Don't: Uncaptioned Use the full-page example to follow page links.
Rules
| Rule | Why | Check |
|---|---|---|
| What changes for each role is computed by Loom's code from the product tour's role homes on Harbor UI's synthetic records (scripts/site/roles), not drawn by hand. | A showcase is proof only if the product's own code produced what it shows. | Automated: scripts/site/interactives/check.ts (recomputes the showcase data and fails on drift) |
| Data comes from the synthetic Harbor UI records. | No client data on the site. | Automated: tests/unit/site-role-screen.test.ts (source parity and supported role IDs) |
API
| Prop | Type | Required | Default | What it does |
|---|---|---|---|---|
variant | picker | screen | No | "picker" | Picker: six role panels with optional native tabs. Screen: one intrinsic static role-home figure with tour links; no tabs or enhancement. |
role | system-owner | engineering-lead | product-manager | executive-sponsor | product-designer | frontend-developer | accessibility-owner | content-owner | ops-lead | No | Required only for screen. One of the actual captured role IDs exported by scripts/site/roles. | |
lang | en | No | "en" | Source English only; neither variant has translated UI or captions. |
Contract details
The full contract: identity, anatomy, variants, states, tokens, layout, motion, accessibility, content, tests
Identity
| Aspect | Value |
|---|---|
| Id | roles-picker |
| Layer | Organisms |
| Category | showcase |
| Owner | Site system lead |
| Status | beta |
| Version | 1.0.0 |
| Source | apps/showcase/site/system/organisms/roles-picker.ts (private repository; the contract and the render function are this one module) |
| Version | Date | Change |
|---|---|---|
| 1.0.0 | 2026-10-26 | What changes for each role: computed by Loom's code from the product tour's role homes on Harbor UI's synthetic records (scripts/site/roles). |
Use when
- The roles hub (picker) or a single role page (screen). Why: Pick a role in the hub; see the captured role home beside its explanation.
Do not use when
- For an Ops overview screen. Why: ProductScreen owns the separate Ops screen IDs; this family uses the existing role-home screenshots.
Anatomy
| Part | What it is |
|---|---|
| tabs | div.roles-change-tabs: one tab per role |
| panel | div.roles-change-panel: who, from, steps, measure |
| shot | figure.roles-shot: the role home in the tour |
Used in: pages directly.
Variants
| Variant | When | Why |
|---|---|---|
| picker | The roles hub. | Readers choose among the six role views. |
| screen | An individual role page. | The existing captured role home illustrates that role with explicit sample provenance. |
States
| State | Rendered or not applicable |
|---|---|
| default | Picker: all six source-English role panels are present and readable without its page script; roles.client.js enhances them to one selected tab panel. Screen: the selected captured image, descriptive alt, sample-data caption and native tour links; no script or tab panels. |
| hover | Picker tabs and the native tour links in both variants use their own hover affordances; the static image has no interactive state of its own. |
| focus-visible | Picker: selected tab, tab panels and links participate in keyboard reading order. Screen: only its native tour links are focusable; the image has descriptive alt. |
| active | Picker only: selecting a role updates aria-selected, tabIndex and hidden panels. Screen has no role-selection or playback state; native links retain their normal activation. |
| disabled | Not applicable: No disabled role state; all authored role tabs remain available. |
| loading | Not applicable: All role records/panels are rendered at build time. The optional page script changes tab behavior; no pending-data UI or host ready state is promised. |
| empty | Not applicable: The renderer uses the fixed synthetic role records; there is no caller empty-data variant. |
| error | Not applicable: No client fetch or error/retry view in this picker. A page-script failure leaves the pre-rendered panels; it is not managed host recovery. |
| current | Picker only: aria-selected identifies the selected role tab and its associated panel. Screen has no selected-tab/current-view state. |
| visited | Not applicable: No visited state on this root; any contained native links retain their own browser history. |
| expanded | Not applicable: No disclosure state on this root; replacing a view is not aria-expanded. |
Tokens
| Part | Token |
|---|---|
| tab.current | --ink underline |
| panel.background | --surface |
| --roles-picker-margin-space-9 | var(--space-9) |
| --roles-picker-border-line-strong | var(--line-strong) |
| --roles-picker-background-surface | var(--surface) |
| --roles-picker-border-bottom-line-strong | var(--line-strong) |
| --roles-picker-gap-space-1 | var(--space-1) |
| --roles-picker-padding-space-5 | var(--space-5) |
| --roles-picker-border-right-line | var(--line) |
| --roles-picker-color-ink | var(--ink) |
| --roles-picker-transition-dur-moderate | var(--dur-moderate) |
| --roles-picker-transition-ease-standard | var(--ease-standard) |
| --roles-picker-background-surface-2 | var(--surface-2) |
| --roles-picker-background-ink | var(--ink) |
| --roles-picker-color-cotton | var(--cotton) |
| --roles-picker-box-shadow-saffron | var(--saffron) |
| --roles-picker-outline-focus | var(--focus) |
| --roles-picker-font-type-small-strong | var(--type-small-strong) |
| --roles-picker-font-type-strong | var(--type-strong) |
| --roles-picker-padding-space-8 | var(--space-8) |
| --roles-picker-padding-gutter | var(--gutter) |
| --roles-picker-gap-space-5 | var(--space-5) |
| --roles-picker-border-top-line | var(--line) |
| --roles-picker-font-type-h3 | var(--type-h3) |
| --roles-picker-font-type-small | var(--type-small) |
| --roles-picker-color-ink-2 | var(--ink-2) |
| --roles-picker-gap-space-4 | var(--space-4) |
| --roles-picker-border-top-rule-hair | var(--rule-hair) |
| --roles-picker-border-top-line-strong | var(--line-strong) |
| --roles-picker-padding-top-space-3 | var(--space-3) |
| --roles-picker-padding-top-space-5 | var(--space-5) |
| --roles-picker-border-bottom-line | var(--line) |
Layout
| Aspect | Value |
|---|---|
| breakpoints | Required review at 390, 768, 1024 and 1440 px; execution is not implied by this declaration; columns stack under 768 px and controls fill the row on phones. |
| wrapping | Picker text wraps in its panels. Screen keeps the source image aspect ratio of 1280 by 1600 and scales to the prose column. |
| textExpansion | Source English only; no translated variant is claimed. |
Spacing and rhythm
| Aspect | Value |
|---|---|
| internal | Inside the Showcase frame's padding, controls and caption at its foot. |
| external | Full width of its section. |
| rhythm | One showcase per section, after the section's claim (D-SITE-008). |
Motion
| Aspect | Value |
|---|---|
| None | Role tabs change panels on reader selection; no timed playback or autoplay. |
Runtime and reuse
Render-only element; mount any registered child enhancements separately. Preserve required Site CSS, fonts, responsive and locale rules from nativeElement.
Accessibility passport
| Aspect | Value |
|---|---|
| role | Picker: role tablist with labelled tabpanels. Screen: figure, image with descriptive alt, caption and native links into the product tour. |
| name | Its heading or caption |
| keyboard | Picker: Tab reaches the selected tab; Arrow keys cycle roles; Home/End jump first/last. Screen: Tab and Enter operate its two native tour links; no arrow-key handler. |
| focus | Picker uses roving tabIndex and focusable panels. Screen leaves focus to its two native links; no managed focus. |
| screenReader | Picker exposes selected tab and associated panel; all panels remain readable without scripting. Screen exposes descriptive image alt and its sample-data caption. |
| contrast | Ink on surface 14:1; state dyes only as marks with words. |
| target | Controls are 44 px. |
Content
| Slot | Length | Voice |
|---|---|---|
| step | 1 sentence | What Loom asks or gives |
Good: “Two decisions wait on you.”
Bad: “Empower your team”
Japanese: English only (sample records are English).
Icons and illustrations
None: this element uses no icon or illustration of its own.
Decisions and exceptions
Usage
53 uses across the built pages (measured by npm run site:system).
Tests
| Aspect | Value |
|---|---|
| unit | tests/unit/site-system.test.ts: renders every do and don't example through the registry and checks the classes it owns |
| visual | Not yet: there are no screenshot baselines; the Library page is checked for sideways scroll at 1440 px by tests/site/system.spec.ts |
| a11y | tests/site/system.spec.ts: axe (0 serious or critical) on the element's Library page in light and dark |
| parity | scripts/site/system/check.ts: Library examples are rendered by the same render function the site imports |
Figma
not-yet: The Loom Site Figma library is the last stage of the plan; this element is not mapped yet.