Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 5 additions & 9 deletions apps/frontend/src/components/AppShell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ import { Button } from "@/components/ui/button";
import { Sheet, SheetContent, SheetTitle } from "@/components/ui/sheet";
import { deriveInitials } from "@/lib/initials";
import { cn } from "@/lib/utils";
import { AisThemeToggle } from "@/pages/dev/AisThemeToggle";
import { useAuthStore } from "@/stores/authStore";
import { useUIStore } from "@/stores/uiStore";

Expand Down Expand Up @@ -161,10 +160,11 @@ function NavItemLink({
title={collapsed ? label : undefined}
className={({ isActive }) =>
cn(
// 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",
// 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",
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",
Expand Down Expand Up @@ -384,10 +384,6 @@ export function AppShell() {
<Menu className="h-4 w-4" aria-hidden />
</Button>
<div className="flex items-center gap-3">
{/* Dev-only AIS theme prototype switch — lets the design review
happen on real screens (dashboard, project list), not just the
/dev/design-preview gallery. Compiled out of prod builds. */}
{import.meta.env.DEV ? <AisThemeToggle /> : null}
{/* Global ⌘K palette trigger (W9-#54). The button is a
discoverability affordance — the keyboard shortcut works
whether or not this button is on screen. */}
Expand Down
8 changes: 4 additions & 4 deletions apps/frontend/src/components/ui/badge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,17 +49,17 @@
// critical text-red-700 → 5.54:1
// high text-orange-800 → 6.47:1
// medium text-yellow-800 → 5.91:1
// low text-blue-700 → 5.83:1
// low text-teal-800 → 6.59:1 (W13 — token moved to
// teal-700, text follows the hue)
// 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. Token values in
// `index.css` are NOT changed (W11 prohibition: "Severity 색 변경 0").
// recognisable — only the text shade is darkened.
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-blue-700",
low: "border-transparent bg-risk-low/10 text-teal-800",
info: "border-transparent bg-risk-info/15 text-slate-600",
success: "border-transparent bg-emerald-100 text-emerald-700",
},
Expand All @@ -86,4 +86,4 @@
);
Badge.displayName = "Badge";

export { badgeVariants };

Check warning on line 89 in apps/frontend/src/components/ui/badge.tsx

View workflow job for this annotation

GitHub Actions / lint (frontend)

Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components
33 changes: 17 additions & 16 deletions apps/frontend/src/components/ui/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,26 @@
import { cn } from "@/lib/utils";

/**
* Button — W11-A polish.
* Button — W13 AIS adoption (pill shape) on top of the W11-A polish.
*
* 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.
* - `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.
*
* The hex colors themselves are NOT in this file — they flow from the
* tokens declared in `src/index.css` (W11-A).
* tokens declared in `src/index.css` (W13).
*/
const buttonVariants = cva(
"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",
"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",
{
variants: {
variant: {
Expand All @@ -40,8 +41,8 @@
},
size: {
default: "h-9 px-4 py-2",
sm: "h-8 rounded-md px-3 text-xs",
lg: "h-10 rounded-md px-6",
sm: "h-8 px-3 text-xs",
lg: "h-10 px-6",
icon: "h-9 w-9",
},
},
Expand Down Expand Up @@ -72,4 +73,4 @@
);
Button.displayName = "Button";

export { buttonVariants };

Check warning on line 76 in apps/frontend/src/components/ui/button.tsx

View workflow job for this annotation

GitHub Actions / lint (frontend)

Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components
147 changes: 79 additions & 68 deletions apps/frontend/src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,28 @@
@tailwind utilities;

/* ---------------------------------------------------------------------------
* TrustedOSS Portal — design tokens
* TRUSCA — design tokens
*
* 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.
* 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.
*
* 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.
* 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)
*
* 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 (unchanged — domain semantics fixed):
* Critical / High / Medium / Low / Info.
* 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.
*
* Forward-compat note:
* Dark mode tokens are NOT defined in this phase (2026-05-27 user decision).
Expand All @@ -34,79 +36,83 @@
@layer base {
:root {
/* -------------------------------------------------------------------
* shadcn/ui base tokens — re-tuned to Vercel light palette.
* shadcn/ui base tokens — Google AI Studio light palette (W13).
*
* 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 — 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%; /* #18181bwarm near-black (was #0f172a navy) */
/* 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%; /* #1f1f1fGoogle grey-900 */

/* Elevated surfaces sit on top of --background. Pure white reads as
* "card on page" against the off-white canvas. */
/* Surfaces share the white canvas; --muted carries the grey panel /
* table-header tint instead. */
--card: 0 0% 100%; /* #ffffff */
--card-foreground: 240 6% 10%;
--card-foreground: 0 0% 12%;
--popover: 0 0% 100%;
--popover-foreground: 240 6% 10%;
--popover-foreground: 0 0% 12%;

/* Muted = table header / sidebar tint / placeholder fill. */
--muted: 240 5% 96%; /* #f4f4f5 */
--muted-foreground: 240 4% 46%; /* #71717apasses 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%;
--muted: 210 17% 98%; /* #f8f9fa */
--muted-foreground: 213 5% 39%; /* #5f6368Google 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%;

/* -------------------------------------------------------------------
* Radius hierarchy (Linear polish — different sizes for different
* affordances so depth reads at a glance).
* Radius hierarchy (W13 — one step rounder than W11 across the board).
*
* --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
* --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
*
* shadcn convention reads `--radius` as the card default; smaller /
* larger variants derive via `calc()` in tailwind.config.ts.
* 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.
* ----------------------------------------------------------------- */
--radius: 0.375rem;
--radius: 0.5rem;

/* -------------------------------------------------------------------
* Elevation — subtle, Vercel-style. Light shadows only; no glow.
* Used by Card (sm), Dropdown / Popover (md), Drawer / Dialog (lg).
* 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).
* ----------------------------------------------------------------- */
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.04);
--shadow-sm: 0 0 0 0 rgb(0 0 0 / 0);
--shadow-md:
0 2px 8px -2px rgb(0 0 0 / 0.08),
0 1px 2px 0 rgb(0 0 0 / 0.04);
0 1px 2px 0 rgb(60 64 67 / 0.3),
0 1px 3px 1px rgb(60 64 67 / 0.15);
--shadow-lg:
0 10px 28px -8px rgb(0 0 0 / 0.12),
0 3px 8px -3px rgb(0 0 0 / 0.06);
0 1px 3px 0 rgb(60 64 67 / 0.3),
0 4px 8px 3px rgb(60 64 67 / 0.15);

/* -------------------------------------------------------------------
* Motion (Linear polish) — short, ease-out. Three steps cover the
Expand All @@ -122,14 +128,19 @@
--ease-out: cubic-bezier(0.16, 1, 0.3, 1);

/* -------------------------------------------------------------------
* Risk severity (unchanged — domain meaning is fixed).
* Risk severity — 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: #2563eb;
--risk-low: #0f766e;
--risk-info: #71717a;

/* Layout density (unchanged). */
Expand Down
48 changes: 0 additions & 48 deletions apps/frontend/src/lib/devTheme.ts

This file was deleted.

8 changes: 0 additions & 8 deletions apps/frontend/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,6 @@ import { AppProviders } from "@/components/AppProviders";
import "@/lib/i18n";
import "@/index.css";

if (import.meta.env.DEV) {
// AIS theme prototype (dev only) — dynamic imports keep the CSS chunk and
// the helper module out of production builds entirely; Vite replaces DEV
// with `false` and drops the whole branch.
void import("@/styles/theme-ais.css");
void import("@/lib/devTheme").then((m) => m.initAisTheme());
}

const container = document.getElementById("root");
if (!container) {
throw new Error("Root container #root not found");
Expand Down
Loading
Loading