Compare commits

...

6 Commits

Author SHA1 Message Date
K-D-C b965c1dfbd chore: pin Claude Code worktree bgIsolation to none for this repo
Lint / lint (push) Has been cancelled
2026-06-06 11:37:57 +00:00
K-D-C 6457347dd6 docs(patterns): add 'Patterns We Like' scrapbook + link from README
A running list of UI effects/interactions worth keeping, each with the four-point
bar (does a job, respects reduced-motion, transform/opacity only, degrades) it
must clear before shipping. README structure + quick-start now point to it.
2026-06-06 11:37:57 +00:00
Paul Roberts f0987778ec Merge fix-docs-archive: mark kdcdocs as archived
Lint / lint (push) Has been cancelled
2026-05-17 18:24:23 +00:00
Paul Roberts b635fd1fdb docs: mark kdcdocs as archived, not drift
kdcdocs was sunset earlier — SOPs already merged into kdcsurveyadd /docs.
Move it from the migration backlog to an "Archived" row in the consolidation report.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 18:24:23 +00:00
Paul Roberts c2befb64db Merge branch 'consolidation' — design system v1.0.0
Lint / lint (push) Has been cancelled
2026-05-17 17:53:41 +00:00
Paul Roberts 6593bdf689 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>
2026-05-17 17:51:39 +00:00
30 changed files with 998 additions and 248 deletions
+1
View File
@@ -0,0 +1 @@
{"worktree": {"bgIsolation": "none"}}
+24 -2
View File
@@ -4,7 +4,29 @@ All notable changes to the KDC Design System will be documented in this file.
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and
this project adheres to [Semantic Versioning](https://semver.org/).
## [Unreleased]
## [1.0.0] — 2026-05-17
### Changed (breaking)
- **Brand ink** `brand.ink` `#000000``#1A1530` (purple-tinted; matches all on-brand product surfaces).
- **Canvas** `brand.canvas` `#F5F4F0``#F7F4ED` (warmer; consensus across surveyadd / vault / void).
- **Neutral ramp** swapped from cold slate (`#EDEFF3` / `#6B7280` / `#111827`) to a purple-tinted ramp aligned with `brand.ink`.
- **Semantic palette** retuned for the warm canvas — `success` `#16A34A``#2F7A5A`, `warning` `#F59E0B``#C9892B`, `danger` `#DC2626``#B0382F`, `info` `#0EA5E9``#5C2D90`. Each now ships a `*Soft` background tint.
- **Elevation shadows** retinted with `rgb(26 21 48)` (brand ink) instead of `rgb(17 24 39)` (slate). New keys: `elevation.focusRing`.
- **`brand.primaryHover`** `#582A8D``#5C2D90` (matches `primary-2` in product CSS).
### Added
- Initial repository scaffold: `design.md`, brand, foundations, components, themes, templates, docs.
- `brand.primaryLight` `#8456C2`, `brand.primarySoft` `#F1E9F8`, `brand.canvasCool` `#F7F6FB`.
- `accent.gold` `#C9892B` (+ strong/soft), `accent.mint` `#46C194` (workspace marketing).
- `scale.brand-50/100/200/500/600/700/800` for Tailwind-style usage.
- `neutral.50/150/400/600/700/800` (extra steps).
- Semantic-soft tints, role tokens `text.soft`, `text.disabled`, `text.brand`, `border.subtle`.
- Component variants in `design.md`: `button.accent`, `button.outline`, `card.elevated`, `badge.success/warning/danger`.
- Fleshed-out READMEs for all components (buttons, cards, inputs, badges, modals, forms, navigation, tables, tooltips) and foundations (colors, typography, spacing, radius, elevation, motion, breakpoints, grid).
- Real theme files for `themes/light`, `themes/dark`, `themes/high-contrast`.
- `docs/consolidation-2026-05.md` — extraction report across the 8 KDC repos.
### Notes
- Reconciliation source: `kdcsurveyadd`, `kdcvault`, `kdc_void_planner` (refined product family) and `k-d-c-workspace` (marketing dashboard). These three product apps share an essentially identical implementation; the design system now reflects their consensus.
- Drift identified in `kdcdiary`, `kdctracker`, `kdcdocs`, `kdcmapper` — different purples and typefaces. Tracked in the consolidation report; migration is out of scope for this release.
## [Unreleased]
+3 -2
View File
@@ -26,7 +26,7 @@ KDCDesignSystem/
├── components/ # Per-component specs and anatomy
├── themes/ # Light, dark, high-contrast theme overrides
├── templates/ # Web, mobile, email, print, presentation
├── docs/ # Getting started, principles, a11y, voice & tone
├── docs/ # Getting started, principles, patterns, a11y, voice & tone
├── examples/ # Reference implementations
└── scripts/ # Token build / export scripts
```
@@ -36,10 +36,11 @@ KDCDesignSystem/
1. Read [`docs/getting-started/README.md`](./docs/getting-started/README.md).
2. Skim [`design.md`](./design.md) for the token vocabulary.
3. Pick your platform under [`examples/`](./examples/).
4. Browse [`docs/patterns/README.md`](./docs/patterns/README.md) — a scrapbook of UI effects & interactions we like (and the bar each clears before shipping).
## Status
Version: **beta**. Tokens and components are still settling — expect breaking changes.
Version: **1.0.0** (reconciled 2026-05-17). Canonical palette and components match the production implementations in `kdcsurveyadd`, `kdcvault`, and `kdc_void_planner`. See [`docs/consolidation-2026-05.md`](./docs/consolidation-2026-05.md) for the cross-repo audit and outstanding drift in `kdcdiary` / `kdctracker` / `kdcdocs` / `kdcmapper`.
## Contributing
+28 -12
View File
@@ -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.
+44 -11
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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.
+37 -12
View File
@@ -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
View File
@@ -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.
+27 -13
View File
@@ -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`.
+102 -37
View File
@@ -1,34 +1,60 @@
---
version: beta
version: 1.0.0
name: Kode Design Consultants Ltd Design System
description: The official design system for Kode Design Consultants — tokens, components, and guidelines for building consistent KDC product and brand experiences.
description: The official design system for Kode Design Consultants — tokens, components, and guidelines for building consistent KDC product and brand experiences. Reconciled 2026-05-17 from production usage across kdcsurveyadd, kdcvault, kdc_void_planner and k-d-c-workspace.
colors:
brand.primary: "#6B35A7"
brand.primaryHover: "#582A8D"
brand.primaryHover: "#5C2D90"
brand.primaryPressed: "#45216E"
brand.ink: "#000000"
brand.canvas: "#F5F4F0"
brand.primaryLight: "#8456C2"
brand.primarySoft: "#F1E9F8"
brand.ink: "#1A1530"
brand.canvas: "#F7F4ED"
brand.canvasCool: "#F7F6FB"
accent.gold: "#C9892B"
accent.goldStrong: "#A86F1F"
accent.goldSoft: "#F5E9D2"
accent.mint: "#46C194"
neutral.0: "#FFFFFF"
neutral.50: "#F5F4F0"
neutral.100: "#EDEFF3"
neutral.200: "#D9DCE3"
neutral.300: "#B8BDC9"
neutral.500: "#6B7280"
neutral.700: "#374151"
neutral.900: "#111827"
neutral.50: "#FBF8F1"
neutral.100: "#F7F4ED"
neutral.150: "#ECE9F3"
neutral.200: "#E6E0D2"
neutral.300: "#D4CCB8"
neutral.400: "#A39DB0"
neutral.500: "#8C869C"
neutral.600: "#6B647A"
neutral.700: "#4A4360"
neutral.800: "#3A2D5A"
neutral.900: "#1A1530"
neutral.1000: "#000000"
semantic.success: "#16A34A"
semantic.warning: "#F59E0B"
semantic.danger: "#DC2626"
semantic.info: "#0EA5E9"
semantic.success: "#2F7A5A"
semantic.successSoft: "#E2F0E8"
semantic.warning: "#C9892B"
semantic.warningSoft: "#F5E9D2"
semantic.danger: "#B0382F"
semantic.dangerSoft: "#F7E4E1"
semantic.info: "#5C2D90"
surface.background: "{colors.neutral.0}"
surface.canvas: "{colors.brand.canvas}"
surface.subtle: "{colors.neutral.50}"
surface.muted: "{colors.neutral.100}"
text.default: "{colors.neutral.900}"
text.muted: "{colors.neutral.500}"
surface.muted: "{colors.brand.primarySoft}"
text.default: "{colors.brand.ink}"
text.soft: "{colors.neutral.700}"
text.muted: "{colors.neutral.600}"
text.disabled: "{colors.neutral.400}"
text.inverse: "{colors.neutral.0}"
text.brand: "{colors.brand.primary}"
border.default: "{colors.neutral.200}"
border.strong: "{colors.neutral.300}"
border.subtle: "{colors.neutral.150}"
elevation:
"0": "none"
"1": "0 1px 2px rgba(26,21,48,0.04)"
"2": "0 4px 12px -4px rgba(26,21,48,0.08)"
"3": "0 16px 40px -16px rgba(26,21,48,0.18)"
"4": "0 24px 80px -24px rgba(26,21,48,0.22)"
focusRing: "0 0 0 3px rgba(107,53,167,0.18)"
typography:
display:
fontFamily: "Opificio"
@@ -117,53 +143,90 @@ components:
textColor: "{colors.text.inverse}"
typography: "{typography.bodyM}"
rounded: "{rounded.md}"
padding: "12px 20px"
padding: "10px 16px"
height: "40px"
shadow: "{elevation.1}"
button.primaryHover:
backgroundColor: "{colors.brand.primaryHover}"
textColor: "{colors.text.inverse}"
button.primaryPressed:
backgroundColor: "{colors.brand.primaryPressed}"
textColor: "{colors.text.inverse}"
button.accent:
backgroundColor: "{colors.accent.gold}"
textColor: "{colors.text.inverse}"
typography: "{typography.bodyM}"
rounded: "{rounded.md}"
padding: "10px 16px"
height: "40px"
description: "Submit / emphasis button. Used sparingly — one per surface."
button.secondary:
backgroundColor: "{colors.surface.subtle}"
textColor: "{colors.text.default}"
borderColor: "{colors.border.default}"
typography: "{typography.bodyM}"
rounded: "{rounded.md}"
padding: "12px 20px"
padding: "10px 16px"
height: "40px"
button.ghost:
backgroundColor: "transparent"
textColor: "{colors.brand.primary}"
typography: "{typography.bodyM}"
rounded: "{rounded.md}"
padding: "12px 20px"
padding: "10px 16px"
height: "40px"
button.outline:
backgroundColor: "transparent"
textColor: "{colors.text.default}"
borderColor: "{colors.border.strong}"
typography: "{typography.bodyM}"
rounded: "{rounded.md}"
padding: "10px 16px"
height: "40px"
input.default:
backgroundColor: "{colors.surface.background}"
textColor: "{colors.text.default}"
borderColor: "{colors.border.default}"
typography: "{typography.bodyM}"
rounded: "{rounded.md}"
padding: "10px 12px"
padding: "8px 12px"
height: "40px"
focusRing: "{elevation.focusRing}"
card.default:
backgroundColor: "{colors.surface.background}"
textColor: "{colors.text.default}"
borderColor: "{colors.border.default}"
rounded: "{rounded.lg}"
padding: "24px"
shadow: "{elevation.1}"
card.elevated:
backgroundColor: "{colors.surface.background}"
rounded: "{rounded.xl}"
padding: "28px"
shadow: "{elevation.3}"
badge.default:
backgroundColor: "{colors.surface.muted}"
textColor: "{colors.text.default}"
backgroundColor: "{colors.brand.primarySoft}"
textColor: "{colors.brand.primaryHover}"
typography: "{typography.caption}"
rounded: "{rounded.full}"
padding: "2px 8px"
height: "20px"
padding: "2px 10px"
height: "22px"
badge.success:
backgroundColor: "{colors.semantic.successSoft}"
textColor: "{colors.semantic.success}"
badge.warning:
backgroundColor: "{colors.semantic.warningSoft}"
textColor: "{colors.semantic.warning}"
badge.danger:
backgroundColor: "{colors.semantic.dangerSoft}"
textColor: "{colors.semantic.danger}"
modal.default:
backgroundColor: "{colors.surface.background}"
textColor: "{colors.text.default}"
rounded: "{rounded.xl}"
padding: "24px"
width: "480px"
shadow: "{elevation.4}"
---
## Overview
@@ -180,13 +243,14 @@ KDS targets WCAG 2.2 AA at minimum across all components and themes.
## Colors
Color is organized in three layers:
Color is organized in four layers:
1. **Brand**the four-color brand palette: `brand.primary` (`#6B35A7` purple), `brand.ink` (`#000000`), `neutral.0` (`#FFFFFF`), and `brand.canvas` (`#F5F4F0` off-white).
2. **Neutral**a 12-step gray ramp powering surfaces, text, and borders. `neutral.50` is set to `brand.canvas` so the off-white reads as the natural page surface.
3. **Semantic** — meaning-bearing colors for status: success, warning, danger, info. These sit alongside the brand palette but are not part of brand identity.
1. **Brand**Kode purple (`brand.primary` `#6B35A7`) plus its hover / pressed / light / soft variants, anchored by a purple-tinted ink (`brand.ink` `#1A1530` — never pure black) and the warm off-white canvas (`brand.canvas` `#F7F4ED`).
2. **Accent**`accent.gold` (`#C9892B`) is the product emphasis colour for submit / commit actions; `accent.mint` (`#46C194`) is reserved for marketing surfaces (workspace dashboard, decks). Use one accent per surface — never both.
3. **Neutral** — a purple-tinted 13-step ramp (`neutral.0``neutral.1000`). The ramp deliberately walks toward the brand ink rather than a cold grey, so muted text and borders stay on-brand. `neutral.50` is a warm inset surface, `neutral.100` aliases `brand.canvas`, `neutral.900` aliases `brand.ink`.
4. **Semantic**`success` / `warning` / `danger` / `info` plus matching `*Soft` background tints, tuned to sit calmly on the warm canvas (e.g. `success` is `#2F7A5A`, not the louder `#16A34A`).
Always reference *role* tokens (`text.default`, `surface.subtle`, `border.default`) in product code rather than raw palette values, so themes can swap without churn.
Always reference *role* tokens (`text.default`, `surface.canvas`, `border.default`) in product code — never raw palette values so themes can swap without churn.
Contrast minimums:
@@ -238,12 +302,13 @@ Elevation is conveyed through a small set of layered shadows plus subtle backgro
| Level | Use | Shadow |
|-------|-----|--------|
| 0 | flush surfaces, page background | none |
| 1 | cards, list rows | `0 1px 2px rgba(17,24,39,0.06)` |
| 2 | dropdowns, popovers | `0 4px 12px rgba(17,24,39,0.10)` |
| 3 | modals, dialogs | `0 16px 32px rgba(17,24,39,0.16)` |
| 4 | toasts, command bars | `0 24px 48px rgba(17,24,39,0.20)` |
| 1 | cards, list rows, buttons | `0 1px 2px rgba(26,21,48,0.04)` |
| 2 | dropdowns, popovers | `0 4px 12px -4px rgba(26,21,48,0.08)` |
| 3 | modals, dialogs | `0 16px 40px -16px rgba(26,21,48,0.18)` |
| 4 | toasts, command bars | `0 24px 80px -24px rgba(26,21,48,0.22)` |
| focusRing | any focusable element | `0 0 0 3px rgba(107,53,167,0.18)` |
In dark theme, shadows are de-emphasized and elevation is communicated primarily through lighter surface tints.
Shadows are tinted with the brand ink (`rgb(26 21 48)`) — not cold slate — so they read warm against the canvas. In dark theme, shadows are de-emphasized and elevation is communicated primarily through lighter surface tints.
## Shapes
+73
View File
@@ -0,0 +1,73 @@
# Design system consolidation — 2026-05-17
Output of the cross-repo audit that produced **v1.0.0**. Read this first if you're wondering "why does this token look the way it does."
## Scope
Scanned 8 repos under `/root/`:
`kdcsurveyadd` · `kdcvault` · `kdc_void_planner` · `k-d-c-workspace` · `kdcdiary` · `kdctracker` · `kdcdocs` · `kdcmapper`
For each: every distinct colour, font, radius, shadow, and reusable component was extracted via a parallel `Explore` agent. Findings are summarised below; the raw notes are in agent transcripts under `.claude/jobs/`.
## Three families, one canonical
| Family | Repos | Primary | Ink | Canvas | Font | Stack |
|--------|-------|---------|-----|--------|------|-------|
| **Refined product** *(canonical)* | `kdcsurveyadd`, `kdcvault`, `kdc_void_planner` | `#6B35A7` | `#1A1530` | `#F7F4ED` (warm) | Manrope | Tailwind v4 + shadcn/ui + base-ui |
| **Marketing dashboard** | `k-d-c-workspace` (dashboard) | `#6B35A7` | `#1a1530` | `#f7f6fb` (cool) | Manrope | Tailwind v4 + custom components |
| **Legacy / exploratory** *(drift)* | `kdcdiary`, `kdctracker`, `kdcmapper` | varies | varies | varies | Geist or Inter+Outfit | varies |
| **Archived** *(out of scope)* | `kdcdocs` | `#A37CD9` | dark | dark | Geist | Sunset — SOPs merged into `kdcsurveyadd /docs`. Read-only reference. |
The product family agrees almost line-for-line on tokens — including the bespoke gold accent `#C9892B`, the focus ring `0 0 0 3px rgba(107,53,167,0.18)`, the ink-tinted shadow stack, and a 4-step radius scale (`4 / 8 / 12 / 16`). v1.0.0 promotes that consensus into the canonical spec.
The dashboard agrees on brand fundamentals (purple, Manrope, ink) but uses a **cooler canvas** (`#f7f6fb`) and a **mint accent** (`#46C194`) instead of gold. Both are now first-class tokens: `brand.canvasCool` and `accent.mint`. Marketing surfaces use those; product surfaces use the warm canvas + gold.
## Drift inventory
| Repo | Primary used | Font used | Status |
|------|--------------|-----------|--------|
| `kdcdiary` | `#6B4EFF` | Geist + JetBrains Mono | drift — different purple, different typeface |
| `kdctracker` | `#6B4EFF` | Geist + JetBrains Mono | drift — same as diary, looks like a shared template |
| `kdcmapper` | `#5567E8` (indigo) | Inter + Outfit + JetBrains Mono | drift — indigo palette, no Manrope, glassmorphism style |
`kdcdocs` was previously sunset (SOPs merged into `kdcsurveyadd /docs`) — excluded from migration.
**Not in scope for v1.0.0.** A follow-up project should migrate these to canonical — order of cheapest first:
1. **kdctracker** — single-file HTML, ~1 day to swap CSS vars.
2. **kdcdiary** — React app with inline styles, ~2 days.
3. **kdcmapper** — biggest lift, fully bespoke Tailwind theme. Plan a dedicated re-skin sprint.
## What changed in v1.0.0
See `CHANGELOG.md` for the full diff. Highlights:
- Brand ink moved from pure `#000000` to `#1A1530` — every product surface already uses the purple-tinted ink, the spec was wrong.
- Canvas warmed to `#F7F4ED`.
- Neutral ramp rebuilt around the purple ink so muted text and borders stay on-brand.
- Semantic palette retuned for the warm canvas (`success` `#2F7A5A`, etc.) and each ships a `*Soft` background tint.
- Shadow stack retinted with brand-ink rgb.
- Added `accent.gold` and `accent.mint` as first-class tokens.
- Filled in every previously-stub README in `components/` and `foundations/` with real specs.
- Real `themes/light`, `themes/dark`, `themes/high-contrast` JSON.
## What's still TODO
Carrying these forward to v1.1 (not blockers):
- **Wordmark second-line** — design.md still encodes the secondary lockup at `fontWeight: 200`. The workspace brand guide lists Manrope weights starting at 300; either re-confirm 200 is licensed or bump to 300.
- **Opificio cuts** — only Bold is in `brand/logos-source/`. If Rounded or Regular are licensed, add them with the same naming convention.
- **Workspace brand guide** — `@Context/Assets/Brand/colour-palette.md` still has empty `_Name_` cells. The values in this design system can populate them; do it once and then point the brand guide at this repo as canonical.
- **Component anatomy SVGs** — every component README references `anatomy.svg`. The files exist but are placeholder geometry; commission real ones.
- **Token build pipeline** — `scripts/build-tokens.js` still produces a tokens dump only. A v1.1 should emit Tailwind v4 `@theme` blocks, CSS variables, and Style Dictionary JSON so apps can consume directly.
- **Migration runbooks** — one per drift app (`migrate-kdcdiary.md` etc.), with a token-mapping table so the work is grab-and-go.
## How to use the new spec
1. **In a new Tailwind v4 app**, copy the `@theme` block from `kdcvault/frontend/src/index.css` lines 562 — that's the canonical reference implementation.
2. **Import** `@fontsource-variable/manrope` and reference `var(--font-manrope)`.
3. **Always use role tokens** (`text.default`, `surface.canvas`) in component code, never raw palette values.
4. **For a new shadcn component**, `npx shadcn@latest add <component>` then re-style with the existing `.kdc-*` utility classes from `kdcsurveyadd/app/globals.css`.
Questions / drift you spot: open an issue or PR against this repo.
+90
View File
@@ -0,0 +1,90 @@
# Patterns We Like
A running scrapbook of UI effects, interactions and micro-patterns we've come across and want to keep — so a future app or project can reach for something proven instead of reinventing it.
This list sits slightly apart from the canonical [principles](../principles/README.md) on purpose. Those say *"clarity over cleverness"* and *"motion is feedback, never decorates."* The entries here are often **delight** effects that flirt with that line. That's the point of the doc: keep the tempting stuff in one place **and** record the bar each must clear before it earns a spot in a shipping product. A pattern we like is not a pattern we'll always use.
## The bar (when a delight effect earns its place)
Before any effect below ships in a KDC product, it has to clear all four:
-**It does a job.** It communicates state, affordance, continuity or confirmation — not just "looks nice." If it's purely decorative, it doesn't ship (per [Motion](../../foundations/motion/README.md)).
-**It respects `prefers-reduced-motion: reduce`** — collapses to a static/instant state, no exceptions.
-**It animates `transform` / `opacity` only** — never `width` / `height` / layout. Compositor-friendly or it doesn't go in.
-**It degrades gracefully** — works without it, and doesn't trap focus, block input, or break on keyboard/touch.
If an entry can't meet the bar in a given spot, that's a "no" for that spot — not a reason to drop the pattern from the list.
## How to add an entry
Keep it light — append a block in this shape. Don't turn it into a form.
```
### <name>
**Status:** idea | trialed | adopted-in-<app>
**What it is:** one line.
**Why we like it:** one or two lines.
**Snippet:** the smallest working version.
**Fits / avoid:** where it earns its place, and where it doesn't.
**Watch-outs:** the honest gotchas — performance, a11y, edge cases.
**Found:** where it came from (link / who / when).
```
**Status legend**`idea` (caught our eye, untried) · `trialed` (prototyped in a branch) · `adopted-in-<app>` (shipped, name the app).
---
## Entries
### Proximity-magnification dock
**Status:** idea
**What it is:** macOS-dock-style hover magnification — across a row (or column) of equal items, the one nearest the cursor scales up, neighbours scale less, tapering to nothing past a set radius. The bulge follows the pointer.
**Why we like it:** tiny amount of code, self-resetting (far items naturally land back at scale 1), and `scale` is compositor-cheap. Used in the right place it doubles as a targeting affordance — it literally points at the thing you're about to click.
**Snippet** — the whole trick is "distance → 0..1 closeness → size":
```js
// Vanilla, horizontal. 120 = reach in px; 0.5 = max growth (→ 1.5×).
onpointermove = e =>
document.querySelectorAll(".dock > *").forEach(el => {
const r = el.getBoundingClientRect();
const t = Math.max(0, 1 - Math.abs(e.clientX - (r.x + r.width / 2)) / 120);
el.style.scale = 1 + t * 0.5;
});
```
In our apps (all React 19) it belongs in an effect with cached rects + rAF + cleanup, and gated on reduced-motion:
```jsx
useEffect(() => {
if (matchMedia("(prefers-reduced-motion: reduce)").matches) return; // clears the bar
const items = [...dockRef.current.children];
let rects = items.map(el => el.getBoundingClientRect()); // cache — don't measure per-move
const remeasure = () => { rects = items.map(el => el.getBoundingClientRect()); };
const onMove = e => requestAnimationFrame(() => items.forEach((el, i) => {
const c = rects[i].x + rects[i].width / 2; // use clientY + rect.y for a vertical dock
const t = Math.max(0, 1 - Math.abs(e.clientX - c) / 120);
el.style.scale = 1 + t * 0.5;
}));
window.addEventListener("pointermove", onMove);
window.addEventListener("resize", remeasure);
return () => { window.removeEventListener("pointermove", onMove); window.removeEventListener("resize", remeasure); };
}, []);
```
**Fits / avoid:**
- ✅ A row/column of **≥ 5 equal icon targets you actually click** — tool palettes, icon rails, app launchers. (KDC candidate: the floor-plan tool palette in `kdc_void_planner``Palette.tsx`, a vertical rail of ~10 tool buttons. Vertical, so drive it off `clientY`.)
-**23 item navs** — the ripple is invisible with so few items.
-**Step indicators / status strips** — those communicate *progress/state*, not click targets. Magnifying them is semantically confusing and fails the bar (it does no job).
**Watch-outs:**
- **Layout thrash.** `getBoundingClientRect()` per `pointermove` forces synchronous layout. Cache the rects (re-measure on `resize`/`scroll`), and batch the writes in `requestAnimationFrame`.
- **Horizontal-only** as written (`clientX`). Swap to `clientY` + `rect.y` for a vertical rail.
- **Neighbours overlap rather than push apart.** `scale` doesn't reflow, so a growing item grows *over* its neighbours instead of displacing them (the real Dock shoves siblings outward). Looks fine with generous gaps/padding; less polished without.
- **`transform-origin`.** For a docked edge, set the origin to that edge (e.g. `transform-origin: bottom` / `left`) so items grow *out of* the dock, not in both directions.
- **Reduced motion + input parity.** Bail when `prefers-reduced-motion` is set; never let the scale affect hit-testing/keyboard order.
**Found:** Paul, May 2026 — a "proximity hack" snippet. Decoded + React-adapted in-session.
+27 -5
View File
@@ -1,8 +1,30 @@
# Foundations · Breakpoints
Reference documentation for the **breakpoints** foundation. The canonical token values
live in [`../../design.md`](../../design.md) and the machine-readable export
in [`../../tokens/`](../../tokens/).
Five mobile-first breakpoints, aligned to Tailwind defaults so utility classes work without remapping.
This page is for human-facing rationale: when to use which token, examples,
and platform notes.
| Token | Min width | Use |
|-------|-----------|-----|
| `sm` | 640px | Larger phone / small tablet |
| `md` | 768px | Tablet portrait |
| `lg` | 1024px | Tablet landscape / small laptop |
| `xl` | 1280px | Standard desktop |
| `2xl` | 1536px | Wide desktop |
## App shell behaviour
| Width | Layout |
|-------|--------|
| < 768px (`md`) | Sidebar off-canvas (drawer). Top bar collapses to brand + menu icon. |
| 7681023px (`md``lg`) | Sidebar icon-only (72px). |
| ≥ 1024px (`lg`) | Sidebar expanded (240px). |
| ≥ 1280px (`xl`) | Optional right-rail / detail column appears. |
## Container
Max content width is 1200px, centered, with side padding of 16px (mobile) → 24px (tablet) → 32px (desktop).
## Rules
- ✅ Always mobile-first. Stack to single column below `md`; introduce side-by-side at `md`+.
- ✅ Test the 320px width — that's the floor.
- ❌ Don't use device-specific breakpoints (iPad, iPhone). Design for content reflow, not specific hardware.
+32 -5
View File
@@ -1,8 +1,35 @@
# Foundations · Colors
Reference documentation for the **colors** foundation. The canonical token values
live in [`../../design.md`](../../design.md) and the machine-readable export
in [`../../tokens/`](../../tokens/).
Canonical values live in [`../../design.md`](../../design.md) and [`../../tokens/colors.json`](../../tokens/colors.json). This page is the human-facing rationale.
This page is for human-facing rationale: when to use which token, examples,
and platform notes.
## The system in one paragraph
KDC's palette is built around **Kode purple** `#6B35A7`, a **purple-tinted ink** `#1A1530` (never pure black), and a **warm off-white canvas** `#F7F4ED`. Together they give surfaces a craft / editorial feel rather than the cold slate look of generic SaaS. A muted **gold accent** `#C9892B` is reserved for emphasis CTAs in product surfaces; a **mint accent** `#46C194` is reserved for marketing surfaces. Both accents are deliberately scarce — one per surface.
## Layers
1. **Brand**`brand.primary` and its four variants (`primaryHover`, `primaryPressed`, `primaryLight`, `primarySoft`), `brand.ink`, `brand.canvas`.
2. **Accent**`accent.gold` (product emphasis) and `accent.mint` (marketing).
3. **Neutral** — a 13-step ink-tinted ramp (`neutral.0``neutral.1000`). Aliases: `100``canvas`, `900``ink`.
4. **Semantic**`success` `#2F7A5A`, `warning` `#C9892B`, `danger` `#B0382F`, `info` `#5C2D90`. Each has a `*Soft` tint for backgrounds.
5. **Role tokens** — always consume `text.default`, `surface.canvas`, `border.default`, etc. in product code. Never hardcode the palette.
## Choosing a colour
- **Default text** → `text.default` (`brand.ink`). For supporting copy, `text.soft`; for meta, `text.muted`.
- **Page surface** → `surface.canvas` (`brand.canvas`) for product UIs, `surface.background` (`#FFFFFF`) for content cards.
- **Primary CTA** → `brand.primary` + hover `brand.primaryHover`.
- **Emphasis CTA / commit** → `accent.gold`. Only one per surface, and only when there's a meaningful one-way action.
- **Status pill / badge** → `semantic.{success|warning|danger}Soft` background + matching semantic text.
## Contrast (WCAG 2.2 AA minimums)
- Body text on background ≥ 4.5:1.
- Large text (≥ 24px, or ≥ 19px bold) and UI icons ≥ 3:1.
- `text.muted` `#6B647A` on `surface.background` `#FFFFFF` → 5.4:1 ✓
- `brand.primary` `#6B35A7` on `surface.background` → 6.9:1 ✓
- `accent.gold` `#C9892B` on `surface.background` → 3.0:1 — meets large-text only; pair with text labels for small UI.
## Drift to be aware of
The legacy apps (kdcdiary, kdctracker, kdcdocs, kdcmapper) currently use different purples (#6B4EFF, #A37CD9, #5567E8) and fonts (Geist, Inter+Outfit). Treat anything not in this swatch sheet as drift; new work follows the canonical palette.
+49 -13
View File
@@ -4,24 +4,60 @@
| Token | Hex | Use |
|-------|-----|-----|
| `brand.primary` | `#6B35A7` | Kode purple — primary brand color, links, primary actions. |
| `brand.ink` | `#000000` | Wordmark, high-emphasis text. |
| `brand.primary` | `#6B35A7` | Kode purple — primary brand colour, links, primary actions. |
| `brand.primaryHover` | `#5C2D90` | Hover state on `brand.primary` surfaces. |
| `brand.primaryPressed`| `#45216E` | Pressed / deepest variant. |
| `brand.primaryLight` | `#8456C2` | Gradient end, dark-mode primary. |
| `brand.primarySoft` | `#F1E9F8` | Tinted surface for chips, callouts, focus halos. |
| `brand.ink` | `#1A1530` | Brand ink — high-emphasis text. **Not** pure black. |
| `brand.canvas` | `#F7F4ED` | Warm off-white — default page surface. |
| `brand.canvasCool` | `#F7F6FB` | Cool off-white — marketing dashboard only. |
| `neutral.0` | `#FFFFFF` | Pure white — cards, modals, top-bar. |
| `brand.canvas` | `#F5F4F0` | Brand off-white — default page background. |
## Primary state ramp
## Accent
| Token | Hex |
|-------|-----|
| `brand.primary` | `#6B35A7` |
| `brand.primaryHover` | `#582A8D` |
| `brand.primaryPressed` | `#45216E` |
| Token | Hex | Use |
|-------|-----|-----|
| `accent.gold` | `#C9892B` | Product emphasis CTA (one per surface). |
| `accent.goldStrong` | `#A86F1F` | Pressed / dark text on gold-soft. |
| `accent.goldSoft` | `#F5E9D2` | Background for accent chips and warning pills. |
| `accent.mint` | `#46C194` | Marketing accent — workspace dashboard only. |
## Neutral ramp (purple-tinted)
| Token | Hex | Role |
|-------|-----|------|
| `neutral.0` | `#FFFFFF` | Pure surface |
| `neutral.50` | `#FBF8F1` | Inset / secondary surface |
| `neutral.100` | `#F7F4ED` | = `brand.canvas` |
| `neutral.150` | `#ECE9F3` | Cool subtle line |
| `neutral.200` | `#E6E0D2` | Hairline border |
| `neutral.300` | `#D4CCB8` | Strong border |
| `neutral.400` | `#A39DB0` | Disabled / placeholder |
| `neutral.500` | `#8C869C` | Muted meta text |
| `neutral.600` | `#6B647A` | Mute text |
| `neutral.700` | `#4A4360` | Soft ink |
| `neutral.800` | `#3A2D5A` | Secondary ink |
| `neutral.900` | `#1A1530` | = `brand.ink` |
| `neutral.1000` | `#000000` | Pure black (sparingly) |
## Semantic
| Token | Hex | Soft tint |
|-------|-----|-----------|
| `semantic.success` | `#2F7A5A` | `#E2F0E8` |
| `semantic.warning` | `#C9892B` | `#F5E9D2` |
| `semantic.danger` | `#B0382F` | `#F7E4E1` |
| `semantic.info` | `#5C2D90` | `#F1E9F8` |
## Brand-scale (Tailwind compatibility)
| Token | Hex |
|-------|-----|
| `semantic.success` | `#16A34A` |
| `semantic.warning` | `#F59E0B` |
| `semantic.danger` | `#DC2626` |
| `semantic.info` | `#0EA5E9` |
| `scale.brand-50` | `#F3EDFB` |
| `scale.brand-100` | `#E6DAF7` |
| `scale.brand-200` | `#F1E9F8` |
| `scale.brand-500` | `#6B35A7` (= `brand.primary`) |
| `scale.brand-600` | `#5D2C93` |
| `scale.brand-700` | `#52287F` |
| `scale.brand-800` | `#45216E` |
+24 -5
View File
@@ -1,8 +1,27 @@
# Foundations · Elevation
Reference documentation for the **elevation** foundation. The canonical token values
live in [`../../design.md`](../../design.md) and the machine-readable export
in [`../../tokens/`](../../tokens/).
Five-step shadow scale tinted with the brand ink so shadows read **warm** against the canvas — not cold slate.
This page is for human-facing rationale: when to use which token, examples,
and platform notes.
## Scale
| Level | Shadow | Use |
|-------|--------|-----|
| `0` | none | Flush surfaces, page background |
| `1` | `0 1px 2px rgba(26,21,48,0.04)` | Cards, list rows, buttons |
| `2` | `0 4px 12px -4px rgba(26,21,48,0.08)` | Dropdowns, popovers |
| `3` | `0 16px 40px -16px rgba(26,21,48,0.18)` | Modals, dialogs |
| `4` | `0 24px 80px -24px rgba(26,21,48,0.22)` | Toasts, command palette |
| `focusRing` | `0 0 0 3px rgba(107,53,167,0.18)` | Any focusable element |
`rgb(26 21 48)` is `brand.ink`; `rgb(107 53 167)` is `brand.primary`.
## Rules
- ✅ Treat the scale as discrete. Hover may bump elevation one step, never two.
- ✅ Use `focusRing` consistently — never invent a per-component focus style.
- ❌ Don't use elevation for decoration. Reserve it for hierarchy and affordance.
- ❌ Don't use grey / black shadows; they look dirty on the warm canvas.
## Dark theme
In dark theme, elevation is communicated primarily through **lighter surface tints** rather than darker shadows. Shadows still exist but with reduced alpha (0.30.4 of brand.ink). See `themes/dark/theme.json`.
+20 -5
View File
@@ -1,8 +1,23 @@
# Foundations · Grid
Reference documentation for the **grid** foundation. The canonical token values
live in [`../../design.md`](../../design.md) and the machine-readable export
in [`../../tokens/`](../../tokens/).
12-column responsive grid, with gutters that scale by breakpoint.
This page is for human-facing rationale: when to use which token, examples,
and platform notes.
| Breakpoint | Columns | Gutter | Margin |
|------------|---------|--------|--------|
| < `md` (mobile) | 4 | 16px | 16px |
| `md` (tablet) | 8 | 24px | 24px |
| ≥ `lg` (desktop) | 12 | 24px | 32px |
Max content width: **1200px**, centered.
## Composition
- **Cards** snap to multiples of 3 columns (4-up / 3-up / 2-up).
- **Form fields** snap to multiples of 4 (full / half / third / quarter).
- **Sidebar layouts** reserve 3 columns left (240px ≈ 3 cols at 1200px container) and 9 columns for content.
## Rules
- ✅ Use CSS Grid for page layout, Flexbox for component internals.
- ✅ When in doubt, give content more room — KDS is generous with whitespace.
- ❌ Don't fight the grid for visual interest. Asymmetry comes from content rhythm, not from breaking the column structure.
+40 -5
View File
@@ -1,8 +1,43 @@
# Foundations · Motion
Reference documentation for the **motion** foundation. The canonical token values
live in [`../../design.md`](../../design.md) and the machine-readable export
in [`../../tokens/`](../../tokens/).
Motion is functional, not decorative — it provides feedback (state change), continuity (route / modal transitions), and confirmation (success / commit).
This page is for human-facing rationale: when to use which token, examples,
and platform notes.
## Durations
| Token | ms | Use |
|-------|-----|-----|
| `instant` | 0 | Reset / disable |
| `fast` | 120 | Hover, focus, control state |
| `base` | 200 | Tooltips, dropdowns |
| `slow` | 320 | Modals, sheets, route transitions |
| `slower` | 480 | Hero animations, marketing |
## Easing
| Token | Curve | Use |
|-------|-------|-----|
| `standard` | `cubic-bezier(0.2, 0, 0, 1)` | Default — most state changes |
| `enter` | `cubic-bezier(0, 0, 0.2, 1)` | Enter-only (decelerate to rest) |
| `exit` | `cubic-bezier(0.4, 0, 1, 1)` | Exit-only (accelerate away) |
| `emphasis` | `cubic-bezier(0.2, 0, 0, 1.2)` | Overshoot — success, commit, attention |
## Signature animation
`kdc-fade-up` — used on the product family for content reveal:
```css
@keyframes kdc-fade-up {
from { opacity: 0; transform: translateY(6px); }
to { opacity: 1; transform: translateY(0); }
}
.kdc-fade-up { animation: kdc-fade-up 360ms cubic-bezier(0.2, 0, 0, 1) both; }
```
Stagger children at 4060ms intervals.
## Rules
- ✅ Respect `prefers-reduced-motion: reduce` — collapse all transitions to ≤ 10ms.
- ✅ Use `fast` for state, `base` for surfaces, `slow` for layout shifts.
- ❌ Don't animate `width` / `height` — animate `transform` and `opacity`.
- ❌ Don't bounce or spin without reason. Loading spinners are the only exception.
+25 -5
View File
@@ -1,8 +1,28 @@
# Foundations · Radius
Reference documentation for the **radius** foundation. The canonical token values
live in [`../../design.md`](../../design.md) and the machine-readable export
in [`../../tokens/`](../../tokens/).
Corner radius scales with component density. Pick from the curated set — never hand-roll a value.
This page is for human-facing rationale: when to use which token, examples,
and platform notes.
## Scale
| Token | px | Use |
|-------|-----|-----|
| `none` | 0 | Resets, full-bleed images |
| `xs` | 2 | Chips, inline tags |
| `sm` | 4 | Inputs, small controls, tooltips |
| `md` | 8 | Buttons, default surfaces |
| `lg` | 12 | Cards, sections |
| `xl` | 16 | Modals, sheets, elevated cards |
| `2xl` | 24 | Marketing surfaces, feature panels |
| `full` | 9999 | Pills, avatars, badges, circular buttons |
## Rules
- ✅ Match radius to size: small controls get `sm`, big surfaces get `lg`/`xl`. A 600px modal at `rounded.sm` looks broken.
- ✅ Nested radii — the inner radius should be `outer - padding` to keep the corner concentric. Most of the time `lg` outside + `md` inside reads right.
- ❌ Don't mix more than two radii in a single composition.
- ❌ Don't use `full` on anything taller than 32px unless it's intentionally circular.
## Per-platform notes
- iOS: Apple's continuous corners are not directly available in CSS — accept the small perceptual difference rather than approximating.
- Tailwind v4: bind these to `--radius-{token}` in `@theme`, e.g. `--radius-md: 8px`. The product family does this in `globals.css`.
+36 -5
View File
@@ -1,8 +1,39 @@
# Foundations · Spacing
Reference documentation for the **spacing** foundation. The canonical token values
live in [`../../design.md`](../../design.md) and the machine-readable export
in [`../../tokens/`](../../tokens/).
KDS is built on a **4px base unit**. All spacing, sizing, and layout dimensions are multiples of 4.
This page is for human-facing rationale: when to use which token, examples,
and platform notes.
## Scale
| Token | px | rem | Use |
|-------|-----|-----|-----|
| `0` | 0 | 0 | Reset |
| `1` | 4 | 0.25 | Hairline / icon-to-text |
| `2` | 8 | 0.5 | Tight stack |
| `3` | 12 | 0.75 | Default field-to-label |
| `4` | 16 | 1 | Default container padding (mobile) |
| `5` | 20 | 1.25 | Card inner gap |
| `6` | 24 | 1.5 | Default card padding, section gap |
| `8` | 32 | 2 | Form section separator |
| `10` | 40 | 2.5 | Page section separator (small) |
| `12` | 48 | 3 | Page section separator (default) |
| `16` | 64 | 4 | Hero block padding |
| `20` | 80 | 5 | Marketing hero |
| `24` | 96 | 6 | Marketing hero, very generous |
## Density
Product apps expose `[data-density="compact|default|cozy"]` on the `<html>` root (see kdcmapper). Each density rebinds two CSS variables:
| Density | `--gap` | `--pad` |
|---------|---------|---------|
| compact | 12px (`3`) | 14px |
| default | 18px | 22px |
| cozy | 18px | 22px |
Use density for table-heavy / wizard-heavy screens. Don't fork component padding per screen — change density instead.
## Rules
- ✅ Always pick from the scale. If the value isn't on the scale, you're probably making the wrong choice.
- ✅ Pair gap and padding from the same step where possible (`gap-4` inside `p-4`).
- ❌ Don't use 1px, 3px, 5px etc. as spacing — those are border / decoration only.
+45 -5
View File
@@ -1,14 +1,54 @@
# Foundations · Typography
KDS uses two brand typefaces:
KDS uses two brand typefaces.
| Family | Use | Weights |
|--------|-----|---------|
| **Opificio** | Wordmark and display moments only. | Bold (700) — canonical. Rounded and Regular cuts available for editorial. |
| **Manrope** | All UI typography — headings, body, labels, captions. | 200 ExtraLight, 300 Light, 400 Regular, 500 Medium, 600 SemiBold, 700 Bold, 800 ExtraBold |
| **Manrope** (variable) | All UI typography — headings, body, labels, captions. | 200 ExtraLight, 300 Light, 400 Regular, 500 Medium, 600 SemiBold, 700 Bold, 800 ExtraBold |
| **Roboto Mono** *(non-brand)* | Code, tabular figures, identifiers. | 400, 500 |
Roboto Mono is available as a non-brand fallback for code and tabular figures.
Manrope is loaded as a variable font via `@fontsource-variable/manrope` (product apps) or `next/font/google` (Next.js apps). Use the **Variable** build wherever possible — it lets us interpolate weight without shipping multiple files.
The wordmark lockup pairs them: `kode` in Opificio Bold (72pt, 52pt spacing) above `design consultants` in Manrope ExtraLight (20pt, 76pt spacing).
## Wordmark lockup
Canonical token values live in [`../../design.md`](../../design.md); machine-readable typography tokens are in [`../../tokens/typography.json`](../../tokens/typography.json).
`kode` in Opificio Bold (72pt) above `design consultants` in Manrope ExtraLight (20pt, +4% letter-spacing).
## Type scale (UI)
| Token | Family | Size | Weight | Line height | Use |
|-------|--------|------|--------|-------------|-----|
| `display` | Opificio | 72px | 700 | 80px | Hero / wordmark only |
| `wordmarkSecondary` | Manrope | 20px | 200 | 28px | Wordmark second line |
| `headingXL` | Manrope | 32px | 700 | 40px | Page hero |
| `headingL` | Manrope | 24px | 600 | 32px | Section heading |
| `headingM` | Manrope | 20px | 600 | 28px | Subsection / card title |
| `headingS` | Manrope | 16px | 600 | 24px | Group label |
| `bodyL` | Manrope | 18px | 400 | 28px | Lead paragraph |
| `bodyM` *(default)* | Manrope | 16px | 400 | 24px | Body copy |
| `bodyS` | Manrope | 14px | 400 | 20px | Secondary / helper |
| `caption` | Manrope | 12px | 500 | 16px | Labels, badges, meta |
| `mono` | Roboto Mono | 14px | 400 | 20px | Code, identifiers |
## OpenType features
Product apps enable `font-feature-settings: "ss01", "cv11"` on Manrope. These swap the default `a` and adjust the `1` / `l` for clearer numeric reading on the warm canvas. Keep them on by default — they are part of the brand voice.
For numerics use `font-variant-numeric: tabular-nums` (utility class `.tnum`) so columns don't jitter.
## Display weight
`.font-display` class is `Manrope 800` with `letter-spacing: -0.02em`. Use for hero headlines where Opificio would be too much.
## Web font delivery
- Self-host Manrope from `/assets/fonts/manrope/` and preload **400** + **600** in the document head.
- Opificio is licensed; self-host from `/assets/fonts/opificio/`. Don't expose source files publicly.
- Fallback stack: `'Manrope', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif`.
## Do / Don't
- ✅ Default body to `bodyM` with `line-height: 1.5` for readability.
- ✅ Use Opificio for the wordmark and nowhere else.
- ❌ Don't mix Manrope with Geist / Inter / Outfit. The legacy apps that do so are drift to be migrated.
- ❌ Don't justify text. Don't underline anything that isn't a link.
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@kdc/design-system",
"version": "0.1.0-alpha.0",
"version": "1.0.0",
"description": "KDC Design System — tokens, components, and brand assets.",
"private": true,
"scripts": {
+20 -8
View File
@@ -1,13 +1,25 @@
{
"name": "KDC Dark",
"$description": "Dark theme — lifted purple primary on near-black ink surface. Source: kdcvault dark overrides.",
"colors": {
"surface.background": "#0B0D12",
"surface.subtle": "#12151B",
"surface.muted": "#1B1F27",
"text.default": "#F7F8FA",
"text.muted": "#B8BDC9",
"text.inverse": "#111827",
"border.default": "#2A2F3A",
"border.strong": "#3A4150"
"surface.background": "#0E0B1A",
"surface.canvas": "#14102A",
"surface.subtle": "#1C1838",
"surface.muted": "#2A2040",
"text.default": "#F1ECFA",
"text.soft": "#C8C0DA",
"text.muted": "#8A82A0",
"text.disabled": "#5A526E",
"text.inverse": "#1A1530",
"text.brand": "#A37CD9",
"border.default": "#2A2D47",
"border.strong": "#3D3A55",
"border.subtle": "#1F1C36",
"accent.primary": "#8456C2",
"accent.emphasis": "#D9A858",
"status.success": "#5BB58A",
"status.warning": "#D9A858",
"status.danger": "#D9695E",
"status.info": "#A37CD9"
}
}
+15 -3
View File
@@ -1,13 +1,25 @@
{
"name": "KDC High Contrast",
"$description": "WCAG AAA-targeted theme. Pure black/white with brand purple preserved as the only chromatic accent.",
"colors": {
"surface.background": "#000000",
"surface.subtle": "#000000",
"surface.muted": "#0A0A0A",
"surface.canvas": "#000000",
"surface.subtle": "#0A0A0A",
"surface.muted": "#141414",
"text.default": "#FFFFFF",
"text.soft": "#FFFFFF",
"text.muted": "#EDEFF3",
"text.disabled": "#B8BDC9",
"text.inverse": "#000000",
"text.brand": "#C7A6E8",
"border.default": "#FFFFFF",
"border.strong": "#FFFFFF"
"border.strong": "#FFFFFF",
"border.subtle": "#FFFFFF",
"accent.primary": "#C7A6E8",
"accent.emphasis": "#FFD27A",
"status.success": "#7AE0A0",
"status.warning": "#FFD27A",
"status.danger": "#FF8A8A",
"status.info": "#A6D8FF"
}
}
+18 -6
View File
@@ -1,13 +1,25 @@
{
"name": "KDC Light",
"$description": "Default product theme — warm canvas, purple-tinted ink. Source: kdcsurveyadd, kdcvault, kdc_void_planner.",
"colors": {
"surface.background": "#FFFFFF",
"surface.subtle": "#F7F8FA",
"surface.muted": "#EDEFF3",
"text.default": "#111827",
"text.muted": "#6B7280",
"surface.canvas": "#F7F4ED",
"surface.subtle": "#FBF8F1",
"surface.muted": "#F1E9F8",
"text.default": "#1A1530",
"text.soft": "#3A2D5A",
"text.muted": "#6B647A",
"text.disabled": "#A39DB0",
"text.inverse": "#FFFFFF",
"border.default": "#D9DCE3",
"border.strong": "#B8BDC9"
"text.brand": "#6B35A7",
"border.default": "#E6E0D2",
"border.strong": "#D4CCB8",
"border.subtle": "#ECE9F3",
"accent.primary": "#6B35A7",
"accent.emphasis": "#C9892B",
"status.success": "#2F7A5A",
"status.warning": "#C9892B",
"status.danger": "#B0382F",
"status.info": "#5C2D90"
}
}
+42 -16
View File
@@ -1,29 +1,55 @@
{
"$schema": "https://design-tokens.github.io/community-group/format/",
"$description": "Reconciled from production usage in kdcsurveyadd, kdcvault, kdc_void_planner (refined product family) and k-d-c-workspace dashboard (marketing surface). 2026-05-17.",
"color": {
"brand": {
"primary": { "$value": "#6B35A7", "$type": "color", "$description": "Kode purple — primary brand color." },
"primary-hover": { "$value": "#582A8D", "$type": "color" },
"primary-pressed": { "$value": "#45216E", "$type": "color" },
"ink": { "$value": "#000000", "$type": "color", "$description": "Brand black — wordmark and high-emphasis text." },
"canvas": { "$value": "#F5F4F0", "$type": "color", "$description": "Brand off-white — default page surface." }
"primary": { "$value": "#6B35A7", "$type": "color", "$description": "Kode purple — primary brand color. Confirmed across all on-brand surfaces." },
"primary-hover": { "$value": "#5C2D90", "$type": "color", "$description": "Hover / pressed-light. (alias: primary-2)" },
"primary-pressed": { "$value": "#45216E", "$type": "color", "$description": "Pressed / deep. Legacy alias for darkest variant." },
"primary-light": { "$value": "#8456C2", "$type": "color", "$description": "Light variant for gradients and dark-mode primary. (alias: primary-3)" },
"primary-soft": { "$value": "#F1E9F8", "$type": "color", "$description": "Tinted background for primary surfaces (chips, callouts, focus halos)." },
"ink": { "$value": "#1A1530", "$type": "color", "$description": "Brand ink — primary text. Purple-tinted near-black, NOT pure #000. Confirmed across surveyadd / vault / void / workspace." },
"canvas": { "$value": "#F7F4ED", "$type": "color", "$description": "Brand off-white — warm page surface for product UIs." },
"canvas-cool": { "$value": "#F7F6FB", "$type": "color", "$description": "Cool off-white variant used on the marketing dashboard. Avoid mixing with `canvas` on the same surface." }
},
"accent": {
"gold": { "$value": "#C9892B", "$type": "color", "$description": "Product accent — used for emphasis CTAs and submit buttons in surveyadd / vault / void." },
"gold-strong": { "$value": "#A86F1F", "$type": "color" },
"gold-soft": { "$value": "#F5E9D2", "$type": "color" },
"mint": { "$value": "#46C194", "$type": "color", "$description": "Marketing accent — secondary brand colour used on the workspace dashboard." }
},
"neutral": {
"0": { "$value": "#FFFFFF", "$type": "color" },
"50": { "$value": "#F5F4F0", "$type": "color", "$description": "Aliased to brand.canvas." },
"100": { "$value": "#EDEFF3", "$type": "color" },
"200": { "$value": "#D9DCE3", "$type": "color" },
"300": { "$value": "#B8BDC9", "$type": "color" },
"500": { "$value": "#6B7280", "$type": "color" },
"700": { "$value": "#374151", "$type": "color" },
"900": { "$value": "#111827", "$type": "color" },
"50": { "$value": "#FBF8F1", "$type": "color", "$description": "Inset / secondary surface (matches surface-2 in product family)." },
"100": { "$value": "#F7F4ED", "$type": "color", "$description": "Aliased to brand.canvas." },
"150": { "$value": "#ECE9F3", "$type": "color", "$description": "Cool subtle line (workspace)." },
"200": { "$value": "#E6E0D2", "$type": "color", "$description": "Hairline border." },
"300": { "$value": "#D4CCB8", "$type": "color", "$description": "Strong border." },
"400": { "$value": "#A39DB0", "$type": "color", "$description": "Disabled / mute-2 text." },
"500": { "$value": "#8C869C", "$type": "color", "$description": "Muted meta text." },
"600": { "$value": "#6B647A", "$type": "color", "$description": "Mute text." },
"700": { "$value": "#4A4360", "$type": "color", "$description": "Soft ink — supporting copy." },
"800": { "$value": "#3A2D5A", "$type": "color", "$description": "Secondary ink." },
"900": { "$value": "#1A1530", "$type": "color", "$description": "Aliased to brand.ink." },
"1000": { "$value": "#000000", "$type": "color" }
},
"semantic": {
"success": { "$value": "#16A34A", "$type": "color" },
"warning": { "$value": "#F59E0B", "$type": "color" },
"danger": { "$value": "#DC2626", "$type": "color" },
"info": { "$value": "#0EA5E9", "$type": "color" }
"success": { "$value": "#2F7A5A", "$type": "color", "$description": "Muted green tuned for the warm canvas." },
"success-soft": { "$value": "#E2F0E8", "$type": "color" },
"warning": { "$value": "#C9892B", "$type": "color", "$description": "Aliased to accent.gold." },
"warning-soft": { "$value": "#F5E9D2", "$type": "color" },
"danger": { "$value": "#B0382F", "$type": "color" },
"danger-soft": { "$value": "#F7E4E1", "$type": "color" },
"info": { "$value": "#5C2D90", "$type": "color", "$description": "Re-uses primary-hover for informational signals." }
},
"scale": {
"brand-50": { "$value": "#F3EDFB", "$type": "color" },
"brand-100": { "$value": "#E6DAF7", "$type": "color" },
"brand-200": { "$value": "#F1E9F8", "$type": "color", "$description": "Aliased to brand.primary-soft." },
"brand-500": { "$value": "#6B35A7", "$type": "color", "$description": "Aliased to brand.primary." },
"brand-600": { "$value": "#5D2C93", "$type": "color" },
"brand-700": { "$value": "#52287F", "$type": "color" },
"brand-800": { "$value": "#45216E", "$type": "color" }
}
}
}
+6 -4
View File
@@ -1,9 +1,11 @@
{
"$description": "Ink-tinted shadow scale (rgb 26 21 48 = brand.ink). Matches the surveyadd / vault / void product family.",
"elevation": {
"0": { "shadow": "none" },
"1": { "shadow": "0 1px 2px rgba(17,24,39,0.06)" },
"2": { "shadow": "0 4px 12px rgba(17,24,39,0.10)" },
"3": { "shadow": "0 16px 32px rgba(17,24,39,0.16)" },
"4": { "shadow": "0 24px 48px rgba(17,24,39,0.20)" }
"1": { "shadow": "0 1px 2px rgba(26,21,48,0.04)" },
"2": { "shadow": "0 4px 12px -4px rgba(26,21,48,0.08)" },
"3": { "shadow": "0 16px 40px -16px rgba(26,21,48,0.18)" },
"4": { "shadow": "0 24px 80px -24px rgba(26,21,48,0.22)" },
"focus-ring": { "shadow": "0 0 0 3px rgba(107,53,167,0.18)", "$description": "Primary focus halo. Use on any focusable element." }
}
}