From 74a994dbeaa230c0c9ee071a0dc95f6b554c9aca Mon Sep 17 00:00:00 2001 From: Drew Stone Date: Wed, 23 Sep 2026 18:53:18 -0700 Subject: [PATCH] feat(tokens): opt-in colour ladders and a semantic token layer - styles/ladders.css: 12 ramps x 12 steps, light and dark, from Radix Colors 3.0.0, with role aliases and a per-domain ramp under [data-domain]. - styles/system.css: every tokens.css family mapped to a ladder step, plus semantic tokens, role radii, type roles, motion and three shadow levels. - scripts/gen-ladders.mjs and radix-ramps.json generate both files, so an app that ships ahead of a release keeps an identical copy. Opt in: nothing changes for an app that does not import the two files. Co-Authored-By: Claude Opus 5.5 (1M context) --- .changeset/colour-ladders.md | 11 + packages/brand/README.md | 21 + packages/brand/package.json | 5 +- packages/brand/scripts/gen-ladders.mjs | 338 ++++++++++++++ packages/brand/scripts/radix-ramps.json | 362 ++++++++++++++ packages/brand/src/styles/ladders.css | 595 ++++++++++++++++++++++++ packages/brand/src/styles/system.css | 427 +++++++++++++++++ 7 files changed, 1758 insertions(+), 1 deletion(-) create mode 100644 .changeset/colour-ladders.md create mode 100644 packages/brand/scripts/gen-ladders.mjs create mode 100644 packages/brand/scripts/radix-ramps.json create mode 100644 packages/brand/src/styles/ladders.css create mode 100644 packages/brand/src/styles/system.css diff --git a/.changeset/colour-ladders.md b/.changeset/colour-ladders.md new file mode 100644 index 0000000..196e9e9 --- /dev/null +++ b/.changeset/colour-ladders.md @@ -0,0 +1,11 @@ +--- +"@tangle-network/brand": minor +--- + +Add opt-in colour ladders and a semantic token layer. + +`@tangle-network/brand/styles/ladders.css` defines 12 ramps of 12 steps, light and dark, from Radix Colors 3.0.0, with role aliases (`--gray-*`, `--accent-*`, `--success-*`, `--warning-*`, `--danger-*`, `--info-*`) and a per-domain ramp under `[data-domain]`. + +`@tangle-network/brand/styles/system.css` maps every `tokens.css` family onto a ladder step and adds semantic tokens, role radii, type roles, motion and three shadow levels. Light pages become white and dark becomes a neutral mauve ladder. It is opt in: nothing changes for an app that does not import it. + +`scripts/gen-ladders.mjs` generates both files; `pnpm --filter @tangle-network/brand gen:ladders` rewrites them. diff --git a/packages/brand/README.md b/packages/brand/README.md index 8e077f5..7f1c0d9 100644 --- a/packages/brand/README.md +++ b/packages/brand/README.md @@ -39,6 +39,27 @@ Fonts are **not** bundled — see [Fonts](#fonts) below. @import "@tangle-network/brand/styles/globals"; ``` +### Colour ladders (opt in) + +```css +@import "@tangle-network/brand/styles"; +@import "@tangle-network/brand/styles/ladders.css"; +@import "@tangle-network/brand/styles/system.css"; +``` + +`ladders.css` defines 12 ramps of 12 steps each, in light and dark, from Radix Colors 3.0.0: mauve (neutral), iris (brand), green, amber, red and blue (status), and plum, orange, grass, bronze, crimson and olive (domains). +Each step has one job: 1–2 backgrounds, 3–5 control fills, 6–8 borders, 9–10 solid fills, 11 secondary text and 12 primary text. +Role aliases (`--gray-*`, `--accent-*`, `--success-*`, `--warning-*`, `--danger-*`, `--info-*`) point at the ramps. +Setting `data-domain="tax"` (or another domain key) on an element gives its subtree `--domain-1..12`. + +`system.css` maps every token family in `tokens.css` onto a ladder step and adds semantic tokens (`--bg-page`, `--line`, `--fg-muted`, `--accent-text`, `--ink`), role radii, type roles, motion and three shadow levels. +Light pages become white, and dark becomes a neutral mauve ladder. +Import it after `tokens.css`; it is opt in because it changes every surface of the app that loads it. + +Both files are generated. +Change a value in `scripts/gen-ladders.mjs` or `scripts/radix-ramps.json`, then run `pnpm --filter @tangle-network/brand gen:ladders`. +An app that ships ahead of a release copies the two generated files verbatim, so its copy stays identical. + ### Logo ```tsx diff --git a/packages/brand/package.json b/packages/brand/package.json index f62a9c8..b5eb8a6 100644 --- a/packages/brand/package.json +++ b/packages/brand/package.json @@ -36,10 +36,13 @@ "./styles/tokens.css": "./src/styles/tokens.css", "./styles/globals.css": "./src/styles/globals.css", "./styles/theme.css": "./src/styles/theme.css", - "./styles/named-themes.css": "./src/styles/named-themes.css" + "./styles/named-themes.css": "./src/styles/named-themes.css", + "./styles/ladders.css": "./src/styles/ladders.css", + "./styles/system.css": "./src/styles/system.css" }, "scripts": { "build": "tsup src/index.ts --format esm --dts --clean", + "gen:ladders": "node scripts/gen-ladders.mjs", "dev": "tsup src/index.ts --format esm --dts --watch" }, "peerDependencies": { diff --git a/packages/brand/scripts/gen-ladders.mjs b/packages/brand/scripts/gen-ladders.mjs new file mode 100644 index 0000000..a98ad9e --- /dev/null +++ b/packages/brand/scripts/gen-ladders.mjs @@ -0,0 +1,338 @@ +#!/usr/bin/env node +// Writes ladders.css and system.css from radix-ramps.json. +// +// node scripts/gen-ladders.mjs [ramps.json] [out-dir] +// +// radix-ramps.json holds the @radix-ui/colors 3.0.0 values as static data, so +// the generated CSS has no runtime dependency. Apps that ship ahead of a brand +// release copy the two generated files verbatim; running this script is the +// only way to change a value, so every copy stays identical. +import fs from 'node:fs' +import path from 'node:path' +import { fileURLToPath } from 'node:url' + +const here = path.dirname(fileURLToPath(import.meta.url)) +const RAMPS_FILE = process.argv[2] ?? path.join(here, 'radix-ramps.json') +const OUT = process.argv[3] ?? path.join(here, '..', 'src', 'styles') +const R = JSON.parse(fs.readFileSync(RAMPS_FILE, 'utf8')) + +const RAMPS = ['mauve', 'iris', 'green', 'amber', 'red', 'blue', 'plum', 'orange', 'grass', 'bronze', 'crimson', 'olive'] +const ROLE = { gray: 'mauve', accent: 'iris', success: 'green', warning: 'amber', danger: 'red', info: 'blue' } +const DOMAIN = { + personal: 'plum', + hospitality: 'orange', + tax: 'grass', + law: 'blue', + procurement: 'bronze', + negotiation: 'crimson', + logistics: 'amber', + defense: 'olive', +} +for (const r of RAMPS) { + for (const t of ['light', 'dark']) { + if (!Array.isArray(R[r]?.[t]) || R[r][t].length !== 12) throw new Error(`${r} ${t}: expected 12 steps`) + } +} + +const DARK_SEL = ':root,\n[data-sandbox-ui],\n[data-theme="dark"],\n.dark' +const LIGHT_SEL = '[data-sandbox-theme="vault"],\n[data-theme="light"],\n.light' + +// ── ladders.css ──────────────────────────────────────────────────────────── +const steps = (theme) => + RAMPS.map((r) => R[r][theme].map((v, i) => ` --${r}-${i + 1}: ${v};`).join('\n')).join('\n') +const roles = Object.entries(ROLE) + .map(([role, ramp]) => Array.from({ length: 12 }, (_, i) => ` --${role}-${i + 1}: var(--${ramp}-${i + 1});`).join('\n')) + .join('\n') +const domainBlock = (sel, ramp) => + `${sel} {\n${Array.from({ length: 12 }, (_, i) => ` --domain-${i + 1}: var(--${ramp}-${i + 1});`).join('\n')}\n --domain-bubble: ${R[ramp].light[10]};\n}` + +const ladders = `/* Colour ladders. Generated by scripts/gen-ladders.mjs from @radix-ui/colors 3.0.0; do not edit by hand. + * + * Every ramp has 12 steps, and each step has one job: + * 1-2 app backgrounds + * 3-5 control fills: rest, hover, pressed + * 6-8 borders: subtle, default, hover + * 9-10 solid fills + * 11 readable secondary text + * 12 readable primary text + * Light and dark use the same names, so a component written against a step + * works in both themes. Dark is the :root default and light wins by source + * order, the same as tokens.css. The role aliases repeat in both blocks because + * a var() resolves where it is declared. */ +${DARK_SEL} { +${steps('dark')} +${roles} +} +${LIGHT_SEL} { +${steps('light')} +${roles} +} + +/* Domain ramp. Set data-domain on a cover, tile, avatar or listing and read + * --domain-1..12. An unknown domain gets the brand ramp. --domain-bubble is the + * light step 11 in both themes: white text on it passes 4.5:1 for every domain. + * A step 9 fill never carries text (white on orange 9 is 2.97:1). */ +${domainBlock(':root,\n[data-domain]', 'iris')} +${Object.entries(DOMAIN).map(([d, r]) => domainBlock(`[data-domain="${d}"]`, r)).join('\n')} +` + +// ── system.css ───────────────────────────────────────────────────────────── +const hex = (ref, theme) => { + if (ref.startsWith('#')) return ref + const [ramp, step] = ref.split('-') + return R[ramp][theme][Number(step) - 1] +} +const hsl = (h) => { + const [r, g, b] = [1, 3, 5].map((i) => parseInt(h.slice(i, i + 2), 16) / 255) + const max = Math.max(r, g, b) + const min = Math.min(r, g, b) + const l = (max + min) / 2 + let s = 0 + let hue = 0 + if (max !== min) { + const d = max - min + s = l > 0.5 ? d / (2 - max - min) : d / (max + min) + hue = max === r ? (g - b) / d + (g < b ? 6 : 0) : max === g ? (b - r) / d + 2 : (r - g) / d + 4 + hue *= 60 + } + return `${Math.round(hue)} ${Math.round(s * 1000) / 10}% ${Math.round(l * 1000) / 10}%` +} + +const W = '#ffffff' +// [token, light, dark, kind]. kind 'hsl' emits an "H S% L%" triplet for the +// shadcn layer; otherwise the value is var(--ramp-n) or a literal. +const MAP = [ + // Product semantic tokens. + ['bg-page', W, 'mauve-1'], + ['bg-subtle', 'mauve-2', 'mauve-2'], + ['bg-card', W, 'mauve-2'], + ['bg-raised', W, 'mauve-3'], + ['bg-control', 'mauve-3', 'mauve-3'], + ['bg-control-hover', 'mauve-4', 'mauve-4'], + ['bg-control-active', 'mauve-5', 'mauve-5'], + ['line-subtle', 'mauve-6', 'mauve-6'], + ['line', 'mauve-7', 'mauve-7'], + ['line-strong', 'mauve-8', 'mauve-8'], + // Input borders: 3.30:1 on white and 3.45:1 on dark step 2, above the 3:1 floor for controls. + ['line-field', 'mauve-9', 'mauve-9'], + ['fg', 'mauve-12', 'mauve-12'], + ['fg-muted', 'mauve-11', 'mauve-11'], + // Icons and disabled states only; 3.77:1 is below the body-text floor. + ['fg-faint', 'mauve-10', 'mauve-10'], + ['ink', 'mauve-12', 'mauve-12'], + ['on-ink', W, 'mauve-1'], + ['accent-solid', 'iris-9', 'iris-9'], + ['accent-solid-hover', 'iris-10', 'iris-10'], + ['accent-text', 'iris-11', 'iris-11'], + ['accent-soft', 'iris-3', 'iris-3'], + ['accent-soft-hover', 'iris-4', 'iris-4'], + ['accent-line', 'iris-7', 'iris-7'], + // shadcn / sandbox-ui HSL layer. + ['hsl-background', W, 'mauve-1', 'hsl'], + ['hsl-foreground', 'mauve-12', 'mauve-12', 'hsl'], + ['hsl-card', W, 'mauve-2', 'hsl'], + ['hsl-card-foreground', 'mauve-12', 'mauve-12', 'hsl'], + ['hsl-popover', W, 'mauve-3', 'hsl'], + ['hsl-popover-foreground', 'mauve-12', 'mauve-12', 'hsl'], + ['hsl-primary', 'iris-9', 'iris-9', 'hsl'], + ['hsl-primary-foreground', W, W, 'hsl'], + ['hsl-secondary', 'mauve-3', 'mauve-3', 'hsl'], + ['hsl-secondary-foreground', 'mauve-12', 'mauve-12', 'hsl'], + ['hsl-muted', 'mauve-3', 'mauve-3', 'hsl'], + ['hsl-muted-foreground', 'mauve-11', 'mauve-11', 'hsl'], + ['hsl-accent', 'mauve-4', 'mauve-4', 'hsl'], + ['hsl-accent-foreground', 'mauve-12', 'mauve-12', 'hsl'], + ['hsl-destructive', 'red-11', 'red-4', 'hsl'], + ['hsl-destructive-foreground', W, 'red-12', 'hsl'], + ['hsl-border', 'mauve-6', 'mauve-6', 'hsl'], + // `border-input` is the field outline in the ui Input, so it takes the field step. + ['hsl-input', 'mauve-9', 'mauve-9', 'hsl'], + ['hsl-ring', 'iris-9', 'iris-10', 'hsl'], + ['hsl-success', 'green-9', 'green-9', 'hsl'], + ['hsl-warning', 'amber-9', 'amber-9', 'hsl'], + ['hsl-info', 'blue-9', 'blue-9', 'hsl'], + ['sidebar-background', 'mauve-2', 'mauve-2', 'hsl'], + ['sidebar-foreground', 'mauve-12', 'mauve-12', 'hsl'], + ['sidebar-primary', 'iris-9', 'iris-9', 'hsl'], + ['sidebar-primary-foreground', W, W, 'hsl'], + ['sidebar-accent', 'mauve-4', 'mauve-4', 'hsl'], + ['sidebar-accent-foreground', 'mauve-12', 'mauve-12', 'hsl'], + ['sidebar-border', 'mauve-6', 'mauve-6', 'hsl'], + ['sidebar-ring', 'iris-9', 'iris-10', 'hsl'], + // MD3 family. + ['md3-surface', W, 'mauve-1'], + ['md3-surface-dim', 'mauve-2', 'mauve-1'], + ['md3-surface-bright', W, 'mauve-5'], + ['md3-surface-container-lowest', W, 'mauve-1'], + ['md3-surface-container-low', 'mauve-2', 'mauve-2'], + ['md3-surface-container', W, 'mauve-2'], + ['md3-surface-container-high', 'mauve-3', 'mauve-3'], + ['md3-surface-container-highest', W, 'mauve-4'], + ['md3-surface-variant', 'mauve-3', 'mauve-3'], + ['md3-on-surface', 'mauve-12', 'mauve-12'], + ['md3-on-surface-variant', 'mauve-11', 'mauve-11'], + ['md3-primary', 'iris-11', 'iris-11'], + ['md3-primary-dim', 'iris-10', 'iris-10'], + ['md3-primary-container', 'iris-9', 'iris-9'], + ['md3-on-primary', W, W], + ['md3-on-primary-container', W, W], + ['md3-outline', 'mauve-9', 'mauve-9'], + ['md3-outline-variant', 'mauve-6', 'mauve-6'], + ['md3-error', 'red-11', 'red-11'], + ['md3-error-container', 'red-3', 'red-3'], + ['md3-on-error-container', 'red-12', 'red-12'], + // Depth, bg, text and border families. + ['depth-1', W, 'mauve-1'], + ['depth-2', W, 'mauve-2'], + ['depth-3', 'mauve-2', 'mauve-3'], + ['depth-4', W, 'mauve-4'], + ['bg-root', W, 'mauve-1'], + ['bg-dark', 'mauve-2', 'mauve-1'], + ['bg-elevated', W, 'mauve-3'], + ['bg-section', 'mauve-2', 'mauve-1'], + ['bg-input', W, 'mauve-2'], + ['bg-hover', 'mauve-3', 'mauve-3'], + ['bg-selection', 'iris-4', 'iris-5'], + ['text-primary', 'mauve-12', 'mauve-12'], + ['text-secondary', 'mauve-12', 'mauve-12'], + ['text-muted', 'mauve-11', 'mauve-11'], + ['text-dim', 'mauve-11', 'mauve-11'], + ['border-subtle', 'mauve-6', 'mauve-6'], + ['border-default', 'mauve-7', 'mauve-7'], + ['border-hover', 'mauve-8', 'mauve-8'], + ['border-accent', 'iris-7', 'iris-7'], + ['border-accent-hover', 'iris-8', 'iris-8'], + ['btn-primary-bg', 'iris-9', 'iris-9'], + ['btn-primary-hover', 'iris-10', 'iris-10'], + ['btn-primary-text', W, W], + ['btn-cta-bg', 'iris-9', 'iris-9'], + ['btn-cta-text', W, W], + ['accent-surface-soft', 'iris-2', 'iris-2'], + ['accent-surface-strong', 'iris-4', 'iris-4'], + // Status chip: step 3 fill, step 6 border. Its text is step 12 in light, + // because step 11 on step 3 measures 4.21-4.25:1 there, and step 11 in dark. + ...['success:green', 'warning:amber', 'danger:red', 'info:blue', 'violet:iris', 'orange:orange', 'teal:grass'].flatMap( + (p) => { + const [n, r] = p.split(':') + return [ + [`surface-${n}-bg`, `${r}-3`, `${r}-3`], + [`surface-${n}-border`, `${r}-6`, `${r}-6`], + [`surface-${n}-text`, `${r}-12`, `${r}-11`], + ] + }, + ), + ['surface-neutral-bg', 'mauve-3', 'mauve-3'], + ['surface-neutral-border', 'mauve-6', 'mauve-6'], + ['surface-neutral-text', 'mauve-11', 'mauve-11'], + // Status text set straight on the page: step 11 passes 4.5:1 on white and on dark steps 1-2. + ['status-running', 'green-11', 'green-11'], + ['status-creating', 'iris-11', 'iris-11'], + ['status-stopped', 'amber-11', 'amber-11'], + ['status-warm', 'orange-11', 'orange-11'], + ['status-cold', 'blue-11', 'blue-11'], + ['status-error', 'red-11', 'red-11'], + ['status-deleted', 'mauve-11', 'mauve-11'], + ['brand-primary', 'iris-9', 'iris-9'], + ['brand-strong', 'iris-12', 'iris-3'], + ['brand-vibrant', 'iris-9', 'iris-9'], +] + +const emit = (theme) => + MAP.map(([t, l, d, kind]) => { + const ref = theme === 'light' ? l : d + if (kind === 'hsl') return ` --${t}: ${hsl(hex(ref, theme))};` + return ` --${t}: ${ref.startsWith('#') ? ref : `var(--${ref})`};` + }).join('\n') + +// Type roles use Tailwind's --text- naming, so a `text-` utility +// carries size, leading, tracking and weight together. +const TYPE = [ + ['display', 'clamp(2.5rem, 2rem + 3vw, 4rem)', '1.02', '-0.035em', '650'], + ['hero', 'clamp(2.25rem, 2.2rem + 1.6vw, 3.5rem)', '1.05', '-0.03em', '650'], + ['title', '2rem', '2.25rem', '-0.03em', '600'], + ['page', '1.625rem', '1.875rem', '-0.02em', '600'], + ['section', '1.375rem', '1.625rem', '-0.02em', '600'], + ['lead', '1.125rem', '1.5rem', '0em', '600'], + ['chat', '0.9375rem', '1.375rem', '0em', '400'], + ['figure', '2.75rem', '2.75rem', '-0.02em', '600'], +] +const typeVars = TYPE.map( + ([n, size, lh, tr, w]) => + ` --text-${n}: ${size};\n --text-${n}--line-height: ${lh};\n --text-${n}--letter-spacing: ${tr};\n --text-${n}--font-weight: ${w};`, +).join('\n') + +const shared = ` + /* The brand call to action. Use it once per page, on the primary consumer + * action. White text passes on every stop (5.37, 5.39 and 5.18:1). */ + --cta: linear-gradient(90deg, #5b5bd6 0%, #6e56cf 55%, #8e4ec6 100%); + --cta-hover: linear-gradient(90deg, #5151cd 0%, #654dc4 55%, #8347b9 100%); + /* Radius by role. The numeric --radius-sm..xl scale is unchanged, so no + * existing surface re-rounds; new surfaces pick the role they play. */ + --radius-tag: 4px; + --radius-chip: 8px; + --radius-field: 12px; + --radius-panel: 16px; + --radius-cover: 20px; + --radius-sheet: 24px; + --radius-pill: 999px; + /* Motion. */ + --ease-standard: cubic-bezier(0.2, 0, 0, 1); + --ease-emphasized: cubic-bezier(0.32, 0.72, 0, 1); + --dur-press: 120ms; + --dur-hover: 200ms; + --dur-card: 250ms; + --dur-sheet: 320ms; + --dur-reveal: 450ms; + /* Type. 12 px is the floor. */ + --font-size-xs: 0.75rem; +${typeVars}` + +const shadowsLight = ` + /* Elevation. In light, shadow separates a card from the white page. */ + --shadow-1: 0 0 0 1px rgb(33 31 38 / 0.03), 0 2px 6px rgb(33 31 38 / 0.05), 0 4px 8px rgb(33 31 38 / 0.08); + --shadow-2: 0 0 0 1px rgb(33 31 38 / 0.04), 0 6px 16px rgb(33 31 38 / 0.12); + --shadow-3: 0 8px 28px rgb(33 31 38 / 0.28); + --shadow-card: var(--shadow-1); + --shadow-dropdown: var(--shadow-2); + --shadow-overlay: var(--shadow-3);` +const shadowsDark = ` + /* Elevation. In dark, a white hairline states the edge; only floating layers cast a shadow. */ + --shadow-1: inset 0 0 0 1px rgb(255 255 255 / 0.06); + --shadow-2: inset 0 0 0 1px rgb(255 255 255 / 0.08), 0 8px 24px rgb(0 0 0 / 0.5); + --shadow-3: inset 0 0 0 1px rgb(255 255 255 / 0.1), 0 16px 48px rgb(0 0 0 / 0.6); + --shadow-card: var(--shadow-1); + --shadow-dropdown: var(--shadow-2); + --shadow-overlay: var(--shadow-3);` + +const system = `/* Semantic layer. Generated by scripts/gen-ladders.mjs; edit the MAP there, not this file. + * + * Requires ladders.css. Every token family (hsl, md3, depth, bg, text, border, + * surface, status, btn) points at a ladder step, so components built on these + * tokens re-skin without edits. Light pages are white and cards separate from + * them by shadow and media. Dark steps surfaces up in small lightness + * increments, draws hairline edges, and casts a shadow only under floating + * layers. Chrome is neutral (mauve); colour belongs to content and to one + * call to action per page. */ +${DARK_SEL} { +${emit('dark')}${shared}${shadowsDark} + color-scheme: dark; +} +${LIGHT_SEL} { +${emit('light')}${shared}${shadowsLight} + color-scheme: light; +} +@media (prefers-reduced-motion: reduce) { +${`${DARK_SEL},\n${LIGHT_SEL}`.replace(/^/gm, ' ')} { + --dur-press: 0ms; + --dur-hover: 0ms; + --dur-card: 0ms; + --dur-sheet: 0ms; + --dur-reveal: 0ms; + } +} +` + +fs.mkdirSync(OUT, { recursive: true }) +fs.writeFileSync(path.join(OUT, 'ladders.css'), ladders) +fs.writeFileSync(path.join(OUT, 'system.css'), system) +console.log(`wrote ladders.css (${ladders.length} B) and system.css (${MAP.length} tokens per theme) to ${OUT}`) diff --git a/packages/brand/scripts/radix-ramps.json b/packages/brand/scripts/radix-ramps.json new file mode 100644 index 0000000..f47367b --- /dev/null +++ b/packages/brand/scripts/radix-ramps.json @@ -0,0 +1,362 @@ +{ + "mauve": { + "light": [ + "#fdfcfd", + "#faf9fb", + "#f2eff3", + "#eae7ec", + "#e3dfe6", + "#dbd8e0", + "#d0cdd7", + "#bcbac7", + "#8e8c99", + "#84828e", + "#65636d", + "#211f26" + ], + "dark": [ + "#121113", + "#1a191b", + "#232225", + "#2b292d", + "#323035", + "#3c393f", + "#49474e", + "#625f69", + "#6f6d78", + "#7c7a85", + "#b5b2bc", + "#eeeef0" + ] + }, + "iris": { + "light": [ + "#fdfdff", + "#f8f8ff", + "#f0f1fe", + "#e6e7ff", + "#dadcff", + "#cbcdff", + "#b8baf8", + "#9b9ef0", + "#5b5bd6", + "#5151cd", + "#5753c6", + "#272962" + ], + "dark": [ + "#13131e", + "#171625", + "#202248", + "#262a65", + "#303374", + "#3d3e82", + "#4a4a95", + "#5958b1", + "#5b5bd6", + "#6e6ade", + "#b1a9ff", + "#e0dffe" + ] + }, + "green": { + "light": [ + "#fbfefc", + "#f4fbf6", + "#e6f6eb", + "#d6f1df", + "#c4e8d1", + "#adddc0", + "#8eceaa", + "#5bb98b", + "#30a46c", + "#2b9a66", + "#218358", + "#193b2d" + ], + "dark": [ + "#0e1512", + "#121b17", + "#132d21", + "#113b29", + "#174933", + "#20573e", + "#28684a", + "#2f7c57", + "#30a46c", + "#33b074", + "#3dd68c", + "#b1f1cb" + ] + }, + "amber": { + "light": [ + "#fefdfb", + "#fefbe9", + "#fff7c2", + "#ffee9c", + "#fbe577", + "#f3d673", + "#e9c162", + "#e2a336", + "#ffc53d", + "#ffba18", + "#ab6400", + "#4f3422" + ], + "dark": [ + "#16120c", + "#1d180f", + "#302008", + "#3f2700", + "#4d3000", + "#5c3d05", + "#714f19", + "#8f6424", + "#ffc53d", + "#ffd60a", + "#ffca16", + "#ffe7b3" + ] + }, + "red": { + "light": [ + "#fffcfc", + "#fff7f7", + "#feebec", + "#ffdbdc", + "#ffcdce", + "#fdbdbe", + "#f4a9aa", + "#eb8e90", + "#e5484d", + "#dc3e42", + "#ce2c31", + "#641723" + ], + "dark": [ + "#191111", + "#201314", + "#3b1219", + "#500f1c", + "#611623", + "#72232d", + "#8c333a", + "#b54548", + "#e5484d", + "#ec5d5e", + "#ff9592", + "#ffd1d9" + ] + }, + "blue": { + "light": [ + "#fbfdff", + "#f4faff", + "#e6f4fe", + "#d5efff", + "#c2e5ff", + "#acd8fc", + "#8ec8f6", + "#5eb1ef", + "#0090ff", + "#0588f0", + "#0d74ce", + "#113264" + ], + "dark": [ + "#0d1520", + "#111927", + "#0d2847", + "#003362", + "#004074", + "#104d87", + "#205d9e", + "#2870bd", + "#0090ff", + "#3b9eff", + "#70b8ff", + "#c2e6ff" + ] + }, + "plum": { + "light": [ + "#fefcff", + "#fdf7fd", + "#fbebfb", + "#f7def8", + "#f2d1f3", + "#e9c2ec", + "#deade3", + "#cf91d8", + "#ab4aba", + "#a144af", + "#953ea3", + "#53195d" + ], + "dark": [ + "#181118", + "#201320", + "#351a35", + "#451d47", + "#512454", + "#5e3061", + "#734079", + "#92549c", + "#ab4aba", + "#b658c4", + "#e796f3", + "#f4d4f4" + ] + }, + "orange": { + "light": [ + "#fefcfb", + "#fff7ed", + "#ffefd6", + "#ffdfb5", + "#ffd19a", + "#ffc182", + "#f5ae73", + "#ec9455", + "#f76b15", + "#ef5f00", + "#cc4e00", + "#582d1d" + ], + "dark": [ + "#17120e", + "#1e160f", + "#331e0b", + "#462100", + "#562800", + "#66350c", + "#7e451d", + "#a35829", + "#f76b15", + "#ff801f", + "#ffa057", + "#ffe0c2" + ] + }, + "grass": { + "light": [ + "#fbfefb", + "#f5fbf5", + "#e9f6e9", + "#daf1db", + "#c9e8ca", + "#b2ddb5", + "#94ce9a", + "#65ba74", + "#46a758", + "#3e9b4f", + "#2a7e3b", + "#203c25" + ], + "dark": [ + "#0e1511", + "#141a15", + "#1b2a1e", + "#1d3a24", + "#25482d", + "#2d5736", + "#366740", + "#3e7949", + "#46a758", + "#53b365", + "#71d083", + "#c2f0c2" + ] + }, + "bronze": { + "light": [ + "#fdfcfc", + "#fdf7f5", + "#f6edea", + "#efe4df", + "#e7d9d3", + "#dfcdc5", + "#d3bcb3", + "#c2a499", + "#a18072", + "#957468", + "#7d5e54", + "#43302b" + ], + "dark": [ + "#141110", + "#1c1917", + "#262220", + "#302a27", + "#3b3330", + "#493e3a", + "#5a4c47", + "#6f5f58", + "#a18072", + "#ae8c7e", + "#d4b3a5", + "#ede0d9" + ] + }, + "crimson": { + "light": [ + "#fffcfd", + "#fef7f9", + "#ffe9f0", + "#fedce7", + "#facedd", + "#f3bed1", + "#eaacc3", + "#e093b2", + "#e93d82", + "#df3478", + "#cb1d63", + "#621639" + ], + "dark": [ + "#191114", + "#201318", + "#381525", + "#4d122f", + "#5c1839", + "#6d2545", + "#873356", + "#b0436e", + "#e93d82", + "#ee518a", + "#ff92ad", + "#fdd3e8" + ] + }, + "olive": { + "light": [ + "#fcfdfc", + "#f8faf8", + "#eff1ef", + "#e7e9e7", + "#dfe2df", + "#d7dad7", + "#cccfcc", + "#b9bcb8", + "#898e87", + "#7f847d", + "#60655f", + "#1d211c" + ], + "dark": [ + "#111210", + "#181917", + "#212220", + "#282a27", + "#2f312e", + "#383a36", + "#454843", + "#5c625b", + "#687066", + "#767d74", + "#afb5ad", + "#eceeec" + ] + } +} \ No newline at end of file diff --git a/packages/brand/src/styles/ladders.css b/packages/brand/src/styles/ladders.css new file mode 100644 index 0000000..58dced3 --- /dev/null +++ b/packages/brand/src/styles/ladders.css @@ -0,0 +1,595 @@ +/* Colour ladders. Generated by scripts/gen-ladders.mjs from @radix-ui/colors 3.0.0; do not edit by hand. + * + * Every ramp has 12 steps, and each step has one job: + * 1-2 app backgrounds + * 3-5 control fills: rest, hover, pressed + * 6-8 borders: subtle, default, hover + * 9-10 solid fills + * 11 readable secondary text + * 12 readable primary text + * Light and dark use the same names, so a component written against a step + * works in both themes. Dark is the :root default and light wins by source + * order, the same as tokens.css. The role aliases repeat in both blocks because + * a var() resolves where it is declared. */ +:root, +[data-sandbox-ui], +[data-theme="dark"], +.dark { + --mauve-1: #121113; + --mauve-2: #1a191b; + --mauve-3: #232225; + --mauve-4: #2b292d; + --mauve-5: #323035; + --mauve-6: #3c393f; + --mauve-7: #49474e; + --mauve-8: #625f69; + --mauve-9: #6f6d78; + --mauve-10: #7c7a85; + --mauve-11: #b5b2bc; + --mauve-12: #eeeef0; + --iris-1: #13131e; + --iris-2: #171625; + --iris-3: #202248; + --iris-4: #262a65; + --iris-5: #303374; + --iris-6: #3d3e82; + --iris-7: #4a4a95; + --iris-8: #5958b1; + --iris-9: #5b5bd6; + --iris-10: #6e6ade; + --iris-11: #b1a9ff; + --iris-12: #e0dffe; + --green-1: #0e1512; + --green-2: #121b17; + --green-3: #132d21; + --green-4: #113b29; + --green-5: #174933; + --green-6: #20573e; + --green-7: #28684a; + --green-8: #2f7c57; + --green-9: #30a46c; + --green-10: #33b074; + --green-11: #3dd68c; + --green-12: #b1f1cb; + --amber-1: #16120c; + --amber-2: #1d180f; + --amber-3: #302008; + --amber-4: #3f2700; + --amber-5: #4d3000; + --amber-6: #5c3d05; + --amber-7: #714f19; + --amber-8: #8f6424; + --amber-9: #ffc53d; + --amber-10: #ffd60a; + --amber-11: #ffca16; + --amber-12: #ffe7b3; + --red-1: #191111; + --red-2: #201314; + --red-3: #3b1219; + --red-4: #500f1c; + --red-5: #611623; + --red-6: #72232d; + --red-7: #8c333a; + --red-8: #b54548; + --red-9: #e5484d; + --red-10: #ec5d5e; + --red-11: #ff9592; + --red-12: #ffd1d9; + --blue-1: #0d1520; + --blue-2: #111927; + --blue-3: #0d2847; + --blue-4: #003362; + --blue-5: #004074; + --blue-6: #104d87; + --blue-7: #205d9e; + --blue-8: #2870bd; + --blue-9: #0090ff; + --blue-10: #3b9eff; + --blue-11: #70b8ff; + --blue-12: #c2e6ff; + --plum-1: #181118; + --plum-2: #201320; + --plum-3: #351a35; + --plum-4: #451d47; + --plum-5: #512454; + --plum-6: #5e3061; + --plum-7: #734079; + --plum-8: #92549c; + --plum-9: #ab4aba; + --plum-10: #b658c4; + --plum-11: #e796f3; + --plum-12: #f4d4f4; + --orange-1: #17120e; + --orange-2: #1e160f; + --orange-3: #331e0b; + --orange-4: #462100; + --orange-5: #562800; + --orange-6: #66350c; + --orange-7: #7e451d; + --orange-8: #a35829; + --orange-9: #f76b15; + --orange-10: #ff801f; + --orange-11: #ffa057; + --orange-12: #ffe0c2; + --grass-1: #0e1511; + --grass-2: #141a15; + --grass-3: #1b2a1e; + --grass-4: #1d3a24; + --grass-5: #25482d; + --grass-6: #2d5736; + --grass-7: #366740; + --grass-8: #3e7949; + --grass-9: #46a758; + --grass-10: #53b365; + --grass-11: #71d083; + --grass-12: #c2f0c2; + --bronze-1: #141110; + --bronze-2: #1c1917; + --bronze-3: #262220; + --bronze-4: #302a27; + --bronze-5: #3b3330; + --bronze-6: #493e3a; + --bronze-7: #5a4c47; + --bronze-8: #6f5f58; + --bronze-9: #a18072; + --bronze-10: #ae8c7e; + --bronze-11: #d4b3a5; + --bronze-12: #ede0d9; + --crimson-1: #191114; + --crimson-2: #201318; + --crimson-3: #381525; + --crimson-4: #4d122f; + --crimson-5: #5c1839; + --crimson-6: #6d2545; + --crimson-7: #873356; + --crimson-8: #b0436e; + --crimson-9: #e93d82; + --crimson-10: #ee518a; + --crimson-11: #ff92ad; + --crimson-12: #fdd3e8; + --olive-1: #111210; + --olive-2: #181917; + --olive-3: #212220; + --olive-4: #282a27; + --olive-5: #2f312e; + --olive-6: #383a36; + --olive-7: #454843; + --olive-8: #5c625b; + --olive-9: #687066; + --olive-10: #767d74; + --olive-11: #afb5ad; + --olive-12: #eceeec; + --gray-1: var(--mauve-1); + --gray-2: var(--mauve-2); + --gray-3: var(--mauve-3); + --gray-4: var(--mauve-4); + --gray-5: var(--mauve-5); + --gray-6: var(--mauve-6); + --gray-7: var(--mauve-7); + --gray-8: var(--mauve-8); + --gray-9: var(--mauve-9); + --gray-10: var(--mauve-10); + --gray-11: var(--mauve-11); + --gray-12: var(--mauve-12); + --accent-1: var(--iris-1); + --accent-2: var(--iris-2); + --accent-3: var(--iris-3); + --accent-4: var(--iris-4); + --accent-5: var(--iris-5); + --accent-6: var(--iris-6); + --accent-7: var(--iris-7); + --accent-8: var(--iris-8); + --accent-9: var(--iris-9); + --accent-10: var(--iris-10); + --accent-11: var(--iris-11); + --accent-12: var(--iris-12); + --success-1: var(--green-1); + --success-2: var(--green-2); + --success-3: var(--green-3); + --success-4: var(--green-4); + --success-5: var(--green-5); + --success-6: var(--green-6); + --success-7: var(--green-7); + --success-8: var(--green-8); + --success-9: var(--green-9); + --success-10: var(--green-10); + --success-11: var(--green-11); + --success-12: var(--green-12); + --warning-1: var(--amber-1); + --warning-2: var(--amber-2); + --warning-3: var(--amber-3); + --warning-4: var(--amber-4); + --warning-5: var(--amber-5); + --warning-6: var(--amber-6); + --warning-7: var(--amber-7); + --warning-8: var(--amber-8); + --warning-9: var(--amber-9); + --warning-10: var(--amber-10); + --warning-11: var(--amber-11); + --warning-12: var(--amber-12); + --danger-1: var(--red-1); + --danger-2: var(--red-2); + --danger-3: var(--red-3); + --danger-4: var(--red-4); + --danger-5: var(--red-5); + --danger-6: var(--red-6); + --danger-7: var(--red-7); + --danger-8: var(--red-8); + --danger-9: var(--red-9); + --danger-10: var(--red-10); + --danger-11: var(--red-11); + --danger-12: var(--red-12); + --info-1: var(--blue-1); + --info-2: var(--blue-2); + --info-3: var(--blue-3); + --info-4: var(--blue-4); + --info-5: var(--blue-5); + --info-6: var(--blue-6); + --info-7: var(--blue-7); + --info-8: var(--blue-8); + --info-9: var(--blue-9); + --info-10: var(--blue-10); + --info-11: var(--blue-11); + --info-12: var(--blue-12); +} +[data-sandbox-theme="vault"], +[data-theme="light"], +.light { + --mauve-1: #fdfcfd; + --mauve-2: #faf9fb; + --mauve-3: #f2eff3; + --mauve-4: #eae7ec; + --mauve-5: #e3dfe6; + --mauve-6: #dbd8e0; + --mauve-7: #d0cdd7; + --mauve-8: #bcbac7; + --mauve-9: #8e8c99; + --mauve-10: #84828e; + --mauve-11: #65636d; + --mauve-12: #211f26; + --iris-1: #fdfdff; + --iris-2: #f8f8ff; + --iris-3: #f0f1fe; + --iris-4: #e6e7ff; + --iris-5: #dadcff; + --iris-6: #cbcdff; + --iris-7: #b8baf8; + --iris-8: #9b9ef0; + --iris-9: #5b5bd6; + --iris-10: #5151cd; + --iris-11: #5753c6; + --iris-12: #272962; + --green-1: #fbfefc; + --green-2: #f4fbf6; + --green-3: #e6f6eb; + --green-4: #d6f1df; + --green-5: #c4e8d1; + --green-6: #adddc0; + --green-7: #8eceaa; + --green-8: #5bb98b; + --green-9: #30a46c; + --green-10: #2b9a66; + --green-11: #218358; + --green-12: #193b2d; + --amber-1: #fefdfb; + --amber-2: #fefbe9; + --amber-3: #fff7c2; + --amber-4: #ffee9c; + --amber-5: #fbe577; + --amber-6: #f3d673; + --amber-7: #e9c162; + --amber-8: #e2a336; + --amber-9: #ffc53d; + --amber-10: #ffba18; + --amber-11: #ab6400; + --amber-12: #4f3422; + --red-1: #fffcfc; + --red-2: #fff7f7; + --red-3: #feebec; + --red-4: #ffdbdc; + --red-5: #ffcdce; + --red-6: #fdbdbe; + --red-7: #f4a9aa; + --red-8: #eb8e90; + --red-9: #e5484d; + --red-10: #dc3e42; + --red-11: #ce2c31; + --red-12: #641723; + --blue-1: #fbfdff; + --blue-2: #f4faff; + --blue-3: #e6f4fe; + --blue-4: #d5efff; + --blue-5: #c2e5ff; + --blue-6: #acd8fc; + --blue-7: #8ec8f6; + --blue-8: #5eb1ef; + --blue-9: #0090ff; + --blue-10: #0588f0; + --blue-11: #0d74ce; + --blue-12: #113264; + --plum-1: #fefcff; + --plum-2: #fdf7fd; + --plum-3: #fbebfb; + --plum-4: #f7def8; + --plum-5: #f2d1f3; + --plum-6: #e9c2ec; + --plum-7: #deade3; + --plum-8: #cf91d8; + --plum-9: #ab4aba; + --plum-10: #a144af; + --plum-11: #953ea3; + --plum-12: #53195d; + --orange-1: #fefcfb; + --orange-2: #fff7ed; + --orange-3: #ffefd6; + --orange-4: #ffdfb5; + --orange-5: #ffd19a; + --orange-6: #ffc182; + --orange-7: #f5ae73; + --orange-8: #ec9455; + --orange-9: #f76b15; + --orange-10: #ef5f00; + --orange-11: #cc4e00; + --orange-12: #582d1d; + --grass-1: #fbfefb; + --grass-2: #f5fbf5; + --grass-3: #e9f6e9; + --grass-4: #daf1db; + --grass-5: #c9e8ca; + --grass-6: #b2ddb5; + --grass-7: #94ce9a; + --grass-8: #65ba74; + --grass-9: #46a758; + --grass-10: #3e9b4f; + --grass-11: #2a7e3b; + --grass-12: #203c25; + --bronze-1: #fdfcfc; + --bronze-2: #fdf7f5; + --bronze-3: #f6edea; + --bronze-4: #efe4df; + --bronze-5: #e7d9d3; + --bronze-6: #dfcdc5; + --bronze-7: #d3bcb3; + --bronze-8: #c2a499; + --bronze-9: #a18072; + --bronze-10: #957468; + --bronze-11: #7d5e54; + --bronze-12: #43302b; + --crimson-1: #fffcfd; + --crimson-2: #fef7f9; + --crimson-3: #ffe9f0; + --crimson-4: #fedce7; + --crimson-5: #facedd; + --crimson-6: #f3bed1; + --crimson-7: #eaacc3; + --crimson-8: #e093b2; + --crimson-9: #e93d82; + --crimson-10: #df3478; + --crimson-11: #cb1d63; + --crimson-12: #621639; + --olive-1: #fcfdfc; + --olive-2: #f8faf8; + --olive-3: #eff1ef; + --olive-4: #e7e9e7; + --olive-5: #dfe2df; + --olive-6: #d7dad7; + --olive-7: #cccfcc; + --olive-8: #b9bcb8; + --olive-9: #898e87; + --olive-10: #7f847d; + --olive-11: #60655f; + --olive-12: #1d211c; + --gray-1: var(--mauve-1); + --gray-2: var(--mauve-2); + --gray-3: var(--mauve-3); + --gray-4: var(--mauve-4); + --gray-5: var(--mauve-5); + --gray-6: var(--mauve-6); + --gray-7: var(--mauve-7); + --gray-8: var(--mauve-8); + --gray-9: var(--mauve-9); + --gray-10: var(--mauve-10); + --gray-11: var(--mauve-11); + --gray-12: var(--mauve-12); + --accent-1: var(--iris-1); + --accent-2: var(--iris-2); + --accent-3: var(--iris-3); + --accent-4: var(--iris-4); + --accent-5: var(--iris-5); + --accent-6: var(--iris-6); + --accent-7: var(--iris-7); + --accent-8: var(--iris-8); + --accent-9: var(--iris-9); + --accent-10: var(--iris-10); + --accent-11: var(--iris-11); + --accent-12: var(--iris-12); + --success-1: var(--green-1); + --success-2: var(--green-2); + --success-3: var(--green-3); + --success-4: var(--green-4); + --success-5: var(--green-5); + --success-6: var(--green-6); + --success-7: var(--green-7); + --success-8: var(--green-8); + --success-9: var(--green-9); + --success-10: var(--green-10); + --success-11: var(--green-11); + --success-12: var(--green-12); + --warning-1: var(--amber-1); + --warning-2: var(--amber-2); + --warning-3: var(--amber-3); + --warning-4: var(--amber-4); + --warning-5: var(--amber-5); + --warning-6: var(--amber-6); + --warning-7: var(--amber-7); + --warning-8: var(--amber-8); + --warning-9: var(--amber-9); + --warning-10: var(--amber-10); + --warning-11: var(--amber-11); + --warning-12: var(--amber-12); + --danger-1: var(--red-1); + --danger-2: var(--red-2); + --danger-3: var(--red-3); + --danger-4: var(--red-4); + --danger-5: var(--red-5); + --danger-6: var(--red-6); + --danger-7: var(--red-7); + --danger-8: var(--red-8); + --danger-9: var(--red-9); + --danger-10: var(--red-10); + --danger-11: var(--red-11); + --danger-12: var(--red-12); + --info-1: var(--blue-1); + --info-2: var(--blue-2); + --info-3: var(--blue-3); + --info-4: var(--blue-4); + --info-5: var(--blue-5); + --info-6: var(--blue-6); + --info-7: var(--blue-7); + --info-8: var(--blue-8); + --info-9: var(--blue-9); + --info-10: var(--blue-10); + --info-11: var(--blue-11); + --info-12: var(--blue-12); +} + +/* Domain ramp. Set data-domain on a cover, tile, avatar or listing and read + * --domain-1..12. An unknown domain gets the brand ramp. --domain-bubble is the + * light step 11 in both themes: white text on it passes 4.5:1 for every domain. + * A step 9 fill never carries text (white on orange 9 is 2.97:1). */ +:root, +[data-domain] { + --domain-1: var(--iris-1); + --domain-2: var(--iris-2); + --domain-3: var(--iris-3); + --domain-4: var(--iris-4); + --domain-5: var(--iris-5); + --domain-6: var(--iris-6); + --domain-7: var(--iris-7); + --domain-8: var(--iris-8); + --domain-9: var(--iris-9); + --domain-10: var(--iris-10); + --domain-11: var(--iris-11); + --domain-12: var(--iris-12); + --domain-bubble: #5753c6; +} +[data-domain="personal"] { + --domain-1: var(--plum-1); + --domain-2: var(--plum-2); + --domain-3: var(--plum-3); + --domain-4: var(--plum-4); + --domain-5: var(--plum-5); + --domain-6: var(--plum-6); + --domain-7: var(--plum-7); + --domain-8: var(--plum-8); + --domain-9: var(--plum-9); + --domain-10: var(--plum-10); + --domain-11: var(--plum-11); + --domain-12: var(--plum-12); + --domain-bubble: #953ea3; +} +[data-domain="hospitality"] { + --domain-1: var(--orange-1); + --domain-2: var(--orange-2); + --domain-3: var(--orange-3); + --domain-4: var(--orange-4); + --domain-5: var(--orange-5); + --domain-6: var(--orange-6); + --domain-7: var(--orange-7); + --domain-8: var(--orange-8); + --domain-9: var(--orange-9); + --domain-10: var(--orange-10); + --domain-11: var(--orange-11); + --domain-12: var(--orange-12); + --domain-bubble: #cc4e00; +} +[data-domain="tax"] { + --domain-1: var(--grass-1); + --domain-2: var(--grass-2); + --domain-3: var(--grass-3); + --domain-4: var(--grass-4); + --domain-5: var(--grass-5); + --domain-6: var(--grass-6); + --domain-7: var(--grass-7); + --domain-8: var(--grass-8); + --domain-9: var(--grass-9); + --domain-10: var(--grass-10); + --domain-11: var(--grass-11); + --domain-12: var(--grass-12); + --domain-bubble: #2a7e3b; +} +[data-domain="law"] { + --domain-1: var(--blue-1); + --domain-2: var(--blue-2); + --domain-3: var(--blue-3); + --domain-4: var(--blue-4); + --domain-5: var(--blue-5); + --domain-6: var(--blue-6); + --domain-7: var(--blue-7); + --domain-8: var(--blue-8); + --domain-9: var(--blue-9); + --domain-10: var(--blue-10); + --domain-11: var(--blue-11); + --domain-12: var(--blue-12); + --domain-bubble: #0d74ce; +} +[data-domain="procurement"] { + --domain-1: var(--bronze-1); + --domain-2: var(--bronze-2); + --domain-3: var(--bronze-3); + --domain-4: var(--bronze-4); + --domain-5: var(--bronze-5); + --domain-6: var(--bronze-6); + --domain-7: var(--bronze-7); + --domain-8: var(--bronze-8); + --domain-9: var(--bronze-9); + --domain-10: var(--bronze-10); + --domain-11: var(--bronze-11); + --domain-12: var(--bronze-12); + --domain-bubble: #7d5e54; +} +[data-domain="negotiation"] { + --domain-1: var(--crimson-1); + --domain-2: var(--crimson-2); + --domain-3: var(--crimson-3); + --domain-4: var(--crimson-4); + --domain-5: var(--crimson-5); + --domain-6: var(--crimson-6); + --domain-7: var(--crimson-7); + --domain-8: var(--crimson-8); + --domain-9: var(--crimson-9); + --domain-10: var(--crimson-10); + --domain-11: var(--crimson-11); + --domain-12: var(--crimson-12); + --domain-bubble: #cb1d63; +} +[data-domain="logistics"] { + --domain-1: var(--amber-1); + --domain-2: var(--amber-2); + --domain-3: var(--amber-3); + --domain-4: var(--amber-4); + --domain-5: var(--amber-5); + --domain-6: var(--amber-6); + --domain-7: var(--amber-7); + --domain-8: var(--amber-8); + --domain-9: var(--amber-9); + --domain-10: var(--amber-10); + --domain-11: var(--amber-11); + --domain-12: var(--amber-12); + --domain-bubble: #ab6400; +} +[data-domain="defense"] { + --domain-1: var(--olive-1); + --domain-2: var(--olive-2); + --domain-3: var(--olive-3); + --domain-4: var(--olive-4); + --domain-5: var(--olive-5); + --domain-6: var(--olive-6); + --domain-7: var(--olive-7); + --domain-8: var(--olive-8); + --domain-9: var(--olive-9); + --domain-10: var(--olive-10); + --domain-11: var(--olive-11); + --domain-12: var(--olive-12); + --domain-bubble: #60655f; +} diff --git a/packages/brand/src/styles/system.css b/packages/brand/src/styles/system.css new file mode 100644 index 0000000..820e5fb --- /dev/null +++ b/packages/brand/src/styles/system.css @@ -0,0 +1,427 @@ +/* Semantic layer. Generated by scripts/gen-ladders.mjs; edit the MAP there, not this file. + * + * Requires ladders.css. Every token family (hsl, md3, depth, bg, text, border, + * surface, status, btn) points at a ladder step, so components built on these + * tokens re-skin without edits. Light pages are white and cards separate from + * them by shadow and media. Dark steps surfaces up in small lightness + * increments, draws hairline edges, and casts a shadow only under floating + * layers. Chrome is neutral (mauve); colour belongs to content and to one + * call to action per page. */ +:root, +[data-sandbox-ui], +[data-theme="dark"], +.dark { + --bg-page: var(--mauve-1); + --bg-subtle: var(--mauve-2); + --bg-card: var(--mauve-2); + --bg-raised: var(--mauve-3); + --bg-control: var(--mauve-3); + --bg-control-hover: var(--mauve-4); + --bg-control-active: var(--mauve-5); + --line-subtle: var(--mauve-6); + --line: var(--mauve-7); + --line-strong: var(--mauve-8); + --line-field: var(--mauve-9); + --fg: var(--mauve-12); + --fg-muted: var(--mauve-11); + --fg-faint: var(--mauve-10); + --ink: var(--mauve-12); + --on-ink: var(--mauve-1); + --accent-solid: var(--iris-9); + --accent-solid-hover: var(--iris-10); + --accent-text: var(--iris-11); + --accent-soft: var(--iris-3); + --accent-soft-hover: var(--iris-4); + --accent-line: var(--iris-7); + --hsl-background: 270 5.6% 7.1%; + --hsl-foreground: 240 6.2% 93.7%; + --hsl-card: 270 3.8% 10.2%; + --hsl-card-foreground: 240 6.2% 93.7%; + --hsl-popover: 260 4.2% 13.9%; + --hsl-popover-foreground: 240 6.2% 93.7%; + --hsl-primary: 240 60% 59.8%; + --hsl-primary-foreground: 0 0% 100%; + --hsl-secondary: 260 4.2% 13.9%; + --hsl-secondary-foreground: 240 6.2% 93.7%; + --hsl-muted: 260 4.2% 13.9%; + --hsl-muted-foreground: 258 6.9% 71.8%; + --hsl-accent: 270 4.7% 16.9%; + --hsl-accent-foreground: 240 6.2% 93.7%; + --hsl-destructive: 348 68.4% 18.6%; + --hsl-destructive-foreground: 350 100% 91%; + --hsl-border: 270 5% 23.5%; + --hsl-input: 251 4.8% 44.9%; + --hsl-ring: 242 63.7% 64.3%; + --hsl-success: 151 54.7% 41.6%; + --hsl-warning: 42 100% 62%; + --hsl-info: 206 100% 50%; + --sidebar-background: 270 3.8% 10.2%; + --sidebar-foreground: 240 6.2% 93.7%; + --sidebar-primary: 240 60% 59.8%; + --sidebar-primary-foreground: 0 0% 100%; + --sidebar-accent: 270 4.7% 16.9%; + --sidebar-accent-foreground: 240 6.2% 93.7%; + --sidebar-border: 270 5% 23.5%; + --sidebar-ring: 242 63.7% 64.3%; + --md3-surface: var(--mauve-1); + --md3-surface-dim: var(--mauve-1); + --md3-surface-bright: var(--mauve-5); + --md3-surface-container-lowest: var(--mauve-1); + --md3-surface-container-low: var(--mauve-2); + --md3-surface-container: var(--mauve-2); + --md3-surface-container-high: var(--mauve-3); + --md3-surface-container-highest: var(--mauve-4); + --md3-surface-variant: var(--mauve-3); + --md3-on-surface: var(--mauve-12); + --md3-on-surface-variant: var(--mauve-11); + --md3-primary: var(--iris-11); + --md3-primary-dim: var(--iris-10); + --md3-primary-container: var(--iris-9); + --md3-on-primary: #ffffff; + --md3-on-primary-container: #ffffff; + --md3-outline: var(--mauve-9); + --md3-outline-variant: var(--mauve-6); + --md3-error: var(--red-11); + --md3-error-container: var(--red-3); + --md3-on-error-container: var(--red-12); + --depth-1: var(--mauve-1); + --depth-2: var(--mauve-2); + --depth-3: var(--mauve-3); + --depth-4: var(--mauve-4); + --bg-root: var(--mauve-1); + --bg-dark: var(--mauve-1); + --bg-elevated: var(--mauve-3); + --bg-section: var(--mauve-1); + --bg-input: var(--mauve-2); + --bg-hover: var(--mauve-3); + --bg-selection: var(--iris-5); + --text-primary: var(--mauve-12); + --text-secondary: var(--mauve-12); + --text-muted: var(--mauve-11); + --text-dim: var(--mauve-11); + --border-subtle: var(--mauve-6); + --border-default: var(--mauve-7); + --border-hover: var(--mauve-8); + --border-accent: var(--iris-7); + --border-accent-hover: var(--iris-8); + --btn-primary-bg: var(--iris-9); + --btn-primary-hover: var(--iris-10); + --btn-primary-text: #ffffff; + --btn-cta-bg: var(--iris-9); + --btn-cta-text: #ffffff; + --accent-surface-soft: var(--iris-2); + --accent-surface-strong: var(--iris-4); + --surface-success-bg: var(--green-3); + --surface-success-border: var(--green-6); + --surface-success-text: var(--green-11); + --surface-warning-bg: var(--amber-3); + --surface-warning-border: var(--amber-6); + --surface-warning-text: var(--amber-11); + --surface-danger-bg: var(--red-3); + --surface-danger-border: var(--red-6); + --surface-danger-text: var(--red-11); + --surface-info-bg: var(--blue-3); + --surface-info-border: var(--blue-6); + --surface-info-text: var(--blue-11); + --surface-violet-bg: var(--iris-3); + --surface-violet-border: var(--iris-6); + --surface-violet-text: var(--iris-11); + --surface-orange-bg: var(--orange-3); + --surface-orange-border: var(--orange-6); + --surface-orange-text: var(--orange-11); + --surface-teal-bg: var(--grass-3); + --surface-teal-border: var(--grass-6); + --surface-teal-text: var(--grass-11); + --surface-neutral-bg: var(--mauve-3); + --surface-neutral-border: var(--mauve-6); + --surface-neutral-text: var(--mauve-11); + --status-running: var(--green-11); + --status-creating: var(--iris-11); + --status-stopped: var(--amber-11); + --status-warm: var(--orange-11); + --status-cold: var(--blue-11); + --status-error: var(--red-11); + --status-deleted: var(--mauve-11); + --brand-primary: var(--iris-9); + --brand-strong: var(--iris-3); + --brand-vibrant: var(--iris-9); + /* The brand call to action. Use it once per page, on the primary consumer + * action. White text passes on every stop (5.37, 5.39 and 5.18:1). */ + --cta: linear-gradient(90deg, #5b5bd6 0%, #6e56cf 55%, #8e4ec6 100%); + --cta-hover: linear-gradient(90deg, #5151cd 0%, #654dc4 55%, #8347b9 100%); + /* Radius by role. The numeric --radius-sm..xl scale is unchanged, so no + * existing surface re-rounds; new surfaces pick the role they play. */ + --radius-tag: 4px; + --radius-chip: 8px; + --radius-field: 12px; + --radius-panel: 16px; + --radius-cover: 20px; + --radius-sheet: 24px; + --radius-pill: 999px; + /* Motion. */ + --ease-standard: cubic-bezier(0.2, 0, 0, 1); + --ease-emphasized: cubic-bezier(0.32, 0.72, 0, 1); + --dur-press: 120ms; + --dur-hover: 200ms; + --dur-card: 250ms; + --dur-sheet: 320ms; + --dur-reveal: 450ms; + /* Type. 12 px is the floor. */ + --font-size-xs: 0.75rem; + --text-display: clamp(2.5rem, 2rem + 3vw, 4rem); + --text-display--line-height: 1.02; + --text-display--letter-spacing: -0.035em; + --text-display--font-weight: 650; + --text-hero: clamp(2.25rem, 2.2rem + 1.6vw, 3.5rem); + --text-hero--line-height: 1.05; + --text-hero--letter-spacing: -0.03em; + --text-hero--font-weight: 650; + --text-title: 2rem; + --text-title--line-height: 2.25rem; + --text-title--letter-spacing: -0.03em; + --text-title--font-weight: 600; + --text-page: 1.625rem; + --text-page--line-height: 1.875rem; + --text-page--letter-spacing: -0.02em; + --text-page--font-weight: 600; + --text-section: 1.375rem; + --text-section--line-height: 1.625rem; + --text-section--letter-spacing: -0.02em; + --text-section--font-weight: 600; + --text-lead: 1.125rem; + --text-lead--line-height: 1.5rem; + --text-lead--letter-spacing: 0em; + --text-lead--font-weight: 600; + --text-chat: 0.9375rem; + --text-chat--line-height: 1.375rem; + --text-chat--letter-spacing: 0em; + --text-chat--font-weight: 400; + --text-figure: 2.75rem; + --text-figure--line-height: 2.75rem; + --text-figure--letter-spacing: -0.02em; + --text-figure--font-weight: 600; + /* Elevation. In dark, a white hairline states the edge; only floating layers cast a shadow. */ + --shadow-1: inset 0 0 0 1px rgb(255 255 255 / 0.06); + --shadow-2: inset 0 0 0 1px rgb(255 255 255 / 0.08), 0 8px 24px rgb(0 0 0 / 0.5); + --shadow-3: inset 0 0 0 1px rgb(255 255 255 / 0.1), 0 16px 48px rgb(0 0 0 / 0.6); + --shadow-card: var(--shadow-1); + --shadow-dropdown: var(--shadow-2); + --shadow-overlay: var(--shadow-3); + color-scheme: dark; +} +[data-sandbox-theme="vault"], +[data-theme="light"], +.light { + --bg-page: #ffffff; + --bg-subtle: var(--mauve-2); + --bg-card: #ffffff; + --bg-raised: #ffffff; + --bg-control: var(--mauve-3); + --bg-control-hover: var(--mauve-4); + --bg-control-active: var(--mauve-5); + --line-subtle: var(--mauve-6); + --line: var(--mauve-7); + --line-strong: var(--mauve-8); + --line-field: var(--mauve-9); + --fg: var(--mauve-12); + --fg-muted: var(--mauve-11); + --fg-faint: var(--mauve-10); + --ink: var(--mauve-12); + --on-ink: #ffffff; + --accent-solid: var(--iris-9); + --accent-solid-hover: var(--iris-10); + --accent-text: var(--iris-11); + --accent-soft: var(--iris-3); + --accent-soft-hover: var(--iris-4); + --accent-line: var(--iris-7); + --hsl-background: 0 0% 100%; + --hsl-foreground: 257 10.1% 13.5%; + --hsl-card: 0 0% 100%; + --hsl-card-foreground: 257 10.1% 13.5%; + --hsl-popover: 0 0% 100%; + --hsl-popover-foreground: 257 10.1% 13.5%; + --hsl-primary: 240 60% 59.8%; + --hsl-primary-foreground: 0 0% 100%; + --hsl-secondary: 285 14.3% 94.5%; + --hsl-secondary-foreground: 257 10.1% 13.5%; + --hsl-muted: 285 14.3% 94.5%; + --hsl-muted-foreground: 252 4.8% 40.8%; + --hsl-accent: 276 11.6% 91.6%; + --hsl-accent-foreground: 257 10.1% 13.5%; + --hsl-destructive: 358 64.8% 49%; + --hsl-destructive-foreground: 0 0% 100%; + --hsl-border: 262 11.4% 86.3%; + --hsl-input: 249 6% 57.5%; + --hsl-ring: 240 60% 59.8%; + --hsl-success: 151 54.7% 41.6%; + --hsl-warning: 42 100% 62%; + --hsl-info: 206 100% 50%; + --sidebar-background: 270 20% 98%; + --sidebar-foreground: 257 10.1% 13.5%; + --sidebar-primary: 240 60% 59.8%; + --sidebar-primary-foreground: 0 0% 100%; + --sidebar-accent: 276 11.6% 91.6%; + --sidebar-accent-foreground: 257 10.1% 13.5%; + --sidebar-border: 262 11.4% 86.3%; + --sidebar-ring: 240 60% 59.8%; + --md3-surface: #ffffff; + --md3-surface-dim: var(--mauve-2); + --md3-surface-bright: #ffffff; + --md3-surface-container-lowest: #ffffff; + --md3-surface-container-low: var(--mauve-2); + --md3-surface-container: #ffffff; + --md3-surface-container-high: var(--mauve-3); + --md3-surface-container-highest: #ffffff; + --md3-surface-variant: var(--mauve-3); + --md3-on-surface: var(--mauve-12); + --md3-on-surface-variant: var(--mauve-11); + --md3-primary: var(--iris-11); + --md3-primary-dim: var(--iris-10); + --md3-primary-container: var(--iris-9); + --md3-on-primary: #ffffff; + --md3-on-primary-container: #ffffff; + --md3-outline: var(--mauve-9); + --md3-outline-variant: var(--mauve-6); + --md3-error: var(--red-11); + --md3-error-container: var(--red-3); + --md3-on-error-container: var(--red-12); + --depth-1: #ffffff; + --depth-2: #ffffff; + --depth-3: var(--mauve-2); + --depth-4: #ffffff; + --bg-root: #ffffff; + --bg-dark: var(--mauve-2); + --bg-elevated: #ffffff; + --bg-section: var(--mauve-2); + --bg-input: #ffffff; + --bg-hover: var(--mauve-3); + --bg-selection: var(--iris-4); + --text-primary: var(--mauve-12); + --text-secondary: var(--mauve-12); + --text-muted: var(--mauve-11); + --text-dim: var(--mauve-11); + --border-subtle: var(--mauve-6); + --border-default: var(--mauve-7); + --border-hover: var(--mauve-8); + --border-accent: var(--iris-7); + --border-accent-hover: var(--iris-8); + --btn-primary-bg: var(--iris-9); + --btn-primary-hover: var(--iris-10); + --btn-primary-text: #ffffff; + --btn-cta-bg: var(--iris-9); + --btn-cta-text: #ffffff; + --accent-surface-soft: var(--iris-2); + --accent-surface-strong: var(--iris-4); + --surface-success-bg: var(--green-3); + --surface-success-border: var(--green-6); + --surface-success-text: var(--green-12); + --surface-warning-bg: var(--amber-3); + --surface-warning-border: var(--amber-6); + --surface-warning-text: var(--amber-12); + --surface-danger-bg: var(--red-3); + --surface-danger-border: var(--red-6); + --surface-danger-text: var(--red-12); + --surface-info-bg: var(--blue-3); + --surface-info-border: var(--blue-6); + --surface-info-text: var(--blue-12); + --surface-violet-bg: var(--iris-3); + --surface-violet-border: var(--iris-6); + --surface-violet-text: var(--iris-12); + --surface-orange-bg: var(--orange-3); + --surface-orange-border: var(--orange-6); + --surface-orange-text: var(--orange-12); + --surface-teal-bg: var(--grass-3); + --surface-teal-border: var(--grass-6); + --surface-teal-text: var(--grass-12); + --surface-neutral-bg: var(--mauve-3); + --surface-neutral-border: var(--mauve-6); + --surface-neutral-text: var(--mauve-11); + --status-running: var(--green-11); + --status-creating: var(--iris-11); + --status-stopped: var(--amber-11); + --status-warm: var(--orange-11); + --status-cold: var(--blue-11); + --status-error: var(--red-11); + --status-deleted: var(--mauve-11); + --brand-primary: var(--iris-9); + --brand-strong: var(--iris-12); + --brand-vibrant: var(--iris-9); + /* The brand call to action. Use it once per page, on the primary consumer + * action. White text passes on every stop (5.37, 5.39 and 5.18:1). */ + --cta: linear-gradient(90deg, #5b5bd6 0%, #6e56cf 55%, #8e4ec6 100%); + --cta-hover: linear-gradient(90deg, #5151cd 0%, #654dc4 55%, #8347b9 100%); + /* Radius by role. The numeric --radius-sm..xl scale is unchanged, so no + * existing surface re-rounds; new surfaces pick the role they play. */ + --radius-tag: 4px; + --radius-chip: 8px; + --radius-field: 12px; + --radius-panel: 16px; + --radius-cover: 20px; + --radius-sheet: 24px; + --radius-pill: 999px; + /* Motion. */ + --ease-standard: cubic-bezier(0.2, 0, 0, 1); + --ease-emphasized: cubic-bezier(0.32, 0.72, 0, 1); + --dur-press: 120ms; + --dur-hover: 200ms; + --dur-card: 250ms; + --dur-sheet: 320ms; + --dur-reveal: 450ms; + /* Type. 12 px is the floor. */ + --font-size-xs: 0.75rem; + --text-display: clamp(2.5rem, 2rem + 3vw, 4rem); + --text-display--line-height: 1.02; + --text-display--letter-spacing: -0.035em; + --text-display--font-weight: 650; + --text-hero: clamp(2.25rem, 2.2rem + 1.6vw, 3.5rem); + --text-hero--line-height: 1.05; + --text-hero--letter-spacing: -0.03em; + --text-hero--font-weight: 650; + --text-title: 2rem; + --text-title--line-height: 2.25rem; + --text-title--letter-spacing: -0.03em; + --text-title--font-weight: 600; + --text-page: 1.625rem; + --text-page--line-height: 1.875rem; + --text-page--letter-spacing: -0.02em; + --text-page--font-weight: 600; + --text-section: 1.375rem; + --text-section--line-height: 1.625rem; + --text-section--letter-spacing: -0.02em; + --text-section--font-weight: 600; + --text-lead: 1.125rem; + --text-lead--line-height: 1.5rem; + --text-lead--letter-spacing: 0em; + --text-lead--font-weight: 600; + --text-chat: 0.9375rem; + --text-chat--line-height: 1.375rem; + --text-chat--letter-spacing: 0em; + --text-chat--font-weight: 400; + --text-figure: 2.75rem; + --text-figure--line-height: 2.75rem; + --text-figure--letter-spacing: -0.02em; + --text-figure--font-weight: 600; + /* Elevation. In light, shadow separates a card from the white page. */ + --shadow-1: 0 0 0 1px rgb(33 31 38 / 0.03), 0 2px 6px rgb(33 31 38 / 0.05), 0 4px 8px rgb(33 31 38 / 0.08); + --shadow-2: 0 0 0 1px rgb(33 31 38 / 0.04), 0 6px 16px rgb(33 31 38 / 0.12); + --shadow-3: 0 8px 28px rgb(33 31 38 / 0.28); + --shadow-card: var(--shadow-1); + --shadow-dropdown: var(--shadow-2); + --shadow-overlay: var(--shadow-3); + color-scheme: light; +} +@media (prefers-reduced-motion: reduce) { + :root, + [data-sandbox-ui], + [data-theme="dark"], + .dark, + [data-sandbox-theme="vault"], + [data-theme="light"], + .light { + --dur-press: 0ms; + --dur-hover: 0ms; + --dur-card: 0ms; + --dur-sheet: 0ms; + --dur-reveal: 0ms; + } +}