Adopt WorkClaw's shadcn/ui Neutral token contract (Tailwind v4)
Replace the hand-rolled @theme color block with a real shadcn init shape: bare :root (light) + .dark (dark) tokens mapped to --color-* via @theme inline, plus the missing semantic/chart/radius tokens. --accent is now neutral (#262626) to match WorkClaw; the ~16 coral-via-accent usages migrate to the dedicated `coral` brand token so they stay coral. Adds cn() (clsx + tailwind-merge) and components.json so future `npx shadcn add` works. Clawmates-specific tokens (coral brand, marketing palette, measured shadows, motion, layout, type scale) are preserved verbatim in a separate @theme block. Root <html> gains `dark` (app/login read .dark; marketing stays light via explicit colors). We do NOT add shadcn's Button/Card/etc. — they'd conflict with the existing measured-to-WorkClaw components. Verified value-preserving: typecheck/lint/86 unit/31 E2E all green, and all 6 visual baselines pass with no diff. Compiled + live CSS confirm bg-accent→#262626 (neutral), text-coral→#f96565, chart tokens present. Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
e6f50c7ecb
commit
5ff2b2ca33
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://ui.shadcn.com/schema.json",
|
||||||
|
"style": "new-york",
|
||||||
|
"rsc": true,
|
||||||
|
"tsx": true,
|
||||||
|
"tailwind": {
|
||||||
|
"config": "",
|
||||||
|
"css": "src/app/globals.css",
|
||||||
|
"baseColor": "neutral",
|
||||||
|
"cssVariables": true,
|
||||||
|
"prefix": ""
|
||||||
|
},
|
||||||
|
"iconLibrary": "lucide",
|
||||||
|
"aliases": {
|
||||||
|
"components": "@/components",
|
||||||
|
"utils": "@/lib/utils",
|
||||||
|
"ui": "@/components/ui",
|
||||||
|
"lib": "@/lib",
|
||||||
|
"hooks": "@/hooks"
|
||||||
|
}
|
||||||
|
}
|
||||||
Generated
+34
@@ -10,12 +10,15 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@clerk/nextjs": "^7.5.0",
|
"@clerk/nextjs": "^7.5.0",
|
||||||
"@tanstack/react-query": "^5.101.0",
|
"@tanstack/react-query": "^5.101.0",
|
||||||
|
"class-variance-authority": "^0.7.1",
|
||||||
|
"clsx": "^2.1.1",
|
||||||
"geist": "^1.7.2",
|
"geist": "^1.7.2",
|
||||||
"lucide-react": "^1.17.0",
|
"lucide-react": "^1.17.0",
|
||||||
"next": "16.2.9",
|
"next": "16.2.9",
|
||||||
"nuqs": "^2.8.9",
|
"nuqs": "^2.8.9",
|
||||||
"react": "19.2.4",
|
"react": "19.2.4",
|
||||||
"react-dom": "19.2.4",
|
"react-dom": "19.2.4",
|
||||||
|
"tailwind-merge": "^3.6.0",
|
||||||
"zod": "^4.4.3"
|
"zod": "^4.4.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -3480,12 +3483,33 @@
|
|||||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/class-variance-authority": {
|
||||||
|
"version": "0.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz",
|
||||||
|
"integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"clsx": "^2.1.1"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://polar.sh/cva"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/client-only": {
|
"node_modules/client-only": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
|
||||||
"integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
|
"integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/clsx": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/color-convert": {
|
"node_modules/color-convert": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
@@ -7355,6 +7379,16 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tailwind-merge": {
|
||||||
|
"version": "3.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.6.0.tgz",
|
||||||
|
"integrity": "sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/dcastil"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/tailwindcss": {
|
"node_modules/tailwindcss": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.0.tgz",
|
||||||
|
|||||||
@@ -13,12 +13,15 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@clerk/nextjs": "^7.5.0",
|
"@clerk/nextjs": "^7.5.0",
|
||||||
"@tanstack/react-query": "^5.101.0",
|
"@tanstack/react-query": "^5.101.0",
|
||||||
|
"class-variance-authority": "^0.7.1",
|
||||||
|
"clsx": "^2.1.1",
|
||||||
"geist": "^1.7.2",
|
"geist": "^1.7.2",
|
||||||
"lucide-react": "^1.17.0",
|
"lucide-react": "^1.17.0",
|
||||||
"next": "16.2.9",
|
"next": "16.2.9",
|
||||||
"nuqs": "^2.8.9",
|
"nuqs": "^2.8.9",
|
||||||
"react": "19.2.4",
|
"react": "19.2.4",
|
||||||
"react-dom": "19.2.4",
|
"react-dom": "19.2.4",
|
||||||
|
"tailwind-merge": "^3.6.0",
|
||||||
"zod": "^4.4.3"
|
"zod": "^4.4.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
+109
-25
@@ -1,47 +1,132 @@
|
|||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
@import "../styles/motion.css";
|
@import "../styles/motion.css";
|
||||||
|
|
||||||
/* Design tokens — merged from the reverse-engineered reference system
|
/* shadcn/ui "dark" class variant (the app runs under .dark; marketing is a
|
||||||
(docs/tokens.css, measured from live DOM) on top of spec §2. Dark-only
|
separate explicit-light scope). */
|
||||||
app scheme; coral brand accent. Values are spec-verbatim; aliases keep
|
@custom-variant dark (&:is(.dark *));
|
||||||
existing component class names stable. */
|
|
||||||
|
/* ───────────────────────────────────────────────────────────────────────
|
||||||
|
shadcn/ui Neutral base preset (bare tokens). The app reads `.dark`; the
|
||||||
|
light `:root` palette is kept for contract parity. `@theme inline` below
|
||||||
|
maps these to the --color-* utilities (bg-background, text-accent, …).
|
||||||
|
--accent is the neutral subtle surface (the coral brand lives in the
|
||||||
|
separate --color-coral* tokens).
|
||||||
|
─────────────────────────────────────────────────────────────────────── */
|
||||||
|
:root {
|
||||||
|
--radius: 0.5rem;
|
||||||
|
|
||||||
|
--background: #fff;
|
||||||
|
--foreground: #0a0a0a;
|
||||||
|
--card: #fff;
|
||||||
|
--card-foreground: #0a0a0a;
|
||||||
|
--popover: #fff;
|
||||||
|
--popover-foreground: #0a0a0a;
|
||||||
|
--primary: #171717;
|
||||||
|
--primary-foreground: #fff;
|
||||||
|
--secondary: #f5f5f5;
|
||||||
|
--secondary-foreground: #171717;
|
||||||
|
--muted: #f5f5f5;
|
||||||
|
--muted-foreground: #737373;
|
||||||
|
--accent: #f5f5f5;
|
||||||
|
--accent-foreground: #171717;
|
||||||
|
--destructive: #ef4444;
|
||||||
|
--destructive-foreground: #fafafa;
|
||||||
|
--border: #e5e5e5;
|
||||||
|
--input: #e5e5e5;
|
||||||
|
--ring: #171717;
|
||||||
|
--chart-1: #3c83f6;
|
||||||
|
--chart-2: #2a9d90;
|
||||||
|
--chart-3: #274754;
|
||||||
|
--chart-4: #e8c468;
|
||||||
|
--chart-5: #f4a462;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
--background: #0a0a0a;
|
||||||
|
--foreground: #fafafa;
|
||||||
|
--card: #0a0a0a;
|
||||||
|
--card-foreground: #fafafa;
|
||||||
|
--popover: #0a0a0a;
|
||||||
|
--popover-foreground: #fafafa;
|
||||||
|
--primary: #fafafa;
|
||||||
|
--primary-foreground: #171717;
|
||||||
|
--secondary: #262626;
|
||||||
|
--secondary-foreground: #fafafa;
|
||||||
|
--muted: #262626;
|
||||||
|
--muted-foreground: #a3a3a3;
|
||||||
|
--accent: #262626;
|
||||||
|
--accent-foreground: #fafafa;
|
||||||
|
--destructive: #7f1d1d;
|
||||||
|
--destructive-foreground: #fafafa;
|
||||||
|
--border: #262626;
|
||||||
|
--input: #262626;
|
||||||
|
--ring: #fafafa;
|
||||||
|
--chart-1: #2662d9;
|
||||||
|
--chart-2: #2eb88a;
|
||||||
|
--chart-3: #e88c30;
|
||||||
|
--chart-4: #af57db;
|
||||||
|
--chart-5: #e23670;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Map the bare tokens into Tailwind's --color-* / radius utilities. */
|
||||||
|
@theme inline {
|
||||||
|
--color-background: var(--background);
|
||||||
|
--color-foreground: var(--foreground);
|
||||||
|
--color-card: var(--card);
|
||||||
|
--color-card-foreground: var(--card-foreground);
|
||||||
|
--color-popover: var(--popover);
|
||||||
|
--color-popover-foreground: var(--popover-foreground);
|
||||||
|
--color-primary: var(--primary);
|
||||||
|
--color-primary-foreground: var(--primary-foreground);
|
||||||
|
--color-secondary: var(--secondary);
|
||||||
|
--color-secondary-foreground: var(--secondary-foreground);
|
||||||
|
--color-muted: var(--muted);
|
||||||
|
--color-muted-foreground: var(--muted-foreground);
|
||||||
|
--color-accent: var(--accent);
|
||||||
|
--color-accent-foreground: var(--accent-foreground);
|
||||||
|
--color-destructive: var(--destructive);
|
||||||
|
--color-destructive-foreground: var(--destructive-foreground);
|
||||||
|
--color-border: var(--border);
|
||||||
|
--color-input: var(--input);
|
||||||
|
--color-ring: var(--ring);
|
||||||
|
--color-chart-1: var(--chart-1);
|
||||||
|
--color-chart-2: var(--chart-2);
|
||||||
|
--color-chart-3: var(--chart-3);
|
||||||
|
--color-chart-4: var(--chart-4);
|
||||||
|
--color-chart-5: var(--chart-5);
|
||||||
|
|
||||||
|
--radius-sm: calc(var(--radius) - 4px);
|
||||||
|
--radius-md: calc(var(--radius) - 2px);
|
||||||
|
--radius-lg: var(--radius);
|
||||||
|
--radius-xl: calc(var(--radius) + 4px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ───────────────────────────────────────────────────────────────────────
|
||||||
|
Clawmates-specific tokens (not part of the shadcn contract): the coral
|
||||||
|
brand, raised surfaces, marketing light palette, measured shadows, motion
|
||||||
|
curves, layout spacing, type scale, custom radii, fonts. Values verbatim
|
||||||
|
from the reverse-engineered reference system.
|
||||||
|
─────────────────────────────────────────────────────────────────────── */
|
||||||
@theme {
|
@theme {
|
||||||
/* ---- Color: function-grouped ---- */
|
/* raised surfaces + extra foregrounds */
|
||||||
--color-background: #0a0a0a;
|
|
||||||
--color-foreground: #fafafa;
|
|
||||||
--color-card: #0a0a0a;
|
|
||||||
--color-popover: #0a0a0a;
|
|
||||||
--color-surface-warm: #141414; /* raised tier 1 */
|
--color-surface-warm: #141414; /* raised tier 1 */
|
||||||
--color-surface-warm-muted: #1a1a1a; /* raised tier 2 */
|
--color-surface-warm-muted: #1a1a1a; /* raised tier 2 */
|
||||||
--color-subtle: #141414;
|
--color-subtle: #141414;
|
||||||
--color-hover-bg: #171717; /* generic hover fill */
|
--color-hover-bg: #171717; /* generic hover fill */
|
||||||
--color-divider-subtle: #1a1a1a;
|
--color-divider-subtle: #1a1a1a;
|
||||||
--color-border: #262626;
|
|
||||||
--color-input: #262626;
|
|
||||||
--color-muted: #262626;
|
|
||||||
--color-secondary: #262626;
|
|
||||||
--color-muted-foreground: #a3a3a3;
|
|
||||||
--color-subtle-foreground: #737373;
|
--color-subtle-foreground: #737373;
|
||||||
|
|
||||||
/* brand accent (coral) + variants; --color-accent is our legacy alias */
|
/* brand accent (coral) + variants */
|
||||||
--color-coral: #f96565;
|
--color-coral: #f96565;
|
||||||
--color-accent: #f96565;
|
|
||||||
--color-coral-light: #fa7575;
|
--color-coral-light: #fa7575;
|
||||||
--color-coral-dark: #e85555;
|
--color-coral-dark: #e85555;
|
||||||
--color-cream: #f5f5f5; /* cream primary button fill */
|
--color-cream: #f5f5f5; /* cream primary button fill */
|
||||||
--color-primary: #fafafa;
|
|
||||||
--color-primary-foreground: #171717;
|
|
||||||
--color-ring: #fafafa;
|
|
||||||
--color-focus-ring: #f5f5f5;
|
--color-focus-ring: #f5f5f5;
|
||||||
|
|
||||||
/* chat bubbles (measured) */
|
/* chat bubbles (measured) */
|
||||||
--color-bubble-user: #1a1a1a;
|
--color-bubble-user: #1a1a1a;
|
||||||
--color-bubble-user-text: #d4d4d4;
|
--color-bubble-user-text: #d4d4d4;
|
||||||
|
|
||||||
/* semantic */
|
|
||||||
--color-destructive: #7f1d1d;
|
|
||||||
--color-destructive-foreground: #fafafa;
|
|
||||||
|
|
||||||
/* gradient stops (coral tile) */
|
/* gradient stops (coral tile) */
|
||||||
--color-gradient-coral-top: #fa7575;
|
--color-gradient-coral-top: #fa7575;
|
||||||
--color-gradient-coral-bottom: #e85555;
|
--color-gradient-coral-bottom: #e85555;
|
||||||
@@ -54,8 +139,7 @@
|
|||||||
--text-xxxl: 36px;
|
--text-xxxl: 36px;
|
||||||
--text-xxxl--line-height: 44px;
|
--text-xxxl--line-height: 44px;
|
||||||
|
|
||||||
/* ---- Radius scale ---- */
|
/* ---- Custom radii ---- */
|
||||||
--radius: 0.5rem;
|
|
||||||
--radius-squircle: 17px; /* 48px claw avatars */
|
--radius-squircle: 17px; /* 48px claw avatars */
|
||||||
--radius-button: 9999px; /* pill buttons + nav items */
|
--radius-button: 9999px; /* pill buttons + nav items */
|
||||||
--radius-toggle: 8px;
|
--radius-toggle: 8px;
|
||||||
|
|||||||
@@ -43,7 +43,10 @@ export default function RootLayout({
|
|||||||
children,
|
children,
|
||||||
}: Readonly<{ children: React.ReactNode }>) {
|
}: Readonly<{ children: React.ReactNode }>) {
|
||||||
return (
|
return (
|
||||||
<html lang="en" className={`${geist.variable} ${geistMono.variable} h-full`}>
|
<html
|
||||||
|
lang="en"
|
||||||
|
className={`dark ${geist.variable} ${geistMono.variable} h-full`}
|
||||||
|
>
|
||||||
{/* No `antialiased` (-webkit-font-smoothing: antialiased) — it inherits
|
{/* No `antialiased` (-webkit-font-smoothing: antialiased) — it inherits
|
||||||
everywhere and renders text thinner/lighter than the reference. The
|
everywhere and renders text thinner/lighter than the reference. The
|
||||||
browser default (`auto`) gives the crisper/heavier weight WorkClaw
|
browser default (`auto`) gives the crisper/heavier weight WorkClaw
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ export function LoginForm() {
|
|||||||
type="email"
|
type="email"
|
||||||
required
|
required
|
||||||
autoComplete="email"
|
autoComplete="email"
|
||||||
className="rounded-(--radius) border border-input bg-subtle px-3 py-2 text-sm text-foreground outline-none focus:border-accent"
|
className="rounded-(--radius) border border-input bg-subtle px-3 py-2 text-sm text-foreground outline-none focus:border-coral"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<label className="flex flex-col gap-1 text-xs text-muted-foreground">
|
<label className="flex flex-col gap-1 text-xs text-muted-foreground">
|
||||||
@@ -54,18 +54,18 @@ export function LoginForm() {
|
|||||||
type="password"
|
type="password"
|
||||||
required
|
required
|
||||||
autoComplete="current-password"
|
autoComplete="current-password"
|
||||||
className="rounded-(--radius) border border-input bg-subtle px-3 py-2 text-sm text-foreground outline-none focus:border-accent"
|
className="rounded-(--radius) border border-input bg-subtle px-3 py-2 text-sm text-foreground outline-none focus:border-coral"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
{failed && (
|
{failed && (
|
||||||
<p role="alert" className="text-xs text-accent">
|
<p role="alert" className="text-xs text-coral">
|
||||||
Invalid email or password.
|
Invalid email or password.
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={pending}
|
disabled={pending}
|
||||||
className="mt-1 rounded-(--radius-button) bg-accent px-4 py-2 text-sm font-medium text-background shadow-(--shadow-cta) hover:bg-coral-light disabled:opacity-50"
|
className="mt-1 rounded-(--radius-button) bg-coral px-4 py-2 text-sm font-medium text-background shadow-(--shadow-cta) hover:bg-coral-light disabled:opacity-50"
|
||||||
>
|
>
|
||||||
{pending ? "Signing in…" : "Sign in"}
|
{pending ? "Signing in…" : "Sign in"}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -51,12 +51,12 @@ function AgentMessage({
|
|||||||
{message.status === "streaming" && (
|
{message.status === "streaming" && (
|
||||||
<span
|
<span
|
||||||
aria-hidden
|
aria-hidden
|
||||||
className="ml-0.5 inline-block h-4 w-0.5 translate-y-0.5 bg-accent motion-safe:animate-[caret-blink_1s_steps(1)_infinite]"
|
className="ml-0.5 inline-block h-4 w-0.5 translate-y-0.5 bg-coral motion-safe:animate-[caret-blink_1s_steps(1)_infinite]"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
{message.status === "error" && (
|
{message.status === "error" && (
|
||||||
<p role="alert" className="pt-1 text-xs text-accent">
|
<p role="alert" className="pt-1 text-xs text-coral">
|
||||||
Something went wrong with this reply.
|
Something went wrong with this reply.
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ export function StepTrace({ steps }: { steps: UiStep[] }) {
|
|||||||
<span
|
<span
|
||||||
className={
|
className={
|
||||||
step.status === "error"
|
step.status === "error"
|
||||||
? "text-accent"
|
? "text-coral"
|
||||||
: step.status === "running"
|
: step.status === "running"
|
||||||
? "text-muted-foreground motion-safe:animate-[pulse_1.5s_ease-in-out_infinite]"
|
? "text-muted-foreground motion-safe:animate-[pulse_1.5s_ease-in-out_infinite]"
|
||||||
: "text-foreground"
|
: "text-foreground"
|
||||||
|
|||||||
@@ -39,12 +39,12 @@ export function PromoRedeem() {
|
|||||||
required
|
required
|
||||||
aria-label="Promo code"
|
aria-label="Promo code"
|
||||||
placeholder="PROMOCODE"
|
placeholder="PROMOCODE"
|
||||||
className="min-w-0 flex-1 rounded-(--radius) border border-input bg-background px-3 py-1.5 font-mono text-xs uppercase outline-none focus:border-accent"
|
className="min-w-0 flex-1 rounded-(--radius) border border-input bg-background px-3 py-1.5 font-mono text-xs uppercase outline-none focus:border-coral"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={pending}
|
disabled={pending}
|
||||||
className="rounded-(--radius-button) bg-accent px-4 py-1.5 text-xs font-medium text-background hover:bg-coral-light disabled:opacity-50"
|
className="rounded-(--radius-button) bg-coral px-4 py-1.5 text-xs font-medium text-background hover:bg-coral-light disabled:opacity-50"
|
||||||
>
|
>
|
||||||
Redeem
|
Redeem
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -43,9 +43,9 @@ export function ApprovalCard({ approval, onDecide, agentName }: ApprovalCardProp
|
|||||||
return (
|
return (
|
||||||
<section
|
<section
|
||||||
aria-label="Review and approve"
|
aria-label="Review and approve"
|
||||||
className="mt-2 max-w-md rounded-(--radius) border border-accent/40 bg-surface-warm p-3 shadow-(--shadow-card) motion-safe:animate-[scale-in_var(--duration-fast)_var(--ease-app)]"
|
className="mt-2 max-w-md rounded-(--radius) border border-coral/40 bg-surface-warm p-3 shadow-(--shadow-card) motion-safe:animate-[scale-in_var(--duration-fast)_var(--ease-app)]"
|
||||||
>
|
>
|
||||||
<p className="text-xs font-semibold uppercase tracking-wide text-accent">
|
<p className="text-xs font-semibold uppercase tracking-wide text-coral">
|
||||||
Review & approve
|
Review & approve
|
||||||
</p>
|
</p>
|
||||||
<p className="pt-1 text-sm">
|
<p className="pt-1 text-sm">
|
||||||
@@ -76,7 +76,7 @@ export function ApprovalCard({ approval, onDecide, agentName }: ApprovalCardProp
|
|||||||
type="button"
|
type="button"
|
||||||
disabled={pending}
|
disabled={pending}
|
||||||
onClick={() => decide("approve")}
|
onClick={() => decide("approve")}
|
||||||
className="rounded-(--radius-button) bg-accent px-4 py-1.5 text-xs font-medium text-background shadow-(--shadow-cta) hover:bg-coral-light disabled:opacity-50"
|
className="rounded-(--radius-button) bg-coral px-4 py-1.5 text-xs font-medium text-background shadow-(--shadow-cta) hover:bg-coral-light disabled:opacity-50"
|
||||||
>
|
>
|
||||||
Approve
|
Approve
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ export function SessionsColumn({
|
|||||||
placeholder="Search"
|
placeholder="Search"
|
||||||
value={query}
|
value={query}
|
||||||
onChange={(e) => setQuery(e.target.value)}
|
onChange={(e) => setQuery(e.target.value)}
|
||||||
className="w-full rounded-(--radius) border border-input bg-background px-2 py-1.5 text-xs outline-none focus:border-accent"
|
className="w-full rounded-(--radius) border border-input bg-background px-2 py-1.5 text-xs outline-none focus:border-coral"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<ul aria-label="Sessions" className="flex-1 overflow-y-auto px-2">
|
<ul aria-label="Sessions" className="flex-1 overflow-y-auto px-2">
|
||||||
@@ -46,7 +46,7 @@ export function SessionsColumn({
|
|||||||
aria-current={active ? "true" : undefined}
|
aria-current={active ? "true" : undefined}
|
||||||
onClick={() => onOpen(session)}
|
onClick={() => onOpen(session)}
|
||||||
className={`w-full rounded-(--radius) border-l-2 px-2 py-2 text-left hover:bg-surface-warm ${
|
className={`w-full rounded-(--radius) border-l-2 px-2 py-2 text-left hover:bg-surface-warm ${
|
||||||
active ? "border-accent bg-surface-warm" : "border-transparent"
|
active ? "border-coral bg-surface-warm" : "border-transparent"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<span className="block truncate text-xs text-foreground">
|
<span className="block truncate text-xs text-foreground">
|
||||||
@@ -64,7 +64,7 @@ export function SessionsColumn({
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={onNewSession}
|
onClick={onNewSession}
|
||||||
className="w-full rounded-(--radius-button) border border-border px-3 py-1.5 text-xs text-muted-foreground hover:border-accent hover:text-foreground"
|
className="w-full rounded-(--radius-button) border border-border px-3 py-1.5 text-xs text-muted-foreground hover:border-coral hover:text-foreground"
|
||||||
>
|
>
|
||||||
+ New session
|
+ New session
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ export function Avatar({
|
|||||||
<span
|
<span
|
||||||
aria-hidden
|
aria-hidden
|
||||||
className={`inline-flex items-center justify-center font-medium text-background ${radius} ${SIZES[size]}`}
|
className={`inline-flex items-center justify-center font-medium text-background ${radius} ${SIZES[size]}`}
|
||||||
style={{ backgroundColor: accent || "var(--color-accent)" }}
|
style={{ backgroundColor: accent || "var(--color-coral)" }}
|
||||||
>
|
>
|
||||||
{initials}
|
{initials}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -272,7 +272,7 @@ export function CreateClawForm() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{step === "slack" && confirming && (
|
{step === "slack" && confirming && (
|
||||||
<div className="rounded-(--radius) border border-accent/40 bg-surface-warm p-4">
|
<div className="rounded-(--radius) border border-coral/40 bg-surface-warm p-4">
|
||||||
<p className="text-sm font-medium">Create {draft.name}?</p>
|
<p className="text-sm font-medium">Create {draft.name}?</p>
|
||||||
<p className="pt-1 text-xs text-muted-foreground">
|
<p className="pt-1 text-xs text-muted-foreground">
|
||||||
{draft.jobTitle} ·{" "}
|
{draft.jobTitle} ·{" "}
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import { clsx, type ClassValue } from "clsx";
|
||||||
|
import { twMerge } from "tailwind-merge";
|
||||||
|
|
||||||
|
/** shadcn's class-merge helper: clsx for conditionals, tailwind-merge to
|
||||||
|
* dedupe conflicting Tailwind utilities. */
|
||||||
|
export function cn(...inputs: ClassValue[]) {
|
||||||
|
return twMerge(clsx(inputs));
|
||||||
|
}
|
||||||
@@ -78,8 +78,8 @@
|
|||||||
to { transform: translateX(0); opacity: 1; }
|
to { transform: translateX(0); opacity: 1; }
|
||||||
}
|
}
|
||||||
@keyframes toolAccentPulse {
|
@keyframes toolAccentPulse {
|
||||||
0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--color-accent) 45%, transparent); }
|
0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--color-coral) 45%, transparent); }
|
||||||
50% { box-shadow: 0 0 0 4px color-mix(in srgb, var(--color-accent) 12%, transparent); }
|
50% { box-shadow: 0 0 0 4px color-mix(in srgb, var(--color-coral) 12%, transparent); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Provisioning (new-agent spin-up), spec §3. */
|
/* Provisioning (new-agent spin-up), spec §3. */
|
||||||
|
|||||||
Reference in New Issue
Block a user