refactor(ui): revert demo-only decoration tokens to inline literals (B1 middle path)

DECISION-SHEET.md B1 user ruling: first-party intentional one-off
decoration on demo/UX-lab surfaces stays inline; production decoration
tokens stay tokens. Reverted 27 singleton tokens whose only consumers
are *UxLab.tsx pages — 19 gradients (--gradient-extract-5,6,8-24) and 8
shadows (--shadow-extract-15,16,17,19-23) — restoring the original
bracket-literal utilities (verified byte-exact against pre-extraction
commit 38532283e) at 35 call sites, deleting their index.css
definitions, allowlisting the 4 UxLab pages for check-token-gates, and
extending the allowlist criteria doc-comment. KEPT the 22
production-consumed tokens (gradients 1,2,3,4,7,25,26 + 15 shadows);
--shadow-extract-4/5 kept because ChatComposer.tsx and IssueChatThread.tsx
consume them in production, not just ChatComposer.test.tsx. Zero
rendered-output change (visual suite 510/510).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
scotttong 2026-07-06 16:42:01 -07:00
parent 47778b233f
commit 98429ed6bc
5 changed files with 60 additions and 58 deletions

View File

@ -1470,26 +1470,7 @@ span.paperclip-mention-chip[data-mention-kind="external-object"] {
--gradient-extract-2: linear-gradient(180deg,rgba(255,255,255,0.05),rgba(255,255,255,0.02)); /* Costs.tsx subtle white card gradient. */
--gradient-extract-3: radial-gradient(circle at top left,rgba(244,114,182,0.08),transparent 35%),radial-gradient(circle at bottom right,rgba(56,189,248,0.1),transparent 32%); /* AccountingModelCard.tsx decorative overlay. */
--gradient-extract-4: linear-gradient(180deg,rgba(255,70,70,0.10),rgba(255,255,255,0.02)); /* BudgetIncidentCard.tsx incident-card gradient. */
--gradient-extract-5: radial-gradient(circle at top left,rgba(8,145,178,0.08),transparent 36%),radial-gradient(circle at bottom right,rgba(245,158,11,0.10),transparent 28%); /* RunTranscriptUxLab.tsx:78 hero gradient. */
--gradient-extract-6: linear-gradient(135deg,rgba(8,145,178,0.08),transparent 28%),linear-gradient(180deg,rgba(245,158,11,0.08),transparent 40%),var(--background); /* RunTranscriptUxLab.tsx:203 hero-card gradient. */
--gradient-extract-7: radial-gradient(circle at top right,rgba(255,255,255,0.22),transparent 34%),radial-gradient(circle at bottom left,rgba(255,255,255,0.08),transparent 36%); /* ProfileSettings.tsx:162 decorative overlay. */
--gradient-extract-8: radial-gradient(circle at top,rgba(8,145,178,0.18),transparent 48%),linear-gradient(180deg,rgba(15,23,42,0.96),rgba(2,6,23,1)); /* InviteUxLab.tsx:510 dark hero gradient. */
--gradient-extract-9: linear-gradient(135deg,rgba(8,145,178,0.10),transparent 28%),linear-gradient(180deg,rgba(245,158,11,0.10),transparent 44%),var(--background); /* IssueChatUxLab.tsx:139 + InviteUxLab.tsx:700 hero-card gradient (identical string, 2 sites). */
--gradient-extract-10: linear-gradient(180deg,rgba(168,85,247,0.06),transparent 28%),var(--background); /* IssueChatUxLab.tsx:203 + InviteUxLab.tsx:909 accent gradient (identical string, 2 sites). */
--gradient-extract-11: linear-gradient(180deg,rgba(16,185,129,0.06),transparent 28%),var(--background); /* IssueChatUxLab.tsx:226 accent gradient. */
--gradient-extract-12: linear-gradient(180deg,rgba(6,182,212,0.05),transparent 28%),var(--background); /* IssueChatUxLab.tsx:263 accent gradient. */
--gradient-extract-13: linear-gradient(180deg,rgba(59,130,246,0.06),transparent 28%),var(--background); /* IssueChatUxLab.tsx:294 accent gradient. */
--gradient-extract-14: linear-gradient(180deg,rgba(168,85,247,0.05),transparent 26%),var(--background); /* IssueChatUxLab.tsx:315 accent gradient. */
--gradient-extract-15: linear-gradient(180deg,rgba(245,158,11,0.08),transparent 26%),var(--background); /* IssueChatUxLab.tsx:339 accent gradient. */
--gradient-extract-16: linear-gradient(135deg,rgba(245,158,11,0.10),transparent 28%),linear-gradient(180deg,rgba(8,145,178,0.08),transparent 44%),var(--background); /* SystemNoticeUxLab.tsx:140 hero-card gradient. */
--gradient-extract-17: linear-gradient(180deg,rgba(245,158,11,0.05),transparent 28%),var(--background); /* SystemNoticeUxLab.tsx:193 accent gradient. */
--gradient-extract-18: linear-gradient(180deg,rgba(8,145,178,0.05),transparent 28%),var(--background); /* SystemNoticeUxLab.tsx:225 accent gradient. */
--gradient-extract-19: linear-gradient(180deg,rgba(244,63,94,0.05),transparent 28%),var(--background); /* SystemNoticeUxLab.tsx:289 accent gradient. */
--gradient-extract-20: linear-gradient(180deg,rgba(16,185,129,0.05),transparent 28%),var(--background); /* SystemNoticeUxLab.tsx:331 accent gradient. */
--gradient-extract-21: linear-gradient(180deg,rgba(59,130,246,0.05),transparent 30%),var(--background); /* InviteUxLab.tsx:753 accent gradient. */
--gradient-extract-22: linear-gradient(180deg,rgba(234,179,8,0.06),transparent 28%),var(--background); /* InviteUxLab.tsx:807 accent gradient. */
--gradient-extract-23: linear-gradient(180deg,rgba(16,185,129,0.06),transparent 30%),var(--background); /* InviteUxLab.tsx:884 accent gradient. */
--gradient-extract-24: linear-gradient(180deg,rgba(244,114,182,0.06),transparent 28%),var(--background); /* InviteUxLab.tsx:921 accent gradient. */
}
/* ── Extracted verbatim TYPE tokens (Phase 2 Batch 2, design/token-extraction) ── */
@ -1701,15 +1682,7 @@ span.paperclip-mention-chip[data-mention-kind="external-object"] {
--shadow-extract-12: 0 0 0 2px hsl(var(--background)); /* Extracted from ui/src/components/SidebarNavItem.tsx (shadow-[0_0_0_2px_hsl(var(--background))]). */
--shadow-extract-13: 0 0 0 3px rgba(245,158,11,0.18); /* Extracted from ui/src/components/environment-variables-editor/index.tsx (shadow-[0_0_0_3px_rgba(245,158,11,0.18)]). */
--shadow-extract-14: 0 0 12px rgba(6,182,212,0.08); /* Extracted from ui/src/pages/AgentDetail.tsx (shadow-[0_0_12px_rgba(6,182,212,0.08)]). */
--shadow-extract-15: 0 24px 60px rgba(15,23,42,0.08); /* Extracted from ui/src/pages/InviteUxLab.tsx (shadow-[0_24px_60px_rgba(15,23,42,0.08)]). */
--shadow-extract-16: 0 30px 80px rgba(2,6,23,0.55); /* Extracted from ui/src/pages/InviteUxLab.tsx (shadow-[0_30px_80px_rgba(2,6,23,0.55)]). */
--shadow-extract-17: 0 30px 80px rgba(15,23,42,0.10); /* Extracted from ui/src/pages/InviteUxLab.tsx (shadow-[0_30px_80px_rgba(15,23,42,0.10)]). */
--shadow-extract-18: 0 18px 44px -28px rgba(0,0,0,0.45); /* Extracted from ui/src/pages/ProfileSettings.tsx (shadow-[0_18px_44px_-28px_rgba(0,0,0,0.45)]). */
--shadow-extract-19: 0 20px 50px rgba(6,182,212,0.10); /* Extracted from ui/src/pages/RunTranscriptUxLab.tsx (shadow-[0_20px_50px_rgba(6,182,212,0.10)]). */
--shadow-extract-20: 0 20px 40px rgba(15,23,42,0.10); /* Extracted from ui/src/pages/RunTranscriptUxLab.tsx (shadow-[0_20px_40px_rgba(15,23,42,0.10)]). */
--shadow-extract-21: 0 0 0 6px rgba(34,211,238,0.12); /* Extracted from ui/src/pages/RunTranscriptUxLab.tsx (shadow-[0_0_0_6px_rgba(34,211,238,0.12)]). */
--shadow-extract-22: 0 28px 70px rgba(15,23,42,0.10); /* Extracted from ui/src/pages/RunTranscriptUxLab.tsx (shadow-[0_28px_70px_rgba(15,23,42,0.10)]). */
--shadow-extract-23: 0 12px 24px rgba(6,182,212,0.12); /* Extracted from ui/src/pages/RunTranscriptUxLab.tsx (shadow-[0_12px_24px_rgba(6,182,212,0.12)]). */
}
/* ── Extracted verbatim MISC tokens (Phase 2 Batch 4, design/token-extraction) ── */
@ -1862,6 +1835,15 @@ span.paperclip-mention-chip[data-mention-kind="external-object"] {
narrative (why each decision was made); this block is the source of truth
for WHICH sites are exempt.
CRITERIA UPDATE (Run 2 review, DECISION-SHEET.md B1 user ruling): the
allowlist criteria now also include FIRST-PARTY INTENTIONAL ONE-OFF
DECORATION deliberate, never-reused decorative gradients/shadows on
demo/UX-lab surfaces stay inline (spirit over letter of DESIGN.md
principle 2) rather than being minted as singleton --gradient-extract-*
and --shadow-extract-* tokens. Production-surface decoration tokens were
KEPT; only tokens whose sole consumers were *UxLab.tsx demo pages were
reverted to their original literal form under this rule.
Format (one line per entry, do not reflow):
* allow <repo-relative-path> <reason>
@ -1880,6 +1862,10 @@ span.paperclip-mention-chip[data-mention-kind="external-object"] {
* allow ui/src/components/ui/alert-dialog.tsx same tw-animate-css dead-class situation as dialog.tsx (zoom-out-[0.97], zoom-in-[0.97])
* allow ui/src/components/ProjectWorkspaceSummaryCard.tsx min-[420px]: is a Tailwind arbitrary BREAKPOINT variant (responsive container-query-style prefix), not a value applied to a property; the variant position cannot reference a CSS custom property, so it cannot be tokenized without changing Tailwind's own variant syntax
* allow ui/src/components/FileTree.tsx max-[480px]: arbitrary breakpoint variant (x2), same reasoning as min-[420px] above
* allow ui/src/pages/RunTranscriptUxLab.tsx first-party intentional one-off decoration (demo/UX-lab page): hero/card gradients + shadows reverted from singleton tokens per DECISION-SHEET.md B1 user ruling
* allow ui/src/pages/InviteUxLab.tsx first-party intentional one-off decoration (demo/UX-lab page): hero/card gradients + shadows reverted from singleton tokens per DECISION-SHEET.md B1 user ruling
* allow ui/src/pages/IssueChatUxLab.tsx first-party intentional one-off decoration (demo/UX-lab page): hero/card gradients + shadows reverted from singleton tokens per DECISION-SHEET.md B1 user ruling
* allow ui/src/pages/SystemNoticeUxLab.tsx first-party intentional one-off decoration (demo/UX-lab page): hero/card gradients + shadows reverted from singleton tokens per DECISION-SHEET.md B1 user ruling
Test-fixture hex-literal policy (Batch 4, resolving TOKEN-AUDIT.md section
8 item 10 "Needs human decision" for these specific sites): the following

View File

@ -1,3 +1,7 @@
// token-extraction: allowlisted — intentional one-off decoration (DECISION-SHEET.md B1
// user ruling). The bg-[...gradient...] / shadow-[...] literals in this demo/UX-lab page
// are deliberate one-off decoration, reverted from --gradient-extract-*/--shadow-extract-*
// tokens; the file is on the check-token-gates allowlist in ui/src/index.css.
import type { ReactNode } from "react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
@ -109,7 +113,7 @@ function LabSection({
return (
<section
className={cn(
"rounded-(--rad-28) border border-border/70 bg-background/80 p-4 shadow-(--shadow-extract-15) sm:p-5",
"rounded-(--rad-28) border border-border/70 bg-background/80 p-4 shadow-[0_24px_60px_rgba(15,23,42,0.08)] sm:p-5",
accentClassName,
)}
>
@ -168,7 +172,7 @@ function InviteLandingShell({
right: ReactNode;
}) {
return (
<div className="overflow-hidden rounded-(--rad-28) border border-zinc-800 bg-zinc-950 shadow-(--shadow-extract-16)">
<div className="overflow-hidden rounded-(--rad-28) border border-zinc-800 bg-zinc-950 shadow-[0_30px_80px_rgba(2,6,23,0.55)]">
<div className="grid gap-px bg-zinc-800 lg:grid-cols-(--gtc-37)">
<section className={cn(panelClassName, "space-y-6 bg-zinc-950")}>{left}</section>
<section className={cn(panelClassName, "h-full bg-zinc-950")}>{right}</section>
@ -452,7 +456,7 @@ function InviteResultPreview({
function AuthScreenPreview({ mode, error }: { mode: "sign_in" | "sign_up"; error?: string }) {
return (
<div className="overflow-hidden rounded-(--rad-28) border border-border/70 bg-background shadow-(--shadow-extract-15)">
<div className="overflow-hidden rounded-(--rad-28) border border-border/70 bg-background shadow-[0_24px_60px_rgba(15,23,42,0.08)]">
<div className="grid gap-px bg-border/60 md:grid-cols-2">
<div className="flex min-h-(--sz-420px) flex-col justify-center bg-background px-8 py-10">
<div className="mx-auto w-full max-w-md">
@ -508,7 +512,7 @@ function AuthScreenPreview({ mode, error }: { mode: "sign_in" | "sign_up"; error
</div>
</div>
</div>
<div className="hidden min-h-(--sz-420px) items-center justify-center bg-(image:--gradient-extract-8) px-8 py-10 md:flex">
<div className="hidden min-h-(--sz-420px) items-center justify-center bg-[radial-gradient(circle_at_top,rgba(8,145,178,0.18),transparent_48%),linear-gradient(180deg,rgba(15,23,42,0.96),rgba(2,6,23,1))] px-8 py-10 md:flex">
<div className="max-w-sm space-y-4 text-zinc-200">
<div className="inline-flex items-center gap-2 rounded-full border border-cyan-400/30 bg-cyan-500/[0.08] px-3 py-1 text-(length:--fs-10) uppercase tracking-(--ls-0_22) text-cyan-200">
Auth preview
@ -698,7 +702,7 @@ function CompanyInvitesPreview() {
export function InviteUxLab() {
return (
<div className="space-y-6">
<div className="overflow-hidden rounded-(--rad-32) border border-border/70 bg-(image:--gradient-extract-9) shadow-(--shadow-extract-17)">
<div className="overflow-hidden rounded-(--rad-32) border border-border/70 bg-[linear-gradient(135deg,rgba(8,145,178,0.10),transparent_28%),linear-gradient(180deg,rgba(245,158,11,0.10),transparent_44%),var(--background)] shadow-[0_30px_80px_rgba(15,23,42,0.10)]">
<div className="grid gap-6 lg:grid-cols-(--gtc-39)">
<div className="p-6 sm:p-7">
<div className="inline-flex items-center gap-2 rounded-full border border-cyan-500/25 bg-cyan-500/[0.08] px-3 py-1 text-(length:--fs-10) font-semibold uppercase tracking-(--ls-0_24) text-cyan-700 dark:text-cyan-300">
@ -751,7 +755,7 @@ export function InviteUxLab() {
eyebrow="Top-level states"
title="Landing state coverage"
description="Small cards for the fast-return invite states that do not render the full split-screen layout."
accentClassName="bg-(image:--gradient-extract-21)"
accentClassName="bg-[linear-gradient(180deg,rgba(59,130,246,0.05),transparent_30%),var(--background)]"
>
<div className="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
<StatusCard
@ -805,7 +809,7 @@ export function InviteUxLab() {
eyebrow="Invite landing"
title="Split-screen invite flows"
description="These frames mirror the production invite surface closely enough to review spacing, hierarchy, and control states while keeping data fixture-driven."
accentClassName="bg-(image:--gradient-extract-22)"
accentClassName="bg-[linear-gradient(180deg,rgba(234,179,8,0.06),transparent_28%),var(--background)]"
>
<div className="space-y-5">
<InviteLandingShell
@ -882,7 +886,7 @@ export function InviteUxLab() {
eyebrow="Result states"
title="Approval and completion screens"
description="These are the post-submit states returned from invite acceptance, including optional claim and onboarding metadata."
accentClassName="bg-(image:--gradient-extract-23)"
accentClassName="bg-[linear-gradient(180deg,rgba(16,185,129,0.06),transparent_30%),var(--background)]"
>
<div className="grid gap-5 xl:grid-cols-3">
<InviteResultPreview
@ -907,7 +911,7 @@ export function InviteUxLab() {
eyebrow="Standalone auth"
title="Auth page states"
description="The general `/auth` page uses a different composition from invite landing. These previews keep both sign-in and sign-up variants visible."
accentClassName="bg-(image:--gradient-extract-10)"
accentClassName="bg-[linear-gradient(180deg,rgba(168,85,247,0.06),transparent_28%),var(--background)]"
>
<div className="space-y-5">
<AuthScreenPreview mode="sign_in" error="Invalid email or password" />
@ -919,7 +923,7 @@ export function InviteUxLab() {
eyebrow="Company settings"
title="Company invite management"
description="This section captures the board-side invite creation flow, copied-link state, audit table, and the edge states that are otherwise tedious to stage."
accentClassName="bg-(image:--gradient-extract-24)"
accentClassName="bg-[linear-gradient(180deg,rgba(244,114,182,0.06),transparent_28%),var(--background)]"
>
<CompanyInvitesPreview />
</LabSection>

View File

@ -1,3 +1,7 @@
// token-extraction: allowlisted — intentional one-off decoration (DECISION-SHEET.md B1
// user ruling). The bg-[...gradient...] / shadow-[...] literals in this demo/UX-lab page
// are deliberate one-off decoration, reverted from --gradient-extract-*/--shadow-extract-*
// tokens; the file is on the check-token-gates allowlist in ui/src/index.css.
import { useEffect, useRef, useState, type ReactNode } from "react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
@ -49,7 +53,7 @@ function LabSection({
<section
id={id}
className={cn(
"rounded-(--rad-28) border border-border/70 bg-background/80 p-4 shadow-(--shadow-extract-15) sm:p-5",
"rounded-(--rad-28) border border-border/70 bg-background/80 p-4 shadow-[0_24px_60px_rgba(15,23,42,0.08)] sm:p-5",
accentClassName,
)}
>
@ -136,7 +140,7 @@ export function IssueChatUxLab() {
return (
<div className="space-y-6">
<div className="overflow-hidden rounded-(--rad-32) border border-border/70 bg-(image:--gradient-extract-9) shadow-(--shadow-extract-17)">
<div className="overflow-hidden rounded-(--rad-32) border border-border/70 bg-[linear-gradient(135deg,rgba(8,145,178,0.10),transparent_28%),linear-gradient(180deg,rgba(245,158,11,0.10),transparent_44%),var(--background)] shadow-[0_30px_80px_rgba(15,23,42,0.10)]">
<div className="grid gap-6 lg:grid-cols-(--gtc-39)">
<div className="p-6 sm:p-7">
<div className="inline-flex items-center gap-2 rounded-full border border-cyan-500/25 bg-cyan-500/[0.08] px-3 py-1 text-(length:--fs-10) font-semibold uppercase tracking-(--ls-0_24) text-cyan-700 dark:text-cyan-300">
@ -200,7 +204,7 @@ export function IssueChatUxLab() {
eyebrow="Animation demo"
title="Rotating reasoning text"
description="Isolated ticker that cycles sample reasoning lines on a timer. The outgoing line slides up and fades out while the incoming line slides up from below. Runs in a loop so you can tune timing and easing without needing a live stream."
accentClassName="bg-(image:--gradient-extract-10)"
accentClassName="bg-[linear-gradient(180deg,rgba(168,85,247,0.06),transparent_28%),var(--background)]"
>
<div className="space-y-4">
<div className="rounded-xl border border-border/60 bg-accent/10 p-4">
@ -223,7 +227,7 @@ export function IssueChatUxLab() {
eyebrow="Status tokens"
title="Working / Worked header verb"
description='The "Working" token uses the shimmer-text gradient sweep to signal an active run. Once the run completes it becomes the static "Worked" token.'
accentClassName="bg-(image:--gradient-extract-11)"
accentClassName="bg-[linear-gradient(180deg,rgba(16,185,129,0.06),transparent_28%),var(--background)]"
>
<div className="grid gap-4 sm:grid-cols-2">
<div className="rounded-xl border border-border/60 bg-accent/10 p-4">
@ -260,7 +264,7 @@ export function IssueChatUxLab() {
eyebrow="Primary preview"
title="Live execution thread"
description="Shows the fully active state: timeline events, historical run marker, a running assistant reply with reasoning and tools, and a queued follow-up from the user."
accentClassName="bg-(image:--gradient-extract-12)"
accentClassName="bg-[linear-gradient(180deg,rgba(6,182,212,0.05),transparent_28%),var(--background)]"
>
<IssueChatThread
comments={issueChatUxLiveComments}
@ -291,7 +295,7 @@ export function IssueChatUxLab() {
eyebrow="Submitting state"
title="Pending message bubble"
description='When a user sends a message, the bubble briefly shows a "Sending..." label at reduced opacity until the server confirms receipt. This preview renders that transient state.'
accentClassName="bg-(image:--gradient-extract-13)"
accentClassName="bg-[linear-gradient(180deg,rgba(59,130,246,0.06),transparent_28%),var(--background)]"
>
<IssueChatThread
comments={issueChatUxSubmittingComments}
@ -312,7 +316,7 @@ export function IssueChatUxLab() {
eyebrow="Settled review"
title="Durable comments and feedback"
description="Shows the post-run state: assistant comment feedback controls, historical run context, and timeline reassignment without any active stream."
accentClassName="bg-(image:--gradient-extract-14)"
accentClassName="bg-[linear-gradient(180deg,rgba(168,85,247,0.05),transparent_26%),var(--background)]"
>
<IssueChatThread
comments={issueChatUxReviewComments}
@ -336,7 +340,7 @@ export function IssueChatUxLab() {
eyebrow="Empty thread"
title="Empty state and disabled composer"
description="Keeps the message area visible even when there is no thread yet, and replaces the composer with an explicit warning when replies are blocked."
accentClassName="bg-(image:--gradient-extract-15)"
accentClassName="bg-[linear-gradient(180deg,rgba(245,158,11,0.08),transparent_26%),var(--background)]"
>
<IssueChatThread
comments={[]}

View File

@ -1,3 +1,7 @@
// token-extraction: allowlisted — intentional one-off decoration (DECISION-SHEET.md B1
// user ruling). The bg-[...gradient...] / shadow-[...] literals in this demo/UX-lab page
// are deliberate one-off decoration, reverted from --gradient-extract-*/--shadow-extract-*
// tokens; the file is on the check-token-gates allowlist in ui/src/index.css.
import { useState } from "react";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
@ -60,7 +64,7 @@ function RunDetailPreview({
density: TranscriptDensity;
}) {
return (
<div className="overflow-hidden rounded-xl border border-border/70 bg-background/80 shadow-(--shadow-extract-15)">
<div className="overflow-hidden rounded-xl border border-border/70 bg-background/80 shadow-[0_24px_60px_rgba(15,23,42,0.08)]">
<div className="border-b border-border/60 bg-background/90 px-5 py-4">
<div className="flex flex-wrap items-center gap-2">
<Badge variant="outline" className="uppercase tracking-(--ls-0_18) text-(length:--fs-10)">
@ -75,7 +79,7 @@ function RunDetailPreview({
Transcript ({runTranscriptFixtureEntries.length})
</div>
</div>
<div className="max-h-(--sz-720px) overflow-y-auto bg-(image:--gradient-extract-5) p-5">
<div className="max-h-(--sz-720px) overflow-y-auto bg-[radial-gradient(circle_at_top_left,rgba(8,145,178,0.08),transparent_36%),radial-gradient(circle_at_bottom_right,rgba(245,158,11,0.10),transparent_28%)] p-5">
<RunTranscriptView
entries={runTranscriptFixtureEntries}
mode={mode}
@ -97,7 +101,7 @@ function LiveWidgetPreview({
density: TranscriptDensity;
}) {
return (
<div className="overflow-hidden rounded-xl border border-cyan-500/25 bg-background/85 shadow-(--shadow-extract-19)">
<div className="overflow-hidden rounded-xl border border-cyan-500/25 bg-background/85 shadow-[0_20px_50px_rgba(6,182,212,0.10)]">
<div className="border-b border-border/60 bg-cyan-500/[0.05] px-5 py-4">
<div className="text-xs font-semibold uppercase tracking-(--ls-0_2) text-cyan-700 dark:text-cyan-300">
Live Runs
@ -149,7 +153,7 @@ function DashboardPreview({
return (
<div className="max-w-md">
<div className={cn(
"flex h-(--sz-320px) flex-col overflow-hidden rounded-xl border shadow-(--shadow-extract-20)",
"flex h-(--sz-320px) flex-col overflow-hidden rounded-xl border shadow-[0_20px_40px_rgba(15,23,42,0.10)]",
streaming
? "border-cyan-500/25 bg-cyan-500/[0.04]"
: "border-border bg-background/75",
@ -160,7 +164,7 @@ function DashboardPreview({
<div className="flex items-center gap-2">
<span className={cn(
"inline-flex h-2.5 w-2.5 rounded-full",
streaming ? "bg-cyan-500 shadow-(--shadow-extract-21)" : "bg-muted-foreground/35",
streaming ? "bg-cyan-500 shadow-[0_0_0_6px_rgba(34,211,238,0.12)]" : "bg-muted-foreground/35",
)} />
<Identity name={runTranscriptFixtureMeta.agentName} size="sm" />
</div>
@ -200,7 +204,7 @@ export function RunTranscriptUxLab() {
return (
<div className="space-y-6">
<div className="overflow-hidden rounded-2xl border border-border/70 bg-(image:--gradient-extract-6) shadow-(--shadow-extract-22)">
<div className="overflow-hidden rounded-2xl border border-border/70 bg-[linear-gradient(135deg,rgba(8,145,178,0.08),transparent_28%),linear-gradient(180deg,rgba(245,158,11,0.08),transparent_40%),var(--background)] shadow-[0_28px_70px_rgba(15,23,42,0.10)]">
<div className="grid gap-6 lg:grid-cols-(--gtc-19)">
<aside className="border-b border-border/60 bg-background/75 p-5 lg:border-b-0 lg:border-r">
<div className="mb-5">
@ -225,7 +229,7 @@ export function RunTranscriptUxLab() {
className={cn(
"w-full rounded-xl border px-4 py-3 text-left transition-all",
selectedSurface === option.id
? "border-cyan-500/35 bg-cyan-500/[0.10] shadow-(--shadow-extract-23)"
? "border-cyan-500/35 bg-cyan-500/[0.10] shadow-[0_12px_24px_rgba(6,182,212,0.12)]"
: "border-border/70 bg-background/70 hover:border-cyan-500/20 hover:bg-cyan-500/[0.04]",
)}
>

View File

@ -1,3 +1,7 @@
// token-extraction: allowlisted — intentional one-off decoration (DECISION-SHEET.md B1
// user ruling). The bg-[...gradient...] / shadow-[...] literals in this demo/UX-lab page
// are deliberate one-off decoration, reverted from --gradient-extract-*/--shadow-extract-*
// tokens; the file is on the check-token-gates allowlist in ui/src/index.css.
import type { ReactNode } from "react";
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
import { Badge } from "@/components/ui/badge";
@ -32,7 +36,7 @@ function LabSection({
<section
id={id}
className={cn(
"rounded-(--rad-28) border border-border/70 bg-background/85 p-4 shadow-(--shadow-extract-15) sm:p-5",
"rounded-(--rad-28) border border-border/70 bg-background/85 p-4 shadow-[0_24px_60px_rgba(15,23,42,0.08)] sm:p-5",
accentClassName,
)}
>
@ -137,7 +141,7 @@ export function SystemNoticeUxLab() {
return (
<div className="space-y-6">
<div className="overflow-hidden rounded-(--rad-32) border border-border/70 bg-(image:--gradient-extract-16) shadow-(--shadow-extract-17)">
<div className="overflow-hidden rounded-(--rad-32) border border-border/70 bg-[linear-gradient(135deg,rgba(245,158,11,0.10),transparent_28%),linear-gradient(180deg,rgba(8,145,178,0.08),transparent_44%),var(--background)] shadow-[0_30px_80px_rgba(15,23,42,0.10)]">
<div className="grid gap-6 lg:grid-cols-(--gtc-39)">
<div className="p-6 sm:p-7">
<div className="inline-flex items-center gap-2 rounded-full border border-amber-500/25 bg-amber-500/[0.08] px-3 py-1 text-(length:--fs-10) font-semibold uppercase tracking-(--ls-0_24) text-amber-700 dark:text-amber-300">
@ -190,7 +194,7 @@ export function SystemNoticeUxLab() {
eyebrow="Tone matrix"
title="Three tones, two states"
description="Each tone pairs a unique icon and tone label so the notice is recognizable without color. Collapsed is the default; the Details affordance reveals operational metadata only when reviewers ask for it."
accentClassName="bg-(image:--gradient-extract-17)"
accentClassName="bg-[linear-gradient(180deg,rgba(245,158,11,0.05),transparent_28%),var(--background)]"
>
<div className="space-y-5">
<FixtureFrame caption={warningCollapsed.caption}>
@ -222,7 +226,7 @@ export function SystemNoticeUxLab() {
eyebrow="Hierarchy in thread"
title="Distinct from user and agent comments"
description="Side-by-side with adjacent comment types so reviewers can confirm the system row reads as a system row — full width, no avatar gutter, no chat bubble — while user and agent comments keep their existing rounded bubbles."
accentClassName="bg-(image:--gradient-extract-18)"
accentClassName="bg-[linear-gradient(180deg,rgba(8,145,178,0.05),transparent_28%),var(--background)]"
>
<div className="space-y-4 rounded-2xl border border-border/70 bg-background/70 p-4">
<MockUserBubble
@ -286,7 +290,7 @@ export function SystemNoticeUxLab() {
eyebrow="Before"
title="Today's nested treatment"
description="The same content rendered through the existing user-bubble + warning-callout path. Two containers, same gray background as user comments, and the warning icon is forced inside a chat row."
accentClassName="bg-(image:--gradient-extract-19)"
accentClassName="bg-[linear-gradient(180deg,rgba(244,63,94,0.05),transparent_28%),var(--background)]"
>
<div className="space-y-3 rounded-2xl border border-border/70 bg-background/70 p-4">
<div className="flex items-start gap-2.5">
@ -328,7 +332,7 @@ export function SystemNoticeUxLab() {
eyebrow="After"
title="System notice replacement"
description="One container, system-authored label, hidden details. The chat surface keeps user and agent bubbles unchanged."
accentClassName="bg-(image:--gradient-extract-20)"
accentClassName="bg-[linear-gradient(180deg,rgba(16,185,129,0.05),transparent_28%),var(--background)]"
>
<div className="space-y-3 rounded-2xl border border-border/70 bg-background/70 p-4">
<SystemNotice {...dangerCollapsed} />