<!-- https://getkonjo.com/design/reference/components · source: .claude/skills/design/references/components.md -->

<!-- GENERATED by scripts/design/sync-components.mjs — do not edit by hand. -->
<!-- Add a component to src/components/ui/index.ts, then: npm run design:sync -->

# Shared UI components

35 components are exported from
[`src/components/ui/index.ts`](../../../../src/components/ui/index.ts).
**Check this list before you build anything.** If something close already exists, extend
it — a second implementation of a row, a sheet or an empty state is the most common way
this codebase drifts. Import from the barrel:

```ts
import { Button, Card, Stack, Text } from '../../components/ui';
```

| Component | What it is |
|---|---|
| `ActionMenu` | a sheet of actions behind a ⋯, with `ActionRow` as its row |
| `ActionRow` | a sheet of actions behind a ⋯, with `ActionRow` as its row |
| `Avatar` | circular user image with initials fallback |
| `Badge` | the generic status pill |
| `BeltDot` | colored circle representing a belt rank |
| `BeltStateBadge` | small pill that captions a non-verified BeltDisplay state |
| `BottomSheet` | thin wrapper around @gorhom/bottom-sheet's BottomSheetModal |
| `Button` | the app's button |
| `Card` | a white block sitting on the tinted page |
| `Checkbox` | a decision about a thing that is already there |
| `ChildHeader` | the shared child-screen header: back chevron, no title |
| `EmptyState` | shared empty-state primitive for list screens |
| `FeedRow` | one row in the Social feed |
| `Field` | label, control, helper, error |
| `Hero` | the full-bleed loud block that replaces the retired DiagonalBand |
| `InlineComposer` | the "Share something…" tap target sitting above the Social feed |
| `Input` | the typed field |
| `ListFadeIn` | one-shot first-load fade for the tab-home lists |
| `MediaThumbnail` | 12px-radius image with optional play overlay |
| `PrimaryCTACard` | the full-width red CTA used for "Log a session" on the Train tab home and "Save session" at the bottom of post-class capture |
| `RankPicker` | choose a rank from the dojo's own ladder |
| `RankPickerSheet` | The dojo's own rank ladder, in a modal sheet |
| `Row` | the generic list row: leading slot, title, optional subtitle, trailing slot, chevron |
| `SearchField` | the search input twenty files were each building alone |
| `SectionLabel` | a section header in the `eyebrow` register |
| `SegmentedControl` | generic, theme-aware segmented toggle |
| `SelectTrigger` | the control that opens a sheet |
| `SessionRow` | one history row on the Train tab home / history list |
| `Skeleton` | the shape of the thing that is coming |
| `Stack` | vertical or horizontal flow with exactly one spacing decision |
| `StatPill` | small rounded pill showing a value + label |
| `TagChip` | multi-purpose pill |
| `Text` | the typography primitive |
| `ToastProvider` | <ToastProvider> + useToast() — minimal imperative toast primitive |
| `ValidationBanner` | inline banner replacing single-button Alert.alert() validation/error popups |

## Ships, but read this first

| Component | The catch |
|---|---|
| `SectionLabel` | renders `typography.eyebrow` — it is the eyebrow register, not a second one. Pass sentence-case children; the style uppercases them. |

## Retired — exported, but never in new work

| Component | Why |
|---|---|
| `BeltRankPicker` | hardcodes the nineteen Cuong Nhu ranks from a constant, which `foundations/rank.md` forbids — a screen renders whatever ladder the dojo has. Use `RankPicker` inline, or `RankPickerSheet` for a modal. Five consumers remain; migrating one also means deciding what `users.belt_rank` holds for a dojo that is not Cuong Nhu (product-facts.md, "Not yet decided") |
