feat: design system v1.0.0 — reconcile tokens from cross-repo audit
Extracted real production usage from 8 KDC repos and consolidated the consensus (kdcsurveyadd / kdcvault / kdc_void_planner) into the canonical design.md + tokens. Highlights: - brand.ink #000000 -> #1A1530 (purple-tinted, matches real usage) - brand.canvas #F5F4F0 -> #F7F4ED (warm) - neutral ramp rebuilt around ink (purple-tinted, not cold slate) - semantic palette retuned for warm canvas + soft tints - elevation shadows retinted with brand-ink rgb - new accent.gold (product) and accent.mint (marketing) tokens - real themes/light, themes/dark, themes/high-contrast - fleshed out every component + foundation README - docs/consolidation-2026-05.md captures the full audit + drift inventory Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+28
-12
@@ -1,25 +1,41 @@
|
||||
# Badges
|
||||
|
||||
Specification for the **badges** primitive.
|
||||
Small inline labels for status, counts, and metadata. Pill-shaped by default. Reference: `kdcvault/frontend/src/components/ui/badge.tsx`.
|
||||
|
||||
## Anatomy
|
||||
TODO — drop `anatomy.svg` next to this file.
|
||||
|
||||
See `anatomy.svg`. Optional leading dot or icon · label · `rounded.full` shape · 22px default height.
|
||||
|
||||
## Variants
|
||||
- default
|
||||
- (list per-component variants)
|
||||
|
||||
## States
|
||||
default · hover · focus-visible · active/pressed · disabled · loading
|
||||
| Token | Use | Background | Text |
|
||||
|-------|-----|------------|------|
|
||||
| `badge.default` | Neutral tag | `brand.primarySoft` | `brand.primaryHover` |
|
||||
| `badge.success` | Positive status | `semantic.successSoft` | `semantic.success` |
|
||||
| `badge.warning` | Caution / attention | `semantic.warningSoft` | `semantic.warning` |
|
||||
| `badge.danger` | Failure / blocked | `semantic.dangerSoft` | `semantic.danger` |
|
||||
| `badge.outline` | Inline tag w/o fill | transparent | `text.muted` + `border.default` |
|
||||
| `badge.solid` | High-emphasis count | `brand.primary` | `text.inverse` |
|
||||
|
||||
## Sizes
|
||||
|
||||
| Size | Height | Padding | Typography |
|
||||
|------|--------|---------|------------|
|
||||
| `sm` | 18px | `0 6px` | `caption` |
|
||||
| `md` (default) | 22px | `2px 10px` | `caption` |
|
||||
| `lg` | 26px | `4px 12px` | `bodyS` |
|
||||
|
||||
## Tokens used
|
||||
See `design.md` → `components.badges.*`.
|
||||
|
||||
`components.badge.*`, `colors.semantic.*Soft`.
|
||||
|
||||
## Accessibility
|
||||
- All interactive instances are keyboard-reachable.
|
||||
- Focus state has ≥ 3:1 contrast against its background.
|
||||
- Pair color with an icon or text to convey meaning.
|
||||
|
||||
- Decorative badges don't need ARIA, but status badges should set `role="status"` and include a text label (not colour alone).
|
||||
- Counts greater than 99 should render as `99+`.
|
||||
|
||||
## Do / Don't
|
||||
- ✅ Use the canonical token references.
|
||||
- ❌ Don't hardcode colors, sizes, or radii.
|
||||
|
||||
- ✅ Pair colour with text or an icon to convey status.
|
||||
- ❌ Don't stack more than two badges in a row — fold them into a single chip group instead.
|
||||
- ❌ Don't use `badge.solid` for ambient counts; reserve it for active selections.
|
||||
|
||||
@@ -1,25 +1,58 @@
|
||||
# Buttons
|
||||
|
||||
Specification for the **buttons** primitive.
|
||||
Specification for the **buttons** primitive, reconciled from production usage in kdcsurveyadd, kdcvault, and kdc_void_planner (CVA-driven shadcn variants).
|
||||
|
||||
## Anatomy
|
||||
TODO — drop `anatomy.svg` next to this file.
|
||||
|
||||
See `anatomy.svg` — label · optional leading/trailing icon · 8px gap · 40px height (default size).
|
||||
|
||||
## Variants
|
||||
- default
|
||||
- (list per-component variants)
|
||||
|
||||
| Token | Use | Background | Text |
|
||||
|-------|-----|------------|------|
|
||||
| `button.primary` | Default CTA (one per surface) | `brand.primary` | `text.inverse` |
|
||||
| `button.accent` | Emphasis / submit / commit action | `accent.gold` | `text.inverse` |
|
||||
| `button.secondary` | Neutral default action | `surface.subtle` | `text.default` |
|
||||
| `button.outline` | Lower-emphasis bordered | transparent | `text.default` + `border.strong` |
|
||||
| `button.ghost` | Tertiary, inline within text | transparent | `brand.primary` |
|
||||
| `button.destructive` | Irreversible negative action | `semantic.danger` | `text.inverse` |
|
||||
| `button.link` | Visually a link, behaves as a button | transparent | `brand.primary` (underlined on hover) |
|
||||
|
||||
Reference implementation: `kdcvault/frontend/src/components/ui/button.tsx` (class-variance-authority).
|
||||
|
||||
## Sizes
|
||||
|
||||
| Size | Height | Padding | Typography |
|
||||
|------|--------|---------|------------|
|
||||
| `xs` | 28px | `4px 10px` | `caption` |
|
||||
| `sm` | 32px | `6px 12px` | `bodyS` |
|
||||
| `md` (default) | 40px | `10px 16px` | `bodyM` |
|
||||
| `lg` | 48px | `12px 20px` | `bodyM` |
|
||||
| `icon` | 40×40px | `0` | — |
|
||||
|
||||
## States
|
||||
default · hover · focus-visible · active/pressed · disabled · loading
|
||||
|
||||
`default · hover · focus-visible · active/pressed · disabled · loading`
|
||||
|
||||
- **Hover** swaps `primary` → `primaryHover` (`#5C2D90`), darkens accent by ~12%.
|
||||
- **Focus-visible** applies `elevation.focusRing` (`0 0 0 3px rgba(107,53,167,0.18)`).
|
||||
- **Disabled** sets `opacity: 0.5` and `pointer-events: none`. Keep colour — don't desaturate.
|
||||
- **Loading** swaps the label for a 14px spinner; preserve width to avoid layout shift.
|
||||
|
||||
## Tokens used
|
||||
See `design.md` → `components.buttons.*`.
|
||||
|
||||
See `design.md` → `components.button.*` and `elevation.focusRing`.
|
||||
|
||||
## Accessibility
|
||||
- All interactive instances are keyboard-reachable.
|
||||
- Focus state has ≥ 3:1 contrast against its background.
|
||||
- Pair color with an icon or text to convey meaning.
|
||||
|
||||
- All buttons are keyboard-reachable and have a visible focus ring.
|
||||
- Icon-only buttons must carry `aria-label`.
|
||||
- Focus and hover states meet ≥ 3:1 contrast against the button surface.
|
||||
- Touch target ≥ 44×44px on mobile (`md` size satisfies this).
|
||||
|
||||
## Do / Don't
|
||||
- ✅ Use the canonical token references.
|
||||
- ❌ Don't hardcode colors, sizes, or radii.
|
||||
|
||||
- ✅ One `primary` *or* one `accent` per surface, never both side-by-side.
|
||||
- ✅ Use `accent.gold` for the *forward-moving* action in a multi-step flow.
|
||||
- ❌ Don't hardcode `#6B35A7` — use `colors.brand.primary`.
|
||||
- ❌ Don't put two CTAs of equal emphasis next to each other.
|
||||
|
||||
+23
-11
@@ -1,25 +1,37 @@
|
||||
# Cards
|
||||
|
||||
Specification for the **cards** primitive.
|
||||
A surface for grouping related content. Two canonical variants: `default` (flat, hairline border) and `elevated` (lifted shadow).
|
||||
|
||||
## Anatomy
|
||||
TODO — drop `anatomy.svg` next to this file.
|
||||
|
||||
See `anatomy.svg`. Standard parts: `CardHeader` · `CardTitle` · `CardDescription` · `CardAction` (top-right slot) · `CardContent` · `CardFooter`. Reference: `kdcvault/frontend/src/components/ui/card.tsx`.
|
||||
|
||||
## Variants
|
||||
- default
|
||||
- (list per-component variants)
|
||||
|
||||
| Token | Background | Border | Shadow | Radius | Padding |
|
||||
|-------|------------|--------|--------|--------|---------|
|
||||
| `card.default` | `surface.background` | `border.default` (1px) | `elevation.1` | `rounded.lg` (12px) | 24px |
|
||||
| `card.elevated` | `surface.background` | none | `elevation.3` | `rounded.xl` (16px) | 28px |
|
||||
| `card.muted` | `surface.subtle` | `border.subtle` | none | `rounded.lg` | 24px |
|
||||
| `card.outlined` | `surface.background` | `border.strong` (1px) | none | `rounded.lg` | 24px |
|
||||
|
||||
## States
|
||||
default · hover · focus-visible · active/pressed · disabled · loading
|
||||
|
||||
- **Hover (interactive cards only)** — bump shadow one level (`1` → `2`) and shift `translateY(-2px)` over `motion.duration.fast`.
|
||||
- **Focus-visible** — apply `elevation.focusRing` on the card root.
|
||||
- **Selected** — 2px `brand.primary` left border or full `border.strong` ring.
|
||||
|
||||
## Tokens used
|
||||
See `design.md` → `components.cards.*`.
|
||||
|
||||
`components.card.*`, `elevation.1`–`3`, `colors.surface.*`, `colors.border.*`.
|
||||
|
||||
## Accessibility
|
||||
- All interactive instances are keyboard-reachable.
|
||||
- Focus state has ≥ 3:1 contrast against its background.
|
||||
- Pair color with an icon or text to convey meaning.
|
||||
|
||||
- If the card is clickable, the entire card is the hit target; expose it as a `<button>` or `<a>` (not a `<div onClick>`).
|
||||
- Don't rely on hover state alone to indicate interactivity — pair with cursor + raised elevation.
|
||||
|
||||
## Do / Don't
|
||||
- ✅ Use the canonical token references.
|
||||
- ❌ Don't hardcode colors, sizes, or radii.
|
||||
|
||||
- ✅ Use `card.default` for list rows and grid tiles. Reserve `card.elevated` for modals, popovers, and feature highlights.
|
||||
- ❌ Don't nest cards more than one level deep — collapse to sections instead.
|
||||
- ❌ Don't mix `card.default` and `card.elevated` in the same grid.
|
||||
|
||||
+37
-13
@@ -1,25 +1,49 @@
|
||||
# Forms
|
||||
|
||||
Specification for the **forms** primitive.
|
||||
Form composition: field stack, label / helper / error pairing, multi-step wizards. Reference: `kdcsurveyadd/components/surveys/SurveyWizard.tsx` (4-step wizard) and `kdcsurveyadd/components/surveys/StepIndicator.tsx`.
|
||||
|
||||
## Anatomy
|
||||
TODO — drop `anatomy.svg` next to this file.
|
||||
|
||||
## Variants
|
||||
- default
|
||||
- (list per-component variants)
|
||||
See `anatomy.svg`. Field row = label + optional helper hint + control + error slot. Sections separated by 32px; fields within a section by 16px.
|
||||
|
||||
## States
|
||||
default · hover · focus-visible · active/pressed · disabled · loading
|
||||
## Field structure
|
||||
|
||||
```
|
||||
<label> field name (bodyM, 600, text.default)
|
||||
<helper> optional hint (bodyS, 400, text.muted)
|
||||
<input> (input.default)
|
||||
<error> message when invalid (bodyS, 500, semantic.danger, role="alert")
|
||||
```
|
||||
|
||||
## Layout
|
||||
|
||||
- **Single column** by default — multi-column only for ≥ 3 closely-related short fields (address, date range).
|
||||
- **Field width** matches expected content: numeric → 120px, short text → 240px, free text → fill container.
|
||||
- **Required indicator** — append `*` to label in `semantic.danger`; never use placeholder text or icon-only.
|
||||
- **Optional fields** — append `(optional)` in `text.muted` `caption`.
|
||||
|
||||
## Wizard pattern (multi-step)
|
||||
|
||||
- Step indicator on top: `step n of N` + horizontal progress (`StepIndicator`).
|
||||
- Context strip below — pills summarising decisions so far (jump-back affordance).
|
||||
- Footer fixed: `Back` (ghost, left) · `Save & exit` (outline) · `Continue` / `Submit` (primary or accent, right).
|
||||
- Validate per-step on `Continue`; never reveal step `n+1` errors prematurely.
|
||||
|
||||
## Tokens used
|
||||
See `design.md` → `components.forms.*`.
|
||||
|
||||
`components.input.*`, `components.button.*`, `colors.semantic.danger`, `typography.bodyM`/`bodyS`/`caption`.
|
||||
|
||||
## Accessibility
|
||||
- All interactive instances are keyboard-reachable.
|
||||
- Focus state has ≥ 3:1 contrast against its background.
|
||||
- Pair color with an icon or text to convey meaning.
|
||||
|
||||
- Use a `<form>` element with explicit `<button type="submit">`.
|
||||
- Group related fields with `<fieldset>` + `<legend>`.
|
||||
- Errors are announced via `role="alert"` and referenced by `aria-describedby`.
|
||||
- On submit failure, focus moves to the first invalid field.
|
||||
- Wizard step indicator uses `aria-current="step"` on the active step.
|
||||
|
||||
## Do / Don't
|
||||
- ✅ Use the canonical token references.
|
||||
- ❌ Don't hardcode colors, sizes, or radii.
|
||||
|
||||
- ✅ Validate on blur for individual fields; on submit for the whole form.
|
||||
- ✅ Preserve user input across step changes — never silently discard.
|
||||
- ❌ Don't show inline validation on first focus (only on blur or submit attempt).
|
||||
- ❌ Don't disable the submit button to indicate invalid state — show the errors instead.
|
||||
|
||||
+35
-11
@@ -1,25 +1,49 @@
|
||||
# Inputs
|
||||
|
||||
Specification for the **inputs** primitive.
|
||||
Text inputs, textareas, selects. Single visual treatment, semantic states. Reference: `kdcvault/frontend/src/components/ui/input.tsx` and `.kdc-input` / `.kdc-input-underline` utilities in `kdcsurveyadd/app/globals.css`.
|
||||
|
||||
## Anatomy
|
||||
TODO — drop `anatomy.svg` next to this file.
|
||||
|
||||
See `anatomy.svg`. Label · input shell · helper / error text. Label sits above the input; helper text below in `text.muted` `bodyS`.
|
||||
|
||||
## Variants
|
||||
- default
|
||||
- (list per-component variants)
|
||||
|
||||
| Token | Use | Background | Border |
|
||||
|-------|-----|------------|--------|
|
||||
| `input.default` | Standard field | `surface.background` | `border.default` (1px) |
|
||||
| `input.filled` | High-density forms | `surface.subtle` | none |
|
||||
| `input.underline` | Marketing / minimal forms | transparent | bottom 1px `border.default` |
|
||||
|
||||
## Sizes
|
||||
|
||||
| Size | Height | Padding |
|
||||
|------|--------|---------|
|
||||
| `sm` | 32px | `6px 10px` |
|
||||
| `md` (default) | 40px | `8px 12px` |
|
||||
| `lg` | 48px | `12px 16px` |
|
||||
|
||||
## States
|
||||
default · hover · focus-visible · active/pressed · disabled · loading
|
||||
|
||||
- **Hover** — border `border.default` → `border.strong`, `120ms` transition.
|
||||
- **Focus-visible** — apply `elevation.focusRing`, border `brand.primary`.
|
||||
- **Disabled** — `opacity: 0.5`, `cursor: not-allowed`, background `surface.subtle`.
|
||||
- **Error** — border `semantic.danger`, helper text `semantic.danger`, focus ring `rgba(176,56,47,0.18)`.
|
||||
- **Read-only** — background `surface.subtle`, remove border, keep text colour.
|
||||
|
||||
## Tokens used
|
||||
See `design.md` → `components.inputs.*`.
|
||||
|
||||
`components.input.*`, `elevation.focusRing`, `colors.border.*`, `colors.semantic.danger`.
|
||||
|
||||
## Accessibility
|
||||
- All interactive instances are keyboard-reachable.
|
||||
- Focus state has ≥ 3:1 contrast against its background.
|
||||
- Pair color with an icon or text to convey meaning.
|
||||
|
||||
- Every input has a programmatically associated `<label>` (visible or `sr-only` only when an adjacent icon supplies meaning).
|
||||
- Error messages reference the input via `aria-describedby` and use `role="alert"` on first appearance.
|
||||
- Placeholder text is decorative — never the only label.
|
||||
- Maintain 4.5:1 contrast on placeholder text (currently `text.muted` `#6B647A`).
|
||||
|
||||
## Do / Don't
|
||||
- ✅ Use the canonical token references.
|
||||
- ❌ Don't hardcode colors, sizes, or radii.
|
||||
|
||||
- ✅ Show errors *under* the input, not as a tooltip.
|
||||
- ✅ Use `tabular-nums` for numeric-only inputs (`.tnum` utility).
|
||||
- ❌ Don't use placeholder text as a label.
|
||||
- ❌ Don't shrink the height below 32px — it breaks touch targets.
|
||||
|
||||
+28
-13
@@ -1,25 +1,40 @@
|
||||
# Modals
|
||||
|
||||
Specification for the **modals** primitive.
|
||||
Overlaid surfaces that interrupt the user — dialogs, confirmations, sheets, drawers. Built on `base-ui` / Radix primitives in the product family (`kdcvault/frontend/src/components/ui/dialog.tsx`, `sheet.tsx`).
|
||||
|
||||
## Anatomy
|
||||
TODO — drop `anatomy.svg` next to this file.
|
||||
|
||||
See `anatomy.svg`. Backdrop · panel · header (title + close) · body · footer (actions, right-aligned).
|
||||
|
||||
## Variants
|
||||
- default
|
||||
- (list per-component variants)
|
||||
|
||||
## States
|
||||
default · hover · focus-visible · active/pressed · disabled · loading
|
||||
| Token | Use | Width | Position |
|
||||
|-------|-----|-------|----------|
|
||||
| `modal.default` | Confirmations, forms (≤ 5 fields) | 480px | centered |
|
||||
| `modal.wide` | Multi-step wizards, content browsers | 720px | centered |
|
||||
| `modal.sheet` | Slide-in side panel (editing, filters) | 420px | right edge |
|
||||
| `modal.drawer` | Bottom sheet (mobile) | 100% | bottom edge |
|
||||
| `modal.fullscreen` | Immersive flows | 100% | full viewport |
|
||||
|
||||
## Tokens used
|
||||
See `design.md` → `components.modals.*`.
|
||||
## Tokens & layout
|
||||
|
||||
- Radius `rounded.xl` (16px); sheet variants use `rounded.lg` on inner edge only.
|
||||
- Background `surface.background`; backdrop `rgba(26,21,48,0.45)`.
|
||||
- Shadow `elevation.4` (`0 24px 80px -24px rgba(26,21,48,0.22)`).
|
||||
- Padding 24px; footer separated by `1px border.default` top edge.
|
||||
- Open animation: `kdc-fade-up` (360ms cubic-bezier, `translateY(6px)` → 0).
|
||||
|
||||
## Accessibility
|
||||
- All interactive instances are keyboard-reachable.
|
||||
- Focus state has ≥ 3:1 contrast against its background.
|
||||
- Pair color with an icon or text to convey meaning.
|
||||
|
||||
- `role="dialog"` (or `alertdialog` for destructive confirmations) with `aria-labelledby` / `aria-describedby`.
|
||||
- Trap focus inside the modal; first focusable element receives focus on open.
|
||||
- Close on `Esc`; restore focus to the trigger element on close.
|
||||
- Backdrop click closes only non-destructive modals; confirmations require explicit action.
|
||||
- Disable body scroll while open.
|
||||
|
||||
## Do / Don't
|
||||
- ✅ Use the canonical token references.
|
||||
- ❌ Don't hardcode colors, sizes, or radii.
|
||||
|
||||
- ✅ Right-align primary action in the footer (`primary` or `accent`); secondary / cancel on the left.
|
||||
- ✅ Use a `sheet` for editing context that benefits from staying tethered to a list.
|
||||
- ❌ Don't stack modals — open a `sheet` from inside a modal instead.
|
||||
- ❌ Don't put more than one CTA in a single modal footer.
|
||||
|
||||
@@ -1,25 +1,50 @@
|
||||
# Navigation
|
||||
|
||||
Specification for the **navigation** primitive.
|
||||
App shell, sidebar, top bar, tabs, breadcrumbs. Reference: `kdcvault/frontend/src/components/ui/sidebar.tsx`, `kdcsurveyadd/components/sidebar-nav.tsx`, `k-d-c-workspace/dashboard/components/AppShell.tsx`.
|
||||
|
||||
## Anatomy
|
||||
TODO — drop `anatomy.svg` next to this file.
|
||||
|
||||
App shell = top bar (60px, `surface.background`, hairline bottom border) + sidebar (240px collapsed → 72px icon-only, dark `brand.ink` on workspace; canvas-toned in product apps) + main outlet.
|
||||
|
||||
## Variants
|
||||
- default
|
||||
- (list per-component variants)
|
||||
|
||||
## States
|
||||
default · hover · focus-visible · active/pressed · disabled · loading
|
||||
| Pattern | Use |
|
||||
|---------|-----|
|
||||
| **Top bar + sidebar** | Default product layout (vault, void planner, surveyadd) |
|
||||
| **Top bar only** | Simple tools (mapper, tracker) |
|
||||
| **Sidebar collapsible** | `SidebarProvider` controls expanded / icon-only / off-canvas (mobile) |
|
||||
| **Command palette** | ⌘K trigger; `modal.default` shell over backdrop |
|
||||
| **Tabs** | In-page section switching — underline (`border.bottom-2 brand.primary` active) |
|
||||
| **Breadcrumbs** | Hierarchical path; `text.muted` non-active, `text.default` current, `›` separator in `text.disabled` |
|
||||
|
||||
## Sidebar items
|
||||
|
||||
- 40px row height, 12px horizontal padding, 12px gap between icon and label.
|
||||
- Icon 20×20px from the curated Phosphor set (see `assets/icons/`).
|
||||
- Active state: `brand.primarySoft` background, `brand.primary` text, 2px left border in `brand.primary`.
|
||||
- Hover: `surface.subtle` background.
|
||||
- Section labels: `caption` uppercase, `text.muted`, 24px row.
|
||||
|
||||
## Top bar
|
||||
|
||||
- 60px tall, sticky.
|
||||
- Left: brand mark · workspace switcher. Right: search · notifications · user menu.
|
||||
- `backdrop-filter: saturate(180%) blur(14px)` on scroll (translucent over content).
|
||||
|
||||
## Tokens used
|
||||
See `design.md` → `components.navigation.*`.
|
||||
|
||||
`components.button.*` (icon buttons), `colors.surface.*`, `colors.border.*`, `elevation.focusRing`.
|
||||
|
||||
## Accessibility
|
||||
- All interactive instances are keyboard-reachable.
|
||||
- Focus state has ≥ 3:1 contrast against its background.
|
||||
- Pair color with an icon or text to convey meaning.
|
||||
|
||||
- Sidebar uses `<nav aria-label="primary">`; in-page tabs use `role="tablist"` / `role="tab"` / `role="tabpanel"`.
|
||||
- Current page link: `aria-current="page"`.
|
||||
- Skip-to-content link as the first focusable element, visible on focus.
|
||||
- Command palette: trap focus, restore on close, announce via `aria-live`.
|
||||
|
||||
## Do / Don't
|
||||
- ✅ Use the canonical token references.
|
||||
- ❌ Don't hardcode colors, sizes, or radii.
|
||||
|
||||
- ✅ Always show the active route — left border + tinted background, not colour-shift alone.
|
||||
- ✅ Collapse to icon-only below 1024px, off-canvas below 768px.
|
||||
- ❌ Don't nest more than two sidebar levels — flatten into separate sections.
|
||||
- ❌ Don't put primary actions in the sidebar — they belong in the top bar or page header.
|
||||
|
||||
+39
-13
@@ -1,25 +1,51 @@
|
||||
# Tables
|
||||
|
||||
Specification for the **tables** primitive.
|
||||
Dense data display. Default to flat (no zebra striping) with hairline row borders. Reference: `kdctracker/admin.html` (`.card` data tables), `kdcdocs/public/styles.css` (`.kdc-table-head`, `.kdc-row`).
|
||||
|
||||
## Anatomy
|
||||
TODO — drop `anatomy.svg` next to this file.
|
||||
|
||||
## Variants
|
||||
- default
|
||||
- (list per-component variants)
|
||||
See `anatomy.svg`. Header row · grouped header rows (optional) · data rows · footer summary (optional).
|
||||
|
||||
## States
|
||||
default · hover · focus-visible · active/pressed · disabled · loading
|
||||
## Sizing
|
||||
|
||||
| Density | Row height | Cell padding |
|
||||
|---------|------------|--------------|
|
||||
| `compact` | 36px | `6px 12px` |
|
||||
| `default` | 44px | `10px 16px` |
|
||||
| `comfortable` | 56px | `14px 20px` |
|
||||
|
||||
Drive density with `[data-density="compact|default|comfortable"]` (per the kdcmapper convention).
|
||||
|
||||
## Styling
|
||||
|
||||
- Header: `text.muted` `caption` uppercase, 600 weight, `surface.subtle` background.
|
||||
- Cell text: `bodyM`, `text.default`. Numerics use `tabular-nums` (`.tnum` utility).
|
||||
- Row separator: 1px `border.subtle` bottom border. No vertical lines.
|
||||
- Hover row: `surface.subtle`, `120ms` transition.
|
||||
- Selected row: 2px `brand.primary` left border, `brand.primarySoft` background.
|
||||
- Sticky header sits below any toolbar; sticky first column gets a 4px `elevation.2` shadow on right edge when scrolled.
|
||||
|
||||
## Cell types
|
||||
|
||||
- **Status pill** → `badge.*` variants.
|
||||
- **Identifier / code** → `typography.mono` with subtle background `surface.subtle`.
|
||||
- **Numeric** → right-aligned, `tabular-nums`.
|
||||
- **Action cell** → trailing cell, ghost icon buttons; right-aligned, never wrap.
|
||||
|
||||
## Tokens used
|
||||
See `design.md` → `components.tables.*`.
|
||||
|
||||
`colors.surface.*`, `colors.border.*`, `typography.bodyM`/`caption`/`mono`, `components.badge.*`.
|
||||
|
||||
## Accessibility
|
||||
- All interactive instances are keyboard-reachable.
|
||||
- Focus state has ≥ 3:1 contrast against its background.
|
||||
- Pair color with an icon or text to convey meaning.
|
||||
|
||||
- Use proper `<thead>` / `<tbody>` / `<th scope="col">` / `<th scope="row">`.
|
||||
- Sortable columns: button inside `<th>` with `aria-sort="ascending|descending|none"`.
|
||||
- Empty state: `role="status"`, single line in `text.muted`.
|
||||
- Long tables: pagination or `aria-rowcount` for virtualised lists.
|
||||
|
||||
## Do / Don't
|
||||
- ✅ Use the canonical token references.
|
||||
- ❌ Don't hardcode colors, sizes, or radii.
|
||||
|
||||
- ✅ Use `compact` density for ≥ 50 rows on screen at once.
|
||||
- ✅ Right-align numerics and currencies; left-align text and identifiers.
|
||||
- ❌ Don't zebra-stripe — it competes with hover and selection.
|
||||
- ❌ Don't wrap action buttons; truncate text instead.
|
||||
|
||||
@@ -1,25 +1,39 @@
|
||||
# Tooltips
|
||||
|
||||
Specification for the **tooltips** primitive.
|
||||
Brief, hover/focus-triggered hints. Reference: `kdcvault/frontend/src/components/ui/tooltip.tsx` (base-ui Tooltip).
|
||||
|
||||
## Anatomy
|
||||
TODO — drop `anatomy.svg` next to this file.
|
||||
|
||||
## Variants
|
||||
- default
|
||||
- (list per-component variants)
|
||||
See `anatomy.svg`. Trigger · floating panel · optional arrow. Max-width 240px; wraps to 3 lines maximum.
|
||||
|
||||
## States
|
||||
default · hover · focus-visible · active/pressed · disabled · loading
|
||||
## Styling
|
||||
|
||||
- Background `brand.ink` (`#1A1530`), text `text.inverse`, `caption` typography.
|
||||
- Radius `rounded.sm` (4px), padding `6px 10px`.
|
||||
- Shadow `elevation.2`.
|
||||
- Arrow 6×6px, same fill as panel.
|
||||
- Offset 8px from trigger; flip to opposite side at viewport edge.
|
||||
|
||||
## Timing
|
||||
|
||||
- Open delay 400ms (skip if another tooltip is already open within 500ms).
|
||||
- Close delay 100ms (allow hover-into-tooltip for copy / link interactions).
|
||||
- Open/close transition: `motion.duration.fast` `motion.easing.standard`.
|
||||
|
||||
## Tokens used
|
||||
See `design.md` → `components.tooltips.*`.
|
||||
|
||||
`colors.brand.ink`, `colors.text.inverse`, `typography.caption`, `rounded.sm`, `elevation.2`.
|
||||
|
||||
## Accessibility
|
||||
- All interactive instances are keyboard-reachable.
|
||||
- Focus state has ≥ 3:1 contrast against its background.
|
||||
- Pair color with an icon or text to convey meaning.
|
||||
|
||||
- Trigger is keyboard-focusable; tooltip opens on focus as well as hover.
|
||||
- Use `aria-describedby` on the trigger pointing at the tooltip's `id`.
|
||||
- Never put interactive content inside a tooltip — use a popover instead.
|
||||
- Dismiss on `Esc` and on focus / hover leave.
|
||||
|
||||
## Do / Don't
|
||||
- ✅ Use the canonical token references.
|
||||
- ❌ Don't hardcode colors, sizes, or radii.
|
||||
|
||||
- ✅ Keep copy ≤ 80 chars; one sentence, no period.
|
||||
- ✅ Use to surface a *non-essential* hint (full label, keyboard shortcut, brief explanation).
|
||||
- ❌ Don't put a tooltip on a tappable mobile target — touch users never see it.
|
||||
- ❌ Don't put interactive elements (links, buttons) inside a tooltip — escalate to a `popover`.
|
||||
|
||||
Reference in New Issue
Block a user