v0.21.0 foundation: design-token module (#31)
Establish src/styles/tokens.css — the single source of truth for color, type, spacing, radius, elevation, and motion. Imported first in main.jsx. Sweep subagents map literal values to these tokens; no appearance change is intended beyond consolidating near-duplicate grays (operator reviews before deploy). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@ import React from 'react'
|
|||||||
import ReactDOM from 'react-dom/client'
|
import ReactDOM from 'react-dom/client'
|
||||||
import { BrowserRouter } from 'react-router-dom'
|
import { BrowserRouter } from 'react-router-dom'
|
||||||
import App from './App.jsx'
|
import App from './App.jsx'
|
||||||
|
import './styles/tokens.css'
|
||||||
import './index.css'
|
import './index.css'
|
||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById('root')).render(
|
ReactDOM.createRoot(document.getElementById('root')).render(
|
||||||
|
|||||||
@@ -0,0 +1,162 @@
|
|||||||
|
/* tokens.css — the design-token foundation for rfc-app's UI.
|
||||||
|
*
|
||||||
|
* Roadmap item #31 (comprehensive UX polish). Before this file the app
|
||||||
|
* had ~98 distinct hardcoded hex colors, font sizes scattered across 16
|
||||||
|
* values with no scale, and radii across 13 values — classic prototype
|
||||||
|
* sprawl. This module establishes ONE coherent system; the App.css sweep
|
||||||
|
* (and component-scoped CSS) reference these custom properties instead of
|
||||||
|
* literal values, so "what color/size/space is this" has a single answer.
|
||||||
|
*
|
||||||
|
* Imported FIRST in main.jsx so :root is defined before any other sheet.
|
||||||
|
* Custom properties are not cascade-order-sensitive at use time, but
|
||||||
|
* importing first keeps the dependency obvious.
|
||||||
|
*
|
||||||
|
* Conventions for anyone sweeping values to these tokens:
|
||||||
|
* - Map each literal to the NEAREST semantic token, then fall back to a
|
||||||
|
* primitive ramp step. Consolidating near-duplicate grays is the point.
|
||||||
|
* - Never invent a new literal in a component; add a token here instead.
|
||||||
|
* - Spacing/radii/type use the scales below — no off-scale px values.
|
||||||
|
*/
|
||||||
|
|
||||||
|
:root {
|
||||||
|
/* ===== Color primitives — neutral ramp ===== */
|
||||||
|
--c-white: #ffffff;
|
||||||
|
--c-gray-50: #fafafa;
|
||||||
|
--c-gray-100: #f3f4f6;
|
||||||
|
--c-gray-150: #f0f0ee; /* the app's warm canvas tint */
|
||||||
|
--c-gray-200: #e5e7eb;
|
||||||
|
--c-gray-300: #d1d5db;
|
||||||
|
--c-gray-400: #9ca3af;
|
||||||
|
--c-gray-500: #6b7280;
|
||||||
|
--c-gray-600: #4b5563;
|
||||||
|
--c-gray-700: #374151;
|
||||||
|
--c-gray-800: #1f2937;
|
||||||
|
--c-gray-900: #111111;
|
||||||
|
--c-ink: #1a1a1a; /* near-black used for the header + body text */
|
||||||
|
|
||||||
|
/* ===== Color primitives — accent (indigo/violet) ===== */
|
||||||
|
--c-accent: #5b5bd6;
|
||||||
|
--c-accent-strong: #4338ca;
|
||||||
|
--c-violet: #7c3aed;
|
||||||
|
|
||||||
|
/* ===== Color primitives — status ===== */
|
||||||
|
--c-success-fg: #166534;
|
||||||
|
--c-success-bg: #dcfce7;
|
||||||
|
--c-danger-fg: #991b1b;
|
||||||
|
--c-danger-fg-strong: #b91c1c;
|
||||||
|
--c-danger-bg: #fef2f2;
|
||||||
|
--c-danger-border: #fecaca;
|
||||||
|
--c-warning-fg: #92400e;
|
||||||
|
--c-warning-accent: #b45309;
|
||||||
|
--c-warning-bg: #fef3c7;
|
||||||
|
--c-warning-bg-soft:#fffbeb;
|
||||||
|
|
||||||
|
/* ===== Semantic colors ===== */
|
||||||
|
--color-bg: var(--c-gray-150);
|
||||||
|
--color-surface: var(--c-white);
|
||||||
|
--color-surface-sunken: var(--c-gray-50);
|
||||||
|
--color-surface-muted: var(--c-gray-100);
|
||||||
|
--color-header-bg: var(--c-ink);
|
||||||
|
|
||||||
|
--color-text: var(--c-ink);
|
||||||
|
--color-text-strong: var(--c-gray-900);
|
||||||
|
--color-text-muted: var(--c-gray-500);
|
||||||
|
--color-text-subtle: var(--c-gray-400);
|
||||||
|
--color-text-inverse: var(--c-white);
|
||||||
|
|
||||||
|
--color-border: var(--c-gray-200);
|
||||||
|
--color-border-strong: var(--c-gray-300);
|
||||||
|
|
||||||
|
--color-link: var(--c-accent);
|
||||||
|
--color-accent: var(--c-accent);
|
||||||
|
--color-accent-strong: var(--c-accent-strong);
|
||||||
|
--color-accent-contrast: var(--c-white);
|
||||||
|
|
||||||
|
--color-success-fg: var(--c-success-fg);
|
||||||
|
--color-success-bg: var(--c-success-bg);
|
||||||
|
--color-danger-fg: var(--c-danger-fg);
|
||||||
|
--color-danger-bg: var(--c-danger-bg);
|
||||||
|
--color-warning-fg: var(--c-warning-fg);
|
||||||
|
--color-warning-bg: var(--c-warning-bg);
|
||||||
|
|
||||||
|
/* On the dark header, translucent white is the established pattern. */
|
||||||
|
--color-on-dark-soft: rgba(255, 255, 255, 0.15);
|
||||||
|
--color-on-dark-hover: rgba(255, 255, 255, 0.25);
|
||||||
|
--color-on-dark-muted: #dddddd;
|
||||||
|
|
||||||
|
--color-focus-ring: rgba(91, 91, 214, 0.45);
|
||||||
|
|
||||||
|
/* ===== Type ===== */
|
||||||
|
--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
||||||
|
Helvetica, Arial, sans-serif;
|
||||||
|
--font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
|
||||||
|
monospace;
|
||||||
|
|
||||||
|
--text-2xs: 10px;
|
||||||
|
--text-xs: 11px;
|
||||||
|
--text-sm: 12px;
|
||||||
|
--text-base: 13px; /* the app's dominant body size */
|
||||||
|
--text-md: 14px;
|
||||||
|
--text-lg: 16px;
|
||||||
|
--text-xl: 18px;
|
||||||
|
--text-2xl: 22px;
|
||||||
|
--text-3xl: 28px;
|
||||||
|
|
||||||
|
--leading-tight: 1.25;
|
||||||
|
--leading-normal: 1.5;
|
||||||
|
--leading-relaxed: 1.65;
|
||||||
|
|
||||||
|
--weight-normal: 400;
|
||||||
|
--weight-medium: 500;
|
||||||
|
--weight-semibold: 600;
|
||||||
|
--weight-bold: 700;
|
||||||
|
|
||||||
|
/* ===== Spacing scale (4-based, with the 2/6/10 half-steps the app
|
||||||
|
* already leans on heavily) ===== */
|
||||||
|
--space-0: 0;
|
||||||
|
--space-1: 2px;
|
||||||
|
--space-2: 4px;
|
||||||
|
--space-3: 6px;
|
||||||
|
--space-4: 8px;
|
||||||
|
--space-5: 10px;
|
||||||
|
--space-6: 12px;
|
||||||
|
--space-7: 16px;
|
||||||
|
--space-8: 20px;
|
||||||
|
--space-9: 24px;
|
||||||
|
--space-10: 32px;
|
||||||
|
--space-11: 48px;
|
||||||
|
--space-12: 64px;
|
||||||
|
|
||||||
|
/* ===== Radius ===== */
|
||||||
|
--radius-xs: 2px;
|
||||||
|
--radius-sm: 4px;
|
||||||
|
--radius-md: 6px;
|
||||||
|
--radius-lg: 8px;
|
||||||
|
--radius-xl: 12px;
|
||||||
|
--radius-pill: 999px;
|
||||||
|
|
||||||
|
/* ===== Elevation ===== */
|
||||||
|
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.06);
|
||||||
|
--shadow-md: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||||
|
--shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.12);
|
||||||
|
|
||||||
|
/* ===== Motion ===== */
|
||||||
|
--motion-fast: 120ms;
|
||||||
|
--motion-base: 150ms;
|
||||||
|
--motion-slow: 200ms;
|
||||||
|
--ease-out: cubic-bezier(0.16, 1, 0.3, 1);
|
||||||
|
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
|
||||||
|
/* ===== Layout ===== */
|
||||||
|
--header-height: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Honor reduced-motion globally — any transition/animation that reads
|
||||||
|
* these duration tokens collapses to instant. */
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
:root {
|
||||||
|
--motion-fast: 0ms;
|
||||||
|
--motion-base: 0ms;
|
||||||
|
--motion-slow: 0ms;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user