---
id: glyco-components
title: Components — Primitives, Forms, Feedback, Charts
chapter: 03
source-of-truth: src/components/ui/ (primitives), src/design-system/ (forms & chart wrappers)
component-stack: "@base-ui/react 1.x + class-variance-authority (CVA) + Tailwind v4 tokens"
conventions: { naming: kebab-case files / PascalCase exports, client: "'use client' on interactive components", exports: named }
---

# 03 — Components

Primitives live in **`src/components/ui/`** and are built on **@base-ui/react** (unstyled,
accessible) + **CVA** variants that serialize to `data-*` attributes
(`data-variant`, `data-size`, `data-state`). They consume tokens exclusively — see
[02-foundations.md](02-foundations.md). Form/chart wrappers live in `src/design-system/`.

## 1. Primitives inventory

| Component | File | Variants | Sizes | Notes |
|-----------|------|----------|-------|-------|
| **Button** | `ui/button.tsx` | `default, primary, secondary, outline, ghost, destructive, link, glass` | `xs, sm, default(md), lg` + `icon-*` mirrors | `loading`/`loadingText` (spinner + aria-live), `iconLeft/iconRight`; disabled = `aria-disabled` + `pointer-events-none` (stays focusable), 60% opacity; pill shape global; <44px sizes get `::after` hit-area extender |
| **Card** | `ui/card.tsx` | `solid, glass-regular, glass-clear, outline` | `default, sm` | Polymorphic: becomes `<button>` when `onClick` present; `interactive`, `hoverWash: none\|accent`; slots `CardHeader/Title/Description/Action/Content/Footer`; namespaced states `Card.Skeleton`, `Card.Empty`, `Card.Error`; concentric 4px radius gutter |
| **Badge** | `ui/badge.tsx` | `default, secondary, destructive, outline, ghost, link` | — | inline icon support |
| **Input** | `ui/input.tsx` | — | h-8 default | focus ring 3px via `--focus-ring`; `aria-invalid` drives error border |
| **Select** | `ui/select.tsx` | — | `sm, default` | full @base-ui composition (Trigger/Content/Item/ScrollButtons) |
| **Tabs** | `ui/tabs.tsx` | `default` (muted pill), `line` (underline) | — | horizontal/vertical |
| **Checkbox / Label** | `ui/checkbox.tsx`, `ui/label.tsx` | — | — | @base-ui |
| **Dialog** | `ui/dialog.tsx` | — | — | modal, overlay, focus trap, Esc closes |
| **Sheet** | `ui/sheet.tsx` | side drawer | — | used by ActionDrawer, mobile nav |
| **Popover** | `ui/popover.tsx` | — | — | @floating-ui positioning |
| **Toast / Toaster** | `ui/toast.tsx`, `ui/toaster.tsx` | `success, info, warning, error` | — | semantic icons; `aria-live=polite` (success/info) / `assertive` (warning/error); portal mounts after hydration |
| **Calendar** | `ui/calendar.tsx` | — | — | react-day-picker, localized |
| **Table** | `ui/table.tsx` | — | — | standard slots |
| **Skeleton list** | `ui/skeleton-list.tsx` | `rows, cards, kpi` | — | `animate-pulse` |
| **Empty state** | `ui/empty-state.tsx` | — | — | `icon, title, description, action` |
| **Glass surface** | `ui/glass-surface.tsx` | `card, brief, composer` | — | the strong glass recipe; `composer` adds identity ring + outer glow; `disableBlur` for tests/reduced-transparency |
| **Liquid glass card** | `ui/liquid-glass-card.tsx` | **DEPRECATED** | — | use `<Card variant="glass-regular\|glass-clear">` |
| **Actions menu** | `ui/actions-menu.tsx` | — | — | keyboard-navigable floating menu |
| **Confirmation dialog** | `ui/confirmation-dialog.tsx` | risk levels | — | CustomEvent request/resolve; writes LGPD audit log |
| **Avatar / Separator / Scroll area / Field group** | respective files | — | — | standard |

## 2. Forms

Stack: **react-hook-form 7 + Zod 4 + @hookform/resolvers** wrapped by
`src/design-system/forms/`:

- **`Form`** (`forms/form.tsx`) — takes `schema: ZodType`, `onSubmit`, `onInvalid`; wraps
  `FormProvider`; `noValidate`; exposes `aria-busy` while submitting.
- **`Field`** (`forms/field.tsx`) — `name, label, helper, errorOverride`; wires
  `useController`, chains `aria-describedby` (helper + error), renders required indicator,
  shows the error below the input.

Pattern:

```tsx
<Form schema={schema} onSubmit={save}>
  <Field name="email" label="Email" helper="Usado para login">
    <Input type="email" autoComplete="email" />
  </Field>
  <Button type="submit" variant="primary" loading={saving}>Salvar</Button>
</Form>
```

Validation copy is pt-BR, actionable, never blames the user. Errors set `aria-invalid` on
the input (border/ring switches to danger tokens).

## 3. Feedback

| Pattern | Implementation |
|---------|----------------|
| Async result | `Toast` via `useToast()` — success/info polite, warning/error assertive |
| Loading | `Skeleton` variants (`rows/cards/kpi`) for content; `Button loading` for actions; never blank screens |
| Empty | `EmptyState` with pt-BR imperative description + CTA ("Cadastre seu primeiro paciente…") |
| Data load failure | graceful `Card.Error` / `DataLoadError` pattern — page renders, failed card explains and offers retry |
| Irreversible actions | `ConfirmationDialog` (risk-leveled) or copilot `ConfirmBar`; undo via `UndoToast` where reversible |

## 4. Charts

**Recharts 3** themed by CSS (no per-chart color config):

- Base wrappers: `src/design-system/charts/` — `line-chart.tsx`, `bar-chart.tsx`,
  `area-chart.tsx`, `reference-range.tsx` (clinical reference zone overlay).
- Domain charts: `src/components/bioimpedance/` — `donut-rings-chart`, `semi-gauge-chart`,
  `inline-line-chart`, `inline-bar-chart`; `src/components/home/composicao-chart.tsx`
  (axis-less sparkline with gradient fill).
- Colors: `var(--chart-1…5)`; grids `--border-subtle`; labels `--muted-foreground` 11px
  (global Recharts override). Series colors must come from the chart tokens.
- Clinical values get reference-range shading and the `ok/attention/risk` semantics with
  a text label — never color alone.

## 5. KPI display

`src/components/home/kpi-tile.tsx` — the canonical metric component:

- Variants `default, ok, attention, risk` (glow on attention/risk).
- Animated digits via `@number-flow/react`; `tabular-nums`.
- `role="status"`, `aria-label` includes the state in words; an indicator glyph
  accompanies color.

## 6. Component conventions

1. **Files:** kebab-case (`kpi-tile.tsx`); exports: named PascalCase. Tests colocated as
   `*.test.tsx`.
2. **`'use client'`** on every interactive component; pages/layouts stay server components
   by default.
3. **Variants via CVA**, surfaced as `data-variant` / `data-size` / `data-state` for CSS
   targeting. New variants extend CVA — never fork a component.
4. **Tokens only.** A hex value in a component is a bug unless it is one of the documented
   gaps ([02 §7](02-foundations.md)).
5. **Placement** follows feature-sliced rules ([05-architecture.md](05-architecture.md)):
   reusable primitives → `src/components/ui/` (or `src/design-system/` for the new layer);
   feature components → that feature's folder (`src/components/home/`, `…/copilot/`, etc.).
6. **Accessibility is part of the component contract:** focus-visible ring, 44px hit area
   (use the `::after` extender for compact controls), `aria-disabled` over `disabled`,
   `aria-live` for async feedback, color + text for any semantic state.
7. **Hydration:** portal-based components (Toaster, overlays) mount after
   `useEffect`-driven `mounted` flag. Theme-aware SVGs use `var(--foreground)`, never
   JS theme reads.
8. **Animation:** Motion (`motion/react`) for component transitions, GSAP utilities for
   orchestration; always reduced-motion-gated.

## 7. Feature component map (where to look for prior art)

| Domain | Folder | Highlights |
|--------|--------|-----------|
| Home dashboard | `src/components/home/` | `composer` (AI orb + slash menu), `greeting-header`, `glyco-brief`, `kpi-tile`, `bento`, `linha-do-dia`, `skills-grid`, `patients-card`, `composicao-chart`, `empty-invite` |
| Copilot | `src/components/copilot/` | `copilot-thread` (SSE stream + aria-live), `action-drawer`, `quick-action`, `confirm-bar`, `undo-toast`, `receita-action`, `guided-flow`, `copilot-authorization` |
| Bioimpedance | `src/components/bioimpedance/` | `body-segments` (anatomy image), `metric-cards`, `hero-kpi-card`, `clinical-insights`, `clinical-timeline`, `journey-strip`, upload/camera buttons |
| Canvas | `src/components/canvas/` | board, draggable cards, connections (SVG bezier), toolbar, minimap, lasso, shortcuts modal |
| Layout | `src/components/layout/` | `sidebar` (220/64px), `header`, `mobile-header`, `mobile-drawer`, `dashboard-layout`, `quick-actions-modal` (⌘K, cmdk), `consulta-mode-bar`, `glyco-icon`, `logo` |
| LGPD | `src/components/lgpd/` | consent banner/checkbox, cookie banner, policy shell, masked field |
| Settings | `src/components/configuracoes/` | tabbed settings, AI provider cards/grid, 2FA card, WhatsApp templates |
| Clinical | `src/components/receitas/`, `…/protocolos/`, `…/ai/` | prescriptions (Memed, drug interactions), protocol cards/forms, AI reply/plan reviewer |
