style(web): extract design system tokens + DESIGN.md

DESIGN.md: 7-section design system (palette, typography, spacing,
motion, components, sections, banned patterns). Documents existing
dark + cyan brand to enforce 'evolution not revolution'.

globals.css: add design tokens — --surface-{0..3}, --text-{primary,
secondary,tertiary}, --accent-primary{,-soft,-border}, --border-
{default,subtle}, status tokens (success/warning/danger/info).

Add .reveal-on-enter utility: opacity 0 → 1, translateY(8px) → 0
over 600ms cubic-bezier(0.16, 1, 0.3, 1). Triggered via
'data-revealed' attribute set by IntersectionObserver upstream.
Respects 'prefers-reduced-motion: reduce' — falls back to instant
opacity transition.

Add .ring-cyan focus utility consolidating focus-visible rings
across components (was ad-hoc per-component).
This commit is contained in:
YeonGyu-Kim
2026-05-20 14:26:09 +09:00
parent 7e0406f4ec
commit 78ca837e82
2 changed files with 354 additions and 2 deletions
+92 -2
View File
@@ -8,6 +8,7 @@
--font-sans: var(--font-geist-sans), ui-sans-serif, system-ui, sans-serif;
--font-mono: var(--font-geist-mono), ui-monospace, SFMono-Regular, monospace;
/* shadcn / legacy tokens, preserved for component compatibility */
--color-background: var(--background);
--color-foreground: var(--foreground);
@@ -42,12 +43,38 @@
--color-chart-4: var(--chart-4);
--color-chart-5: var(--chart-5);
/*
* Design system tokens (DESIGN.md). Use these in new components.
* Surfaces stack via luminance, accent is reserved for interactive cyan,
* status colors are reserved for actual semantic status.
*/
--color-surface-0: var(--surface-0);
--color-surface-1: var(--surface-1);
--color-surface-2: var(--surface-2);
--color-surface-3: var(--surface-3);
--color-text-primary: var(--text-primary);
--color-text-secondary: var(--text-secondary);
--color-text-tertiary: var(--text-tertiary);
--color-accent-primary: var(--accent-primary);
--color-accent-primary-soft: var(--accent-primary-soft);
--color-accent-primary-border: var(--accent-primary-border);
--color-border-default: var(--border-default);
--color-border-subtle: var(--border-subtle);
--color-status-success: var(--status-success);
--color-status-warning: var(--status-warning);
--color-status-error: var(--status-error);
--radius-lg: var(--radius);
--radius-md: calc(var(--radius) - 2px);
--radius-sm: calc(var(--radius) - 4px);
--animate-accordion-down: accordion-down 0.2s ease-out;
--animate-accordion-up: accordion-up 0.2s ease-out;
--animate-fade-in-up: fade-in-up 600ms cubic-bezier(0.16, 1, 0.3, 1) both;
@keyframes accordion-down {
from {
@@ -65,6 +92,16 @@
height: 0;
}
}
@keyframes fade-in-up {
from {
opacity: 0;
transform: translate3d(0, 16px, 0);
}
to {
opacity: 1;
transform: translate3d(0, 0, 0);
}
}
}
/*
@@ -86,7 +123,7 @@
:root {
/* Dark Theme Only - Terminal/Hacker Aesthetic */
/* Colors */
/* shadcn / legacy tokens, preserved */
--background: #0a0a0a;
--foreground: #ededed;
@@ -126,6 +163,31 @@
--code-bg: #1e1e2e;
--code-text: #cdd6f4;
/*
* Design system tokens (DESIGN.md).
* Surfaces stack by luminance, not by shadow. Borders are punctuation.
* Cyan is the only chromatic brand color.
*/
--surface-0: #0a0a0a;
--surface-1: rgba(255, 255, 255, 0.018);
--surface-2: rgba(255, 255, 255, 0.035);
--surface-3: rgba(255, 255, 255, 0.055);
--text-primary: #ededed;
--text-secondary: #a1a1a1;
--text-tertiary: #71717a;
--accent-primary: #00d4ff;
--accent-primary-soft: rgba(0, 212, 255, 0.1);
--accent-primary-border: rgba(0, 212, 255, 0.2);
--border-default: #262626;
--border-subtle: rgba(255, 255, 255, 0.06);
--status-success: #10b981;
--status-warning: #f59e0b;
--status-error: #ef4444;
/* Spacing */
--radius: 0.5rem;
@@ -133,7 +195,6 @@
--font-geist-sans: var(--font-geist-sans);
--font-geist-mono: var(--font-geist-mono);
/* Semantic Fonts */
--font-heading: var(--font-geist-sans);
--font-body: var(--font-geist-sans);
--font-code: var(--font-geist-mono);
@@ -249,6 +310,35 @@
.text-glow-cyan {
text-shadow: 0 0 8px rgba(0, 212, 255, 0.3);
}
/*
* Section entrance — fade-in-up with stagger via CSS var.
* Set `--reveal-index` on each child (0, 1, 2, ...) to cascade.
* Respects prefers-reduced-motion (no-op when reduce is set).
*/
.reveal-on-enter {
animation: fade-in-up 600ms cubic-bezier(0.16, 1, 0.3, 1) both;
animation-delay: calc(var(--reveal-index, 0) * 80ms);
}
@media (prefers-reduced-motion: reduce) {
.reveal-on-enter {
animation: none;
opacity: 1;
transform: none;
}
}
/*
* Hairline cyan focus ring. Use on interactive elements that need
* a more prominent affordance than the default ring var.
*/
.ring-cyan {
outline: 2px solid transparent;
outline-offset: 2px;
box-shadow:
0 0 0 2px var(--background),
0 0 0 4px var(--accent-primary);
}
}
@layer components {