Explorar el Código

feat(app): color themes (#30824)

Co-authored-by: LukeParkerDev <10430890+Hona@users.noreply.github.com>
Aarav Sareen hace 2 meses
padre
commit
605ae48c6f

+ 2 - 8
packages/app/src/components/settings-v2/dialog-settings-v2.tsx

@@ -16,14 +16,8 @@ export const DialogSettings: Component = () => {
   const platform = usePlatform()
 
   return (
-    <Dialog size="x-large" class="settings-v2-dialog" data-component="settings-v2-dialog">
-      <TabsV2
-        orientation="vertical"
-        variant="settings"
-        defaultValue="general"
-        class="settings-v2"
-        data-component="settings-v2"
-      >
+    <Dialog size="x-large" variant="settings" class="settings-v2-dialog">
+      <TabsV2 orientation="vertical" variant="settings" defaultValue="general" class="settings-v2">
         <TabsV2.List>
           <div class="flex flex-col justify-between h-full w-full">
             <div class="flex flex-col gap-3 w-full">

+ 8 - 8
packages/app/src/components/settings-v2/settings-v2.css

@@ -1,11 +1,15 @@
 @import "@opencode-ai/ui/v2/text-input-v2.css";
 @import "@opencode-ai/ui/v2/button-v2.css";
 
-[data-component="settings-v2"] {
+[data-component="tabs-v2"][data-variant="settings"] {
   height: 100%;
 }
 
-[data-component="settings-v2-dialog"] [data-slot="dialog-body"] {
+[data-component="dialog-v2"][data-variant="settings"] [data-slot="dialog-container"] {
+  background: var(--v2-background-bg-base);
+}
+
+[data-component="dialog-v2"][data-variant="settings"] [data-slot="dialog-body"] {
   padding: 0;
   overflow: hidden;
 }
@@ -81,10 +85,6 @@
   box-shadow: inset 0 0 0 0.5px var(--v2-border-border-muted);
 }
 
-[data-slot="dialog-container"]:has(.settings-v2-dialog) {
-  box-shadow: var(--v2-elevation-overlay);
-}
-
 [data-component="settings-v2-row"] {
   display: flex;
   flex-wrap: wrap;
@@ -153,12 +153,12 @@
   width: 100%;
 }
 
-[data-component="settings-v2-dialog"] [data-component="select-v2-root"] {
+[data-component="dialog-v2"][data-variant="settings"] [data-component="select-v2-root"] {
   width: fit-content;
   max-width: 100%;
 }
 
-[data-component="settings-v2-dialog"] [data-component="button-v2"] {
+[data-component="dialog-v2"][data-variant="settings"] [data-component="button-v2"] {
   width: fit-content;
   max-width: 100%;
 }

+ 1 - 1
packages/app/src/pages/home.tsx

@@ -270,7 +270,7 @@ function HomeDesign() {
   }
 
   return (
-    <div class="rounded-[10px] shadow-[var(--v2-elevation-raised)] m-2 bg-background-base self-stretch flex-1">
+    <div class="rounded-[10px] shadow-[var(--v2-elevation-raised)] m-2 bg-v2-background-bg-base self-stretch flex-1">
       <div class="mx-auto grid w-full h-full max-w-[1080px] gap-8 px-6 pb-16 lg:grid-cols-[280px_minmax(0,720px)]">
         <HomeProjectColumn
           projects={projects()}

+ 1 - 1
packages/desktop/src/renderer/index.tsx

@@ -24,7 +24,7 @@ import { initI18n, t } from "./i18n"
 import { initializationData, initializationReady } from "./initialization"
 import { resetZoom, setPinchZoomEnabled, webviewZoom, zoomIn, zoomOut } from "./webview-zoom"
 import "./styles.css"
-import { useTheme } from "@opencode-ai/ui/theme"
+import { useTheme } from "@opencode-ai/ui/theme/context"
 
 const root = document.getElementById("root")
 if (import.meta.env.DEV && !(root instanceof HTMLElement)) {

+ 2 - 1
packages/ui/package.json

@@ -32,7 +32,8 @@
     "test": "bun test src",
     "test:ci": "mkdir -p .artifacts/unit && bun test src --reporter=junit --reporter-outfile=.artifacts/unit/junit.xml",
     "dev": "vite",
-    "generate:tailwind": "bun run script/tailwind.ts"
+    "generate:tailwind": "bun run script/tailwind.ts",
+    "generate:v2-oc2": "bun run script/build-oc2-v2-overrides.ts"
   },
   "devDependencies": {
     "@tailwindcss/vite": "catalog:",

+ 32 - 0
packages/ui/script/build-oc2-v2-overrides.ts

@@ -0,0 +1,32 @@
+#!/usr/bin/env bun
+
+import { V2_PRIMITIVES_DEFAULT } from "../src/theme/v2/default-primitives"
+import type { DesktopTheme } from "../src/theme/types"
+
+const themePath = import.meta.dir + "/../src/theme/themes/oc-2.json"
+const theme = (await Bun.file(themePath).json()) as DesktopTheme
+const css = await Bun.file(import.meta.dir + "/../src/v2/styles/theme.css").text()
+
+const light = { ...V2_PRIMITIVES_DEFAULT, ...readTokens("light") }
+const dark = { ...V2_PRIMITIVES_DEFAULT, ...readTokens("dark") }
+
+const next: DesktopTheme = {
+  ...theme,
+  light: { ...theme.light, v2Overrides: light },
+  dark: { ...theme.dark, v2Overrides: dark },
+}
+
+await Bun.write(themePath, JSON.stringify(next, null, 2) + "\n")
+console.log("Updated oc-2.json v2Overrides", Object.keys(light).length, "tokens per mode")
+
+function readTokens(mode: "light" | "dark") {
+  const selector = mode === "light" ? ":root" : `\\[data-color-scheme="${mode}"\\]`
+  const block = css.match(new RegExp(`${selector} \\{([\\s\\S]*?)\\n  \\}`))?.[1]
+  if (!block) throw new Error(`Missing ${mode} OC-2 tokens`)
+  return Object.fromEntries(
+    [...block.matchAll(/--(v2-[\w-]+):\s*([^;]+);/g)]
+      // Fonts and the fixed avatar foreground remain global CSS rather than theme overrides.
+      .filter(([, key]) => key !== "v2-avatar-fg" && key !== "v2-font-family-sans")
+      .map(([, key, value]) => [key, value!.replace(/\s+/g, " ").trim()]),
+  )
+}

+ 8 - 2
packages/ui/src/theme/context.tsx

@@ -1,9 +1,12 @@
+// @refresh reload
+
 import { createEffect, onMount } from "solid-js"
 import { createStore } from "solid-js/store"
 import { makeEventListener } from "@solid-primitives/event-listener"
 import { createSimpleContext } from "../context/helper"
 import oc2ThemeJson from "./themes/oc-2.json"
 import { resolveThemeVariant, themeToCss } from "./resolve"
+import { resolveThemeVariantV2, themeV2ToCss } from "./v2/resolve"
 import type { DesktopTheme } from "./types"
 
 export type ColorScheme = "light" | "dark" | "system"
@@ -132,15 +135,17 @@ function applyThemeCss(theme: DesktopTheme, themeId: string, mode: "light" | "da
   const variant = isDark ? theme.dark : theme.light
   const tokens = resolveThemeVariant(variant, isDark)
   const css = themeToCss(tokens)
+  const v2 = themeV2ToCss(resolveThemeVariantV2(variant, isDark))
 
   if (themeId !== "oc-2") {
-    write(isDark ? STORAGE_KEYS.THEME_CSS_DARK : STORAGE_KEYS.THEME_CSS_LIGHT, css)
+    write(isDark ? STORAGE_KEYS.THEME_CSS_DARK : STORAGE_KEYS.THEME_CSS_LIGHT, `${css}\n  ${v2}`)
   }
 
   const fullCss = `:root {
   color-scheme: ${mode};
   --text-mix-blend-mode: ${isDark ? "plus-lighter" : "multiply"};
   ${css}
+  ${v2}
 }`
 
   document.getElementById("oc-theme-preload")?.remove()
@@ -160,7 +165,8 @@ function cacheThemeVariants(theme: DesktopTheme, themeId: string) {
     const variant = isDark ? theme.dark : theme.light
     const tokens = resolveThemeVariant(variant, isDark)
     const css = themeToCss(tokens)
-    write(isDark ? STORAGE_KEYS.THEME_CSS_DARK : STORAGE_KEYS.THEME_CSS_LIGHT, css)
+    const v2 = themeV2ToCss(resolveThemeVariantV2(variant, isDark))
+    write(isDark ? STORAGE_KEYS.THEME_CSS_DARK : STORAGE_KEYS.THEME_CSS_LIGHT, `${css}\n  ${v2}`)
   }
 }
 

+ 7 - 0
packages/ui/src/theme/desktop-theme.schema.json

@@ -154,6 +154,13 @@
           "additionalProperties": {
             "$ref": "#/definitions/ColorValue"
           }
+        },
+        "v2Overrides": {
+          "type": "object",
+          "description": "Optional direct overrides for any v2 CSS variable (without -- prefix)",
+          "additionalProperties": {
+            "type": "string"
+          }
         }
       }
     }

+ 3 - 0
packages/ui/src/theme/index.ts

@@ -8,6 +8,8 @@ export type {
   ResolvedTheme,
   ColorValue,
   CssVarRef,
+  V2ColorValue,
+  ResolvedV2Theme,
 } from "./types"
 
 export {
@@ -30,6 +32,7 @@ export {
 } from "./color"
 
 export { resolveThemeVariant, resolveTheme, themeToCss } from "./resolve"
+export { resolveThemeVariantV2, resolveThemeV2, themeV2ToCss, generateV2Primitives } from "./v2/resolve"
 export { applyTheme, loadThemeFromUrl, getActiveTheme, removeTheme, setColorScheme } from "./loader"
 export { ThemeProvider, useTheme, type ColorScheme } from "./context"
 

+ 14 - 5
packages/ui/src/theme/loader.ts

@@ -1,5 +1,6 @@
-import type { DesktopTheme, ResolvedTheme } from "./types"
+import type { DesktopTheme, ResolvedTheme, ResolvedV2Theme } from "./types"
 import { resolveThemeVariant, themeToCss } from "./resolve"
+import { resolveThemeVariantV2, themeV2ToCss } from "./v2/resolve"
 
 let activeTheme: DesktopTheme | null = null
 const THEME_STYLE_ID = "opencode-theme"
@@ -19,17 +20,25 @@ export function applyTheme(theme: DesktopTheme, themeId?: string): void {
   activeTheme = theme
   const lightTokens = resolveThemeVariant(theme.light, false)
   const darkTokens = resolveThemeVariant(theme.dark, true)
+  const lightV2Tokens = resolveThemeVariantV2(theme.light, false)
+  const darkV2Tokens = resolveThemeVariantV2(theme.dark, true)
   const targetThemeId = themeId ?? theme.id
-  const css = buildThemeCss(lightTokens, darkTokens, targetThemeId)
+  const css = buildThemeCss(lightTokens, darkTokens, lightV2Tokens, darkV2Tokens, targetThemeId)
   const themeStyleElement = ensureLoaderStyleElement()
   themeStyleElement.textContent = css
   document.documentElement.setAttribute("data-theme", targetThemeId)
 }
 
-function buildThemeCss(light: ResolvedTheme, dark: ResolvedTheme, themeId: string): string {
+function buildThemeCss(
+  light: ResolvedTheme,
+  dark: ResolvedTheme,
+  lightV2: ResolvedV2Theme,
+  darkV2: ResolvedV2Theme,
+  themeId: string,
+): string {
   const isDefaultTheme = themeId === "oc-2"
-  const lightCss = themeToCss(light)
-  const darkCss = themeToCss(dark)
+  const lightCss = `${themeToCss(light)}\n  ${themeV2ToCss(lightV2)}`
+  const darkCss = `${themeToCss(dark)}\n  ${themeV2ToCss(darkV2)}`
 
   if (isDefaultTheme) {
     return `

+ 22 - 0
packages/ui/src/theme/themes/matrix.json

@@ -43,6 +43,17 @@
       "markdown-image": "#30b3ff",
       "markdown-image-text": "#24f6d9",
       "markdown-code-block": "#203022"
+    },
+    "v2Overrides": {
+      "v2-text-text-base": "#353535",
+      "v2-text-text-muted": "#748476",
+      "v2-text-text-faint": "#748476",
+      "v2-background-bg-accent": "var(--v2-green-600)",
+      "v2-text-text-accent": "var(--v2-green-600)",
+      "v2-text-text-accent-hover": "var(--v2-green-700)",
+      "v2-icon-icon-accent": "var(--v2-green-600)",
+      "v2-icon-icon-accent-hover": "var(--v2-green-700)",
+      "v2-border-border-focus": "var(--v2-green-500)"
     }
   },
   "dark": {
@@ -86,6 +97,17 @@
       "markdown-image": "#30b3ff",
       "markdown-image-text": "#24f6d9",
       "markdown-code-block": "#62ff94"
+    },
+    "v2Overrides": {
+      "v2-text-text-base": "#ececec",
+      "v2-text-text-muted": "#8ca391",
+      "v2-text-text-faint": "#8ca391",
+      "v2-background-bg-accent": "var(--v2-green-600)",
+      "v2-text-text-accent": "var(--v2-green-400)",
+      "v2-text-text-accent-hover": "var(--v2-green-300)",
+      "v2-icon-icon-accent": "var(--v2-green-400)",
+      "v2-icon-icon-accent-hover": "var(--v2-green-300)",
+      "v2-border-border-focus": "var(--v2-green-500)"
     }
   }
 }

+ 376 - 0
packages/ui/src/theme/themes/oc-2.json

@@ -42,6 +42,194 @@
       "syntax-diff-delete": "#ff8c00",
       "syntax-diff-unknown": "#a753ae",
       "surface-critical-base": "#FFF2F0"
+    },
+    "v2Overrides": {
+      "v2-grey-100": "#ffffffff",
+      "v2-grey-200": "#fafafaff",
+      "v2-grey-300": "#eeeeeeff",
+      "v2-grey-400": "#d4d4d4ff",
+      "v2-grey-500": "#aeaeaeff",
+      "v2-grey-600": "#808080ff",
+      "v2-grey-700": "#5c5c5cff",
+      "v2-grey-800": "#3a3a3aff",
+      "v2-grey-900": "#242424ff",
+      "v2-grey-1000": "#161616ff",
+      "v2-grey-1100": "#080808ff",
+      "v2-grey-1200": "#000000ff",
+      "v2-red-100": "#fcecebff",
+      "v2-red-200": "#f6d5d3ff",
+      "v2-red-300": "#f2bbb7ff",
+      "v2-red-400": "#f29b96ff",
+      "v2-red-500": "#f17471ff",
+      "v2-red-600": "#f1484fff",
+      "v2-red-700": "#d92e3cff",
+      "v2-red-800": "#b82d35ff",
+      "v2-red-900": "#97252bff",
+      "v2-red-1000": "#7a1f23ff",
+      "v2-red-1100": "#5f1a1cff",
+      "v2-red-1200": "#461516ff",
+      "v2-orange-100": "#fdf2edff",
+      "v2-orange-200": "#ffe7dcff",
+      "v2-orange-300": "#ffd8c6ff",
+      "v2-orange-400": "#ffc1a4ff",
+      "v2-orange-500": "#ffa478ff",
+      "v2-orange-600": "#ff8648ff",
+      "v2-orange-700": "#ee7330ff",
+      "v2-orange-800": "#d16427ff",
+      "v2-orange-900": "#b35624ff",
+      "v2-orange-1000": "#954c27ff",
+      "v2-orange-1100": "#723d22ff",
+      "v2-orange-1200": "#5a2c14ff",
+      "v2-yellow-100": "#fefaecff",
+      "v2-yellow-200": "#fcefd0ff",
+      "v2-yellow-300": "#f7e5b5ff",
+      "v2-yellow-400": "#f3da9bff",
+      "v2-yellow-500": "#f2cf76ff",
+      "v2-yellow-600": "#f6c251ff",
+      "v2-yellow-700": "#e7af36ff",
+      "v2-yellow-800": "#cb9f34ff",
+      "v2-yellow-900": "#ac8833ff",
+      "v2-yellow-1000": "#8e7231ff",
+      "v2-yellow-1100": "#68552bff",
+      "v2-yellow-1200": "#4b4025ff",
+      "v2-green-100": "#e7f9eaff",
+      "v2-green-200": "#d0f0d5ff",
+      "v2-green-300": "#b8e9c1ff",
+      "v2-green-400": "#96e3a6ff",
+      "v2-green-500": "#6bd586ff",
+      "v2-green-600": "#49c970ff",
+      "v2-green-700": "#2eaf5aff",
+      "v2-green-800": "#198b43ff",
+      "v2-green-900": "#1d783cff",
+      "v2-green-1000": "#196130ff",
+      "v2-green-1100": "#164c26ff",
+      "v2-green-1200": "#14361dff",
+      "v2-cyan-100": "#e2f7fbff",
+      "v2-cyan-200": "#c4edf4ff",
+      "v2-cyan-300": "#a3e4efff",
+      "v2-cyan-400": "#65d9ebff",
+      "v2-cyan-500": "#00c5dfff",
+      "v2-cyan-600": "#00abcfff",
+      "v2-cyan-700": "#0096b8ff",
+      "v2-cyan-800": "#007d9bff",
+      "v2-cyan-900": "#006c85ff",
+      "v2-cyan-1000": "#005a6eff",
+      "v2-cyan-1100": "#004756ff",
+      "v2-cyan-1200": "#00353fff",
+      "v2-blue-100": "#ecf1feff",
+      "v2-blue-200": "#d7e2fcff",
+      "v2-blue-300": "#c3d4fdff",
+      "v2-blue-400": "#a2bcffff",
+      "v2-blue-500": "#7698fdff",
+      "v2-blue-600": "#3b5cf6ff",
+      "v2-blue-700": "#3250dfff",
+      "v2-blue-800": "#2c47c8ff",
+      "v2-blue-900": "#263fa9ff",
+      "v2-blue-1000": "#22388fff",
+      "v2-blue-1100": "#1c2e70ff",
+      "v2-blue-1200": "#1b2852ff",
+      "v2-purple-100": "#ebecfeff",
+      "v2-purple-200": "#d5d5fcff",
+      "v2-purple-300": "#b9b8f5ff",
+      "v2-purple-400": "#9e99f7ff",
+      "v2-purple-500": "#8271f8ff",
+      "v2-purple-600": "#7152f4ff",
+      "v2-purple-700": "#623be2ff",
+      "v2-purple-800": "#5230c2ff",
+      "v2-purple-900": "#442aa1ff",
+      "v2-purple-1000": "#361f83ff",
+      "v2-purple-1100": "#2b1b6aff",
+      "v2-purple-1200": "#221358ff",
+      "v2-pink-100": "#fdecf3ff",
+      "v2-pink-200": "#f7d5e4ff",
+      "v2-pink-300": "#fabcd8ff",
+      "v2-pink-400": "#f799c6ff",
+      "v2-pink-500": "#f26cb2ff",
+      "v2-pink-600": "#f64aabff",
+      "v2-pink-700": "#e4429eff",
+      "v2-pink-800": "#c83d8bff",
+      "v2-pink-900": "#aa3576ff",
+      "v2-pink-1000": "#8c2d61ff",
+      "v2-pink-1100": "#6f284fff",
+      "v2-pink-1200": "#5c1d3fff",
+      "v2-background-bg-base": "var(--v2-grey-100)",
+      "v2-background-bg-deep": "var(--v2-grey-200)",
+      "v2-background-bg-layer-01": "var(--v2-grey-200)",
+      "v2-background-bg-layer-02": "var(--v2-grey-300)",
+      "v2-background-bg-layer-03": "var(--v2-grey-400)",
+      "v2-background-bg-inverse": "var(--v2-grey-1000)",
+      "v2-background-bg-contrast": "var(--v2-grey-900)",
+      "v2-background-bg-button-neutral": "var(--v2-grey-100)",
+      "v2-background-bg-accent": "var(--v2-blue-600)",
+      "v2-text-text-base": "var(--v2-grey-1000)",
+      "v2-text-text-muted": "var(--v2-grey-700)",
+      "v2-text-text-faint": "var(--v2-grey-600)",
+      "v2-text-text-inverse": "var(--v2-grey-100)",
+      "v2-text-text-contrast": "var(--v2-grey-100)",
+      "v2-text-text-accent": "var(--v2-blue-600)",
+      "v2-text-text-accent-hover": "var(--v2-blue-700)",
+      "v2-icon-icon-base": "var(--v2-grey-800)",
+      "v2-icon-icon-muted": "var(--v2-grey-600)",
+      "v2-icon-icon-inverse": "var(--v2-grey-100)",
+      "v2-icon-icon-contrast": "var(--v2-grey-200)",
+      "v2-icon-icon-accent": "var(--v2-blue-600)",
+      "v2-icon-icon-accent-hover": "var(--v2-blue-700)",
+      "v2-border-border-muted": "var(--v2-alpha-dark-8)",
+      "v2-border-border-base": "var(--v2-alpha-dark-10)",
+      "v2-border-border-strong": "var(--v2-alpha-dark-20)",
+      "v2-border-border-inverse": "var(--v2-grey-1000)",
+      "v2-border-border-focus": "var(--v2-blue-500)",
+      "v2-overlay-simple-overlay-hover": "var(--v2-alpha-dark-4)",
+      "v2-overlay-simple-overlay-pressed": "var(--v2-alpha-dark-8)",
+      "v2-overlay-simple-overlay-contrast-hover": "var(--v2-alpha-light-12)",
+      "v2-overlay-simple-overlay-contrast-pressed": "var(--v2-alpha-light-24)",
+      "v2-overlay-simple-overlay-scrim": "var(--v2-alpha-dark-40)",
+      "v2-overlay-gradient-depth-overlay-depth-top": "var(--v2-alpha-light-100)",
+      "v2-overlay-gradient-depth-overlay-depth-bot": "var(--v2-alpha-light-0)",
+      "v2-overlay-simple-tab-active-scrim": "#fafafa00",
+      "v2-overlay-simple-tab-hover-scrim": "#eeeeee00",
+      "v2-overlay-simple-tab-scrim": "#fafafa00",
+      "v2-state-bg-success": "var(--v2-green-100)",
+      "v2-state-fg-success": "var(--v2-green-800)",
+      "v2-state-border-success": "var(--v2-green-300)",
+      "v2-state-bg-warning": "var(--v2-yellow-100)",
+      "v2-state-fg-warning": "var(--v2-yellow-800)",
+      "v2-state-border-warning": "var(--v2-yellow-300)",
+      "v2-state-bg-danger": "var(--v2-red-100)",
+      "v2-state-fg-danger": "var(--v2-red-800)",
+      "v2-state-border-danger": "var(--v2-red-300)",
+      "v2-state-bg-info": "var(--v2-blue-100)",
+      "v2-state-fg-info": "var(--v2-blue-800)",
+      "v2-state-border-info": "var(--v2-blue-300)",
+      "v2-avatar-bg-orange": "#ee7330ff",
+      "v2-avatar-border-orange": "#d16427ff",
+      "v2-avatar-bg-yellow": "#e7af36ff",
+      "v2-avatar-border-yellow": "#cb9f34ff",
+      "v2-avatar-bg-cyan": "#0096b8ff",
+      "v2-avatar-border-cyan": "#007d9bff",
+      "v2-avatar-bg-green": "#2eaf5aff",
+      "v2-avatar-border-green": "#198b43ff",
+      "v2-avatar-bg-red": "#d92e3cff",
+      "v2-avatar-border-red": "#b82d35ff",
+      "v2-avatar-bg-pink": "#e4429eff",
+      "v2-avatar-border-pink": "#c83d8bff",
+      "v2-avatar-bg-blue": "#3250dfff",
+      "v2-avatar-border-blue": "#2c47c8ff",
+      "v2-avatar-bg-purple": "#623be2ff",
+      "v2-avatar-border-purple": "#5230c2ff",
+      "v2-avatar-bg-gray": "#5c5c5cff",
+      "v2-avatar-border-gray": "#3a3a3aff",
+      "v2-elevation-raised": "0px 2px 4px 0px var(--v2-alpha-dark-4), 0px 1px 2px -1px var(--v2-alpha-dark-8), 0px 0px 0px 0.5px var(--v2-alpha-dark-12), 0px 0px 0px 0px var(--v2-alpha-dark-0)",
+      "v2-elevation-floating": "0px 8px 16px 0px var(--v2-alpha-dark-4), 0px 4px 8px 0px var(--v2-alpha-dark-8), 0px 0px 0px 0.5px var(--v2-alpha-dark-12), 0px 0px 0px 0px var(--v2-alpha-dark-0)",
+      "v2-elevation-overlay": "0px 16px 32px 0px var(--v2-alpha-dark-4), 0px 8px 16px 0px var(--v2-alpha-dark-8), 0px 0px 0px 0.5px var(--v2-alpha-dark-12), 0px 0px 0px 0px var(--v2-alpha-dark-0)",
+      "v2-elevation-button-neutral": "0px 1px 1.5px 0px var(--v2-alpha-dark-10), 0px 0px 0px 0.5px var(--v2-alpha-dark-14), 0px 0px 0px 0px var(--v2-alpha-dark-0)",
+      "v2-elevation-button-contrast": "0px 1px 1.5px 0px var(--v2-alpha-dark-20), 0px 0px 0px 0.5px var(--v2-grey-800), inset 0px 1px 2px 0px var(--v2-alpha-light-14), inset 0px -1px 2px 0px var(--v2-alpha-dark-6), 0px 0px 0px 0px var(--v2-alpha-dark-0)",
+      "v2-elevation-elements": "0px 0.5px 0.5px 0px var(--v2-alpha-dark-40)",
+      "v2-elevation-switch-off": "inset 0px 1px 1px 0px var(--v2-alpha-dark-8), inset 0px 0.5px 0.5px 0px var(--v2-alpha-dark-8), inset 0px 0px 0px 0.5px var(--v2-alpha-dark-10)",
+      "v2-elevation-switch-on": "inset 0px 2px 2px 0px var(--v2-alpha-dark-10), inset 0px 1px 1px 0px var(--v2-alpha-dark-10), inset 0px 0px 0px 0.5px var(--v2-alpha-dark-20)",
+      "v2-illustration-illustration-layer-01": "var(--v2-grey-300)",
+      "v2-illustration-illustration-layer-02": "var(--v2-grey-400)",
+      "v2-illustration-illustration-layer-03": "var(--v2-grey-500)"
     }
   },
   "dark": {
@@ -83,6 +271,194 @@
       "syntax-diff-delete": "#fab283",
       "syntax-diff-unknown": "#edb2f1",
       "surface-critical-base": "#1F0603"
+    },
+    "v2Overrides": {
+      "v2-grey-100": "#ffffffff",
+      "v2-grey-200": "#fafafaff",
+      "v2-grey-300": "#eeeeeeff",
+      "v2-grey-400": "#d4d4d4ff",
+      "v2-grey-500": "#aeaeaeff",
+      "v2-grey-600": "#808080ff",
+      "v2-grey-700": "#5c5c5cff",
+      "v2-grey-800": "#3a3a3aff",
+      "v2-grey-900": "#242424ff",
+      "v2-grey-1000": "#161616ff",
+      "v2-grey-1100": "#080808ff",
+      "v2-grey-1200": "#000000ff",
+      "v2-red-100": "#fcecebff",
+      "v2-red-200": "#f6d5d3ff",
+      "v2-red-300": "#f2bbb7ff",
+      "v2-red-400": "#f29b96ff",
+      "v2-red-500": "#f17471ff",
+      "v2-red-600": "#f1484fff",
+      "v2-red-700": "#d92e3cff",
+      "v2-red-800": "#b82d35ff",
+      "v2-red-900": "#97252bff",
+      "v2-red-1000": "#7a1f23ff",
+      "v2-red-1100": "#5f1a1cff",
+      "v2-red-1200": "#461516ff",
+      "v2-orange-100": "#fdf2edff",
+      "v2-orange-200": "#ffe7dcff",
+      "v2-orange-300": "#ffd8c6ff",
+      "v2-orange-400": "#ffc1a4ff",
+      "v2-orange-500": "#ffa478ff",
+      "v2-orange-600": "#ff8648ff",
+      "v2-orange-700": "#ee7330ff",
+      "v2-orange-800": "#d16427ff",
+      "v2-orange-900": "#b35624ff",
+      "v2-orange-1000": "#954c27ff",
+      "v2-orange-1100": "#723d22ff",
+      "v2-orange-1200": "#5a2c14ff",
+      "v2-yellow-100": "#fefaecff",
+      "v2-yellow-200": "#fcefd0ff",
+      "v2-yellow-300": "#f7e5b5ff",
+      "v2-yellow-400": "#f3da9bff",
+      "v2-yellow-500": "#f2cf76ff",
+      "v2-yellow-600": "#f6c251ff",
+      "v2-yellow-700": "#e7af36ff",
+      "v2-yellow-800": "#cb9f34ff",
+      "v2-yellow-900": "#ac8833ff",
+      "v2-yellow-1000": "#8e7231ff",
+      "v2-yellow-1100": "#68552bff",
+      "v2-yellow-1200": "#4b4025ff",
+      "v2-green-100": "#e7f9eaff",
+      "v2-green-200": "#d0f0d5ff",
+      "v2-green-300": "#b8e9c1ff",
+      "v2-green-400": "#96e3a6ff",
+      "v2-green-500": "#6bd586ff",
+      "v2-green-600": "#49c970ff",
+      "v2-green-700": "#2eaf5aff",
+      "v2-green-800": "#198b43ff",
+      "v2-green-900": "#1d783cff",
+      "v2-green-1000": "#196130ff",
+      "v2-green-1100": "#164c26ff",
+      "v2-green-1200": "#14361dff",
+      "v2-cyan-100": "#e2f7fbff",
+      "v2-cyan-200": "#c4edf4ff",
+      "v2-cyan-300": "#a3e4efff",
+      "v2-cyan-400": "#65d9ebff",
+      "v2-cyan-500": "#00c5dfff",
+      "v2-cyan-600": "#00abcfff",
+      "v2-cyan-700": "#0096b8ff",
+      "v2-cyan-800": "#007d9bff",
+      "v2-cyan-900": "#006c85ff",
+      "v2-cyan-1000": "#005a6eff",
+      "v2-cyan-1100": "#004756ff",
+      "v2-cyan-1200": "#00353fff",
+      "v2-blue-100": "#ecf1feff",
+      "v2-blue-200": "#d7e2fcff",
+      "v2-blue-300": "#c3d4fdff",
+      "v2-blue-400": "#a2bcffff",
+      "v2-blue-500": "#7698fdff",
+      "v2-blue-600": "#3b5cf6ff",
+      "v2-blue-700": "#3250dfff",
+      "v2-blue-800": "#2c47c8ff",
+      "v2-blue-900": "#263fa9ff",
+      "v2-blue-1000": "#22388fff",
+      "v2-blue-1100": "#1c2e70ff",
+      "v2-blue-1200": "#1b2852ff",
+      "v2-purple-100": "#ebecfeff",
+      "v2-purple-200": "#d5d5fcff",
+      "v2-purple-300": "#b9b8f5ff",
+      "v2-purple-400": "#9e99f7ff",
+      "v2-purple-500": "#8271f8ff",
+      "v2-purple-600": "#7152f4ff",
+      "v2-purple-700": "#623be2ff",
+      "v2-purple-800": "#5230c2ff",
+      "v2-purple-900": "#442aa1ff",
+      "v2-purple-1000": "#361f83ff",
+      "v2-purple-1100": "#2b1b6aff",
+      "v2-purple-1200": "#221358ff",
+      "v2-pink-100": "#fdecf3ff",
+      "v2-pink-200": "#f7d5e4ff",
+      "v2-pink-300": "#fabcd8ff",
+      "v2-pink-400": "#f799c6ff",
+      "v2-pink-500": "#f26cb2ff",
+      "v2-pink-600": "#f64aabff",
+      "v2-pink-700": "#e4429eff",
+      "v2-pink-800": "#c83d8bff",
+      "v2-pink-900": "#aa3576ff",
+      "v2-pink-1000": "#8c2d61ff",
+      "v2-pink-1100": "#6f284fff",
+      "v2-pink-1200": "#5c1d3fff",
+      "v2-background-bg-base": "var(--v2-grey-1000)",
+      "v2-background-bg-deep": "var(--v2-grey-1100)",
+      "v2-background-bg-layer-01": "var(--v2-grey-900)",
+      "v2-background-bg-layer-02": "var(--v2-grey-800)",
+      "v2-background-bg-layer-03": "var(--v2-grey-700)",
+      "v2-background-bg-inverse": "var(--v2-grey-100)",
+      "v2-background-bg-contrast": "var(--v2-grey-700)",
+      "v2-background-bg-button-neutral": "var(--v2-alpha-light-6)",
+      "v2-background-bg-accent": "var(--v2-blue-600)",
+      "v2-text-text-base": "var(--v2-grey-200)",
+      "v2-text-text-muted": "var(--v2-grey-500)",
+      "v2-text-text-faint": "var(--v2-grey-600)",
+      "v2-text-text-inverse": "var(--v2-grey-1000)",
+      "v2-text-text-contrast": "var(--v2-grey-100)",
+      "v2-text-text-accent": "var(--v2-blue-400)",
+      "v2-text-text-accent-hover": "var(--v2-blue-300)",
+      "v2-icon-icon-base": "var(--v2-grey-400)",
+      "v2-icon-icon-muted": "var(--v2-grey-600)",
+      "v2-icon-icon-inverse": "var(--v2-grey-1000)",
+      "v2-icon-icon-contrast": "var(--v2-grey-200)",
+      "v2-icon-icon-accent": "var(--v2-blue-400)",
+      "v2-icon-icon-accent-hover": "var(--v2-blue-300)",
+      "v2-border-border-muted": "var(--v2-alpha-light-8)",
+      "v2-border-border-base": "var(--v2-alpha-light-10)",
+      "v2-border-border-strong": "var(--v2-alpha-light-20)",
+      "v2-border-border-inverse": "var(--v2-grey-100)",
+      "v2-border-border-focus": "var(--v2-blue-500)",
+      "v2-overlay-simple-overlay-hover": "var(--v2-alpha-light-6)",
+      "v2-overlay-simple-overlay-pressed": "var(--v2-alpha-light-10)",
+      "v2-overlay-simple-overlay-contrast-hover": "var(--v2-alpha-dark-24)",
+      "v2-overlay-simple-overlay-contrast-pressed": "var(--v2-alpha-dark-40)",
+      "v2-overlay-simple-overlay-scrim": "var(--v2-alpha-light-30)",
+      "v2-overlay-gradient-depth-overlay-depth-top": "var(--v2-alpha-light-100)",
+      "v2-overlay-gradient-depth-overlay-depth-bot": "var(--v2-alpha-light-0)",
+      "v2-overlay-simple-tab-active-scrim": "#24242400",
+      "v2-overlay-simple-tab-hover-scrim": "#3a3a3a00",
+      "v2-overlay-simple-tab-scrim": "#08080800",
+      "v2-state-bg-success": "var(--v2-green-1200)",
+      "v2-state-fg-success": "var(--v2-green-500)",
+      "v2-state-border-success": "var(--v2-green-900)",
+      "v2-state-bg-warning": "var(--v2-yellow-1200)",
+      "v2-state-fg-warning": "var(--v2-yellow-500)",
+      "v2-state-border-warning": "var(--v2-yellow-900)",
+      "v2-state-bg-danger": "var(--v2-red-1200)",
+      "v2-state-fg-danger": "var(--v2-red-500)",
+      "v2-state-border-danger": "var(--v2-red-900)",
+      "v2-state-bg-info": "var(--v2-blue-1200)",
+      "v2-state-fg-info": "var(--v2-blue-500)",
+      "v2-state-border-info": "var(--v2-blue-900)",
+      "v2-avatar-bg-orange": "#723d22ff",
+      "v2-avatar-border-orange": "#ff8648ff",
+      "v2-avatar-bg-yellow": "#68552bff",
+      "v2-avatar-border-yellow": "#e7af36ff",
+      "v2-avatar-bg-cyan": "#005a6eff",
+      "v2-avatar-border-cyan": "#0096b8ff",
+      "v2-avatar-bg-green": "#196130ff",
+      "v2-avatar-border-green": "#49c970ff",
+      "v2-avatar-bg-red": "#7a1f23ff",
+      "v2-avatar-border-red": "#d92e3cff",
+      "v2-avatar-bg-pink": "#8c2d61ff",
+      "v2-avatar-border-pink": "#e4429eff",
+      "v2-avatar-bg-blue": "#263fa9ff",
+      "v2-avatar-border-blue": "#7698fdff",
+      "v2-avatar-bg-purple": "#361f83ff",
+      "v2-avatar-border-purple": "#7152f4ff",
+      "v2-avatar-bg-gray": "#5c5c5cff",
+      "v2-avatar-border-gray": "#aeaeaeff",
+      "v2-elevation-raised": "0px 2px 4px 0px var(--v2-alpha-dark-30), 0px 1px 2px 0px var(--v2-alpha-dark-30), 0px 0px 0px 0.5px var(--v2-alpha-light-16), 0px -0.5px 0px 0px var(--v2-alpha-light-6)",
+      "v2-elevation-floating": "0px 8px 16px 0px var(--v2-alpha-dark-30), 0px 4px 8px 0px var(--v2-alpha-dark-30), 0px 0px 0px 0.5px var(--v2-alpha-light-16), 0px -0.5px 0px 0px var(--v2-alpha-light-6)",
+      "v2-elevation-overlay": "0px 16px 32px 0px var(--v2-alpha-dark-30), 0px 8px 16px 0px var(--v2-alpha-dark-30), 0px 0px 0px 0.5px var(--v2-alpha-light-16), 0px -0.5px 0px 0px var(--v2-alpha-light-6)",
+      "v2-elevation-button-neutral": "0px 1px 2px 0px var(--v2-alpha-dark-40), 0px 0px 0px 0.5px var(--v2-alpha-light-20), 0px -0.5px 0px 0px var(--v2-alpha-light-10)",
+      "v2-elevation-button-contrast": "0px 1px 2px 0px var(--v2-alpha-dark-40), 0px 0px 0px 0.5px var(--v2-alpha-light-40), inset 0px 0px 0px 0px var(--v2-alpha-light-0), inset 0px 0px 0px 0px var(--v2-alpha-light-0), 0px -0.5px 0px 0px var(--v2-alpha-light-30)",
+      "v2-elevation-elements": "0px 0.5px 0.5px 0px var(--v2-alpha-dark-40)",
+      "v2-elevation-switch-off": "inset 0px -0.5px 0px 0px var(--v2-alpha-light-10), inset 0px 0px 0px 0px var(--v2-alpha-light-0), inset 0px 0px 0px 0.5px var(--v2-alpha-light-16)",
+      "v2-elevation-switch-on": "inset 0px -0.5px 0px 0px var(--v2-alpha-light-10), inset 0px 0px 0px 0px var(--v2-alpha-light-0), inset 0px 0px 0px 0.5px var(--v2-alpha-light-16)",
+      "v2-illustration-illustration-layer-01": "var(--v2-grey-900)",
+      "v2-illustration-illustration-layer-02": "var(--v2-grey-800)",
+      "v2-illustration-illustration-layer-03": "var(--v2-grey-700)"
     }
   }
 }

+ 5 - 0
packages/ui/src/theme/types.ts

@@ -34,6 +34,7 @@ export interface ThemePaletteColors {
 
 type ThemeVariantBase = {
   overrides?: Record<string, ColorValue>
+  v2Overrides?: Record<string, V2ColorValue>
 }
 
 export type ThemeVariant =
@@ -67,4 +68,8 @@ export type CssVarRef = `var(--${string})`
 
 export type ColorValue = HexColor | CssVarRef
 
+export type V2ColorValue = HexColor | CssVarRef | string
+
 export type ResolvedTheme = Record<ThemeToken, ColorValue>
+
+export type ResolvedV2Theme = Record<string, V2ColorValue>

+ 48 - 0
packages/ui/src/theme/v2/avatar.ts

@@ -0,0 +1,48 @@
+import type { V2ColorValue } from "../types"
+
+/** Fixed project avatar colors (OC-2); theme-independent like v1 `avatar-background-*`. */
+export const V2_AVATAR_FG = "#ffffffff"
+
+export const V2_AVATAR_LIGHT: Record<string, V2ColorValue> = {
+  "v2-avatar-fg": V2_AVATAR_FG,
+  "v2-avatar-bg-orange": "#ee7330ff",
+  "v2-avatar-border-orange": "#d16427ff",
+  "v2-avatar-bg-yellow": "#e7af36ff",
+  "v2-avatar-border-yellow": "#cb9f34ff",
+  "v2-avatar-bg-cyan": "#0096b8ff",
+  "v2-avatar-border-cyan": "#007d9bff",
+  "v2-avatar-bg-green": "#2eaf5aff",
+  "v2-avatar-border-green": "#198b43ff",
+  "v2-avatar-bg-red": "#d92e3cff",
+  "v2-avatar-border-red": "#b82d35ff",
+  "v2-avatar-bg-pink": "#e4429eff",
+  "v2-avatar-border-pink": "#c83d8bff",
+  "v2-avatar-bg-blue": "#3250dfff",
+  "v2-avatar-border-blue": "#2c47c8ff",
+  "v2-avatar-bg-purple": "#623be2ff",
+  "v2-avatar-border-purple": "#5230c2ff",
+  "v2-avatar-bg-gray": "#5c5c5cff",
+  "v2-avatar-border-gray": "#3a3a3aff",
+}
+
+export const V2_AVATAR_DARK: Record<string, V2ColorValue> = {
+  "v2-avatar-fg": V2_AVATAR_FG,
+  "v2-avatar-bg-orange": "#723d22ff",
+  "v2-avatar-border-orange": "#ff8648ff",
+  "v2-avatar-bg-yellow": "#68552bff",
+  "v2-avatar-border-yellow": "#e7af36ff",
+  "v2-avatar-bg-cyan": "#005a6eff",
+  "v2-avatar-border-cyan": "#0096b8ff",
+  "v2-avatar-bg-green": "#196130ff",
+  "v2-avatar-border-green": "#49c970ff",
+  "v2-avatar-bg-red": "#7a1f23ff",
+  "v2-avatar-border-red": "#d92e3cff",
+  "v2-avatar-bg-pink": "#8c2d61ff",
+  "v2-avatar-border-pink": "#e4429eff",
+  "v2-avatar-bg-blue": "#263fa9ff",
+  "v2-avatar-border-blue": "#7698fdff",
+  "v2-avatar-bg-purple": "#361f83ff",
+  "v2-avatar-border-purple": "#7152f4ff",
+  "v2-avatar-bg-gray": "#5c5c5cff",
+  "v2-avatar-border-gray": "#aeaeaeff",
+}

+ 113 - 0
packages/ui/src/theme/v2/default-primitives.ts

@@ -0,0 +1,113 @@
+import type { V2ColorValue } from "../types"
+
+/** Default v2 hue ramps from `v2/styles/colors.css` (OC-2). Alpha ramps live in CSS only. */
+export const V2_PRIMITIVES_DEFAULT: Record<string, V2ColorValue> = {
+  "v2-grey-100": "#ffffffff",
+  "v2-grey-200": "#fafafaff",
+  "v2-grey-300": "#eeeeeeff",
+  "v2-grey-400": "#d4d4d4ff",
+  "v2-grey-500": "#aeaeaeff",
+  "v2-grey-600": "#808080ff",
+  "v2-grey-700": "#5c5c5cff",
+  "v2-grey-800": "#3a3a3aff",
+  "v2-grey-900": "#242424ff",
+  "v2-grey-1000": "#161616ff",
+  "v2-grey-1100": "#080808ff",
+  "v2-grey-1200": "#000000ff",
+  "v2-red-100": "#fcecebff",
+  "v2-red-200": "#f6d5d3ff",
+  "v2-red-300": "#f2bbb7ff",
+  "v2-red-400": "#f29b96ff",
+  "v2-red-500": "#f17471ff",
+  "v2-red-600": "#f1484fff",
+  "v2-red-700": "#d92e3cff",
+  "v2-red-800": "#b82d35ff",
+  "v2-red-900": "#97252bff",
+  "v2-red-1000": "#7a1f23ff",
+  "v2-red-1100": "#5f1a1cff",
+  "v2-red-1200": "#461516ff",
+  "v2-orange-100": "#fdf2edff",
+  "v2-orange-200": "#ffe7dcff",
+  "v2-orange-300": "#ffd8c6ff",
+  "v2-orange-400": "#ffc1a4ff",
+  "v2-orange-500": "#ffa478ff",
+  "v2-orange-600": "#ff8648ff",
+  "v2-orange-700": "#ee7330ff",
+  "v2-orange-800": "#d16427ff",
+  "v2-orange-900": "#b35624ff",
+  "v2-orange-1000": "#954c27ff",
+  "v2-orange-1100": "#723d22ff",
+  "v2-orange-1200": "#5a2c14ff",
+  "v2-yellow-100": "#fefaecff",
+  "v2-yellow-200": "#fcefd0ff",
+  "v2-yellow-300": "#f7e5b5ff",
+  "v2-yellow-400": "#f3da9bff",
+  "v2-yellow-500": "#f2cf76ff",
+  "v2-yellow-600": "#f6c251ff",
+  "v2-yellow-700": "#e7af36ff",
+  "v2-yellow-800": "#cb9f34ff",
+  "v2-yellow-900": "#ac8833ff",
+  "v2-yellow-1000": "#8e7231ff",
+  "v2-yellow-1100": "#68552bff",
+  "v2-yellow-1200": "#4b4025ff",
+  "v2-green-100": "#e7f9eaff",
+  "v2-green-200": "#d0f0d5ff",
+  "v2-green-300": "#b8e9c1ff",
+  "v2-green-400": "#96e3a6ff",
+  "v2-green-500": "#6bd586ff",
+  "v2-green-600": "#49c970ff",
+  "v2-green-700": "#2eaf5aff",
+  "v2-green-800": "#198b43ff",
+  "v2-green-900": "#1d783cff",
+  "v2-green-1000": "#196130ff",
+  "v2-green-1100": "#164c26ff",
+  "v2-green-1200": "#14361dff",
+  "v2-cyan-100": "#e2f7fbff",
+  "v2-cyan-200": "#c4edf4ff",
+  "v2-cyan-300": "#a3e4efff",
+  "v2-cyan-400": "#65d9ebff",
+  "v2-cyan-500": "#00c5dfff",
+  "v2-cyan-600": "#00abcfff",
+  "v2-cyan-700": "#0096b8ff",
+  "v2-cyan-800": "#007d9bff",
+  "v2-cyan-900": "#006c85ff",
+  "v2-cyan-1000": "#005a6eff",
+  "v2-cyan-1100": "#004756ff",
+  "v2-cyan-1200": "#00353fff",
+  "v2-blue-100": "#ecf1feff",
+  "v2-blue-200": "#d7e2fcff",
+  "v2-blue-300": "#c3d4fdff",
+  "v2-blue-400": "#a2bcffff",
+  "v2-blue-500": "#7698fdff",
+  "v2-blue-600": "#3b5cf6ff",
+  "v2-blue-700": "#3250dfff",
+  "v2-blue-800": "#2c47c8ff",
+  "v2-blue-900": "#263fa9ff",
+  "v2-blue-1000": "#22388fff",
+  "v2-blue-1100": "#1c2e70ff",
+  "v2-blue-1200": "#1b2852ff",
+  "v2-purple-100": "#ebecfeff",
+  "v2-purple-200": "#d5d5fcff",
+  "v2-purple-300": "#b9b8f5ff",
+  "v2-purple-400": "#9e99f7ff",
+  "v2-purple-500": "#8271f8ff",
+  "v2-purple-600": "#7152f4ff",
+  "v2-purple-700": "#623be2ff",
+  "v2-purple-800": "#5230c2ff",
+  "v2-purple-900": "#442aa1ff",
+  "v2-purple-1000": "#361f83ff",
+  "v2-purple-1100": "#2b1b6aff",
+  "v2-purple-1200": "#221358ff",
+  "v2-pink-100": "#fdecf3ff",
+  "v2-pink-200": "#f7d5e4ff",
+  "v2-pink-300": "#fabcd8ff",
+  "v2-pink-400": "#f799c6ff",
+  "v2-pink-500": "#f26cb2ff",
+  "v2-pink-600": "#f64aabff",
+  "v2-pink-700": "#e4429eff",
+  "v2-pink-800": "#c83d8bff",
+  "v2-pink-900": "#aa3576ff",
+  "v2-pink-1000": "#8c2d61ff",
+  "v2-pink-1100": "#6f284fff",
+  "v2-pink-1200": "#5c1d3fff",
+}

+ 24 - 0
packages/ui/src/theme/v2/foreground.ts

@@ -0,0 +1,24 @@
+import { blend, hexToOklch, shift } from "../color"
+import type { ColorValue, HexColor, V2ColorValue } from "../types"
+
+export function mapV2Foreground(
+  ink: HexColor,
+  isDark: boolean,
+  overrides: Record<string, ColorValue> = {},
+): Record<string, V2ColorValue> {
+  const tint = hexToOklch(ink)
+  const body = shift(ink, {
+    l: isDark ? Math.max(0, 0.88 - tint.l) * 0.4 : -Math.max(0, tint.l - 0.18) * 0.24,
+    c: isDark ? 1.04 : 1.02,
+  })
+
+  return {
+    "v2-text-text-base": isDark
+      ? blend("#ffffff", body, 0.9)
+      : shift(body, { l: -0.07, c: 1.04 }),
+    "v2-text-text-muted":
+      overrides["text-weak"] ??
+      shift(body, { l: isDark ? -0.11 : 0.11, c: 0.9 }),
+    "v2-text-text-faint": shift(body, { l: isDark ? -0.2 : 0.21, c: isDark ? 0.78 : 0.72 }),
+  }
+}

+ 150 - 0
packages/ui/src/theme/v2/mapping.ts

@@ -0,0 +1,150 @@
+import type { V2ColorValue } from "../types"
+import { V2_AVATAR_DARK, V2_AVATAR_LIGHT } from "./avatar"
+
+const ref = (name: string): V2ColorValue => `var(--${name})`
+
+const light: Record<string, V2ColorValue> = {
+  "v2-background-bg-base": ref("v2-grey-100"),
+  "v2-background-bg-deep": ref("v2-grey-200"),
+  "v2-background-bg-layer-01": ref("v2-grey-300"),
+  "v2-background-bg-layer-02": ref("v2-grey-400"),
+  "v2-background-bg-layer-03": ref("v2-grey-500"),
+  "v2-background-bg-inverse": ref("v2-grey-1000"),
+  "v2-background-bg-contrast": ref("v2-grey-900"),
+  "v2-background-bg-button-neutral": ref("v2-grey-100"),
+  "v2-background-bg-accent": ref("v2-blue-600"),
+  "v2-text-text-inverse": ref("v2-grey-100"),
+  "v2-text-text-contrast": ref("v2-grey-100"),
+  "v2-text-text-accent": ref("v2-blue-600"),
+  "v2-text-text-accent-hover": ref("v2-blue-700"),
+  "v2-icon-icon-base": ref("v2-grey-1000"),
+  "v2-icon-icon-muted": ref("v2-grey-800"),
+  "v2-icon-icon-inverse": ref("v2-grey-100"),
+  "v2-icon-icon-contrast": ref("v2-grey-200"),
+  "v2-icon-icon-accent": ref("v2-blue-600"),
+  "v2-icon-icon-accent-hover": ref("v2-blue-700"),
+  "v2-border-border-muted": ref("v2-alpha-dark-8"),
+  "v2-border-border-base": ref("v2-alpha-dark-10"),
+  "v2-border-border-strong": ref("v2-alpha-dark-20"),
+  "v2-border-border-inverse": ref("v2-grey-1000"),
+  "v2-border-border-focus": ref("v2-blue-500"),
+  "v2-overlay-simple-overlay-hover": ref("v2-alpha-dark-4"),
+  "v2-overlay-simple-overlay-pressed": ref("v2-alpha-dark-8"),
+  "v2-overlay-simple-overlay-contrast-hover": ref("v2-alpha-light-12"),
+  "v2-overlay-simple-overlay-contrast-pressed": ref("v2-alpha-light-24"),
+  "v2-overlay-simple-overlay-scrim": ref("v2-alpha-dark-40"),
+  "v2-overlay-gradient-depth-overlay-depth-top": ref("v2-alpha-light-100"),
+  "v2-overlay-gradient-depth-overlay-depth-bot": ref("v2-alpha-light-0"),
+  "v2-overlay-simple-tab-active-scrim": "#fafafa00",
+  "v2-overlay-simple-tab-hover-scrim": "#eeeeee00",
+  "v2-overlay-simple-tab-scrim": "#fafafa00",
+  "v2-state-bg-success": ref("v2-green-100"),
+  "v2-state-fg-success": ref("v2-green-800"),
+  "v2-state-border-success": ref("v2-green-300"),
+  "v2-state-bg-warning": ref("v2-yellow-100"),
+  "v2-state-fg-warning": ref("v2-yellow-800"),
+  "v2-state-border-warning": ref("v2-yellow-300"),
+  "v2-state-bg-danger": ref("v2-red-100"),
+  "v2-state-fg-danger": ref("v2-red-800"),
+  "v2-state-border-danger": ref("v2-red-300"),
+  "v2-state-bg-info": ref("v2-blue-100"),
+  "v2-state-fg-info": ref("v2-blue-800"),
+  "v2-state-border-info": ref("v2-blue-300"),
+  ...V2_AVATAR_LIGHT,
+  "v2-elevation-raised":
+    "0px 2px 4px 0px var(--v2-alpha-dark-4), 0px 1px 2px -1px var(--v2-alpha-dark-8), 0px 0px 0px 0.5px var(--v2-alpha-dark-12), 0px 0px 0px 0px var(--v2-alpha-dark-0)",
+  "v2-elevation-floating":
+    "0px 8px 16px 0px var(--v2-alpha-dark-4), 0px 4px 8px 0px var(--v2-alpha-dark-8), 0px 0px 0px 0.5px var(--v2-alpha-dark-12), 0px 0px 0px 0px var(--v2-alpha-dark-0)",
+  "v2-elevation-overlay":
+    "0px 16px 32px 0px var(--v2-alpha-dark-4), 0px 8px 16px 0px var(--v2-alpha-dark-8), 0px 0px 0px 0.5px var(--v2-alpha-dark-12), 0px 0px 0px 0px var(--v2-alpha-dark-0)",
+  "v2-elevation-button-neutral":
+    "0px 1px 1.5px 0px var(--v2-alpha-dark-10), 0px 0px 0px 0.5px var(--v2-alpha-dark-14), 0px 0px 0px 0px var(--v2-alpha-dark-0)",
+  "v2-elevation-button-contrast":
+    "0px 1px 1.5px 0px var(--v2-alpha-dark-20), 0px 0px 0px 0.5px var(--v2-grey-800), inset 0px 1px 2px 0px var(--v2-alpha-light-14), inset 0px -1px 2px 0px var(--v2-alpha-dark-6), 0px 0px 0px 0px var(--v2-alpha-dark-0)",
+  "v2-elevation-elements": "0px 0.5px 0.5px 0px var(--v2-alpha-dark-40)",
+  "v2-elevation-switch-off":
+    "inset 0px 1px 1px 0px var(--v2-alpha-dark-8), inset 0px 0.5px 0.5px 0px var(--v2-alpha-dark-8), inset 0px 0px 0px 0.5px var(--v2-alpha-dark-10)",
+  "v2-elevation-switch-on":
+    "inset 0px 2px 2px 0px var(--v2-alpha-dark-10), inset 0px 1px 1px 0px var(--v2-alpha-dark-10), inset 0px 0px 0px 0.5px var(--v2-alpha-dark-20)",
+  "v2-illustration-illustration-layer-01": ref("v2-grey-300"),
+  "v2-illustration-illustration-layer-02": ref("v2-grey-400"),
+  "v2-illustration-illustration-layer-03": ref("v2-grey-500"),
+}
+
+const dark: Record<string, V2ColorValue> = {
+  "v2-background-bg-base": ref("v2-grey-1000"),
+  "v2-background-bg-deep": ref("v2-grey-1100"),
+  "v2-background-bg-layer-01": ref("v2-grey-800"),
+  "v2-background-bg-layer-02": ref("v2-grey-600"),
+  "v2-background-bg-layer-03": ref("v2-grey-500"),
+  "v2-background-bg-inverse": ref("v2-grey-100"),
+  "v2-background-bg-contrast": ref("v2-grey-700"),
+  "v2-background-bg-button-neutral": ref("v2-alpha-light-6"),
+  "v2-background-bg-accent": ref("v2-blue-600"),
+  "v2-text-text-inverse": ref("v2-grey-1000"),
+  "v2-text-text-contrast": ref("v2-grey-100"),
+  "v2-text-text-accent": ref("v2-blue-400"),
+  "v2-text-text-accent-hover": ref("v2-blue-300"),
+  "v2-icon-icon-base": ref("v2-grey-300"),
+  "v2-icon-icon-muted": ref("v2-grey-400"),
+  "v2-icon-icon-inverse": ref("v2-grey-1000"),
+  "v2-icon-icon-contrast": ref("v2-grey-200"),
+  "v2-icon-icon-accent": ref("v2-blue-400"),
+  "v2-icon-icon-accent-hover": ref("v2-blue-300"),
+  "v2-border-border-muted": ref("v2-alpha-light-8"),
+  "v2-border-border-base": ref("v2-alpha-light-10"),
+  "v2-border-border-strong": ref("v2-alpha-light-20"),
+  "v2-border-border-inverse": ref("v2-grey-100"),
+  "v2-border-border-focus": ref("v2-blue-500"),
+  "v2-overlay-simple-overlay-hover": ref("v2-alpha-light-6"),
+  "v2-overlay-simple-overlay-pressed": ref("v2-alpha-light-10"),
+  "v2-overlay-simple-overlay-contrast-hover": ref("v2-alpha-dark-24"),
+  "v2-overlay-simple-overlay-contrast-pressed": ref("v2-alpha-dark-40"),
+  "v2-overlay-simple-overlay-scrim": ref("v2-alpha-light-30"),
+  "v2-overlay-gradient-depth-overlay-depth-top": ref("v2-alpha-light-100"),
+  "v2-overlay-gradient-depth-overlay-depth-bot": ref("v2-alpha-light-0"),
+  "v2-overlay-simple-tab-active-scrim": "#24242400",
+  "v2-overlay-simple-tab-hover-scrim": "#3a3a3a00",
+  "v2-overlay-simple-tab-scrim": "#08080800",
+  "v2-state-bg-success": ref("v2-green-1200"),
+  "v2-state-fg-success": ref("v2-green-500"),
+  "v2-state-border-success": ref("v2-green-900"),
+  "v2-state-bg-warning": ref("v2-yellow-1200"),
+  "v2-state-fg-warning": ref("v2-yellow-500"),
+  "v2-state-border-warning": ref("v2-yellow-900"),
+  "v2-state-bg-danger": ref("v2-red-1200"),
+  "v2-state-fg-danger": ref("v2-red-500"),
+  "v2-state-border-danger": ref("v2-red-900"),
+  "v2-state-bg-info": ref("v2-blue-1200"),
+  "v2-state-fg-info": ref("v2-blue-500"),
+  "v2-state-border-info": ref("v2-blue-900"),
+  ...V2_AVATAR_DARK,
+  "v2-elevation-raised":
+    "0px 2px 4px 0px var(--v2-alpha-dark-30), 0px 1px 2px 0px var(--v2-alpha-dark-30), 0px 0px 0px 0.5px var(--v2-alpha-light-16), 0px -0.5px 0px 0px var(--v2-alpha-light-6)",
+  "v2-elevation-floating":
+    "0px 8px 16px 0px var(--v2-alpha-dark-30), 0px 4px 8px 0px var(--v2-alpha-dark-30), 0px 0px 0px 0.5px var(--v2-alpha-light-16), 0px -0.5px 0px 0px var(--v2-alpha-light-6)",
+  "v2-elevation-overlay":
+    "0px 16px 32px 0px var(--v2-alpha-dark-30), 0px 8px 16px 0px var(--v2-alpha-dark-30), 0px 0px 0px 0.5px var(--v2-alpha-light-16), 0px -0.5px 0px 0px var(--v2-alpha-light-6)",
+  "v2-elevation-button-neutral":
+    "0px 1px 2px 0px var(--v2-alpha-dark-40), 0px 0px 0px 0.5px var(--v2-alpha-light-20), 0px -0.5px 0px 0px var(--v2-alpha-light-10)",
+  "v2-elevation-button-contrast":
+    "0px 1px 2px 0px var(--v2-alpha-dark-40), 0px 0px 0px 0.5px var(--v2-alpha-light-40), inset 0px 0px 0px 0px var(--v2-alpha-light-0), inset 0px 0px 0px 0px var(--v2-alpha-light-0), 0px -0.5px 0px 0px var(--v2-alpha-light-30)",
+  "v2-elevation-elements": "0px 0.5px 0.5px 0px var(--v2-alpha-dark-40)",
+  "v2-elevation-switch-off":
+    "inset 0px -0.5px 0px 0px var(--v2-alpha-light-10), inset 0px 0px 0px 0px var(--v2-alpha-light-0), inset 0px 0px 0px 0.5px var(--v2-alpha-light-16)",
+  "v2-elevation-switch-on":
+    "inset 0px -0.5px 0px 0px var(--v2-alpha-light-10), inset 0px 0px 0px 0px var(--v2-alpha-light-0), inset 0px 0px 0px 0.5px var(--v2-alpha-light-16)",
+  "v2-illustration-illustration-layer-01": ref("v2-grey-900"),
+  "v2-illustration-illustration-layer-02": ref("v2-grey-800"),
+  "v2-illustration-illustration-layer-03": ref("v2-grey-700"),
+}
+
+export function mapV2Semantics(isDark: boolean): Record<string, V2ColorValue> {
+  return isDark ? dark : light
+}
+
+export function mergeV2Tokens(
+  ...layers: Record<string, V2ColorValue>[]
+): Record<string, V2ColorValue> {
+  return Object.assign({}, ...layers)
+}

+ 153 - 0
packages/ui/src/theme/v2/resolve.ts

@@ -0,0 +1,153 @@
+// @refresh reload
+
+import { generateNeutralScale, hexToOklch, oklchToHex, shift } from "../color"
+import { mapV2Foreground } from "./foreground"
+import { mapV2Semantics, mergeV2Tokens } from "./mapping"
+import type { DesktopTheme, HexColor, ResolvedV2Theme, ThemeVariant, V2ColorValue } from "../types"
+import { V2_PRIMITIVES_DEFAULT } from "./default-primitives"
+
+const V2_STEPS = [100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1100, 1200] as const
+
+interface PaletteInput {
+  neutral: HexColor
+  ink: HexColor
+  primary: HexColor
+  accent: HexColor
+  success: HexColor
+  warning: HexColor
+  error: HexColor
+  info: HexColor
+  interactive: HexColor
+  diffAdd: HexColor
+  diffDelete: HexColor
+}
+
+function clamp(v: number, min: number, max: number) {
+  return Math.max(min, Math.min(max, v))
+}
+
+/** v2 ramps: 100 = lightest, 1200 = darkest — wider spread than v1 `generateScale`. */
+function generateV2HueScale(seed: HexColor, isDark: boolean): HexColor[] {
+  const base = hexToOklch(seed)
+  const chromaBoost = isDark ? 1 : 1.05
+  const lightSteps = [
+    0.99,
+    0.965,
+    0.93,
+    0.885,
+    0.835,
+    clamp(base.l, 0.48, 0.72),
+    clamp(base.l - 0.07, 0.4, 0.64),
+    clamp(base.l - 0.14, 0.32, 0.55),
+    clamp(base.l - 0.21, 0.24, 0.46),
+    clamp(base.l - 0.28, 0.17, 0.38),
+    clamp(base.l - 0.34, 0.12, 0.3),
+    clamp(base.l - 0.4, 0.08, 0.22),
+  ]
+  const chromaMultipliers = [0.28, 0.48, 0.68, 0.86, 1.02, 1.28, 1.34, 1.28, 1.18, 1.08, 0.98, 0.88]
+
+  return lightSteps.map((l, i) =>
+    oklchToHex({
+      l,
+      c: base.c * chromaMultipliers[i]! * chromaBoost,
+      h: base.h,
+    }),
+  )
+}
+
+/** Grey ramp: 100 = lightest, 1200 = darkest. Derived from palette neutral → ink like v1. */
+function generateV2NeutralScale(neutral: HexColor, ink: HexColor, isDark: boolean): HexColor[] {
+  const scale = generateNeutralScale(neutral, isDark, ink)
+  return isDark ? scale.toReversed() : scale
+}
+
+function assignHueRamp(prefix: string, scale: HexColor[]): Record<string, V2ColorValue> {
+  const tokens: Record<string, V2ColorValue> = {}
+  for (let i = 0; i < V2_STEPS.length; i++) {
+    tokens[`v2-${prefix}-${V2_STEPS[i]}`] = scale[i]!
+  }
+  return tokens
+}
+
+function readPalette(variant: ThemeVariant): PaletteInput {
+  if ("palette" in variant && variant.palette) {
+    const palette = variant.palette
+    return {
+      neutral: palette.neutral,
+      ink: palette.ink,
+      primary: palette.primary,
+      accent: palette.accent ?? palette.info,
+      success: palette.success,
+      warning: palette.warning,
+      error: palette.error,
+      info: palette.info,
+      interactive: palette.interactive ?? palette.primary,
+      diffAdd: palette.diffAdd ?? shift(palette.success, { c: 0.55, l: 0.14 }),
+      diffDelete: palette.diffDelete ?? palette.error,
+    }
+  }
+  if ("seeds" in variant && variant.seeds) {
+    const seeds = variant.seeds
+    return {
+      neutral: seeds.neutral,
+      ink: seeds.neutral,
+      primary: seeds.primary,
+      accent: seeds.info,
+      success: seeds.success,
+      warning: seeds.warning,
+      error: seeds.error,
+      info: seeds.info,
+      interactive: seeds.interactive,
+      diffAdd: seeds.diffAdd,
+      diffDelete: seeds.diffDelete,
+    }
+  }
+  throw new Error("Theme variant requires `palette` or `seeds`")
+}
+
+/** Build v2 primitive ramps (100 = lightest). Alpha ramps are static in `v2/styles/colors.css`. */
+export function generateV2Primitives(variant: ThemeVariant, isDark: boolean): Record<string, V2ColorValue> {
+  const colors = readPalette(variant)
+  const grey = generateV2NeutralScale(colors.neutral, colors.ink, isDark)
+  const blue = generateV2HueScale(colors.interactive, isDark)
+  const green = generateV2HueScale(colors.success, isDark)
+  const yellow = generateV2HueScale(colors.warning, isDark)
+  const red = generateV2HueScale(colors.error, isDark)
+  const purple = generateV2HueScale(colors.accent, isDark)
+  const pink = generateV2HueScale(colors.info, isDark)
+  const orange = generateV2HueScale(shift(colors.warning, { h: -22, l: -0.082, c: 0.94 }), isDark)
+  const cyan = generateV2HueScale(shift(colors.info, { h: -12, l: 0.128, c: 1.12 }), isDark)
+
+  return {
+    ...V2_PRIMITIVES_DEFAULT,
+    ...assignHueRamp("grey", grey),
+    ...assignHueRamp("blue", blue),
+    ...assignHueRamp("green", green),
+    ...assignHueRamp("yellow", yellow),
+    ...assignHueRamp("red", red),
+    ...assignHueRamp("purple", purple),
+    ...assignHueRamp("pink", pink),
+    ...assignHueRamp("orange", orange),
+    ...assignHueRamp("cyan", cyan),
+  }
+}
+
+export function resolveThemeVariantV2(variant: ThemeVariant, isDark: boolean): ResolvedV2Theme {
+  const primitives = generateV2Primitives(variant, isDark)
+  const semantics = mapV2Semantics(isDark)
+  const foreground = mapV2Foreground(readPalette(variant).ink, isDark, variant.overrides)
+  return mergeV2Tokens(primitives, semantics, foreground, variant.v2Overrides ?? {})
+}
+
+export function resolveThemeV2(theme: DesktopTheme): { light: ResolvedV2Theme; dark: ResolvedV2Theme } {
+  return {
+    light: resolveThemeVariantV2(theme.light, false),
+    dark: resolveThemeVariantV2(theme.dark, true),
+  }
+}
+
+export function themeV2ToCss(tokens: ResolvedV2Theme): string {
+  return Object.entries(tokens)
+    .map(([key, value]) => `--${key}: ${value};`)
+    .join("\n  ")
+}

+ 3 - 2
packages/ui/src/v2/components/dialog-v2.css

@@ -7,7 +7,7 @@
   background-color: var(--v2-overlay-simple-overlay-scrim);
 }
 
-[data-component="dialog"] {
+[data-component="dialog-v2"] {
   position: fixed;
   inset: 0;
   z-index: 50;
@@ -25,7 +25,7 @@
     background: var(--v2-background-bg-layer-01);
     box-shadow: var(--v2-elevation-overlay);
     border-radius: 6px;
-    overflow: visible;
+    overflow: hidden;
     pointer-events: auto;
 
     [data-slot="dialog-content"] {
@@ -37,6 +37,7 @@
       max-height: 100%;
       flex: 1;
       overflow: auto;
+      background: transparent;
 
       scrollbar-width: none;
       -ms-overflow-style: none;

+ 1 - 1
packages/ui/src/v2/components/dialog-v2.stories.tsx

@@ -17,7 +17,7 @@ Dialog content wrapper built on Kobalte's dialog primitive with v2 styling.
 - Focus trapping and aria attributes provided by Kobalte Dialog.
 
 ### Theming/tokens
-- Uses \`data-component="dialog"\` and slot attributes.
+- Uses \`data-component="dialog-v2"\` and slot attributes.
 `
 
 export default {

+ 8 - 2
packages/ui/src/v2/components/dialog-v2.tsx

@@ -7,6 +7,7 @@ export interface DialogProps extends ParentProps {
   description?: JSXElement
   action?: JSXElement
   size?: "normal" | "large" | "x-large"
+  variant?: "default" | "settings"
   class?: ComponentProps<"div">["class"]
   classList?: ComponentProps<"div">["classList"]
   fit?: boolean
@@ -17,14 +18,19 @@ export function DialogFooter(props: ParentProps) {
 }
 
 export function Dialog(props: DialogProps) {
-  const [local] = splitProps(props, ["title", "description", "action", "size", "class", "classList", "fit", "children"])
+  const [local] = splitProps(props, ["title", "description", "action", "size", "variant", "class", "classList", "fit", "children"])
   const title = children(() => local.title)
   const description = children(() => local.description)
   const action = children(() => local.action)
   const hasHeader = () => title() || action()
 
   return (
-    <div data-component="dialog" data-fit={local.fit ? true : undefined} data-size={local.size || "normal"}>
+    <div
+      data-component="dialog-v2"
+      data-variant={local.variant === "settings" ? "settings" : undefined}
+      data-fit={local.fit ? true : undefined}
+      data-size={local.size || "normal"}
+    >
       <div data-slot="dialog-container">
         <Kobalte.Content
           data-slot="dialog-content"

+ 1 - 1
packages/ui/src/v2/components/project-avatar-v2.css

@@ -20,7 +20,7 @@
   letter-spacing: 0.05px;
   font-variant-numeric: tabular-nums;
   text-transform: uppercase;
-  color: var(--v2-grey-100);
+  color: var(--v2-avatar-fg);
   text-shadow: 0 0 4px var(--v2-alpha-dark-20);
   user-select: none;
   -webkit-user-select: none;

+ 58 - 55
packages/ui/src/v2/styles/theme.css

@@ -63,25 +63,26 @@
     --v2-state-fg-info: var(--v2-blue-800);
     --v2-state-border-info: var(--v2-blue-300);
 
-    /* ── Project avatar ── */
-    --v2-avatar-bg-orange: var(--v2-orange-700);
-    --v2-avatar-border-orange: var(--v2-orange-800);
-    --v2-avatar-bg-yellow: var(--v2-yellow-700);
-    --v2-avatar-border-yellow: var(--v2-yellow-800);
-    --v2-avatar-bg-cyan: var(--v2-cyan-700);
-    --v2-avatar-border-cyan: var(--v2-cyan-800);
-    --v2-avatar-bg-green: var(--v2-green-700);
-    --v2-avatar-border-green: var(--v2-green-800);
-    --v2-avatar-bg-red: var(--v2-red-700);
-    --v2-avatar-border-red: var(--v2-red-800);
-    --v2-avatar-bg-pink: var(--v2-pink-700);
-    --v2-avatar-border-pink: var(--v2-pink-800);
-    --v2-avatar-bg-blue: var(--v2-blue-700);
-    --v2-avatar-border-blue: var(--v2-blue-800);
-    --v2-avatar-bg-purple: var(--v2-purple-700);
-    --v2-avatar-border-purple: var(--v2-purple-800);
-    --v2-avatar-bg-gray: var(--v2-grey-700);
-    --v2-avatar-border-gray: var(--v2-grey-800);
+    /* ── Project avatar (fixed; theme-independent) ── */
+    --v2-avatar-fg: #ffffffff;
+    --v2-avatar-bg-orange: #ee7330ff;
+    --v2-avatar-border-orange: #d16427ff;
+    --v2-avatar-bg-yellow: #e7af36ff;
+    --v2-avatar-border-yellow: #cb9f34ff;
+    --v2-avatar-bg-cyan: #0096b8ff;
+    --v2-avatar-border-cyan: #007d9bff;
+    --v2-avatar-bg-green: #2eaf5aff;
+    --v2-avatar-border-green: #198b43ff;
+    --v2-avatar-bg-red: #d92e3cff;
+    --v2-avatar-border-red: #b82d35ff;
+    --v2-avatar-bg-pink: #e4429eff;
+    --v2-avatar-border-pink: #c83d8bff;
+    --v2-avatar-bg-blue: #3250dfff;
+    --v2-avatar-border-blue: #2c47c8ff;
+    --v2-avatar-bg-purple: #623be2ff;
+    --v2-avatar-border-purple: #5230c2ff;
+    --v2-avatar-bg-gray: #5c5c5cff;
+    --v2-avatar-border-gray: #3a3a3aff;
 
     /* ── Elevation ── */
     --v2-elevation-raised:
@@ -306,24 +307,25 @@
     --v2-illustration-illustration-layer-02: var(--v2-grey-400);
     --v2-illustration-illustration-layer-03: var(--v2-grey-500);
 
-    --v2-avatar-bg-orange: var(--v2-orange-700);
-    --v2-avatar-border-orange: var(--v2-orange-800);
-    --v2-avatar-bg-yellow: var(--v2-yellow-700);
-    --v2-avatar-border-yellow: var(--v2-yellow-800);
-    --v2-avatar-bg-cyan: var(--v2-cyan-700);
-    --v2-avatar-border-cyan: var(--v2-cyan-800);
-    --v2-avatar-bg-green: var(--v2-green-700);
-    --v2-avatar-border-green: var(--v2-green-800);
-    --v2-avatar-bg-red: var(--v2-red-700);
-    --v2-avatar-border-red: var(--v2-red-800);
-    --v2-avatar-bg-pink: var(--v2-pink-700);
-    --v2-avatar-border-pink: var(--v2-pink-800);
-    --v2-avatar-bg-blue: var(--v2-blue-700);
-    --v2-avatar-border-blue: var(--v2-blue-800);
-    --v2-avatar-bg-purple: var(--v2-purple-700);
-    --v2-avatar-border-purple: var(--v2-purple-800);
-    --v2-avatar-bg-gray: var(--v2-grey-700);
-    --v2-avatar-border-gray: var(--v2-grey-800);
+    --v2-avatar-fg: #ffffffff;
+    --v2-avatar-bg-orange: #ee7330ff;
+    --v2-avatar-border-orange: #d16427ff;
+    --v2-avatar-bg-yellow: #e7af36ff;
+    --v2-avatar-border-yellow: #cb9f34ff;
+    --v2-avatar-bg-cyan: #0096b8ff;
+    --v2-avatar-border-cyan: #007d9bff;
+    --v2-avatar-bg-green: #2eaf5aff;
+    --v2-avatar-border-green: #198b43ff;
+    --v2-avatar-bg-red: #d92e3cff;
+    --v2-avatar-border-red: #b82d35ff;
+    --v2-avatar-bg-pink: #e4429eff;
+    --v2-avatar-border-pink: #c83d8bff;
+    --v2-avatar-bg-blue: #3250dfff;
+    --v2-avatar-border-blue: #2c47c8ff;
+    --v2-avatar-bg-purple: #623be2ff;
+    --v2-avatar-border-purple: #5230c2ff;
+    --v2-avatar-bg-gray: #5c5c5cff;
+    --v2-avatar-border-gray: #3a3a3aff;
   }
 
   /* Explicit dark mode via data attribute (Storybook toggle, runtime JS) */
@@ -385,24 +387,25 @@
     --v2-state-fg-info: var(--v2-blue-500);
     --v2-state-border-info: var(--v2-blue-900);
 
-    --v2-avatar-bg-orange: var(--v2-orange-1100);
-    --v2-avatar-border-orange: var(--v2-orange-600);
-    --v2-avatar-bg-yellow: var(--v2-yellow-1100);
-    --v2-avatar-border-yellow: var(--v2-yellow-700);
-    --v2-avatar-bg-cyan: var(--v2-cyan-1000);
-    --v2-avatar-border-cyan: var(--v2-cyan-700);
-    --v2-avatar-bg-green: var(--v2-green-1000);
-    --v2-avatar-border-green: var(--v2-green-600);
-    --v2-avatar-bg-red: var(--v2-red-1000);
-    --v2-avatar-border-red: var(--v2-red-700);
-    --v2-avatar-bg-pink: var(--v2-pink-1000);
-    --v2-avatar-border-pink: var(--v2-pink-700);
-    --v2-avatar-bg-blue: var(--v2-blue-900);
-    --v2-avatar-border-blue: var(--v2-blue-500);
-    --v2-avatar-bg-purple: var(--v2-purple-1000);
-    --v2-avatar-border-purple: var(--v2-purple-600);
-    --v2-avatar-bg-gray: var(--v2-grey-700);
-    --v2-avatar-border-gray: var(--v2-grey-500);
+    --v2-avatar-fg: #ffffffff;
+    --v2-avatar-bg-orange: #723d22ff;
+    --v2-avatar-border-orange: #ff8648ff;
+    --v2-avatar-bg-yellow: #68552bff;
+    --v2-avatar-border-yellow: #e7af36ff;
+    --v2-avatar-bg-cyan: #005a6eff;
+    --v2-avatar-border-cyan: #0096b8ff;
+    --v2-avatar-bg-green: #196130ff;
+    --v2-avatar-border-green: #49c970ff;
+    --v2-avatar-bg-red: #7a1f23ff;
+    --v2-avatar-border-red: #d92e3cff;
+    --v2-avatar-bg-pink: #8c2d61ff;
+    --v2-avatar-border-pink: #e4429eff;
+    --v2-avatar-bg-blue: #263fa9ff;
+    --v2-avatar-border-blue: #7698fdff;
+    --v2-avatar-bg-purple: #361f83ff;
+    --v2-avatar-border-purple: #7152f4ff;
+    --v2-avatar-bg-gray: #5c5c5cff;
+    --v2-avatar-border-gray: #aeaeaeff;
 
     --v2-elevation-raised:
       0px 2px 4px 0px var(--v2-alpha-dark-30), 0px 1px 2px 0px var(--v2-alpha-dark-30),