diff --git a/apps/frontend/src/components/AppShell.tsx b/apps/frontend/src/components/AppShell.tsx index 6bc61f4d..62eea958 100644 --- a/apps/frontend/src/components/AppShell.tsx +++ b/apps/frontend/src/components/AppShell.tsx @@ -160,11 +160,10 @@ function NavItemLink({ title={collapsed ? label : undefined} className={({ isActive }) => cn( - // W13 — pill nav items match the AIS button shape; the active - // bg-primary/10 + text-primary tint then reads exactly like the - // AIS selected-nav state. Hover/active transitions stay on the - // W11 150 ms ease-out-soft tokens. - "flex items-center rounded-full py-2 text-sm font-medium transition-colors duration-fast ease-out-soft", + // W11-F polish — sidebar nav hover/active transitions land on the + // W11-A 150 ms ease-out-soft tokens for parity with every other + // hoverable affordance (buttons, dropdown items, tabs). + "flex items-center rounded-md py-2 text-sm font-medium transition-colors duration-fast ease-out-soft", collapsed ? "justify-center px-2" : "gap-2 px-3", "hover:bg-accent hover:text-accent-foreground", "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", diff --git a/apps/frontend/src/components/ui/badge.tsx b/apps/frontend/src/components/ui/badge.tsx index 86ad8336..c6496cb2 100644 --- a/apps/frontend/src/components/ui/badge.tsx +++ b/apps/frontend/src/components/ui/badge.tsx @@ -49,17 +49,17 @@ const badgeVariants = cva( // critical text-red-700 → 5.54:1 // high text-orange-800 → 6.47:1 // medium text-yellow-800 → 5.91:1 - // low text-teal-800 → 6.59:1 (W13 — token moved to - // teal-700, text follows the hue) + // low text-blue-700 → 5.83:1 // info text-slate-600 → 6.41:1 // // The dot indicators (SeverityBadge / DependencyScopeBadge / chart // legends) still use `bg-risk-X` raw so the brand colour stays - // recognisable — only the text shade is darkened. + // recognisable — only the text shade is darkened. Token values in + // `index.css` are NOT changed (W11 prohibition: "Severity 색 변경 0"). critical: "border-transparent bg-risk-critical/10 text-red-700", high: "border-transparent bg-risk-high/10 text-orange-800", medium: "border-transparent bg-risk-medium/15 text-yellow-800", - low: "border-transparent bg-risk-low/10 text-teal-800", + low: "border-transparent bg-risk-low/10 text-blue-700", info: "border-transparent bg-risk-info/15 text-slate-600", success: "border-transparent bg-emerald-100 text-emerald-700", }, diff --git a/apps/frontend/src/components/ui/button.tsx b/apps/frontend/src/components/ui/button.tsx index 03169f27..eea5203e 100644 --- a/apps/frontend/src/components/ui/button.tsx +++ b/apps/frontend/src/components/ui/button.tsx @@ -5,26 +5,25 @@ import { forwardRef, type ButtonHTMLAttributes } from "react"; import { cn } from "@/lib/utils"; /** - * Button — W13 AIS adoption (pill shape) on top of the W11-A polish. + * Button — W11-A polish. * - * - `rounded-full` pills are the Google AI Studio button shape. This is - * deliberately NOT driven by the --radius token — raising the token to - * a pill value would full-round cards/dialogs via the tailwind.config - * calc() derivations. Inputs keep the token radius on purpose. - * - `transition-all duration-fast ease-out-soft` — 150 ms eased hover / - * focus transition (W11 Linear polish, retained). - * - Focus ring `ring-2` + `ring-offset-2` (matches --ring, now the blue - * primary, for a coherent focus signal). - * - shadow-sm on default/destructive/outline resolves to a zero-alpha - * no-op under W13 tokens (AIS keeps in-flow surfaces flat); kept in - * the class lists so a future token change re-enables elevation - * without touching this file. + * Changes vs. previous baseline: + * - `transition-colors` → `transition-all duration-fast ease-out-soft` so + * hover/focus is a 150 ms eased transition (Linear polish) covering + * background AND shadow. + * - Focus ring kept at `ring-2` + `ring-offset-2` (matches new --ring token + * which now lines up with --primary for a coherent focus signal). + * - Default variant picks up `shadow-sm` so the primary CTA reads as a + * raised affordance against the off-white page background (Vercel + * deployments-1 "Visit" button reference). + * - Outline variant gets `shadow-sm` too — light elevation, no border + * contrast bump needed. * * The hex colors themselves are NOT in this file — they flow from the - * tokens declared in `src/index.css` (W13). + * tokens declared in `src/index.css` (W11-A). */ const buttonVariants = cva( - "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-full text-sm font-medium ring-offset-background transition-all duration-fast ease-out-soft focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", + "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-all duration-fast ease-out-soft focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", { variants: { variant: { @@ -41,8 +40,8 @@ const buttonVariants = cva( }, size: { default: "h-9 px-4 py-2", - sm: "h-8 px-3 text-xs", - lg: "h-10 px-6", + sm: "h-8 rounded-md px-3 text-xs", + lg: "h-10 rounded-md px-6", icon: "h-9 w-9", }, }, diff --git a/apps/frontend/src/index.css b/apps/frontend/src/index.css index 6b744271..3d6eb07c 100644 --- a/apps/frontend/src/index.css +++ b/apps/frontend/src/index.css @@ -5,26 +5,24 @@ /* --------------------------------------------------------------------------- * TRUSCA — design tokens * - * W13 (2026-06-12) — Google AI Studio light tone, adopted from the dev - * prototype (PR #394) after user review. Supersedes the W11-A Vercel + - * Linear palette; the W11 typography hierarchy, focus ring, and motion - * polish are retained unchanged. + * W11-A (2026-05-27) — Vercel base + Linear polish (option C, light single). + * SoT: docs/ux/design-philosophy-evolution-plan-2026-05-27.md §4. * - * Tone reference — Google AI Studio (light): - * - plain white canvas, grey panel tint, #dadce0 hairline borders - * - Google-blue primary CTA + tonal (light-blue) secondary - * - pill buttons (see button.tsx) and flat cards (border + tone, no - * shadow); shadows are reserved for floating surfaces - * - fonts stay Inter + JetBrains Mono (Google Sans is proprietary) + * Tone reference: + * - Vercel light deployments / domains / deployment detail + * → background / surface / border / sidebar tint / dense rows / primary + * near-black button + * - Linear feature / dashboard + * → typography hierarchy (heading semibold), focus ring (visible 2 + 2 + * offset), hover transitions (150 ms ease-out), elevation shadows, + * dropdown polish. Dark variant intentionally NOT absorbed — Linear + * is dark-native, we re-interpret only the polish layer in light. * * Layout invariants (unchanged): * sidebar 224 px · header 48 px · table row 40 px · Inter + JetBrains Mono. - * Density is kept — only the skin changed. * - * Risk severity colors: - * Critical / High / Medium / Info unchanged. Low moved blue-600 → teal-700 - * in W13 (user decision): the old blue collided with the new Google-blue - * primary, making Low badges read like CTAs / links. + * Risk severity colors (unchanged — domain semantics fixed): + * Critical / High / Medium / Low / Info. * * Forward-compat note: * Dark mode tokens are NOT defined in this phase (2026-05-27 user decision). @@ -36,83 +34,79 @@ @layer base { :root { /* ------------------------------------------------------------------- - * shadcn/ui base tokens — Google AI Studio light palette (W13). + * shadcn/ui base tokens — re-tuned to Vercel light palette. * * HSL is shadcn convention. Hex equivalents are noted for review; * never reference the hex directly in components (use the CSS var or * the Tailwind utility — e.g. `bg-background`, `text-foreground`). * ----------------------------------------------------------------- */ - /* Page background — plain white canvas (AIS). Cards sit flush on it - * and separate via border + tone, not elevation. */ - --background: 0 0% 100%; /* #ffffff */ - --foreground: 0 0% 12%; /* #1f1f1f — Google grey-900 */ + /* Page background — slight off-white so elevated surfaces (cards, + * popovers) read as raised. Vercel uses ~#fafafa for the canvas. */ + --background: 0 0% 98%; /* #fafafa */ + --foreground: 240 6% 10%; /* #18181b — warm near-black (was #0f172a navy) */ - /* Surfaces share the white canvas; --muted carries the grey panel / - * table-header tint instead. */ + /* Elevated surfaces sit on top of --background. Pure white reads as + * "card on page" against the off-white canvas. */ --card: 0 0% 100%; /* #ffffff */ - --card-foreground: 0 0% 12%; + --card-foreground: 240 6% 10%; --popover: 0 0% 100%; - --popover-foreground: 0 0% 12%; + --popover-foreground: 240 6% 10%; /* Muted = table header / sidebar tint / placeholder fill. */ - --muted: 210 17% 98%; /* #f8f9fa */ - --muted-foreground: 213 5% 39%; /* #5f6368 — Google grey-700, passes AA */ - - /* Border / input outline — the AIS standard hairline. */ - --border: 220 9% 87%; /* #dadce0 */ - --input: 220 9% 87%; - - /* Primary CTA — Google blue. Hover flows through `bg-primary/90`. */ - --primary: 217 90% 43%; /* #0b57d0 */ - --primary-foreground: 0 0% 100%; - - /* Secondary = Google "tonal" button (light-blue fill, deep-blue text); - * accent = the faint blue-grey tint AIS uses on hover rows and ghost - * surfaces. */ - --secondary: 217 91% 91%; /* #d3e3fd */ - --secondary-foreground: 217 90% 15%; /* #041e49 */ - --accent: 213 43% 96%; /* #f0f4f9 */ - --accent-foreground: 0 0% 12%; - - /* Destructive — Google red. Near risk-critical (#dc2626) so destructive - * buttons still share the visual language of a Critical badge. */ - --destructive: 4 71% 50%; /* #d93025 */ - --destructive-foreground: 0 0% 100%; - - /* Focus ring — matches the blue primary so the 2 px outline reads as - * "the same thing this button is". */ - --ring: 217 90% 43%; + --muted: 240 5% 96%; /* #f4f4f5 */ + --muted-foreground: 240 4% 46%; /* #71717a — passes AA on --muted */ + + /* Border / input outline — softer than slate-200, more neutral. */ + --border: 240 5% 91%; /* #e5e5ea */ + --input: 240 5% 91%; + + /* Primary CTA — Vercel-style warm near-black instead of navy. + * Reads as "important action" without leaning blue. */ + --primary: 240 6% 10%; /* #18181b */ + --primary-foreground: 0 0% 98%; + + /* Secondary / accent share the muted tint — used for ghost-style + * surfaces and hover row backgrounds. */ + --secondary: 240 5% 96%; + --secondary-foreground: 240 6% 10%; + --accent: 240 5% 96%; + --accent-foreground: 240 6% 10%; + + /* Destructive — aligned with risk-critical hex so destructive + * buttons share the visual language of a Critical severity badge. */ + --destructive: 0 72% 51%; /* ~#dc2626 */ + --destructive-foreground: 0 0% 98%; + + /* Focus ring — matches primary tone so the 2 px outline reads as + * "the same thing this button is", per Linear polish. */ + --ring: 240 6% 10%; /* ------------------------------------------------------------------- - * Radius hierarchy (W13 — one step rounder than W11 across the board). + * Radius hierarchy (Linear polish — different sizes for different + * affordances so depth reads at a glance). * - * --radius-sm 6 px — small inputs, badges, chips - * --radius 8 px — cards, inputs, table chrome (DEFAULT) - * --radius-lg 10 px — drawer, large surfaces - * --radius-xl 14 px — modals, dialogs + * --radius-sm 4 px — small inputs, badges, chips + * --radius 6 px — buttons, cards, table chrome (DEFAULT) + * --radius-lg 8 px — drawer, large surfaces + * --radius-xl 12 px — modals, dialogs * - * Buttons are pills (rounded-full in button.tsx), NOT derived from - * this token — raising --radius to a pill value would full-round - * cards and dialogs via the calc() derivations in tailwind.config.ts. + * shadcn convention reads `--radius` as the card default; smaller / + * larger variants derive via `calc()` in tailwind.config.ts. * ----------------------------------------------------------------- */ - --radius: 0.5rem; + --radius: 0.375rem; /* ------------------------------------------------------------------- - * Elevation — AIS keeps in-flow surfaces flat (cards/buttons separate - * via border + tone), so shadow-sm is a zero-alpha no-op. It must stay - * a VALID box-shadow value (consumers do `box-shadow: var(--shadow-sm)` - * and an empty string / `none` inside a list breaks them). md / lg - * follow the Google elevation-1 / -2 recipes for floating surfaces - * (dropdown / popover / drawer / dialog). + * Elevation — subtle, Vercel-style. Light shadows only; no glow. + * Used by Card (sm), Dropdown / Popover (md), Drawer / Dialog (lg). * ----------------------------------------------------------------- */ - --shadow-sm: 0 0 0 0 rgb(0 0 0 / 0); + --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.04); --shadow-md: - 0 1px 2px 0 rgb(60 64 67 / 0.3), - 0 1px 3px 1px rgb(60 64 67 / 0.15); + 0 2px 8px -2px rgb(0 0 0 / 0.08), + 0 1px 2px 0 rgb(0 0 0 / 0.04); --shadow-lg: - 0 1px 3px 0 rgb(60 64 67 / 0.3), - 0 4px 8px 3px rgb(60 64 67 / 0.15); + 0 10px 28px -8px rgb(0 0 0 / 0.12), + 0 3px 8px -3px rgb(0 0 0 / 0.06); /* ------------------------------------------------------------------- * Motion (Linear polish) — short, ease-out. Three steps cover the @@ -128,19 +122,14 @@ --ease-out: cubic-bezier(0.16, 1, 0.3, 1); /* ------------------------------------------------------------------- - * Risk severity — domain meaning is fixed. + * Risk severity (unchanged — domain meaning is fixed). * Used directly by recharts / lucide as raw hex, also exposed as * Tailwind `text-risk-critical` / `bg-risk-high/10` etc. - * - * W13: --risk-low moved blue-600 (#2563eb) → teal-700 (#0f766e) so Low - * badges no longer share a hue with the Google-blue primary CTA. - * Contrast (WCAG): 5.47:1 on white, 4.76:1 on its own 10 % tint — - * both clear AA, slightly better than the old blue (5.17 / 4.49). * ----------------------------------------------------------------- */ --risk-critical: #dc2626; --risk-high: #ea580c; --risk-medium: #ca8a04; - --risk-low: #0f766e; + --risk-low: #2563eb; --risk-info: #71717a; /* Layout density (unchanged). */ diff --git a/apps/frontend/src/pages/dev/DesignSystemPreview.tsx b/apps/frontend/src/pages/dev/DesignSystemPreview.tsx index 9c434357..ae6cf9af 100644 --- a/apps/frontend/src/pages/dev/DesignSystemPreview.tsx +++ b/apps/frontend/src/pages/dev/DesignSystemPreview.tsx @@ -2,11 +2,9 @@ * Design System Preview — W11-A, expanded into a living reference in W12-E. * * Dev-only sample page. Originally the visual confirm gate for the W11-A - * token redefinition; W12-E grew it into a living component reference + a - * manual review / visual-regression surface covering the W12 primitives. - * W13 (2026-06-12) re-skinned the token set to the Google AI Studio light - * tone (white canvas, blue primary, pill buttons) after the PR #394 - * prototype review. + * token redefinition (Vercel base + Linear polish, light single-theme); W12-E + * grew it into a living component reference + a manual review / visual- + * regression surface covering the W12 primitives. * * Routing: * - Mounted at `/dev/design-preview` (see router.tsx). @@ -107,17 +105,17 @@ export function DesignSystemPreview() { Design system · living reference (dev only)

- TRUSCA — Google AI Studio light + Vercel base + Linear polish

- Living sample of the W13 token set (adopted 2026-06-12): white - canvas, Google-blue primary, tonal secondary, pill buttons, flat - cards. The rest of the app uses these same tokens — walk a few - pages after this to spot any regressions. + Sample of the new token set applied to two foundational + components. The rest of the app still uses these same tokens — + walk a few pages after this to spot any regressions before we + green-light Phase B.

-
+
@@ -150,7 +148,7 @@ export function DesignSystemPreview() {
-
+
@@ -169,7 +167,7 @@ export function DesignSystemPreview() {
-
+
@@ -263,16 +261,16 @@ export function DesignSystemPreview() {
- rounded-sm · 6px + rounded-sm · 4px
- rounded-md · 8px + rounded-md · 6px
- rounded-lg · 10px + rounded-lg · 8px
- rounded-xl · 14px + rounded-xl · 12px

diff --git a/apps/frontend/tailwind.config.ts b/apps/frontend/tailwind.config.ts index 81289642..bbee6e06 100644 --- a/apps/frontend/tailwind.config.ts +++ b/apps/frontend/tailwind.config.ts @@ -91,15 +91,12 @@ const config: Config = { row: "var(--table-row)", }, borderRadius: { - // W13 — radius hierarchy, one step rounder than W11 (--radius 8px). + // W11-A — radius hierarchy. Different sizes for different affordances. // - // sm 6 px — chips / small inputs - // md 8 px — cards / inputs / table chrome (= --radius default) - // lg 10 px — drawer, large panels - // xl 14 px — modals, dialogs - // - // Buttons are pills (`rounded-full` in button.tsx), not part of - // this scale. + // sm 4 px — chips / small inputs + // md 6 px — buttons / cards / table chrome (= --radius default) + // lg 8 px — drawer, large panels + // xl 12 px — modals, dialogs // // shadcn's older `rounded-lg`/`rounded-md`/`rounded-sm` mapping // (lg = --radius, md = lg-2, sm = lg-4) still works for existing diff --git a/apps/frontend/tests/unit/pages/dev/DesignSystemPreview.test.tsx b/apps/frontend/tests/unit/pages/dev/DesignSystemPreview.test.tsx index b3fb28e6..8f90bba5 100644 --- a/apps/frontend/tests/unit/pages/dev/DesignSystemPreview.test.tsx +++ b/apps/frontend/tests/unit/pages/dev/DesignSystemPreview.test.tsx @@ -17,11 +17,11 @@ import { describe, expect, it } from "vitest"; import { DesignSystemPreview } from "@/pages/dev/DesignSystemPreview"; -describe("DesignSystemPreview (W13)", () => { +describe("DesignSystemPreview (W11-A)", () => { it("renders the page header", () => { render(); expect( - screen.getByRole("heading", { name: /Google AI Studio light/i }), + screen.getByRole("heading", { name: /Vercel base \+ Linear polish/i }), ).toBeInTheDocument(); }); @@ -36,11 +36,10 @@ describe("DesignSystemPreview (W13)", () => { it("renders the four radius hierarchy samples", () => { render(); - // W13 — one step rounder than W11 (--radius 8px; see tailwind.config.ts). - expect(screen.getByText(/rounded-sm · 6px/)).toBeInTheDocument(); - expect(screen.getByText(/rounded-md · 8px/)).toBeInTheDocument(); - expect(screen.getByText(/rounded-lg · 10px/)).toBeInTheDocument(); - expect(screen.getByText(/rounded-xl · 14px/)).toBeInTheDocument(); + expect(screen.getByText(/rounded-sm · 4px/)).toBeInTheDocument(); + expect(screen.getByText(/rounded-md · 6px/)).toBeInTheDocument(); + expect(screen.getByText(/rounded-lg · 8px/)).toBeInTheDocument(); + expect(screen.getByText(/rounded-xl · 12px/)).toBeInTheDocument(); }); it("renders the three shadow elevation samples", () => { diff --git a/docs-site/docs/reference/design-system.md b/docs-site/docs/reference/design-system.md index 4d8355e6..32ca05e6 100644 --- a/docs-site/docs/reference/design-system.md +++ b/docs-site/docs/reference/design-system.md @@ -1,14 +1,14 @@ --- id: design-system title: Design system -description: TRUSCA design system — tokens (colour, spacing, radius, shadow, motion, typography), component conventions, micro-interactions, accessibility, and the W13 Google AI Studio re-skin. +description: TRUSCA design system — tokens (colour, spacing, radius, shadow, motion, typography), component conventions, micro-interactions, accessibility, and the W11 visual-identity refresh. sidebar_label: Design system sidebar_position: 10 --- # Design system -The portal frontend follows a single, light-mode design system in the **Google AI Studio** tone (W13, 2026-06-12): white canvas, Google-blue primary, tonal secondary, pill buttons, flat bordered cards. The typography hierarchy, motion, and focus polish introduced with W11 (Linear-inspired) are retained. Dark mode is deferred to v2.5+. +The portal frontend follows a single, light-mode design system inspired by **Vercel** (light base — surfaces, dense rows, sidebar tint) and **Linear** (typography hierarchy, motion, focus polish). Dark mode is deferred to +. :::note Audience Frontend contributors, designers, and reviewers. The tokens here are the canonical reference — components should never hard-code hex values or magic spacing. @@ -26,59 +26,48 @@ TRUSCA is a **risk-first, information-dense, modern enterprise SCA** tool. The v 1. **Communicate severity at a glance.** Risk colours (Critical / High / Medium / Low / Info) appear next to a textual label and an icon or dot — colour is never the sole signal. 2. **Pack data without feeling cramped.** Compact 40 px table rows; 224 px sidebar; 48 px header; 16 / 20 / 24 px card padding scale. -3. **Read as a modern product (Google AI Studio tone).** White canvas (`#ffffff`) with `#dadce0` hairline borders; Google-blue primary (`#0b57d0`) with a tonal light-blue secondary; pill buttons; flat cards that separate via border + tone rather than shadow; semibold headings; visible focus rings. Fonts stay Inter + JetBrains Mono (Google Sans is proprietary). +3. **Read as a modern enterprise product.** Warm near-black (`#18181b`) instead of navy (`#0f172a`); off-white canvas (`#fafafa`) so cards lift visually; subtle shadows; semibold headings; visible focus rings. 4. **Move only as much as needed.** Motion is short and ease-out — 150 ms for hover / focus, 200 ms for drawer slide, 250 ms for page-level chrome. No bounce, no fade-in delays. -### W13 (2026-06-12) — Google AI Studio re-skin +### W11 (2026-05-27) — visual refresh -W13 replaced the W11 Vercel + Linear skin with the Google AI Studio light tone, adopted after a dev-toggle prototype review (PR #394). Structural decisions (sidebar nav, 40 px row, drawer-for-detail, risk semantics) and the W11 polish layer (typography, motion, focus ring) carry over unchanged. What changed: +The W11 milestone replaced the previous "BD-style 2015" aesthetic with the current Vercel+Linear blend. The structural decisions (sidebar nav, 40 px row, drawer-for-detail, risk semantics) carry over unchanged. What changed: -| Surface | W11 (before) | W13 (after) | +| Surface | Before | After | |---|---|---| -| Primary CTA | `#18181b` warm near-black | `#0b57d0` Google blue | -| Secondary | muted grey tint | `#d3e3fd` tonal light blue | -| Page background | `#fafafa` off-white canvas | `#ffffff` white canvas | -| Card surface | white, lifts via shadow-sm | white, flat — separates via border + tone | -| Hover / ghost tint | muted grey | `#f0f4f9` faint blue-grey | -| Border | `#e5e5ea` | `#dadce0` AIS hairline | -| Button shape | rounded-md (6 px) | pill (`rounded-full`) | -| Radius scale | sm 4 / md 6 / lg 8 / xl 12 | sm 6 / md 8 / lg 10 / xl 14 | -| Shadow | sm (card) / md / lg | sm = none (flat); md / lg = Google elevation-1/-2 | -| Focus ring | near-black | `#0b57d0` blue (matches primary) | -| `--risk-low` | `#2563eb` blue-600 | `#0f766e` teal-700 (no longer collides with the blue primary) | - -Risk severity colours for Critical / High / Medium / Info are **unchanged** — the domain semantics are fixed. Low is the single W13 exception (user decision): the old blue read like a CTA / link next to the new Google-blue primary. Where a severity hex fails WCAG AA as body text on a light tint, the badge text shade is darkened within the same hue family (see [Severity colour accessibility](#severity-colour-accessibility) below). - -

-W11 (2026-05-27) — the previous Vercel + Linear refresh - -W11 replaced the original "BD-style 2015" aesthetic (navy `#0f172a`, uniform 8 px radius, default easing) with a Vercel light base + Linear polish: warm near-black `#18181b` primary, `#fafafa` canvas, radius / shadow / motion token hierarchy, semibold headings, visible focus rings, and the drawer + page dual detail surface. The polish layer survives in W13; the colour skin does not. - -
+| Primary CTA | `#0f172a` cool navy | `#18181b` warm near-black | +| Page background | `#ffffff` pure white | `#fafafa` off-white canvas | +| Card surface | grey-tinted | `#ffffff` pure white (lifts off the canvas) | +| Border | `slate-200` | `#e5e5ea` neutral hairline | +| Radius | 8 px uniform | hierarchy — sm 4 / md 6 / lg 8 / xl 12 | +| Shadow | none / default | sm (card) / md (popover) / lg (drawer · dialog) | +| Motion | default browser | 150 / 200 / 250 ms ease-out | +| Heading weight | bold | semibold + tracking-tight | +| Focus ring | shadcn default | 2 px outline + 2 px offset (a11y) | +| Detail surface | drawer-only | dual surface — drawer (quick check) + page nav (deep work) | + +The risk colour palette (Critical / High / Medium / Low / Info) is intentionally **unchanged** — the brand semantics are fixed across releases. Where the raw severity hex fails WCAG AA as body text on a light tint, the badge text shade is darkened within the same hue family (see [Severity colour accessibility](#severity-colour-accessibility) below). ## Colour tokens All colour decisions reference the CSS custom properties declared in `index.css`. Components should never reference hex values directly — use the Tailwind utility (`bg-background`, `text-foreground`, `bg-risk-critical/10`) or the CSS variable. -### Neutral palette (Google AI Studio light) +### Neutral palette (Vercel base) | Token | Hex | HSL | Use | |---|---|---|---| -| `--background` | `#ffffff` | `0 0% 100%` | Page canvas — plain white (AIS). | -| `--card` | `#ffffff` | `0 0% 100%` | Cards, popovers, drawer body, tooltip — flush with the canvas, separated by border + tone. | -| `--foreground` | `#1f1f1f` | `0 0% 12%` | Body text — Google grey-900. | -| `--muted` | `#f8f9fa` | `210 17% 98%` | Subtle fills — table headers, sidebar tint, placeholder backgrounds, disabled inputs. | -| `--muted-foreground` | `#5f6368` | `213 5% 39%` | Secondary text, captions, table column headers — Google grey-700. | -| `--border` | `#dadce0` | `220 9% 87%` | Hairline borders — the AIS standard. Decorative separator only, never the sole means of identifying a UI region. | -| `--input` | `#dadce0` | `220 9% 87%` | Input outline. | -| `--primary` | `#0b57d0` | `217 90% 43%` | Primary CTA — Google blue, "the important action on the page". | -| `--primary-foreground` | `#ffffff` | `0 0% 100%` | Text on primary. | -| `--secondary` | `#d3e3fd` | `217 91% 91%` | Tonal button fill (Google "tonal" pattern). | -| `--secondary-foreground` | `#041e49` | `217 90% 15%` | Text on tonal fill. | -| `--accent` | `#f0f4f9` | `213 43% 96%` | Hover rows, ghost-button hover — faint blue-grey tint. | -| `--destructive` | `#d93025` | `4 71% 50%` | Destructive CTA — Google red, near `--risk-critical` so destructive buttons share severity-badge visual language. | -| `--destructive-foreground` | `#ffffff` | `0 0% 100%` | Text on destructive. | -| `--ring` | `#0b57d0` | `217 90% 43%` | Focus ring. Matches primary so the outline reads as "the same action this is". | +| `--background` | `#fafafa` | `0 0% 98%` | Page canvas. Lets cards lift visually. | +| `--card` | `#ffffff` | `0 0% 100%` | Elevated surfaces — cards, popovers, drawer body, tooltip. | +| `--foreground` | `#18181b` | `240 6% 10%` | Body text. Warm near-black, not navy. | +| `--muted` | `#f4f4f5` | `240 5% 96%` | Subtle fills — table headers, sidebar tint, placeholder backgrounds, disabled inputs. | +| `--muted-foreground` | `#71717a` | `240 4% 46%` | Secondary text, captions, table column headers. | +| `--border` | `#e5e5ea` | `240 5% 91%` | Hairline borders. Decorative separator only — never the sole means of identifying a UI region. | +| `--input` | `#e5e5ea` | `240 5% 91%` | Input outline. | +| `--primary` | `#18181b` | `240 6% 10%` | Primary CTA — "the important action on the page". | +| `--primary-foreground` | `#fafafa` | `0 0% 98%` | Text on primary. | +| `--destructive` | `#dc2626` | `0 72% 51%` | Destructive CTA. Aligned with `--risk-critical` so destructive buttons share severity-badge visual language. | +| `--destructive-foreground` | `#fafafa` | `0 0% 98%` | Text on destructive. | +| `--ring` | `#18181b` | `240 6% 10%` | Focus ring. Matches primary so the outline reads as "the same action this is". | ### Risk severity (domain semantics — fixed) @@ -87,10 +76,10 @@ All colour decisions reference the CSS custom properties declared in `index.css` | `--risk-critical` | `#dc2626` | Critical CVE, forbidden licence, build-blocking finding. | | `--risk-high` | `#ea580c` | High-severity CVE, conditional licence at risk. | | `--risk-medium` | `#ca8a04` | Medium CVE, conditional licence awaiting review. | -| `--risk-low` | `#0f766e` | Low CVE, informational status. Teal-700 since W13 — moved out of the blue family so Low badges don't read like the blue primary CTA. | +| `--risk-low` | `#2563eb` | Low CVE, informational status. | | `--risk-info` | `#71717a` | Neutral informational state. | -The severity hex values are stable across releases (the W13 Low move is the documented exception). They appear in: +The severity hex values are **never changed** between releases. They appear in: - Recharts fills and chart legends (raw hex via the `--risk-X` variable). - `bg-risk-X/N` tints on badges and dot indicators. @@ -120,26 +109,26 @@ When severity colour is used as **body text** (a coloured word inside a badge or ## Radius hierarchy -Different affordances use different radii so depth reads at a glance. W13 moved the whole scale one step rounder. +Different affordances use different radii so depth reads at a glance. | Token | Value | Affordance | |---|---|---| -| `--radius-sm` | 6 px | Small inputs, badges, chips. | -| `--radius` | 8 px | **Default** — cards, inputs, table chrome. | -| `--radius-lg` | 10 px | Drawer, large surfaces. | -| `--radius-xl` | 14 px | Modals, dialogs. | +| `--radius-sm` | 4 px | Small inputs, badges, chips. | +| `--radius` | 6 px | **Default** — buttons, cards, table chrome. | +| `--radius-lg` | 8 px | Drawer, large surfaces. | +| `--radius-xl` | 12 px | Modals, dialogs. | -The Tailwind config derives `rounded-sm`, `rounded-md`, `rounded-lg`, `rounded-xl` from these tokens via `calc()`. **Buttons are pills** (`rounded-full` in `button.tsx`) and deliberately not part of this scale — raising `--radius` to a pill value would full-round cards and dialogs through the `calc()` derivations. +The Tailwind config derives `rounded-sm`, `rounded-md`, `rounded-lg`, `rounded-xl` from these tokens via `calc()`. ## Shadow scale -AIS keeps in-flow surfaces flat — cards and buttons separate via border + tone. Shadows are reserved for floating surfaces and follow the Google elevation recipes. +Vercel-style subtle elevation. Light shadows only — no glow. | Token | Value | Use | |---|---|---| -| `--shadow-sm` | `0 0 0 0 rgb(0 0 0 / 0)` | Intentionally a zero-alpha no-op (flat cards / buttons). Kept a valid box-shadow value so `var(--shadow-sm)` consumers don't break, and so a future token change can re-enable elevation in one place. | -| `--shadow-md` | `0 1px 2px 0 rgb(60 64 67 / 0.3), 0 1px 3px 1px rgb(60 64 67 / 0.15)` | Dropdown, popover, tooltip (Google elevation-1). | -| `--shadow-lg` | `0 1px 3px 0 rgb(60 64 67 / 0.3), 0 4px 8px 3px rgb(60 64 67 / 0.15)` | Drawer, dialog (Google elevation-2). | +| `--shadow-sm` | `0 1px 2px 0 rgb(0 0 0 / 0.04)` | Cards, stat tiles. | +| `--shadow-md` | `0 2px 8px -2px rgb(0 0 0 / 0.08), 0 1px 2px 0 rgb(0 0 0 / 0.04)` | Dropdown, popover, tooltip. | +| `--shadow-lg` | `0 10px 28px -8px rgb(0 0 0 / 0.12), 0 3px 8px -3px rgb(0 0 0 / 0.06)` | Drawer, dialog. | ## Motion @@ -213,12 +202,10 @@ Do not hand-roll a `

` block — extend `PageHeader` if a new layout `apps/frontend/src/components/ui/button.tsx` -- **Pill shape** (`rounded-full`, W13) on every variant and size — the AIS button silhouette. Inputs keep the token radius; only buttons (and sidebar nav items) are pills. -- Default variant uses `bg-primary text-primary-foreground` — solid Google blue. -- `secondary` variant is the Google **tonal** button — light-blue fill (`--secondary`), deep-blue text. +- Default variant uses `bg-primary text-primary-foreground` — solid warm near-black. - `outline` variant uses `border-input bg-background` — for secondary actions. -- `ghost` variant uses no background, hover tint only (`--accent`) — for nav items and toolbar actions. -- `destructive` uses `bg-destructive` — Google red near `--risk-critical`, reserved for irreversible actions (delete, revoke, reject). +- `ghost` variant uses no background, hover tint only — for nav items and toolbar actions. +- `destructive` uses `bg-destructive` — Critical-aligned red, reserved for irreversible actions (delete, revoke, reject). - Hover and focus transitions use `transition-colors duration-fast ease-out` (150 ms). - All variants include the focus ring. @@ -230,9 +217,9 @@ Do not hand-roll a `

` block — extend `PageHeader` if a new layout ### Card -- White surface (`bg-card`) flush on the white canvas — separation comes from the `--border` hairline and the `--muted` tone, not elevation (AIS flat-card pattern; `shadow-sm` resolves to a no-op). -- `rounded-md` (8 px) by default; `rounded-lg` (10 px) for primary content cards. -- `shadow-md` only on floating surfaces (popover / dropdown), never on in-flow cards. +- Pure-white surface (`bg-card`) on the off-white canvas — lifts visually without a heavy shadow. +- `rounded-md` (6 px) by default; `rounded-lg` (8 px) for primary content cards. +- `shadow-sm` for stats / tiles; `shadow-md` for elevated popovers. ### Table @@ -316,30 +303,28 @@ The portal targets **WCAG 2.1 Level AA**. Three policies make this concrete. | Pair | Ratio | Note | |---|---|---| -| `--foreground` on `--background` / `--card` | 16.48:1 | Body text. AAA. | -| `--muted-foreground` on `--background` / `--card` | 6.05:1 | Captions, secondary text. AA. | -| `--muted-foreground` on `--muted` | 5.74:1 | Captions on tinted fills. AA. | -| `--primary-foreground` on `--primary` | 6.39:1 | Primary button label. AA. | -| `--secondary-foreground` on `--secondary` | 12.57:1 | Tonal button label. AAA. | -| `--destructive-foreground` on `--destructive` | 4.77:1 | Destructive button label. AA. | -| `--ring` on `--background` | 6.39:1 | Focus ring. AA (UI ≥ 3:1). | +| `--foreground` on `--background` | 16.97:1 | Body text. AAA. | +| `--foreground` on `--card` | 17.72:1 | Body text on card. AAA. | +| `--muted-foreground` on `--background` | 4.63:1 | Captions, secondary text. AA. | +| `--muted-foreground` on `--card` | 4.83:1 | Captions on card. AA. | +| `--primary-foreground` on `--primary` | 16.97:1 | Primary button label. AAA. | +| `--destructive-foreground` on `--destructive` | 4.63:1 | Destructive button label. AA. | +| `--ring` on `--background` | 16.97:1 | Focus ring. AAA. | -Decorative borders (`--border` on `--background`, 1.37:1) are **intentionally low-contrast** — they are visual separators, not informative UI elements, and WCAG 1.4.11 exempts them. +Decorative borders (`--border` on `--background`, 1.20:1) are **intentionally low-contrast** — they are visual separators, not informative UI elements, and WCAG 1.4.11 exempts them. ### Severity colour accessibility -Severity hex values (`#dc2626` / `#ea580c` / `#ca8a04` / `#0f766e` / `#71717a`) are brand-fixed. Used as **body text** on a light tint some would measure as low as 2.5:1, which fails AA. The fix is structural, not chromatic — when the severity tone is used as text, the rendered text colour uses a deeper shade from the same Tailwind hue family: +Severity hex values (`#dc2626` / `#ea580c` / `#ca8a04` / `#2563eb` / `#71717a`) are brand-fixed. Used as **body text** on a light tint they would measure as low as 2.5:1, which fails AA. The fix is structural, not chromatic — when the severity tone is used as text, the rendered text colour uses a deeper shade from the same Tailwind hue family: | Tone | Tint background | Text colour | Contrast | |---|---|---|---| | `critical` | `bg-risk-critical/10` | `text-red-700` (`#b91c1c`) | 5.54:1 | | `high` | `bg-risk-high/10` | `text-orange-800` (`#9a3412`) | 6.47:1 | | `medium` | `bg-risk-medium/15` | `text-yellow-800` (`#854d0e`) | 5.91:1 | -| `low` | `bg-risk-low/10` | `text-teal-800` (`#115e59`) | 6.59:1 | +| `low` | `bg-risk-low/10` | `text-blue-700` (`#1d4ed8`) | 5.83:1 | | `info` | `bg-risk-info/15` | `text-slate-600` (`#52525b`) | 6.41:1 | -The raw Low token itself (teal-700 `#0f766e`) also clears AA where it's used directly as text (5.47:1 on white, 4.76:1 on its own 10 % tint) — slightly better than the blue-600 it replaced. - The **dot indicators** (in `SeverityBadge`, chart legends, status pills) continue to use the raw `bg-risk-X` token — colour identity stays recognisable; only text shade is darkened. The reference implementation is `apps/frontend/src/components/ui/badge.tsx` (W11-H). ### Colour is not the only signal @@ -388,9 +373,8 @@ All interactive elements are reachable by `Tab` and operable by `Enter` / `Space | W12-C | 2026-06-11 | **Craft elevation — motion (CSS-only).** Route-change entrance fade (`
` keyed on pathname, 250 ms), sidebar collapse aligned to 250 ms, and a global `prefers-reduced-motion` guard. No new dependency (tailwindcss-animate only). Skeleton doc corrected to the real 2000 ms `animate-pulse`. | | W12-D | 2026-06-12 | **Craft elevation — empty / loading polish.** EmptyState gains a layered icon medallion + optional `illustration` slot; new `TableRowsSkeleton` renders per-column loading cells (replacing single full-width bars) on the Scans and Admin Users tables. | | W12-E/F | 2026-06-12 | **Craft elevation — guardrails + docs.** Grew `/dev/design-preview` into a living component reference (typography, badges, empty / loading, feedback) and added a "Frontend UI" section to the contributor coding standards. Visual-regression baseline expansion (4 → ~15) is a CI / operator follow-up — correct linux baselines cannot be generated from a darwin dev box. | -| W13 | 2026-06-12 | **Google AI Studio re-skin (TRUSCA).** Token promotion from the PR #394 dev prototype after user review: white canvas, `#0b57d0` blue primary, `#d3e3fd` tonal secondary, `#dadce0` borders, radius scale +2 px, flat cards (shadow-sm → no-op, md/lg → Google elevation recipes), pill buttons (`rounded-full` in `button.tsx` + sidebar nav), `--risk-low` blue-600 → teal-700 to separate Low badges from the blue primary. Layout density, motion, typography, and the other severity colours unchanged. Visual-regression baselines require a linux refresh after merge. | -The W11 Vercel + Linear skin is retired by W13; the earlier "BD-style 2015" aesthetic was retired by W11. +The previous "BD-style 2015" aesthetic (`#0f172a` navy, pure white canvas, uniform 8 px radius, no shadow, default browser easing) is fully retired by W11. ## See also diff --git a/docs-site/i18n/ko/docusaurus-plugin-content-docs/current/reference/design-system.md b/docs-site/i18n/ko/docusaurus-plugin-content-docs/current/reference/design-system.md index ccbf6efc..4f0713eb 100644 --- a/docs-site/i18n/ko/docusaurus-plugin-content-docs/current/reference/design-system.md +++ b/docs-site/i18n/ko/docusaurus-plugin-content-docs/current/reference/design-system.md @@ -1,14 +1,14 @@ --- id: design-system title: 디자인 시스템 -description: TRUSCA 디자인 시스템 — 토큰(색·spacing·radius·shadow·motion·typography)·컴포넌트 규약·마이크로인터랙션·접근성·W13 Google AI Studio 재스킨. +description: TRUSCA 디자인 시스템 — 토큰(색·spacing·radius·shadow·motion·typography)·컴포넌트 규약·마이크로인터랙션·접근성·W11 시각 정체성 재정의. sidebar_label: 디자인 시스템 sidebar_position: 10 --- # 디자인 시스템 -포털 프론트엔드는 **Google AI Studio** 톤(W13, 2026-06-12)의 단일 light 모드 디자인 시스템을 따릅니다 — 흰 canvas, Google 블루 primary, tonal secondary, 필(pill) 버튼, 보더로 구분되는 평면 카드. W11에서 도입한 타이포 hierarchy·motion·focus polish(Linear 영감)는 그대로 유지합니다. Dark 모드는 v2.5+ 이후로 미룹니다. +포털 프론트엔드는 **Vercel** (light base — surface · dense row · sidebar tint) 과 **Linear** (타이포 hierarchy · motion · focus polish) 에 영감을 받은 단일 light 모드 디자인 시스템을 따릅니다. Dark 모드는 + 이후로 미룹니다. :::note 대상 독자 프론트엔드 기여자, 디자이너, 리뷰어. 본 문서의 토큰은 canonical reference 입니다 — 컴포넌트는 hex 값이나 magic spacing 을 직접 박지 않습니다. @@ -26,59 +26,48 @@ TRUSCA는 **리스크 우선 · 정보 밀도 · 모던 엔터프라이즈** SCA 1. **한눈에 심각도 전달.** Severity 색 (Critical / High / Medium / Low / Info) 은 항상 텍스트 라벨 + 아이콘/dot 와 함께 — 색은 신호의 단독 수단이 아닙니다. 2. **답답하지 않게 데이터 밀도.** 40 px compact 행 · 224 px 사이드바 · 48 px 헤더 · 16/20/24 px 카드 padding 표준화. -3. **모던 제품 톤 (Google AI Studio).** 흰 canvas `#ffffff` + `#dadce0` hairline border · Google 블루 primary `#0b57d0` + tonal 라이트블루 secondary · 필 버튼 · shadow 대신 보더+톤으로 구분되는 평면 카드 · semibold heading · 가시 focus ring. 폰트는 Inter + JetBrains Mono 유지(Google Sans 는 비공개 폰트). +3. **모던 엔터프라이즈 제품 톤.** Navy `#0f172a` 가 아닌 warm near-black `#18181b` · 순수 흰색이 아닌 off-white canvas `#fafafa` · subtle shadow · semibold heading · 가시 focus ring. 4. **필요한 만큼만 움직임.** Hover/focus 150 ms · drawer slide 200 ms · 페이지 크롬 250 ms. ease-out 만. Bounce 없음, fade-in delay 없음. -### W13 (2026-06-12) — Google AI Studio 재스킨 +### W11 (2026-05-27) — 시각 재정의 -W13 에서 W11 의 Vercel + Linear 스킨을 Google AI Studio light 톤으로 교체했습니다. dev 토글 프로토타입(PR #394) 검토 후 채택한 결정입니다. 구조적 결정 (사이드바 nav · 40 px row · drawer-for-detail · severity 의미) 과 W11 polish 레이어 (타이포 · motion · focus ring) 는 유지. 바뀐 것: +W11 마일스톤에서 기존 "BD-style 2015" 미감을 Vercel+Linear 혼합으로 교체했습니다. 구조적 결정 (사이드바 nav · 40 px row · drawer-for-detail · severity 의미) 은 유지. 바뀐 것: -| Surface | W11 (before) | W13 (after) | +| Surface | Before | After | |---|---|---| -| Primary CTA | `#18181b` warm near-black | `#0b57d0` Google 블루 | -| Secondary | muted grey tint | `#d3e3fd` tonal 라이트블루 | -| 페이지 배경 | `#fafafa` off-white canvas | `#ffffff` 흰 canvas | -| 카드 surface | 흰색, shadow-sm 으로 떠 보임 | 흰색, 평면 — 보더+톤으로 구분 | -| Hover / ghost tint | muted grey | `#f0f4f9` 옅은 blue-grey | -| Border | `#e5e5ea` | `#dadce0` AIS hairline | -| 버튼 형태 | rounded-md (6 px) | 필 (`rounded-full`) | -| Radius 스케일 | sm 4 / md 6 / lg 8 / xl 12 | sm 6 / md 8 / lg 10 / xl 14 | -| Shadow | sm (card) / md / lg | sm = 없음 (평면); md/lg = Google elevation-1/-2 | -| Focus ring | near-black | `#0b57d0` 블루 (primary 와 매칭) | -| `--risk-low` | `#2563eb` blue-600 | `#0f766e` teal-700 (블루 primary 와 더 이상 충돌하지 않음) | - -Critical / High / Medium / Info 의 severity 색은 **유지** — 도메인 의미가 고정입니다. Low 만 W13 의 단일 예외(사용자 결정)입니다: 기존 블루가 새 Google 블루 primary 옆에서 CTA/링크처럼 읽혔습니다. Severity hex 가 light tint 위 본문 텍스트로 사용될 때 WCAG AA 가 안 나오면, 같은 hue family 의 더 짙은 shade 로 텍스트 색만 어둡게 — 아래 [Severity 색 접근성](#severity-색-접근성) 참고. - -
-W11 (2026-05-27) — 직전의 Vercel + Linear 재정의 - -W11 은 원래의 "BD-style 2015" 미감(navy `#0f172a` · 8 px 일관 radius · 브라우저 기본 easing)을 Vercel light base + Linear polish 로 교체했습니다: warm near-black `#18181b` primary · `#fafafa` canvas · radius/shadow/motion 토큰 계층 · semibold heading · 가시 focus ring · 드로어+페이지 dual 디테일 surface. polish 레이어는 W13 에 살아남았고, 색 스킨은 교체되었습니다. - -
+| Primary CTA | `#0f172a` cool navy | `#18181b` warm near-black | +| 페이지 배경 | `#ffffff` 순백 | `#fafafa` off-white canvas | +| 카드 surface | grey 톤 | `#ffffff` 순백 (canvas 위로 떠 보임) | +| Border | `slate-200` | `#e5e5ea` neutral hairline | +| Radius | 8 px 일관 | 계층 — sm 4 / md 6 / lg 8 / xl 12 | +| Shadow | 없음 / 기본 | sm (card) / md (popover) / lg (drawer · dialog) | +| Motion | 브라우저 기본 | 150 / 200 / 250 ms ease-out | +| Heading weight | bold | semibold + tracking-tight | +| Focus ring | shadcn 기본 | 2 px outline + 2 px offset (a11y) | +| 디테일 surface | 드로어 전용 | dual surface — 드로어 (빠른 확인) + 페이지 nav (깊은 작업) | + +Severity 팔레트 (Critical / High / Medium / Low / Info) 는 **의도적으로 유지** — 브랜드 의미가 릴리스 간 고정입니다. Severity hex 가 light tint 위 본문 텍스트로 사용될 때 WCAG AA 가 안 나오면, 같은 hue family 의 더 짙은 shade 로 텍스트 색만 어둡게 — 아래 [Severity 색 접근성](#severity-색-접근성) 참고. ## 색 토큰 모든 색 결정은 `index.css` 의 CSS custom property 를 참조합니다. 컴포넌트는 hex 값을 직접 박지 않습니다 — Tailwind utility (`bg-background`, `text-foreground`, `bg-risk-critical/10`) 나 CSS 변수를 사용합니다. -### Neutral 팔레트 (Google AI Studio light) +### Neutral 팔레트 (Vercel base) | 토큰 | Hex | HSL | 용도 | |---|---|---|---| -| `--background` | `#ffffff` | `0 0% 100%` | 페이지 canvas — 순백 (AIS). | -| `--card` | `#ffffff` | `0 0% 100%` | 카드 · popover · 드로어 본문 · 툴팁 — canvas 와 같은 면이며 보더+톤으로 구분. | -| `--foreground` | `#1f1f1f` | `0 0% 12%` | 본문 텍스트 — Google grey-900. | -| `--muted` | `#f8f9fa` | `210 17% 98%` | 미묘한 fill — 테이블 헤더 · 사이드바 tint · placeholder · disabled input. | -| `--muted-foreground` | `#5f6368` | `213 5% 39%` | 보조 텍스트 · caption · 테이블 컬럼 헤더 — Google grey-700. | -| `--border` | `#dadce0` | `220 9% 87%` | Hairline border — AIS 표준. 장식용 separator 만 — UI 영역 식별의 유일한 수단이 되지 않습니다. | -| `--input` | `#dadce0` | `220 9% 87%` | Input outline. | -| `--primary` | `#0b57d0` | `217 90% 43%` | Primary CTA — Google 블루, "이 페이지의 중요 액션". | -| `--primary-foreground` | `#ffffff` | `0 0% 100%` | Primary 위 텍스트. | -| `--secondary` | `#d3e3fd` | `217 91% 91%` | Tonal 버튼 fill (Google "tonal" 패턴). | -| `--secondary-foreground` | `#041e49` | `217 90% 15%` | Tonal fill 위 텍스트. | -| `--accent` | `#f0f4f9` | `213 43% 96%` | Hover 행 · ghost 버튼 hover — 옅은 blue-grey tint. | -| `--destructive` | `#d93025` | `4 71% 50%` | Destructive CTA — Google red. `--risk-critical` 과 비슷해 destructive 버튼이 severity 와 같은 시각 언어. | -| `--destructive-foreground` | `#ffffff` | `0 0% 100%` | Destructive 위 텍스트. | -| `--ring` | `#0b57d0` | `217 90% 43%` | Focus ring. Primary 와 매칭 — outline 이 액션과 같은 색 패밀리로 읽힘. | +| `--background` | `#fafafa` | `0 0% 98%` | 페이지 canvas. 카드가 시각적으로 뜨도록. | +| `--card` | `#ffffff` | `0 0% 100%` | Elevated surface — 카드 · popover · 드로어 본문 · 툴팁. | +| `--foreground` | `#18181b` | `240 6% 10%` | 본문 텍스트. Warm near-black (navy 아님). | +| `--muted` | `#f4f4f5` | `240 5% 96%` | 미묘한 fill — 테이블 헤더 · 사이드바 tint · placeholder · disabled input. | +| `--muted-foreground` | `#71717a` | `240 4% 46%` | 보조 텍스트 · caption · 테이블 컬럼 헤더. | +| `--border` | `#e5e5ea` | `240 5% 91%` | Hairline border. 장식용 separator 만 — UI 영역 식별의 유일한 수단이 되지 않습니다. | +| `--input` | `#e5e5ea` | `240 5% 91%` | Input outline. | +| `--primary` | `#18181b` | `240 6% 10%` | Primary CTA — "이 페이지의 중요 액션". | +| `--primary-foreground` | `#fafafa` | `0 0% 98%` | Primary 위 텍스트. | +| `--destructive` | `#dc2626` | `0 72% 51%` | Destructive CTA. `--risk-critical` 와 같아서 destructive 버튼이 severity 와 같은 시각 언어. | +| `--destructive-foreground` | `#fafafa` | `0 0% 98%` | Destructive 위 텍스트. | +| `--ring` | `#18181b` | `240 6% 10%` | Focus ring. Primary 와 매칭 — outline 이 액션과 같은 색 패밀리로 읽힘. | ### Severity (도메인 의미 — 고정) @@ -87,10 +76,10 @@ W11 은 원래의 "BD-style 2015" 미감(navy `#0f172a` · 8 px 일관 radius · | `--risk-critical` | `#dc2626` | Critical CVE · forbidden 라이선스 · build-blocking finding. | | `--risk-high` | `#ea580c` | High-severity CVE · conditional 라이선스 위험. | | `--risk-medium` | `#ca8a04` | Medium CVE · 검토 대기 conditional 라이선스. | -| `--risk-low` | `#0f766e` | Low CVE · 정보성 상태. W13 부터 teal-700 — Low 배지가 블루 primary CTA 처럼 읽히지 않도록 블루 계열에서 분리. | +| `--risk-low` | `#2563eb` | Low CVE · 정보성 상태. | | `--risk-info` | `#71717a` | 중립 정보. | -Severity hex 는 릴리스 간 안정적으로 유지합니다 (W13 의 Low 이동이 문서화된 단일 예외). 사용처: +Severity hex 는 **릴리스 간 변경하지 않습니다**. 사용처: - Recharts fill · 차트 범례 (raw hex, `--risk-X` 변수 참조). - 배지 · dot 인디케이터의 `bg-risk-X/N` tint. @@ -120,26 +109,26 @@ Severity 톤이 **본문 텍스트** 로 사용될 때 (배지 안의 색 단어 ## Radius 계층 -Affordance 별로 다른 radius — depth 가 한눈에 읽힘. W13 에서 전체 스케일을 한 단계 더 둥글게 올렸습니다. +Affordance 별로 다른 radius — depth 가 한눈에 읽힘. | 토큰 | 값 | Affordance | |---|---|---| -| `--radius-sm` | 6 px | 작은 input · 배지 · 칩. | -| `--radius` | 8 px | **기본** — 카드 · input · 테이블 크롬. | -| `--radius-lg` | 10 px | 드로어 · 큰 surface. | -| `--radius-xl` | 14 px | 모달 · 다이얼로그. | +| `--radius-sm` | 4 px | 작은 input · 배지 · 칩. | +| `--radius` | 6 px | **기본** — 버튼 · 카드 · 테이블 크롬. | +| `--radius-lg` | 8 px | 드로어 · 큰 surface. | +| `--radius-xl` | 12 px | 모달 · 다이얼로그. | -Tailwind config 가 `calc()` 로 `rounded-sm`/`rounded-md`/`rounded-lg`/`rounded-xl` 를 이 토큰에서 파생. **버튼은 필 형태** (`button.tsx` 의 `rounded-full`) 로 이 스케일에 포함하지 않습니다 — `--radius` 를 필 값으로 올리면 `calc()` 파생을 통해 카드·다이얼로그까지 완전히 둥글어지기 때문입니다. +Tailwind config 가 `calc()` 로 `rounded-sm`/`rounded-md`/`rounded-lg`/`rounded-xl` 를 이 토큰에서 파생. ## Shadow 스케일 -AIS 는 본문 흐름 위의 surface 를 평면으로 둡니다 — 카드·버튼은 보더+톤으로 구분합니다. Shadow 는 떠 있는 surface 전용이며 Google elevation 레시피를 따릅니다. +Vercel 톤의 subtle elevation. 가벼운 그림자만 — glow 없음. | 토큰 | 값 | 용도 | |---|---|---| -| `--shadow-sm` | `0 0 0 0 rgb(0 0 0 / 0)` | 의도적 zero-alpha no-op (평면 카드·버튼). `var(--shadow-sm)` 소비자가 깨지지 않도록 유효한 box-shadow 값을 유지 — 미래에 한 곳만 바꿔 elevation 을 되살릴 수 있습니다. | -| `--shadow-md` | `0 1px 2px 0 rgb(60 64 67 / 0.3), 0 1px 3px 1px rgb(60 64 67 / 0.15)` | 드롭다운 · popover · 툴팁 (Google elevation-1). | -| `--shadow-lg` | `0 1px 3px 0 rgb(60 64 67 / 0.3), 0 4px 8px 3px rgb(60 64 67 / 0.15)` | 드로어 · 다이얼로그 (Google elevation-2). | +| `--shadow-sm` | `0 1px 2px 0 rgb(0 0 0 / 0.04)` | 카드 · stat 타일. | +| `--shadow-md` | `0 2px 8px -2px rgb(0 0 0 / 0.08), 0 1px 2px 0 rgb(0 0 0 / 0.04)` | 드롭다운 · popover · 툴팁. | +| `--shadow-lg` | `0 10px 28px -8px rgb(0 0 0 / 0.12), 0 3px 8px -3px rgb(0 0 0 / 0.06)` | 드로어 · 다이얼로그. | ## Motion @@ -213,12 +202,10 @@ stacked 변형에는 선택적 `meta` 슬롯도 있습니다 — 부제 아래 `apps/frontend/src/components/ui/button.tsx` -- **필 형태** (`rounded-full`, W13) — 모든 variant·size 에 적용되는 AIS 버튼 실루엣. Input 은 토큰 radius 유지, 필은 버튼(과 사이드바 nav 항목)만. -- 기본 variant: `bg-primary text-primary-foreground` — solid Google 블루. -- `secondary` variant: Google **tonal** 버튼 — 라이트블루 fill (`--secondary`) + 짙은 블루 텍스트. +- 기본 variant: `bg-primary text-primary-foreground` — solid warm near-black. - `outline` variant: `border-input bg-background` — 보조 액션. -- `ghost` variant: 배경 없음, hover tint (`--accent`) 만 — nav 항목 · toolbar 액션. -- `destructive`: `bg-destructive` — `--risk-critical` 에 가까운 Google red, 되돌릴 수 없는 액션 전용 (delete · revoke · reject). +- `ghost` variant: 배경 없음, hover tint 만 — nav 항목 · toolbar 액션. +- `destructive`: `bg-destructive` — Critical-aligned 빨강, 되돌릴 수 없는 액션 전용 (delete · revoke · reject). - Hover / focus transition: `transition-colors duration-fast ease-out` (150 ms). - 모든 variant 에 focus ring 포함. @@ -230,9 +217,9 @@ stacked 변형에는 선택적 `meta` 슬롯도 있습니다 — 부제 아래 ### Card -- 흰 canvas 와 같은 면의 흰 surface (`bg-card`) — 구분은 `--border` hairline 과 `--muted` 톤으로 (AIS 평면 카드 패턴; `shadow-sm` 은 no-op). -- 기본 `rounded-md` (8 px) · 메인 콘텐츠 카드는 `rounded-lg` (10 px). -- `shadow-md` 는 떠 있는 surface (popover · 드롭다운) 전용 — 본문 흐름 위 카드에는 쓰지 않습니다. +- Off-white canvas 위 순백 surface (`bg-card`) — 무거운 shadow 없이 시각적으로 뜸. +- 기본 `rounded-md` (6 px) · 메인 콘텐츠 카드는 `rounded-lg` (8 px). +- Stat / 타일은 `shadow-sm` · 떠 있는 popover 는 `shadow-md`. ### Table @@ -316,30 +303,28 @@ W11-F polish phase 가 모든 인터랙티브 transition 의 타이밍 · easing | 쌍 | 비율 | 비고 | |---|---|---| -| `--foreground` on `--background` / `--card` | 16.48:1 | 본문. AAA. | -| `--muted-foreground` on `--background` / `--card` | 6.05:1 | Caption · 보조 텍스트. AA. | -| `--muted-foreground` on `--muted` | 5.74:1 | Tint fill 위 caption. AA. | -| `--primary-foreground` on `--primary` | 6.39:1 | Primary 버튼 라벨. AA. | -| `--secondary-foreground` on `--secondary` | 12.57:1 | Tonal 버튼 라벨. AAA. | -| `--destructive-foreground` on `--destructive` | 4.77:1 | Destructive 버튼 라벨. AA. | -| `--ring` on `--background` | 6.39:1 | Focus ring. AA (UI ≥ 3:1). | +| `--foreground` on `--background` | 16.97:1 | 본문. AAA. | +| `--foreground` on `--card` | 17.72:1 | 카드 위 본문. AAA. | +| `--muted-foreground` on `--background` | 4.63:1 | Caption · 보조 텍스트. AA. | +| `--muted-foreground` on `--card` | 4.83:1 | 카드 위 caption. AA. | +| `--primary-foreground` on `--primary` | 16.97:1 | Primary 버튼 라벨. AAA. | +| `--destructive-foreground` on `--destructive` | 4.63:1 | Destructive 버튼 라벨. AA. | +| `--ring` on `--background` | 16.97:1 | Focus ring. AAA. | -장식용 border (`--border` on `--background`, 1.37:1) 는 **의도적으로 저대비** — 시각 분리용일 뿐 정보성 UI 요소가 아니며 WCAG 1.4.11 이 면제 대상. +장식용 border (`--border` on `--background`, 1.20:1) 는 **의도적으로 저대비** — 시각 분리용일 뿐 정보성 UI 요소가 아니며 WCAG 1.4.11 이 면제 대상. ### Severity 색 접근성 -Severity hex (`#dc2626` / `#ea580c` / `#ca8a04` / `#0f766e` / `#71717a`) 는 브랜드 고정. Light tint 위 **본문 텍스트** 로 쓰이면 일부는 2.5:1 까지 떨어져 AA 실패. 해결은 색이 아닌 구조 — severity 톤이 텍스트로 쓰일 때 렌더링 텍스트 색은 같은 Tailwind hue family 의 더 짙은 shade: +Severity hex (`#dc2626` / `#ea580c` / `#ca8a04` / `#2563eb` / `#71717a`) 는 브랜드 고정. Light tint 위 **본문 텍스트** 로 쓰이면 2.5:1 까지 떨어져 AA 실패. 해결은 색이 아닌 구조 — severity 톤이 텍스트로 쓰일 때 렌더링 텍스트 색은 같은 Tailwind hue family 의 더 짙은 shade: | Tone | Tint 배경 | 텍스트 색 | 대비 | |---|---|---|---| | `critical` | `bg-risk-critical/10` | `text-red-700` (`#b91c1c`) | 5.54:1 | | `high` | `bg-risk-high/10` | `text-orange-800` (`#9a3412`) | 6.47:1 | | `medium` | `bg-risk-medium/15` | `text-yellow-800` (`#854d0e`) | 5.91:1 | -| `low` | `bg-risk-low/10` | `text-teal-800` (`#115e59`) | 6.59:1 | +| `low` | `bg-risk-low/10` | `text-blue-700` (`#1d4ed8`) | 5.83:1 | | `info` | `bg-risk-info/15` | `text-slate-600` (`#52525b`) | 6.41:1 | -Low 토큰 자체(teal-700 `#0f766e`)도 텍스트로 직접 쓰이는 곳에서 AA 를 통과합니다 (흰 배경 5.47:1, 자기 10 % tint 위 4.76:1) — 교체 전 blue-600 보다 약간 높습니다. - **Dot 인디케이터** (SeverityBadge · 차트 범례 · status pill) 는 계속 raw `bg-risk-X` 토큰 사용 — 색 정체성은 그대로, 텍스트 shade 만 어둡게. 참조 구현은 `apps/frontend/src/components/ui/badge.tsx` (W11-H). ### 색이 신호의 단독 수단 아님 @@ -388,9 +373,8 @@ Severity 가 표시되는 모든 곳에서 색은 다음 중 하나와 짝지움 | W12-C | 2026-06-11 | **Craft 격상 — 모션 (CSS-only).** 라우트 전환 진입 페이드(`
` 을 pathname 으로 key, 250 ms), 사이드바 접기 250 ms 정렬, 전역 `prefers-reduced-motion` 가드. 새 의존성 없음(tailwindcss-animate 만). 스켈레톤 문서를 실제 2000 ms `animate-pulse` 로 정정. | | W12-D | 2026-06-12 | **Craft 격상 — 빈 상태 · 로딩 폴리시.** EmptyState 에 레이어드 아이콘 메달리온 + 선택적 `illustration` 슬롯 추가, 신규 `TableRowsSkeleton` 이 Scans · Admin Users 테이블에서 컬럼별 로딩 셀(전폭 바 대체)을 렌더링. | | W12-E/F | 2026-06-12 | **Craft 격상 — 가드레일 + 문서.** `/dev/design-preview` 를 살아있는 컴포넌트 레퍼런스(타이포그래피 · 배지 · 빈 / 로딩 · 피드백)로 확장하고, 기여자 coding standards 에 "프론트엔드 UI" 섹션 추가. 시각 회귀 베이스라인 확장(4 → ~15)은 CI / 운영자 후속 — darwin 개발 머신에서는 올바른 linux 베이스라인을 생성할 수 없음. | -| W13 | 2026-06-12 | **Google AI Studio 재스킨 (TRUSCA).** PR #394 dev 프로토타입을 사용자 검토 후 토큰 승격: 흰 canvas · `#0b57d0` 블루 primary · `#d3e3fd` tonal secondary · `#dadce0` border · radius 스케일 +2 px · 평면 카드 (shadow-sm → no-op, md/lg → Google elevation) · 필 버튼 (`button.tsx` `rounded-full` + 사이드바 nav) · `--risk-low` blue-600 → teal-700 (Low 배지를 블루 primary 와 분리). 레이아웃 밀도 · motion · 타이포 · 다른 severity 색은 불변. 시각 회귀 베이스라인은 머지 후 linux 재생성 필요. | -W11 의 Vercel + Linear 스킨은 W13 으로 은퇴. 그 이전 "BD-style 2015" 미감은 W11 로 은퇴. +이전 "BD-style 2015" 미감 (`#0f172a` navy · 순백 canvas · 일관 8 px radius · shadow 없음 · 브라우저 기본 easing) 은 W11 로 완전 은퇴. ## 참고