From 08fa7b2d6210b70b755943d654f300b23d0de903 Mon Sep 17 00:00:00 2001 From: eti Date: Tue, 14 Jul 2026 00:38:02 +0000 Subject: [PATCH] web: adopt semantic tokens and ui primitives Signed-off-by: eti --- .gitignore | 1 + web/src/app.css | 240 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------------------------------------------------------- web/src/routes/+error.svelte | 38 +++++++++++++++++++------------------- web/src/routes/+layout.svelte | 34 +++++++++++++++++----------------- web/src/routes/[handle]/+layout.svelte | 20 ++++++++++---------- web/src/routes/login/+page.svelte | 56 ++++++++++++++++++++++++++++++-------------------------- web/src/lib/components/home/MarketingHome.svelte | 44 ++++++++++++++++++++++---------------------- web/src/lib/components/profile/ActivityStub.svelte | 2 +- web/src/lib/components/profile/EmptyState.svelte | 4 ++-- web/src/lib/components/profile/FollowButton.svelte | 30 +++++++++++++++--------------- web/src/lib/components/profile/FollowCard.svelte | 28 ++++++++++++++-------------- web/src/lib/components/profile/ProfileCard.svelte | 26 +++++++++++++------------- web/src/lib/components/profile/ProfileEditForm.svelte | 36 ++++++++++++++++++------------------ web/src/lib/components/profile/ProfileTabs.svelte | 42 ++++++++++++++++++++++-------------------- web/src/lib/components/profile/StringCard.svelte | 22 ++++++++++++---------- web/src/lib/components/profile/VouchCard.svelte | 28 +++++++++++++++------------- web/src/lib/components/repo/RepoCard.svelte | 34 ++++++++++++++++++---------------- web/src/lib/components/repo/StarButton.svelte | 26 +++++++++++++------------- web/src/lib/components/shell/Footer.svelte | 183 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------------------------------------------------------------------- web/src/lib/components/shell/Topbar.svelte | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------------------------- web/src/lib/components/shell/TopbarSearch.svelte | 108 ++++++++++++++---------------------------------------------------------------------------------------------- web/src/routes/oauth/callback/+page.svelte | 14 +++++++------- web/src/lib/components/profile/tabs/RepoListTab.svelte | 26 +++++++++++++------------- web/src/lib/components/profile/tabs/StarredTab.svelte | 16 ++++++++-------- 24 file(s) changed, 591 insertion(s)(+), 569 deletion(s)(-) diff --git a/.gitignore b/.gitignore --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,4 @@ *.pem *.key .claude/ +.playwright-mcp/ diff --git a/web/src/app.css b/web/src/app.css --- a/web/src/app.css +++ b/web/src/app.css @@ -1,106 +1,147 @@ -@import 'tailwindcss'; +@import "tailwindcss"; @theme { - --font-sans: 'InterVariable', 'system-ui', sans-serif, ui-sans-serif; + --font-sans: "InterVariable", "system-ui", sans-serif, ui-sans-serif; --font-mono: - 'IBMPlexMono', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', - 'Courier New', monospace; - --color-bg: #f1f5f9; - --color-bg-auth: #ffffff; - --color-surface: #ffffff; - --color-surface-hover: #f9fafb; - --color-surface-muted: #f3f4f6; - --color-fg: #111827; - --color-fg-strong: #030712; - --color-fg-muted: #4b5563; - --color-fg-subtle: #6b7280; - --color-fg-faint: #9ca3af; - --color-border: #e5e7eb; + "IBMPlexMono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", + "Courier New", monospace; + + /* ========================================================================= + Semantic color tokens — synced from Figma "Design System" › semantic colors + Names mirror the Figma variables verbatim (`/` → `-`). Light-mode values + here; dark-mode overrides live in the prefers-color-scheme block below. + ========================================================================= */ + + /* --- background --- */ + --color-background-default: #ffffff; + --color-background-subtle: #f9fafb; + --color-background-inset: #f3f4f6; + --color-background-muted: #e5e7eb; + --color-background-canvas: #f3f4f6; + --color-background-highlight: #fef08a; + --color-background-success-subtle: #dcfce7; + --color-background-success-subtle-hover: #bbf7d0; + --color-background-success-emphasis: #16a34a; + --color-background-success-emphasis-hover: #15803d; + --color-background-danger-subtle: #ffe2e2; + --color-background-danger-subtle-hover: #fecaca; + --color-background-danger-emphasis: #fca5a5; + --color-background-danger-emphasis-hover: #f87171; + --color-background-warning-subtle: #fffbeb; + --color-background-warning-subtle-hover: #fef3c7; + --color-background-warning-emphasis: #f59e0b; + --color-background-info-subtle: #eef2ff; + --color-background-info-subtle-hover: #e0e7ff; + --color-background-info-emphasis: #4f46e5; + + /* --- foreground --- */ + --color-foreground-default: #111827; + --color-foreground-muted: #4b5563; + --color-foreground-subtle: #6b7280; + --color-foreground-placeholder: #9ca3af; + --color-foreground-disabled: #d1d5db; + --color-foreground-on-emphasis: #ffffff; + --color-foreground-highlight: #854d0e; + --color-foreground-success: #16a34a; + --color-foreground-success-strong: #166534; + --color-foreground-success-disabled: #16a34a80; + --color-foreground-danger: #dc2626; + --color-foreground-danger-strong: #991b1b; + --color-foreground-warning: #d97706; + --color-foreground-warning-strong: #92400e; + --color-foreground-info: #4338ca; + --color-foreground-info-strong: #3730a3; + --color-foreground-link-default: #111827; + --color-foreground-link-hover: #374151; + --color-foreground-link-disabled: #9ca3af; + --color-foreground-link-danger: #dc2626; + --color-foreground-link-danger-hover: #ef4444; + --color-foreground-link-success: #16a34a; + --color-foreground-link-success-hover: #22c55e; + --color-foreground-link-warning: #d97706; + --color-foreground-link-warning-hover: #f59e0b; + --color-foreground-link-info: #4f46e5; + --color-foreground-link-info-hover: #6366f1; + + /* --- border --- */ + --color-border-default: #e5e7eb; --color-border-subtle: #f3f4f6; --color-border-strong: #d1d5db; - --color-ring: #d1d5db; - --color-raised-depth: #e5e7eb; - --color-raised-accent-depth: rgb(17 24 39 / 0.25); - --color-raised-accent-depth-active: rgb(17 24 39 / 0.5); - --color-raised-depth-hover: #d1d5db; - --color-success: #16a34a; - --color-success-hover: #15803d; - --color-success-border: #15803d; - --color-success-fg: #ffffff; - --color-primary: #4f46e5; - --color-primary-hover: #4338ca; - --color-primary-border: #4338ca; - --color-primary-fg: #ffffff; - --color-info: #3b82f6; - --color-info-soft: #dbeafe; - --color-danger: #dc2626; - --color-danger-hover: #b91c1c; - --color-danger-border: #b91c1c; - --color-danger-fg: #ffffff; - --color-danger-soft: #fef2f2; + --color-border-disabled: #e5e7eb; + --color-border-focus: #9ca3af; + --color-border-focus-success: #22c55e; + --color-border-focus-danger: #ef4444; + --color-border-success: #15803d; + --color-border-danger: #fca5a5; + + /* --- shadow (inner depth) --- */ + --color-shadow-inner-strong: #d1d5db; + --color-shadow-inner-strong-hover: #d1d5db; + --color-shadow-inner-subtle: #e5e7eb; + --shadow-menu: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); } @layer base { @font-face { - font-family: 'InterVariable'; - src: url('/fonts/InterVariable.woff2') format('woff2-variations'); + font-family: "InterVariable"; + src: url("/fonts/InterVariable.woff2") format("woff2-variations"); font-weight: 100 600; font-style: normal; font-display: swap; } @font-face { - font-family: 'InterVariable'; - src: url('/fonts/InterVariable-Italic.woff2') format('woff2-variations'); + font-family: "InterVariable"; + src: url("/fonts/InterVariable-Italic.woff2") format("woff2-variations"); font-weight: 100 600; font-style: italic; font-display: swap; } @font-face { - font-family: 'InterVariable'; - src: url('/fonts/InterDisplay-Bold.woff2') format('woff2'); + font-family: "InterVariable"; + src: url("/fonts/InterDisplay-Bold.woff2") format("woff2"); font-weight: bold; font-style: normal; font-display: swap; } @font-face { - font-family: 'InterVariable'; - src: url('/fonts/InterDisplay-BoldItalic.woff2') format('woff2'); + font-family: "InterVariable"; + src: url("/fonts/InterDisplay-BoldItalic.woff2") format("woff2"); font-weight: bold; font-style: italic; font-display: swap; } @font-face { - font-family: 'IBMPlexMono'; - src: url('/fonts/IBMPlexMono-Regular.woff2') format('woff2'); + font-family: "IBMPlexMono"; + src: url("/fonts/IBMPlexMono-Regular.woff2") format("woff2"); font-weight: normal; font-style: normal; font-display: swap; } @font-face { - font-family: 'IBMPlexMono'; - src: url('/fonts/IBMPlexMono-Italic.woff2') format('woff2'); + font-family: "IBMPlexMono"; + src: url("/fonts/IBMPlexMono-Italic.woff2") format("woff2"); font-weight: normal; font-style: italic; font-display: swap; } @font-face { - font-family: 'IBMPlexMono'; - src: url('/fonts/IBMPlexMono-Bold.woff2') format('woff2'); + font-family: "IBMPlexMono"; + src: url("/fonts/IBMPlexMono-Bold.woff2") format("woff2"); font-weight: bold; font-style: normal; font-display: swap; } @font-face { - font-family: 'IBMPlexMono'; - src: url('/fonts/IBMPlexMono-BoldItalic.woff2') format('woff2'); + font-family: "IBMPlexMono"; + src: url("/fonts/IBMPlexMono-BoldItalic.woff2") format("woff2"); font-weight: bold; font-style: italic; font-display: swap; @@ -113,42 +154,81 @@ @media (prefers-color-scheme: dark) { :root { - --color-bg: #111827; - --color-bg-auth: #111827; - --color-surface: #1f2937; - --color-surface-hover: #374151; - --color-surface-muted: #374151; - --color-fg: #f3f4f6; - --color-fg-strong: #ffffff; - --color-fg-muted: #d1d5db; - --color-fg-subtle: #9ca3af; - --color-border: #374151; - --color-border-subtle: #374151; - --color-border-strong: #4b5563; - --color-ring: #4b5563; - --color-raised-depth: #111827; - --color-raised-depth-hover: #1f2937; - --color-success: #15803d; - --color-success-hover: #166534; - --color-success-border: #16a34a; - --color-primary: #6366f1; - --color-primary-hover: #4f46e5; - --color-primary-border: #4f46e5; - --color-info: #60a5fa; - --color-info-soft: rgb(30 58 138 / 0.3); - --color-danger: #b91c1c; - --color-danger-hover: #991b1b; - --color-danger-border: #dc2626; - --color-danger-soft: rgb(153 27 27 / 0.2); + /* --- background (Dark) --- */ + --color-background-default: #1f2937; + --color-background-subtle: #374151; + --color-background-inset: #374151; + --color-background-muted: #4b5563; + --color-background-canvas: #111827; + --color-background-highlight: #713f12; + --color-background-success-subtle: #14532d; + --color-background-success-subtle-hover: #166534; + --color-background-success-emphasis: #15803d; + --color-background-success-emphasis-hover: #166534; + --color-background-danger-subtle: #7f1d1d; + --color-background-danger-subtle-hover: #991b1b; + --color-background-danger-emphasis: #b91c1c; + --color-background-danger-emphasis-hover: #991b1b; + --color-background-warning-subtle: #78350f; + --color-background-warning-subtle-hover: #92400e; + --color-background-warning-emphasis: #d97706; + --color-background-info-subtle: #312e81; + --color-background-info-subtle-hover: #3730a3; + --color-background-info-emphasis: #4f46e5; + + /* --- foreground (Dark) --- */ + --color-foreground-default: #f3f4f6; + --color-foreground-muted: #9ca3af; + --color-foreground-subtle: #6b7280; + --color-foreground-placeholder: #9ca3af; + --color-foreground-disabled: #6b7280; + --color-foreground-on-emphasis: #ffffff; + --color-foreground-highlight: #fef08a; + --color-foreground-success: #4ade80; + --color-foreground-success-strong: #bbf7d0; + --color-foreground-success-disabled: #15803d; + --color-foreground-danger: #f87171; + --color-foreground-danger-strong: #fecaca; + --color-foreground-warning: #fbbf24; + --color-foreground-warning-strong: #fde68a; + --color-foreground-info: #818cf8; + --color-foreground-info-strong: #c7d2fe; + --color-foreground-link-default: #f3f4f6; + --color-foreground-link-hover: #d1d5db; + --color-foreground-link-disabled: #6b7280; + --color-foreground-link-danger: #ef4444; + --color-foreground-link-danger-hover: #f87171; + --color-foreground-link-success: #22c55e; + --color-foreground-link-success-hover: #4ade80; + --color-foreground-link-warning: #f59e0b; + --color-foreground-link-warning-hover: #fbbf24; + --color-foreground-link-info: #6366f1; + --color-foreground-link-info-hover: #818cf8; + + /* --- border (Dark) --- */ + --color-border-default: #374151; + --color-border-subtle: #1f2937; + --color-border-strong: #6b7280; + --color-border-disabled: #4b5563; + --color-border-focus: #d1d5db; + --color-border-focus-success: #22c55e; + --color-border-focus-danger: #ef4444; + --color-border-success: #15803d; + --color-border-danger: #991b1b; + + /* --- shadow (Dark) --- */ + --color-shadow-inner-strong: #030712; + --color-shadow-inner-strong-hover: #1f2937; + --color-shadow-inner-subtle: #111827; } } body { - @apply flex min-h-screen flex-col bg-bg text-fg transition-colors duration-200; + @apply flex min-h-screen flex-col bg-background-canvas text-foreground-default transition-colors duration-200; font-size: 14px; } a { - @apply text-fg no-underline hover:text-fg-muted; + @apply text-foreground-default no-underline hover:text-foreground-muted; } } diff --git a/web/src/routes/+error.svelte b/web/src/routes/+error.svelte --- a/web/src/routes/+error.svelte +++ b/web/src/routes/+error.svelte @@ -1,36 +1,36 @@ @@ -32,16 +32,16 @@
{#if !isAuthShell}
{/if} -
+
{@render children()}
{#if !isAuthShell} -
+
{/if}
diff --git a/web/src/routes/[handle]/+layout.svelte b/web/src/routes/[handle]/+layout.svelte --- a/web/src/routes/[handle]/+layout.svelte +++ b/web/src/routes/[handle]/+layout.svelte @@ -1,10 +1,10 @@ @@ -29,7 +29,7 @@ {:else} -
+
-
+
{@render children()}
diff --git a/web/src/routes/login/+page.svelte b/web/src/routes/login/+page.svelte --- a/web/src/routes/login/+page.svelte +++ b/web/src/routes/login/+page.svelte @@ -1,24 +1,24 @@
{message}{@render children?.()}
diff --git a/web/src/lib/components/profile/FollowButton.svelte b/web/src/lib/components/profile/FollowButton.svelte --- a/web/src/lib/components/profile/FollowButton.svelte +++ b/web/src/lib/components/profile/FollowButton.svelte @@ -1,28 +1,28 @@