Migrate UI to COSS components + COSS neutral theme; add i18next

Components:
- Replace components/ui primitives with COSS equivalents from the @coss/* shadcn
  registry. Add menu/preview-card/group; remove the superseded dropdown-menu,
  hover-card, button-group; keep carousel (no COSS equivalent).
- Migrate live call sites to canonical COSS APIs preserving layout/behavior:
  sidebar menus (nav-user, team-switcher, nav-notifications), chat-input radio
  menu, patient dialogs/cards, auth forms (FieldGroup -> flex column), settings.
- ai-elements: migrate the live conversation/message with behavior parity
  (Tooltip via render, Group/GroupText); repoint dormant files (hover-card ->
  preview-card, dropdown-menu -> menu, button-group -> group, InputGroupButton
  -> Button). Residual pre-existing Base UI type drift stays behind
  ignoreBuildErrors.

Theme:
- Adopt COSS default neutral tokens in globals.css with light + dark palettes.
- Add next-themes (defaultTheme=dark, enableSystem) and drop the forced dark
  class. Align font variables to the COSS contract (--font-sans/-heading/-mono).
  Make scrollbars theme-aware.

i18n:
- Add i18next + react-i18next (lib/i18n/config.ts, locales/en/translation.json,
  components/i18n-provider.tsx mounted in layout). Convert auth forms, sidebar
  nav, and settings tabs to useTranslation() as the reference pattern.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Khalid Abdi
2026-06-03 18:39:20 +03:00
parent 26e933fb6c
commit 4d6a5dc008
63 changed files with 3729 additions and 2545 deletions
+85 -58
View File
@@ -8,8 +8,8 @@
--color-background: var(--background);
--color-foreground: var(--foreground);
--font-sans: var(--font-sans);
--font-mono: var(--font-geist-mono);
--font-heading: var(--font-sans);
--font-mono: var(--font-mono);
--font-heading: var(--font-heading);
--color-sidebar-ring: var(--sidebar-ring);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
@@ -46,83 +46,110 @@
--radius-2xl: calc(var(--radius) * 1.8);
--radius-3xl: calc(var(--radius) * 2.2);
--radius-4xl: calc(var(--radius) * 2.6);
--color-warning-foreground: var(--warning-foreground);
--color-warning: var(--warning);
--color-success-foreground: var(--success-foreground);
--color-success: var(--success);
--color-info-foreground: var(--info-foreground);
--color-info: var(--info);
--color-destructive-foreground: var(--destructive-foreground);
--animate-skeleton: skeleton 2s -1s infinite linear;
@keyframes skeleton {
to {
background-position: -200% 0;
}
}
}
:root {
--card: oklch(1 0 0);
--card-foreground: oklch(0.145 0 0);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.145 0 0);
--primary: oklch(0.488 0.243 264.376);
--primary-foreground: oklch(0.97 0.014 254.604);
--secondary: oklch(0.967 0.001 286.375);
--secondary-foreground: oklch(0.21 0.006 285.885);
--muted: oklch(0.97 0 0);
--muted-foreground: oklch(0.556 0 0);
--accent: oklch(0.97 0 0);
--accent-foreground: oklch(0.205 0 0);
--destructive: oklch(0.577 0.245 27.325);
--border: oklch(0.922 0 0);
--input: oklch(0.922 0 0);
--ring: oklch(0.708 0 0);
--card: var(--color-white);
--card-foreground: var(--color-neutral-800);
--popover: var(--color-white);
--popover-foreground: var(--color-neutral-800);
--primary: var(--color-neutral-800);
--primary-foreground: var(--color-neutral-50);
--secondary: --alpha(var(--color-black) / 4%);
--secondary-foreground: var(--color-neutral-800);
--muted: --alpha(var(--color-black) / 4%);
--muted-foreground: color-mix(in srgb, var(--color-neutral-500) 90%, var(--color-black));
--accent: --alpha(var(--color-black) / 4%);
--accent-foreground: var(--color-neutral-800);
--destructive: var(--color-red-500);
--border: --alpha(var(--color-black) / 8%);
--input: --alpha(var(--color-black) / 10%);
--ring: var(--color-neutral-400);
--chart-1: oklch(0.809 0.105 251.813);
--chart-2: oklch(0.623 0.214 259.815);
--chart-3: oklch(0.546 0.245 262.881);
--chart-4: oklch(0.488 0.243 264.376);
--chart-5: oklch(0.424 0.199 265.638);
--radius: 0.875rem;
--sidebar: oklch(0.985 0 0);
--sidebar-foreground: oklch(0.145 0 0);
--sidebar-primary: oklch(0.546 0.245 262.881);
--sidebar-primary-foreground: oklch(0.97 0.014 254.604);
--sidebar-accent: oklch(0.97 0 0);
--sidebar-accent-foreground: oklch(0.205 0 0);
--sidebar-border: oklch(0.922 0 0);
--sidebar-ring: oklch(0.708 0 0);
--background: oklch(1 0 0);
--foreground: oklch(0.145 0 0);
--sidebar: var(--color-neutral-50);
--sidebar-foreground: var(--color-neutral-800);
--sidebar-primary: var(--color-neutral-800);
--sidebar-primary-foreground: var(--color-neutral-50);
--sidebar-accent: --alpha(var(--color-black) / 4%);
--sidebar-accent-foreground: var(--color-neutral-800);
--sidebar-border: --alpha(var(--color-black) / 8%);
--sidebar-ring: var(--color-neutral-400);
--background: var(--color-white);
--foreground: var(--color-neutral-800);
--destructive-foreground: var(--color-red-700);
--info: var(--color-blue-500);
--info-foreground: var(--color-blue-700);
--success: var(--color-emerald-500);
--success-foreground: var(--color-emerald-700);
--warning: var(--color-amber-500);
--warning-foreground: var(--color-amber-700);
}
/* Linear-inspired dark palette: near-black canvas, #5e6ad2 indigo accent, hairline borders. */
/* COSS neutral dark palette: near-black canvas, neutral accent, hairline borders. */
.dark {
--background: oklch(0.172 0.004 277);
--foreground: oklch(0.98 0.002 277);
--card: oklch(0.193 0.005 277);
--card-foreground: oklch(0.98 0.002 277);
--popover: oklch(0.193 0.005 277);
--popover-foreground: oklch(0.98 0.002 277);
--primary: oklch(0.556 0.155 277);
--primary-foreground: oklch(0.985 0.001 277);
--secondary: oklch(0.225 0.006 277);
--secondary-foreground: oklch(0.98 0.002 277);
--muted: oklch(0.225 0.006 277);
--muted-foreground: oklch(0.652 0.012 268);
--accent: oklch(0.245 0.008 277);
--accent-foreground: oklch(0.98 0.002 277);
--destructive: oklch(0.704 0.191 22.216);
--border: oklch(1 0 0 / 8%);
--input: oklch(1 0 0 / 10%);
--ring: oklch(0.556 0.155 277);
--background: color-mix(in srgb, var(--color-neutral-950) 95%, var(--color-white));
--foreground: var(--color-neutral-100);
--card: color-mix(in srgb, var(--background) 98%, var(--color-white));
--card-foreground: var(--color-neutral-100);
--popover: color-mix(in srgb, var(--background) 98%, var(--color-white));
--popover-foreground: var(--color-neutral-100);
--primary: var(--color-neutral-100);
--primary-foreground: var(--color-neutral-800);
--secondary: --alpha(var(--color-white) / 4%);
--secondary-foreground: var(--color-neutral-100);
--muted: --alpha(var(--color-white) / 4%);
--muted-foreground: color-mix(in srgb, var(--color-neutral-500) 90%, var(--color-white));
--accent: --alpha(var(--color-white) / 4%);
--accent-foreground: var(--color-neutral-100);
--destructive: color-mix(in srgb, var(--color-red-500) 90%, var(--color-white));
--border: --alpha(var(--color-white) / 6%);
--input: --alpha(var(--color-white) / 8%);
--ring: var(--color-neutral-500);
--chart-1: oklch(0.809 0.105 251.813);
--chart-2: oklch(0.623 0.214 259.815);
--chart-3: oklch(0.546 0.245 262.881);
--chart-4: oklch(0.488 0.243 264.376);
--chart-5: oklch(0.424 0.199 265.638);
--sidebar: oklch(0.138 0.004 277);
--sidebar-foreground: oklch(0.98 0.002 277);
--sidebar-primary: oklch(0.556 0.155 277);
--sidebar-primary-foreground: oklch(0.985 0.001 277);
--sidebar-accent: oklch(0.245 0.008 277);
--sidebar-accent-foreground: oklch(0.98 0.002 277);
--sidebar-border: oklch(1 0 0 / 8%);
--sidebar-ring: oklch(0.556 0.155 277);
--sidebar: var(--color-neutral-950);
--sidebar-foreground: var(--color-neutral-100);
--sidebar-primary: var(--color-neutral-100);
--sidebar-primary-foreground: var(--color-neutral-800);
--sidebar-accent: --alpha(var(--color-white) / 4%);
--sidebar-accent-foreground: var(--color-neutral-100);
--sidebar-border: --alpha(var(--color-white) / 6%);
--sidebar-ring: var(--color-neutral-500);
--destructive-foreground: var(--color-red-400);
--info: var(--color-blue-500);
--info-foreground: var(--color-blue-400);
--success: var(--color-emerald-500);
--success-foreground: var(--color-emerald-400);
--warning: var(--color-amber-500);
--warning-foreground: var(--color-amber-400);
}
@layer base {
* {
@apply border-border outline-ring/50;
scrollbar-width: thin;
scrollbar-color: oklch(1 0 0 / 18%) transparent;
scrollbar-color: color-mix(in srgb, var(--foreground) 20%, transparent) transparent;
}
*::-webkit-scrollbar {
width: 8px;
@@ -132,11 +159,11 @@
background: transparent;
}
*::-webkit-scrollbar-thumb {
background-color: oklch(1 0 0 / 18%);
background-color: color-mix(in srgb, var(--foreground) 20%, transparent);
border-radius: 9999px;
}
*::-webkit-scrollbar-thumb:hover {
background-color: oklch(1 0 0 / 28%);
background-color: color-mix(in srgb, var(--foreground) 32%, transparent);
}
body {
@apply bg-background text-foreground;