From 59e39fe538120275ad5245011729f08f1cc572d1 Mon Sep 17 00:00:00 2001 From: Arthur Belleville Date: Sat, 16 May 2026 13:43:56 +0200 Subject: [PATCH] feat(13-01): replace base.css with 223-line token vocabulary and extract auth.css - Replace 28-line backend/internal/web/ui/base.css stub with full go-backend token vocabulary (223 lines, --color-brand-primary: #804eec) - Create backend/internal/web/ui/auth.css with auth-provider button styles extracted from button.css Phase 8 block - Update backend/tailwind.input.css to import auth.css after base.css --- backend/internal/web/ui/auth.css | 61 ++++++++ backend/internal/web/ui/base.css | 231 ++++++++++++++++++++++++++++--- backend/tailwind.input.css | 1 + 3 files changed, 275 insertions(+), 18 deletions(-) create mode 100644 backend/internal/web/ui/auth.css diff --git a/backend/internal/web/ui/auth.css b/backend/internal/web/ui/auth.css new file mode 100644 index 0000000..dc85493 --- /dev/null +++ b/backend/internal/web/ui/auth.css @@ -0,0 +1,61 @@ +/* auth.css — sign-in provider controls, extracted from button.css in Phase 13 */ + +.auth-provider-stack { + display: grid; + gap: 8px; + margin-bottom: 16px; +} + +.auth-provider-button { + display: inline-flex; + min-height: 44px; + width: 100%; + align-items: center; + justify-content: center; + border-radius: 0.375rem; + border: 1px solid #e2e8f0; + background-color: #ffffff; + padding: 0.625rem 1rem; + color: #0f172a; + font-size: 1rem; + font-weight: 600; + line-height: 1.25; + text-align: center; + text-decoration: none; +} + +.auth-provider-button:hover { + background-color: #f8fafc; +} + +.auth-provider-button:focus-visible { + outline: 2px solid #2563eb; + outline-offset: 2px; +} + +.auth-provider-button-disabled, +.auth-provider-button-disabled:hover { + background-color: #f1f5f9; + color: #94a3b8; + cursor: not-allowed; +} + +.auth-provider-separator { + display: grid; + grid-template-columns: 1fr auto 1fr; + align-items: center; + gap: 8px; + margin: 16px 0; + color: #64748b; + font-size: 0.875rem; + line-height: 1.4; +} + +.auth-provider-separator span { + height: 1px; + background-color: #e2e8f0; +} + +.auth-provider-separator em { + font-style: normal; +} diff --git a/backend/internal/web/ui/base.css b/backend/internal/web/ui/base.css index 6f17db7..6c6df95 100644 --- a/backend/internal/web/ui/base.css +++ b/backend/internal/web/ui/base.css @@ -1,28 +1,223 @@ -/* base.css — global resets and accessibility floor for the design system. - * Plain CSS only (no @apply, no nesting) so the file is consumable by both - * Tailwind v4 standalone (with @source scanning Go files) and any other - * downstream CSS pipeline. - */ +:root { + /* Text */ + --color-text-primary: hsl(0 0% 9%); + --color-text-secondary: #475467; + --color-text-muted: hsl(0 0% 43.5%); + --color-text-faint: #9ca3af; + --color-text-inverse: #ffffff; + --color-text-brand: #804eec; + --color-text-brand-hover: #6f3fd4; + --color-text-brand-strong: #7c3aed; + --color-text-brand-accent: #7f56d9; + --color-text-heading-alt: #1f2937; + --color-text-body-subtle: #374151; + --color-text-google: #1f1f1f; + --color-text-overlay: #344054; + --color-text-disabled: #667085; -*, -*::before, -*::after { + /* Surfaces */ + --color-surface-page: hsl(0 0% 100%); + --color-surface-default: #ffffff; + --color-surface-card: rgba(255, 255, 255, 0.8); + --color-surface-subtle: hsl(0 0% 96.1%); + --color-surface-muted: #f3f4f6; + --color-surface-muted-hover: #e5e7eb; + --color-surface-muted-active: #d1d5db; + --color-surface-muted-inverse: #111827; + --color-surface-elevated: rgba(255, 255, 255, 0.92); + --color-surface-elevated-strong: rgba(255, 255, 255, 0.95); + --color-surface-elevated-soft: rgba(255, 255, 255, 0.9); + --color-surface-overlay: rgba(255, 255, 255, 0.88); + --color-surface-overlay-strong: rgba(255, 255, 255, 0.96); + --color-surface-brand-soft: #ede9fe; + --color-surface-brand-soft-hover: #ddd6fe; + --color-surface-brand-soft-active: #c4b5fd; + --color-surface-brand-muted: #f4f3ff; + --color-surface-neutral-hover: rgba(249, 250, 251, 0.9); + --color-surface-page-tint: #f8f7ff; + --color-surface-page-tint-alt: #f4f7fb; + + /* Borders */ + --color-border-default: hsl(0 0% 90.9%); + --color-border-strong: #d1d5db; + --color-border-muted: #e5e7eb; + --color-border-subtle: #d0d5dd; + --color-border-google: #747775; + --color-border-panel: rgba(30, 27, 46, 0.08); + --color-border-panel-muted: rgba(107, 114, 128, 0.22); + --color-border-panel-strong: rgba(107, 114, 128, 0.35); + --color-border-overlay: rgba(148, 163, 184, 0.22); + --color-border-overlay-strong: rgba(148, 163, 184, 0.3); + + /* Brand and focus */ + --color-brand-ink: #1e1b2e; + --color-brand-primary: #804eec; + --color-brand-primary-hover: #6d28d9; + --color-brand-primary-active: #5b21b6; + --color-brand-secondary: #a855f7; + --color-brand-accent: #3b82f6; + --color-focus-ring: rgba(124, 58, 237, 0.2); + --color-focus-ring-strong: rgba(139, 92, 246, 0.16); + --color-ring-subtle: rgba(30, 27, 46, 0.35); + + /* Status: info */ + --color-status-info-soft-bg: #eff6ff; + --color-status-info-soft-border: #bfdbfe; + --color-status-info-foreground: #2563eb; + + /* Status: warning */ + --color-status-warning-soft-bg: #fff4e2; + --color-status-warning-soft-border: #db9729; + --color-status-warning-foreground: #db9729; + --color-status-warning-strong: #db9729; + --color-status-warning-strong-hover: #c37f12; + --color-status-warning-strong-active: #a9670c; + --color-status-warning-strong-foreground: #ffffff; + --color-status-warning-soft-foreground-strong: #b86e00; + --color-status-warning-soft-bg-hover: #fee6b7; + --color-status-warning-soft-bg-active: #fdd58e; + --color-status-warning-emphasis-bg: #fffbeb; + --color-status-warning-emphasis-border: #fde68a; + --color-status-warning-emphasis-foreground: #ca8a04; + + /* Status: success */ + --color-status-success-soft-bg: #ecfdf3; + --color-status-success-soft-border: #bbf7d0; + --color-status-success-foreground: #16a34a; + --color-status-success-strong: #16a34a; + --color-status-success-strong-hover: #15803d; + --color-status-success-strong-active: #166534; + --color-status-success-strong-foreground: #ffffff; + --color-status-success-soft-foreground-strong: #15803d; + --color-status-success-soft-bg-hover: #d1fadf; + --color-status-success-soft-bg-active: #a6f4c5; + --color-status-success-banner-bg: hsl(143 85% 96%); + --color-status-success-banner-border: hsl(145 92% 87%); + --color-status-success-banner-foreground: hsl(140 100% 27%); + + /* Status: danger */ + --color-status-danger-soft-bg: #fef2f2; + --color-status-danger-soft-bg-alt: #fef3f2; + --color-status-danger-soft-border: #fecaca; + --color-status-danger-foreground: #dc2626; + --color-status-danger-strong: #dc2626; + --color-status-danger-strong-hover: #b91c1c; + --color-status-danger-strong-active: #991b1b; + --color-status-danger-strong-foreground: #ffffff; + --color-status-danger-soft-foreground-strong: #b42318; + --color-status-danger-soft-bg-hover: #fee4e2; + --color-status-danger-soft-bg-active: #fecdca; + --color-status-danger-icon-hover: #ef4444; + --color-status-danger-banner-bg: hsl(359 100% 97%); + --color-status-danger-banner-border: hsl(359 100% 94%); + --color-status-danger-banner-foreground: hsl(360 100% 45%); + + /* Effects */ + --overlay-backdrop-default: rgba(17, 24, 39, 0.52); + --overlay-dark-soft: rgba(30, 27, 46, 0.05); + --overlay-dark-soft-alt: rgba(30, 27, 46, 0.06); + --overlay-dark-border: rgba(30, 27, 46, 0.08); + --overlay-dark-strong: rgba(30, 27, 46, 0.14); + --overlay-brand-soft: rgba(124, 58, 237, 0.1); + --overlay-brand-soft-strong: rgba(124, 58, 237, 0.14); + --overlay-brand-muted: rgba(128, 78, 236, 0.08); + --overlay-brand-faint: rgba(128, 78, 236, 0.04); + --overlay-brand-glow: rgba(128, 78, 236, 0.1); + --overlay-google-state: #303030; + --shadow-auth-card: 0 20px 45px rgba(0, 0, 0, 0.1); + --shadow-surface-sm: 0 10px 30px rgba(15, 23, 42, 0.05); + --shadow-surface-md: 0 10px 30px rgba(15, 23, 42, 0.06); + --shadow-surface-hover: 0 12px 30px rgba(15, 23, 42, 0.08); + --shadow-surface-lg: 0 24px 48px rgba(15, 23, 42, 0.18); + --shadow-surface-xl: 0 32px 70px rgba(15, 23, 42, 0.12); + --shadow-sidebar: 20px 0 45px rgba(30, 27, 46, 0.06); + --shadow-floating-control: 0 10px 24px rgba(30, 27, 46, 0.14); + --shadow-google-button: + 0 1px 2px 0 rgba(60, 64, 67, 0.3), + 0 1px 3px 1px rgba(60, 64, 67, 0.15); + --shadow-brand-action: 0 18px 35px rgba(124, 58, 237, 0.25); + --gradient-shell: + linear-gradient(135deg, var(--overlay-brand-muted), transparent 30%), + linear-gradient(160deg, var(--overlay-dark-soft), transparent 42%), + linear-gradient(to bottom right, var(--overlay-dark-border), var(--color-surface-page), var(--overlay-brand-faint)); + --gradient-card-glow: + linear-gradient(to bottom right, rgba(30, 27, 46, 0.1), var(--overlay-dark-soft), var(--overlay-brand-glow)); + --gradient-overview-badge: + linear-gradient(to right, var(--color-brand-secondary), var(--color-brand-accent)); + --gradient-app-surface: + linear-gradient(180deg, var(--color-surface-overlay-strong) 0%, var(--color-surface-default) 100%); + --gradient-not-found-bg: + radial-gradient(circle at top, var(--overlay-brand-soft-strong), transparent 35%), + linear-gradient(180deg, var(--color-surface-page-tint) 0%, var(--color-surface-page-tint-alt) 100%); + --gradient-not-found-primary: + linear-gradient(135deg, var(--color-text-brand-strong) 0%, var(--color-status-info-foreground) 100%); + + /* Runtime fallbacks */ + --color-project-fallback: #3b82f6; + --color-project-accent-purple: #a855f7; + --color-project-accent-red: #ef4444; + + /* Legacy aliases */ + --background: var(--color-surface-page); + --foreground: var(--color-text-primary); + --muted-foreground: var(--color-text-muted); + --border: var(--color-border-default); + --input: var(--color-border-default); + --card: var(--color-surface-card); + --accent: var(--color-surface-subtle); + --primary: var(--color-brand-ink); + --primary-foreground: var(--color-text-inverse); + --secondary: var(--color-brand-primary); + --ring: var(--color-ring-subtle); +} + +* { box-sizing: border-box; } -html { - -webkit-text-size-adjust: 100%; - text-size-adjust: 100%; +html, +body { + margin: 0; + min-height: 100%; } body { - margin: 0; - font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; - color: #0f172a; - background-color: #ffffff; + background: var(--background); + color: var(--foreground); + font-family: + ui-sans-serif, + system-ui, + -apple-system, + BlinkMacSystemFont, + "Segoe UI", + sans-serif; } -:focus-visible { - outline: 2px solid #2563eb; - outline-offset: 2px; +a { + color: inherit; + text-decoration: none; +} + +button, +input { + font: inherit; +} + +.light-only { + display: block; +} + +.dark-only { + display: none; +} + +.visually-hidden { + border: 0; + clip: rect(0 0 0 0); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; } diff --git a/backend/tailwind.input.css b/backend/tailwind.input.css index f034efd..c4d4ac6 100644 --- a/backend/tailwind.input.css +++ b/backend/tailwind.input.css @@ -5,6 +5,7 @@ @source "./internal/web/**/*.go"; @import "./internal/web/ui/base.css"; +@import "./internal/web/ui/auth.css"; @import "./internal/web/ui/button.css"; @import "./internal/web/ui/card.css"; @import "./internal/web/ui/badge.css";