From 8028ed1905fdb9e5e65b7af0522e8a363abb71ad Mon Sep 17 00:00:00 2001 From: Hiroki Uemura Date: Tue, 6 Oct 2026 20:47:14 +0900 Subject: [PATCH 01/20] feat(core): alias --destructive to the danger solid role Co-Authored-By: Claude Fable 5.1 --- packages/core/src/assets/themes/default.css | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/core/src/assets/themes/default.css b/packages/core/src/assets/themes/default.css index 849f012b5..0f90e6c8a 100644 --- a/packages/core/src/assets/themes/default.css +++ b/packages/core/src/assets/themes/default.css @@ -41,8 +41,8 @@ --sidebar-border: rgba(229, 229, 229, 1); /* ── 5. PALETTE ───────────────────────────────────────────────────────── */ - --destructive: rgba(220, 38, 38, 1); - --destructive-foreground: rgba(254, 242, 242, 1); + --destructive: var(--danger-solid); + --destructive-foreground: var(--danger-contrast); --radius: 0.625rem; --chart-1: rgba(234, 88, 12, 1); --chart-2: rgba(13, 148, 136, 1); @@ -87,8 +87,8 @@ --sidebar-border: rgba(255, 255, 255, 0.1); /* ── 5. PALETTE (dark) ────────────────────────────────────────────────── */ - --destructive: rgba(248, 113, 113, 1); - --destructive-foreground: rgba(254, 242, 242, 1); + --destructive: var(--danger-solid); + --destructive-foreground: var(--danger-contrast); --chart-1: rgba(29, 78, 216, 1); --chart-2: rgba(16, 185, 129, 1); --chart-3: rgba(245, 158, 11, 1); From 1fab9852607f28a893cfabcc9c5e7a76c9cc8088 Mon Sep 17 00:00:00 2001 From: Hiroki Uemura Date: Tue, 6 Oct 2026 20:47:32 +0900 Subject: [PATCH 02/20] feat(core): Button destructive uses the danger roles Co-Authored-By: Claude Fable 5.1 --- packages/core/src/components/button/button.test.tsx | 2 +- packages/core/src/components/button/button.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/core/src/components/button/button.test.tsx b/packages/core/src/components/button/button.test.tsx index ec37bdd68..d8afb2d24 100644 --- a/packages/core/src/components/button/button.test.tsx +++ b/packages/core/src/components/button/button.test.tsx @@ -103,7 +103,7 @@ describe("Button", () => { expect(screen.getByRole("button").className).toContain("bg-primary"); rerender(); - expect(screen.getByRole("button").className).toContain("bg-destructive"); + expect(screen.getByRole("button").className).toContain("bg-danger-solid"); rerender(); expect(screen.getByRole("button").className).toContain("border"); diff --git a/packages/core/src/components/button/button.tsx b/packages/core/src/components/button/button.tsx index a99167cb7..a4d5773fc 100644 --- a/packages/core/src/components/button/button.tsx +++ b/packages/core/src/components/button/button.tsx @@ -6,7 +6,7 @@ import { cn } from "@/lib/utils"; import { mergeRefs, useToolbarItem } from "../toolbar/toolbar"; const buttonVariants = cva( - "astw:inline-flex astw:cursor-pointer astw:items-center astw:justify-center astw:gap-2 astw:whitespace-nowrap astw:rounded-md astw:text-sm astw:font-medium astw:transition-all astw:disabled:pointer-events-none astw:disabled:opacity-50 astw:[&_svg]:pointer-events-none astw:[&_svg:not([class*='size-'])]:size-4 astw:shrink-0 astw:[&_svg]:shrink-0 astw:outline-none astw:focus-visible:border-ring astw:focus-visible:ring-ring/50 astw:focus-visible:ring-[3px] astw:aria-invalid:ring-destructive/20 astw:dark:aria-invalid:ring-destructive/40 astw:aria-invalid:border-destructive", + "astw:inline-flex astw:cursor-pointer astw:items-center astw:justify-center astw:gap-2 astw:whitespace-nowrap astw:rounded-md astw:text-sm astw:font-medium astw:transition-all astw:disabled:pointer-events-none astw:disabled:opacity-50 astw:[&_svg]:pointer-events-none astw:[&_svg:not([class*='size-'])]:size-4 astw:shrink-0 astw:[&_svg]:shrink-0 astw:outline-none astw:focus-visible:border-ring astw:focus-visible:ring-ring/50 astw:focus-visible:ring-[3px] astw:aria-invalid:ring-danger-solid/20 astw:dark:aria-invalid:ring-danger-solid/40 astw:aria-invalid:border-danger-solid", { variants: { /** Visual style variant */ @@ -14,7 +14,7 @@ const buttonVariants = cva( default: "astw:bg-primary astw:text-primary-foreground astw:shadow-xs astw:hover:bg-primary/90", destructive: - "astw:bg-destructive astw:text-white astw:shadow-xs astw:hover:bg-destructive/90 astw:focus-visible:ring-destructive/20 astw:dark:focus-visible:ring-destructive/40 astw:dark:bg-destructive/60", + "astw:bg-danger-solid astw:text-danger-contrast astw:shadow-xs astw:hover:bg-danger-solid-hover astw:focus-visible:ring-danger-solid/20 astw:dark:focus-visible:ring-danger-solid/40", outline: "astw:border astw:border-border astw:bg-background astw:shadow-xs astw:hover:bg-accent astw:hover:text-accent-foreground astw:dark:bg-input/30 astw:dark:border-input astw:dark:hover:bg-input/50", secondary: From 4d9143818ae8c3ab0ec0fa925da75f1fa4278156 Mon Sep 17 00:00:00 2001 From: Hiroki Uemura Date: Tue, 6 Oct 2026 20:47:47 +0900 Subject: [PATCH 03/20] refactor(core): text-destructive utilities use text-danger-text Co-Authored-By: Claude Fable 5.1 --- .../core/src/components/action-panel/ActionPanel.tsx | 2 +- packages/core/src/components/ai-chat/chat-history.tsx | 2 +- packages/core/src/components/ai-chat/tool.tsx | 4 ++-- packages/core/src/components/appshell/appshell.tsx | 2 +- .../core/src/components/csv-importer/CsvImporter.tsx | 10 +++++----- packages/core/src/components/data-table/data-table.tsx | 4 ++-- packages/core/src/components/data-table/toolbar.tsx | 6 +++--- packages/core/src/components/field/field.tsx | 4 ++-- .../default-error-boundary/default-error-boundary.tsx | 4 ++-- 9 files changed, 19 insertions(+), 19 deletions(-) diff --git a/packages/core/src/components/action-panel/ActionPanel.tsx b/packages/core/src/components/action-panel/ActionPanel.tsx index 168c94a2f..1b8a98b8e 100644 --- a/packages/core/src/components/action-panel/ActionPanel.tsx +++ b/packages/core/src/components/action-panel/ActionPanel.tsx @@ -20,7 +20,7 @@ const actionRowInteractiveClasses = "astw:hover:bg-accent astw:hover:text-accent-foreground astw:outline-none astw:focus-visible:ring-2 astw:focus-visible:ring-ring astw:focus-visible:ring-offset-2"; const actionRowDestructiveClasses = - "astw:text-destructive astw:hover:bg-destructive/10 astw:hover:text-destructive"; + "astw:text-danger-text astw:hover:bg-destructive/10 astw:hover:text-danger-text"; const actionRowDisabledClasses = "astw:pointer-events-none astw:opacity-50"; diff --git a/packages/core/src/components/ai-chat/chat-history.tsx b/packages/core/src/components/ai-chat/chat-history.tsx index 712afaab5..2022f5223 100644 --- a/packages/core/src/components/ai-chat/chat-history.tsx +++ b/packages/core/src/components/ai-chat/chat-history.tsx @@ -83,7 +83,7 @@ function ChatHistory({ // Tucked away visually but never removed from the tab // order or the a11y tree — `hidden` would make // `onDelete` mouse-only. Same pattern as `Attachment`. - className="astw:mr-1 astw:shrink-0 astw:rounded astw:p-1 astw:text-muted-foreground astw:opacity-0 astw:transition-opacity astw:hover:text-destructive astw:group-hover:opacity-100 astw:group-focus-within:opacity-100" + className="astw:mr-1 astw:shrink-0 astw:rounded astw:p-1 astw:text-muted-foreground astw:opacity-0 astw:transition-opacity astw:hover:text-danger-text astw:group-hover:opacity-100 astw:group-focus-within:opacity-100" > diff --git a/packages/core/src/components/ai-chat/tool.tsx b/packages/core/src/components/ai-chat/tool.tsx index 667c9fd4d..c97160945 100644 --- a/packages/core/src/components/ai-chat/tool.tsx +++ b/packages/core/src/components/ai-chat/tool.tsx @@ -37,7 +37,7 @@ const STATUS_ICONS: Record = { "input-streaming": , "input-available": , "output-available": , - "output-error": , + "output-error": , }; type ToolProps = Omit, "children"> & { @@ -150,7 +150,7 @@ function ToolOutput({ output, errorText, className, ...props }: ToolOutputProps)

{errorText ? t("aiChatToolError") : t("aiChatToolResult")} diff --git a/packages/core/src/components/appshell/appshell.tsx b/packages/core/src/components/appshell/appshell.tsx index 3ff3449ae..27cf8920a 100644 --- a/packages/core/src/components/appshell/appshell.tsx +++ b/packages/core/src/components/appshell/appshell.tsx @@ -379,7 +379,7 @@ export const AppShellInternal = (props: AppShellInternalProps) => { return (
-

+

Configuration Error

{errorMessage}

diff --git a/packages/core/src/components/csv-importer/CsvImporter.tsx b/packages/core/src/components/csv-importer/CsvImporter.tsx index 3cc9b9253..ff2193f29 100644 --- a/packages/core/src/components/csv-importer/CsvImporter.tsx +++ b/packages/core/src/components/csv-importer/CsvImporter.tsx @@ -156,7 +156,7 @@ function UploadStep({ /> {error && ( -
+
{error}
@@ -242,7 +242,7 @@ function MappingStep({ {isMapped && } {!isMapped && col.required && ( - + )} {!isMapped && !col.required && ( @@ -255,7 +255,7 @@ function MappingStep({ {col.label} {col.required && ( - * + * )} {col.description && ( @@ -362,7 +362,7 @@ function ReviewStep({ >
Total: {rawRows.length} rows - {errorCount > 0 && Errors: {errorCount}} + {errorCount > 0 && Errors: {errorCount}} {warningCount > 0 && ( Warnings: {warningCount} )} @@ -437,7 +437,7 @@ function ReviewStep({ className={cn( "astw:text-xs astw:px-2", issue.level === "error" - ? "astw:text-destructive" + ? "astw:text-danger-text" : "astw:text-warning-text", )} > diff --git a/packages/core/src/components/data-table/data-table.tsx b/packages/core/src/components/data-table/data-table.tsx index 97271cde7..d5df128cb 100644 --- a/packages/core/src/components/data-table/data-table.tsx +++ b/packages/core/src/components/data-table/data-table.tsx @@ -1141,7 +1141,7 @@ function DataTableBody({ className }: { className?: string }) { return ( - + {t("errorPrefix")} {error.message} @@ -1706,7 +1706,7 @@ function RowActionsMenu>({ action.onClick(row); } }} - className={cn(action.variant === "destructive" && "astw:text-destructive")} + className={cn(action.variant === "destructive" && "astw:text-danger-text")} > {action.icon} {action.label} diff --git a/packages/core/src/components/data-table/toolbar.tsx b/packages/core/src/components/data-table/toolbar.tsx index d83455e98..fb2090b30 100644 --- a/packages/core/src/components/data-table/toolbar.tsx +++ b/packages/core/src/components/data-table/toolbar.tsx @@ -703,7 +703,7 @@ function PanelDateRangeInput({ /> )} - {error &&

{error}

} + {error &&

{error}

}
); } @@ -1026,7 +1026,7 @@ function BetweenInputGroup({ className={inputCell} />
- {error &&

{error}

} + {error &&

{error}

}
); } @@ -1980,7 +1980,7 @@ function TemporalFilterEditor({ value={localValueMax} onChange={setLocalValueMax} /> - {betweenError &&

{betweenError}

} + {betweenError &&

{betweenError}

}
) : ( ); @@ -261,7 +261,7 @@ Error.displayName = "Field.Error"; * * {(state) => * state.validity.valid === false && ( - *

+ *

* {state.validity.typeMismatch * ? "Please enter a valid URL (e.g. https://example.com)" * : "This field is required."} diff --git a/packages/core/src/components/internals/default-error-boundary/default-error-boundary.tsx b/packages/core/src/components/internals/default-error-boundary/default-error-boundary.tsx index 9f1d49f4d..aa8c01ba0 100644 --- a/packages/core/src/components/internals/default-error-boundary/default-error-boundary.tsx +++ b/packages/core/src/components/internals/default-error-boundary/default-error-boundary.tsx @@ -17,7 +17,7 @@ export const DefaultErrorBoundary = () => { return (

- +

{t("error404Title")}

{t("error404Body")}

@@ -41,7 +41,7 @@ export const DefaultErrorBoundary = () => { return (
- +

{t("errorTitle")}

{message}

From 46e40198c923f8945e1fb4ad7fae5eed59f9e456 Mon Sep 17 00:00:00 2001 From: Hiroki Uemura Date: Tue, 6 Oct 2026 20:48:00 +0900 Subject: [PATCH 04/20] refactor(core): invalid borders and focus rings use the danger roles Co-Authored-By: Claude Fable 5.1 --- packages/core/src/components/appshell/appshell.tsx | 2 +- packages/core/src/components/checkbox/checkbox.test.tsx | 2 +- packages/core/src/components/checkbox/checkbox.tsx | 4 ++-- packages/core/src/components/csv-importer/CsvImporter.tsx | 2 +- packages/core/src/components/data-table/toolbar.tsx | 2 +- packages/core/src/components/date-field/date-input-group.tsx | 2 +- packages/core/src/components/field/field.tsx | 2 +- packages/core/src/components/input/input.tsx | 2 +- packages/core/src/components/textarea/textarea.tsx | 4 ++-- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/core/src/components/appshell/appshell.tsx b/packages/core/src/components/appshell/appshell.tsx index 27cf8920a..775bb8ad8 100644 --- a/packages/core/src/components/appshell/appshell.tsx +++ b/packages/core/src/components/appshell/appshell.tsx @@ -378,7 +378,7 @@ export const AppShellInternal = (props: AppShellInternalProps) => { // Render inline error UI instead of throwing return (
-
+

Configuration Error

diff --git a/packages/core/src/components/checkbox/checkbox.test.tsx b/packages/core/src/components/checkbox/checkbox.test.tsx index 70c8e17ea..1baf29841 100644 --- a/packages/core/src/components/checkbox/checkbox.test.tsx +++ b/packages/core/src/components/checkbox/checkbox.test.tsx @@ -127,7 +127,7 @@ describe("Checkbox", () => { render(); // shadcn's FormControl signals invalid via aria-invalid (no Base UI Field // in play), so the box must key its destructive styling off it too. - expect(screen.getByRole("checkbox").className).toContain("aria-invalid:border-destructive"); + expect(screen.getByRole("checkbox").className).toContain("aria-invalid:border-danger-solid"); }); describe("Field integration", () => { diff --git a/packages/core/src/components/checkbox/checkbox.tsx b/packages/core/src/components/checkbox/checkbox.tsx index d8c00a8eb..b3574ff31 100644 --- a/packages/core/src/components/checkbox/checkbox.tsx +++ b/packages/core/src/components/checkbox/checkbox.tsx @@ -30,8 +30,8 @@ const checkboxBoxClasses = cn( "astw:data-checked:bg-primary astw:data-checked:border-primary astw:data-checked:text-primary-foreground", "astw:data-indeterminate:bg-primary astw:data-indeterminate:border-primary astw:data-indeterminate:text-primary-foreground", "astw:focus-visible:border-ring astw:focus-visible:ring-ring/50 astw:focus-visible:ring-[3px]", - "astw:data-invalid:border-destructive astw:data-invalid:ring-destructive/20 astw:dark:data-invalid:ring-destructive/40", - "astw:aria-invalid:border-destructive astw:aria-invalid:ring-destructive/20 astw:dark:aria-invalid:ring-destructive/40", + "astw:data-invalid:border-danger-solid astw:data-invalid:ring-danger-solid/20 astw:dark:data-invalid:ring-danger-solid/40", + "astw:aria-invalid:border-danger-solid astw:aria-invalid:ring-danger-solid/20 astw:dark:aria-invalid:ring-danger-solid/40", "astw:data-disabled:cursor-not-allowed", ); diff --git a/packages/core/src/components/csv-importer/CsvImporter.tsx b/packages/core/src/components/csv-importer/CsvImporter.tsx index ff2193f29..9cab6cf9c 100644 --- a/packages/core/src/components/csv-importer/CsvImporter.tsx +++ b/packages/core/src/components/csv-importer/CsvImporter.tsx @@ -425,7 +425,7 @@ function ReviewStep({ type="text" className={cn( "astw:w-full astw:rounded astw:border astw:px-2 astw:py-1 astw:text-sm astw:bg-transparent", - issue?.level === "error" && "astw:border-destructive", + issue?.level === "error" && "astw:border-danger-solid", issue?.level === "warning" && "astw:border-warning-solid", !issue && "astw:border-transparent", )} diff --git a/packages/core/src/components/data-table/toolbar.tsx b/packages/core/src/components/data-table/toolbar.tsx index fb2090b30..e43cc56b7 100644 --- a/packages/core/src/components/data-table/toolbar.tsx +++ b/packages/core/src/components/data-table/toolbar.tsx @@ -990,7 +990,7 @@ function BetweenInputGroup({ "astw:flex astw:items-center astw:h-8 astw:rounded-md astw:border astw:shadow-xs astw:has-focus-visible:ring-[3px]"; const rowOk = "astw:border-input astw:has-focus-visible:border-ring astw:has-focus-visible:ring-ring/50"; - const rowError = "astw:border-destructive astw:has-focus-visible:ring-destructive/30"; + const rowError = "astw:border-danger-solid astw:has-focus-visible:ring-danger-solid/30"; const labelCell = "astw:text-secondary-foreground astw:text-xs astw:px-2.5 astw:border-r astw:border-input astw:bg-muted astw:rounded-l-md astw:h-full astw:flex astw:items-center astw:justify-center astw:shrink-0 astw:min-w-14"; const inputCell = diff --git a/packages/core/src/components/date-field/date-input-group.tsx b/packages/core/src/components/date-field/date-input-group.tsx index 8c445dbec..6da6f0acf 100644 --- a/packages/core/src/components/date-field/date-input-group.tsx +++ b/packages/core/src/components/date-field/date-input-group.tsx @@ -35,7 +35,7 @@ export const groupClasses = cn( // Overrides inputBaseClasses' `min-w-0`; wider locales (e.g. ja-JP) still grow. "astw:flex astw:h-9 astw:min-w-[142px] astw:items-center astw:gap-0 astw:py-0", "astw:focus-within:border-ring astw:focus-within:ring-[3px] astw:focus-within:ring-ring/50", - "astw:data-[invalid]:border-destructive astw:data-[invalid]:ring-destructive/20", + "astw:data-[invalid]:border-danger-solid astw:data-[invalid]:ring-danger-solid/20", "astw:data-[disabled]:cursor-not-allowed astw:data-[disabled]:opacity-50", ); diff --git a/packages/core/src/components/field/field.tsx b/packages/core/src/components/field/field.tsx index adec256b9..1f7c29a5e 100644 --- a/packages/core/src/components/field/field.tsx +++ b/packages/core/src/components/field/field.tsx @@ -140,7 +140,7 @@ function Control({ className, ...props }: React.ComponentProps` tag, so From 2c8b84e8ae0e4671b561eee1bd26c3c41426b7ec Mon Sep 17 00:00:00 2001 From: Hiroki Uemura Date: Tue, 6 Oct 2026 20:48:03 +0900 Subject: [PATCH 05/20] refactor(core): destructive tints use bg-danger-surface Co-Authored-By: Claude Fable 5.1 --- packages/core/src/components/action-panel/ActionPanel.tsx | 2 +- packages/core/src/components/appshell/appshell.tsx | 2 +- packages/core/src/components/csv-importer/CsvImporter.tsx | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/core/src/components/action-panel/ActionPanel.tsx b/packages/core/src/components/action-panel/ActionPanel.tsx index 1b8a98b8e..1883f6b57 100644 --- a/packages/core/src/components/action-panel/ActionPanel.tsx +++ b/packages/core/src/components/action-panel/ActionPanel.tsx @@ -20,7 +20,7 @@ const actionRowInteractiveClasses = "astw:hover:bg-accent astw:hover:text-accent-foreground astw:outline-none astw:focus-visible:ring-2 astw:focus-visible:ring-ring astw:focus-visible:ring-offset-2"; const actionRowDestructiveClasses = - "astw:text-danger-text astw:hover:bg-destructive/10 astw:hover:text-danger-text"; + "astw:text-danger-text astw:hover:bg-danger-surface astw:hover:text-danger-text"; const actionRowDisabledClasses = "astw:pointer-events-none astw:opacity-50"; diff --git a/packages/core/src/components/appshell/appshell.tsx b/packages/core/src/components/appshell/appshell.tsx index 775bb8ad8..96e750038 100644 --- a/packages/core/src/components/appshell/appshell.tsx +++ b/packages/core/src/components/appshell/appshell.tsx @@ -378,7 +378,7 @@ export const AppShellInternal = (props: AppShellInternalProps) => { // Render inline error UI instead of throwing return (
-
+

Configuration Error

diff --git a/packages/core/src/components/csv-importer/CsvImporter.tsx b/packages/core/src/components/csv-importer/CsvImporter.tsx index 9cab6cf9c..236270258 100644 --- a/packages/core/src/components/csv-importer/CsvImporter.tsx +++ b/packages/core/src/components/csv-importer/CsvImporter.tsx @@ -156,7 +156,7 @@ function UploadStep({ /> {error && ( -
+
{error}
@@ -235,7 +235,7 @@ function MappingStep({ key={col.key} className={cn( "astw:border-b astw:border-border astw:last:border-b-0 astw:transition-colors", - !isMapped && col.required && "astw:bg-destructive/5", + !isMapped && col.required && "astw:bg-danger-surface", )} > {/* Status icon */} @@ -416,7 +416,7 @@ function ReviewStep({ key={m.csvHeader} className={cn( "astw:px-1 astw:py-1", - issue?.level === "error" && "astw:bg-destructive/10", + issue?.level === "error" && "astw:bg-danger-surface", issue?.level === "warning" && "astw:bg-warning-surface", )} > From 0665ba7c77841223c6ea963b83ef3fc659f2d302 Mon Sep 17 00:00:00 2001 From: Hiroki Uemura Date: Tue, 6 Oct 2026 20:48:13 +0900 Subject: [PATCH 06/20] refactor(core): themes inherit --destructive from the danger roles Co-Authored-By: Claude Fable 5.1 --- packages/core/src/assets/theme.css | 5 +++-- packages/core/src/assets/themes/_template.css | 8 ++++---- packages/core/src/assets/themes/bloom.css | 2 -- packages/core/src/assets/themes/cream.css | 2 -- 4 files changed, 7 insertions(+), 10 deletions(-) diff --git a/packages/core/src/assets/theme.css b/packages/core/src/assets/theme.css index 97899dee8..e22dcd1a0 100644 --- a/packages/core/src/assets/theme.css +++ b/packages/core/src/assets/theme.css @@ -13,10 +13,11 @@ * 2. SHELL GRADIENT — optional; branded palettes only (bloom, cream). * 3. SYSTEM — surfaces & chrome; copy default or tune for brand tint * (foreground, muted, popover, card, borders). - * 4. PALETTE — optional: radius, charts, shadows, destructive. + * 4. PALETTE — optional: radius, charts, shadows. * * Inherited from default.css — do not duplicate in new palettes: - * --ring, --sidebar-*, --primitive-* (colour scales), --status-*, --alert-*. + * --ring, --sidebar-*, --primitive-* (colour scales), --status-*, --alert-*, + * --destructive, --destructive-foreground (aliases of the danger roles). * The colour roles --{intent}-{role} inherit too; a palette overrides them * only to recolour the status components. * diff --git a/packages/core/src/assets/themes/_template.css b/packages/core/src/assets/themes/_template.css index 6783295fa..882c0e51c 100644 --- a/packages/core/src/assets/themes/_template.css +++ b/packages/core/src/assets/themes/_template.css @@ -17,10 +17,12 @@ * `@tailor-platform/app-shell/styles` in the consuming app. * * DO NOT SET (inherited from `:root` in default.css): - * --ring, --sidebar-*, --primitive-* (colour scales), --status-*, --alert-* + * --ring, --sidebar-*, --primitive-* (colour scales), --status-*, --alert-*, + * --destructive, --destructive-foreground * The colour roles --{info|success|warning|danger|neutral}-{role} also inherit. * Override a role only to recolour the status components (Badge, Alert, ...) - * for this palette; never set --status-* or --alert-*, they alias the roles. + * for this palette; never set --status-*, --alert-* or --destructive*, they alias the roles + * (--destructive is the danger solid; recolour it with --danger-solid). * * PREVIEW: `/showcase/colors` in the Vite example app. * ═══════════════════════════════════════════════════════════════════════════ @@ -57,7 +59,6 @@ --sidebar-border: rgba(229, 229, 229, 1); /* ── 4. PALETTE — optional overrides (else inherited from :root) ──────── */ - --destructive: rgba(220, 38, 38, 1); --radius: 0.625rem; --chart-1: /* brand-adjacent */; --chart-2: /* ... */; @@ -101,7 +102,6 @@ --sidebar-border: rgba(255, 255, 255, 0.1); /* ── 4. PALETTE (dark overrides) ─────────────────────────────────────── */ - --destructive: rgba(248, 113, 113, 1); --chart-1: /* ... */; --chart-2: /* ... */; --chart-3: /* ... */; diff --git a/packages/core/src/assets/themes/bloom.css b/packages/core/src/assets/themes/bloom.css index 2835473a0..6494250e0 100644 --- a/packages/core/src/assets/themes/bloom.css +++ b/packages/core/src/assets/themes/bloom.css @@ -34,7 +34,6 @@ --sidebar-border: rgba(0, 0, 0, 0.08); /* ── 4. PALETTE ───────────────────────────────────────────────────────── */ - --destructive: rgba(220, 38, 38, 1); --radius: 1rem; --chart-1: rgba(83, 90, 232, 1); --chart-2: rgba(0, 151, 156, 1); @@ -78,7 +77,6 @@ --sidebar-border: rgba(255, 255, 255, 0.08); /* ── 4. PALETTE (dark) ────────────────────────────────────────────────── */ - --destructive: rgba(248, 113, 113, 1); --chart-1: rgba(131, 138, 247, 1); --chart-2: rgba(45, 191, 196, 1); --chart-3: rgba(94, 234, 212, 1); diff --git a/packages/core/src/assets/themes/cream.css b/packages/core/src/assets/themes/cream.css index 3d5828707..07d93cd6f 100644 --- a/packages/core/src/assets/themes/cream.css +++ b/packages/core/src/assets/themes/cream.css @@ -34,7 +34,6 @@ --sidebar-border: rgba(0, 0, 0, 0.08); /* ── 4. PALETTE ───────────────────────────────────────────────────────── */ - --destructive: rgba(220, 38, 38, 1); --radius: 1rem; --chart-1: rgba(83, 90, 232, 1); --chart-2: rgba(0, 151, 156, 1); @@ -78,7 +77,6 @@ --sidebar-border: rgba(255, 255, 255, 0.08); /* ── 4. PALETTE (dark) ────────────────────────────────────────────────── */ - --destructive: rgba(248, 113, 113, 1); --chart-1: rgba(131, 138, 247, 1); --chart-2: rgba(45, 191, 196, 1); --chart-3: rgba(94, 234, 212, 1); From 44c6c9a60f138dd90de1e5c14e4f364cdbceacb4 Mon Sep 17 00:00:00 2001 From: Hiroki Uemura Date: Tue, 6 Oct 2026 20:50:00 +0900 Subject: [PATCH 07/20] test(core): cover destructive aliases and Button danger utilities, update snapshots Co-Authored-By: Claude Fable 5.1 --- ...components__ai-chat__ai-chat.test.tsx.snap | 4 +-- ...__components__button__button.test.tsx.snap | 24 +++++++------- ...mponents__checkbox__checkbox.test.tsx.snap | 10 +++--- ...ents__date-field__date-field.test.tsx.snap | 6 ++-- ...ate-field__date-range-picker.test.tsx.snap | 2 +- ...rc__components__field__field.test.tsx.snap | 10 +++--- .../src__components__form__form.test.tsx.snap | 4 +-- ...rc__components__input__input.test.tsx.snap | 12 +++---- ...mponents__textarea__textarea.test.tsx.snap | 8 ++--- ...components__toolbar__toolbar.test.tsx.snap | 2 +- .../core/src/assets/semantic-contrast.test.ts | 16 ++++++++++ .../src/components/button/button.test.tsx | 32 +++++++++++++++++++ 12 files changed, 89 insertions(+), 41 deletions(-) diff --git a/packages/core/__snapshots__/src__components__ai-chat__ai-chat.test.tsx.snap b/packages/core/__snapshots__/src__components__ai-chat__ai-chat.test.tsx.snap index bce0f5fed..a6bedcf54 100644 --- a/packages/core/__snapshots__/src__components__ai-chat__ai-chat.test.tsx.snap +++ b/packages/core/__snapshots__/src__components__ai-chat__ai-chat.test.tsx.snap @@ -1,5 +1,5 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`AIChat > snapshots > default 1`] = `"

Hello there.

"`; +exports[`AIChat > snapshots > default 1`] = `"

Hello there.

"`; -exports[`AIChat > snapshots > with header 1`] = `"
Assistant
"`; +exports[`AIChat > snapshots > with header 1`] = `"
Assistant
"`; diff --git a/packages/core/__snapshots__/src__components__button__button.test.tsx.snap b/packages/core/__snapshots__/src__components__button__button.test.tsx.snap index c172b7310..4663cab3e 100644 --- a/packages/core/__snapshots__/src__components__button__button.test.tsx.snap +++ b/packages/core/__snapshots__/src__components__button__button.test.tsx.snap @@ -1,25 +1,25 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`Button > snapshots > default variant and size 1`] = `""`; +exports[`Button > snapshots > default variant and size 1`] = `""`; -exports[`Button > snapshots > destructive variant 1`] = `""`; +exports[`Button > snapshots > destructive variant 1`] = `""`; -exports[`Button > snapshots > disabled state 1`] = `""`; +exports[`Button > snapshots > disabled state 1`] = `""`; -exports[`Button > snapshots > extra-small size 1`] = `""`; +exports[`Button > snapshots > extra-small size 1`] = `""`; -exports[`Button > snapshots > ghost variant 1`] = `""`; +exports[`Button > snapshots > ghost variant 1`] = `""`; -exports[`Button > snapshots > icon size 1`] = `""`; +exports[`Button > snapshots > icon size 1`] = `""`; -exports[`Button > snapshots > large size 1`] = `""`; +exports[`Button > snapshots > large size 1`] = `""`; -exports[`Button > snapshots > link variant 1`] = `""`; +exports[`Button > snapshots > link variant 1`] = `""`; -exports[`Button > snapshots > outline variant 1`] = `""`; +exports[`Button > snapshots > outline variant 1`] = `""`; -exports[`Button > snapshots > secondary variant 1`] = `""`; +exports[`Button > snapshots > secondary variant 1`] = `""`; -exports[`Button > snapshots > small size 1`] = `""`; +exports[`Button > snapshots > small size 1`] = `""`; -exports[`Button > snapshots > with render prop (as link) 1`] = `"Link Button"`; +exports[`Button > snapshots > with render prop (as link) 1`] = `"Link Button"`; diff --git a/packages/core/__snapshots__/src__components__checkbox__checkbox.test.tsx.snap b/packages/core/__snapshots__/src__components__checkbox__checkbox.test.tsx.snap index 7c10d2068..e8a585192 100644 --- a/packages/core/__snapshots__/src__components__checkbox__checkbox.test.tsx.snap +++ b/packages/core/__snapshots__/src__components__checkbox__checkbox.test.tsx.snap @@ -1,11 +1,11 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`Checkbox > snapshots > checked 1`] = `""`; +exports[`Checkbox > snapshots > checked 1`] = `""`; -exports[`Checkbox > snapshots > disabled 1`] = `""`; +exports[`Checkbox > snapshots > disabled 1`] = `""`; -exports[`Checkbox > snapshots > indeterminate 1`] = `""`; +exports[`Checkbox > snapshots > indeterminate 1`] = `""`; -exports[`Checkbox > snapshots > unchecked 1`] = `""`; +exports[`Checkbox > snapshots > unchecked 1`] = `""`; -exports[`Checkbox > snapshots > with label 1`] = `""`; +exports[`Checkbox > snapshots > with label 1`] = `""`; diff --git a/packages/core/__snapshots__/src__components__date-field__date-field.test.tsx.snap b/packages/core/__snapshots__/src__components__date-field__date-field.test.tsx.snap index ea542ba06..4464f41d5 100644 --- a/packages/core/__snapshots__/src__components__date-field__date-field.test.tsx.snap +++ b/packages/core/__snapshots__/src__components__date-field__date-field.test.tsx.snap @@ -1,7 +1,7 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`snapshots > DateField 1`] = `"
mm
dd
yyyy
"`; +exports[`snapshots > DateField 1`] = `"
mm
dd
yyyy
"`; -exports[`snapshots > DateField — manually labelled + described 1`] = `"
mm
dd
yyyy

Pick a date

Required

"`; +exports[`snapshots > DateField — manually labelled + described 1`] = `"
mm
dd
yyyy

Pick a date

Required

"`; -exports[`snapshots > DatePicker — closed 1`] = `"
mm
dd
yyyy
"`; +exports[`snapshots > DatePicker — closed 1`] = `"
mm
dd
yyyy
"`; diff --git a/packages/core/__snapshots__/src__components__date-field__date-range-picker.test.tsx.snap b/packages/core/__snapshots__/src__components__date-field__date-range-picker.test.tsx.snap index 134c84193..1e0dc0b99 100644 --- a/packages/core/__snapshots__/src__components__date-field__date-range-picker.test.tsx.snap +++ b/packages/core/__snapshots__/src__components__date-field__date-range-picker.test.tsx.snap @@ -1,3 +1,3 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`snapshots > DateRangePicker — closed 1`] = `"
mm
dd
yyyy
mm
dd
yyyy
"`; +exports[`snapshots > DateRangePicker — closed 1`] = `"
mm
dd
yyyy
mm
dd
yyyy
"`; diff --git a/packages/core/__snapshots__/src__components__field__field.test.tsx.snap b/packages/core/__snapshots__/src__components__field__field.test.tsx.snap index d86f84a4d..b00884b4c 100644 --- a/packages/core/__snapshots__/src__components__field__field.test.tsx.snap +++ b/packages/core/__snapshots__/src__components__field__field.test.tsx.snap @@ -1,11 +1,11 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`Field > snapshots > basic field with label and control 1`] = `"
"`; +exports[`Field > snapshots > basic field with label and control 1`] = `"
"`; -exports[`Field > snapshots > disabled field 1`] = `"
"`; +exports[`Field > snapshots > disabled field 1`] = `"
"`; -exports[`Field > snapshots > field with custom className 1`] = `"
"`; +exports[`Field > snapshots > field with custom className 1`] = `"
"`; -exports[`Field > snapshots > field with description 1`] = `"

We will never share your email.

"`; +exports[`Field > snapshots > field with description 1`] = `"

We will never share your email.

"`; -exports[`Field > snapshots > field with error 1`] = `"
Please enter a valid URL.
"`; +exports[`Field > snapshots > field with error 1`] = `"
Please enter a valid URL.
"`; diff --git a/packages/core/__snapshots__/src__components__form__form.test.tsx.snap b/packages/core/__snapshots__/src__components__form__form.test.tsx.snap index 50a1a6737..8e650a7be 100644 --- a/packages/core/__snapshots__/src__components__form__form.test.tsx.snap +++ b/packages/core/__snapshots__/src__components__form__form.test.tsx.snap @@ -1,7 +1,7 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`Form > snapshots > basic form with a field 1`] = `"
"`; +exports[`Form > snapshots > basic form with a field 1`] = `"
"`; exports[`Form > snapshots > form with custom className 1`] = `"
Content
"`; -exports[`Form > snapshots > form with noValidate 1`] = `"
"`; +exports[`Form > snapshots > form with noValidate 1`] = `"
"`; diff --git a/packages/core/__snapshots__/src__components__input__input.test.tsx.snap b/packages/core/__snapshots__/src__components__input__input.test.tsx.snap index 0f907a1f2..d3ccf7465 100644 --- a/packages/core/__snapshots__/src__components__input__input.test.tsx.snap +++ b/packages/core/__snapshots__/src__components__input__input.test.tsx.snap @@ -1,13 +1,13 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`Input > snapshots > default text input 1`] = `""`; +exports[`Input > snapshots > default text input 1`] = `""`; -exports[`Input > snapshots > disabled input 1`] = `""`; +exports[`Input > snapshots > disabled input 1`] = `""`; -exports[`Input > snapshots > email input 1`] = `""`; +exports[`Input > snapshots > email input 1`] = `""`; -exports[`Input > snapshots > file input 1`] = `""`; +exports[`Input > snapshots > file input 1`] = `""`; -exports[`Input > snapshots > password input 1`] = `""`; +exports[`Input > snapshots > password input 1`] = `""`; -exports[`Input > snapshots > with custom className 1`] = `""`; +exports[`Input > snapshots > with custom className 1`] = `""`; diff --git a/packages/core/__snapshots__/src__components__textarea__textarea.test.tsx.snap b/packages/core/__snapshots__/src__components__textarea__textarea.test.tsx.snap index 47cb882d8..b7a1e8b41 100644 --- a/packages/core/__snapshots__/src__components__textarea__textarea.test.tsx.snap +++ b/packages/core/__snapshots__/src__components__textarea__textarea.test.tsx.snap @@ -1,9 +1,9 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`Textarea > snapshots > default 1`] = `""`; +exports[`Textarea > snapshots > default 1`] = `""`; -exports[`Textarea > snapshots > disabled 1`] = `""`; +exports[`Textarea > snapshots > disabled 1`] = `""`; -exports[`Textarea > snapshots > with custom className 1`] = `""`; +exports[`Textarea > snapshots > with custom className 1`] = `""`; -exports[`Textarea > snapshots > with rows 1`] = `""`; +exports[`Textarea > snapshots > with rows 1`] = `""`; diff --git a/packages/core/__snapshots__/src__components__toolbar__toolbar.test.tsx.snap b/packages/core/__snapshots__/src__components__toolbar__toolbar.test.tsx.snap index 95bd03781..8534f44c3 100644 --- a/packages/core/__snapshots__/src__components__toolbar__toolbar.test.tsx.snap +++ b/packages/core/__snapshots__/src__components__toolbar__toolbar.test.tsx.snap @@ -1,3 +1,3 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`Toolbar > renders grouped controls and a separator 1`] = `"
"`; +exports[`Toolbar > renders grouped controls and a separator 1`] = `"
"`; diff --git a/packages/core/src/assets/semantic-contrast.test.ts b/packages/core/src/assets/semantic-contrast.test.ts index 61974dd16..879a63ee7 100644 --- a/packages/core/src/assets/semantic-contrast.test.ts +++ b/packages/core/src/assets/semantic-contrast.test.ts @@ -88,6 +88,8 @@ function contrast(a: Rgba, b: Rgba): number { } const MIN_TEXT = 4.5; +/** WCAG 1.4.11 non-text contrast, for the invalid-input border. */ +const MIN_NON_TEXT = 3; /** Known and accepted: subtle hover text is below AA for some roles. A floor, not a pass. */ const MIN_HOVER_TEXT = 4.2; const modes: Mode[] = ["light", "dark"]; @@ -151,4 +153,18 @@ describe.each(modes)("semantic colour contrast (%s)", (mode) => { ).toBeGreaterThanOrEqual(MIN_TEXT); }); }); + + describe("destructive aliases", () => { + it.each([ + ["--destructive", "--danger-solid"], + ["--destructive-foreground", "--danger-contrast"], + ])("%s resolves to %s", (alias, role) => { + expect(vars.get(alias)).toBe(`var(${role})`); + expect(color(alias)).toEqual(color(role)); + }); + + it.each(pages)("danger solid meets 3:1 against %s (invalid-input border)", (page) => { + expect(contrast(color("--danger-solid"), color(page))).toBeGreaterThanOrEqual(MIN_NON_TEXT); + }); + }); }); diff --git a/packages/core/src/components/button/button.test.tsx b/packages/core/src/components/button/button.test.tsx index d8afb2d24..d18a4c3b5 100644 --- a/packages/core/src/components/button/button.test.tsx +++ b/packages/core/src/components/button/button.test.tsx @@ -1,3 +1,6 @@ +import { readFileSync } from "node:fs"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; import { afterEach, describe, expect, it, vi } from "vitest"; import { cleanup, render, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; @@ -98,6 +101,35 @@ describe("Button", () => { expect(onClick).toHaveBeenCalled(); }); + it("destructive variant uses only bridged danger utilities", () => { + // Precompiled `astw:` utilities emit no CSS when their token is missing + // from the Tailwind bridge, so check every danger class the variant uses. + const bridge = readFileSync( + join(dirname(fileURLToPath(import.meta.url)), "../../assets/theme.bridge.css"), + "utf8", + ); + render(); + const classes = screen.getByRole("button").className.split(/\s+/); + + expect(classes.some((c) => c.includes("destructive"))).toBe(false); + expect(classes).toContain("astw:bg-danger-solid"); + expect(classes).toContain("astw:text-danger-contrast"); + expect(classes).toContain("astw:hover:bg-danger-solid-hover"); + + const tokens = classes + .map( + (c) => + /^astw:(?:dark:)?(?:hover:|focus-visible:)?(?:bg|text|ring)-(danger-[a-z-]+)/.exec( + c, + )?.[1], + ) + .filter((t): t is string => t !== undefined); + expect(tokens.length).toBeGreaterThanOrEqual(3); + for (const token of tokens) { + expect(bridge).toContain(`--color-${token}:`); + } + }); + it("renders different variants", () => { const { rerender } = render(); expect(screen.getByRole("button").className).toContain("bg-primary"); From 0eb782675ad2fe7200df25e9fdab040a2881c50d Mon Sep 17 00:00:00 2001 From: Hiroki Uemura Date: Tue, 6 Oct 2026 20:52:06 +0900 Subject: [PATCH 08/20] docs: document --destructive as the danger solid alias Co-Authored-By: Claude Fable 5.1 --- decisions/semantic-color-roles.md | 14 +++++++++- docs-manifest.json | 26 +++++++++---------- .../concepts/styling-theming.docs.outline.md | 8 +++--- docs-src/guides/migrations.docs.outline.md | 21 +++++++++++++++ docs/concepts/styling-theming.md | 8 +++--- docs/migrations.md | 21 +++++++++++++++ 6 files changed, 78 insertions(+), 20 deletions(-) diff --git a/decisions/semantic-color-roles.md b/decisions/semantic-color-roles.md index 23927ead4..0250fe1b9 100644 --- a/decisions/semantic-color-roles.md +++ b/decisions/semantic-color-roles.md @@ -83,7 +83,7 @@ This step is the status part of a two-axis palette. The rest is planned as follo | ------------------ | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------ | ------------------------------ | | Status (this step) | `--primitive-{blue,green,amber,red}-*` | `--{info,success,warning,danger}-{role}` | done | | Ink and lines | `--primitive-gray-*` | base tokens read it (`--foreground`, `--muted-foreground`, `--border`, `--input`) | done for default; themes later | -| Danger action | red | `--destructive` becomes an alias of `--danger-solid` once its text and border uses move to `--danger-text` / `--danger-border` | next | +| Danger action | red | `--destructive` becomes an alias of `--danger-solid` once its text and border uses move to `--danger-text` / `--danger-border` | done (2026-10-06) | | Brand | an accent scale per theme (bloom and cream read blue, theme-tailor reads cyan) | `--primary`, `--ring` and brand tints read accent steps | with the Theme Generator | `--status-*` and `--alert-*` stay as aliases for consumer code and are deprecated: the docs and changeset say so, and they are removed in the next major. Shipped components read the roles. An ESLint rule in `@tailor-platform/eslint-plugin-app-shell` that flags the old utilities and offers the role as a fix is a separate change. @@ -108,3 +108,15 @@ The component variant names (`Badge` and `Alert` `error`, Badge `neutral`) are p - Alert description text is the same colour as the Alert title in the default palette. - Consumers who override `--status-*` or `--alert-*` keep working for their own `bg-status-*` and `bg-alert-*` utilities, which read those tokens. The shipped components (Badge, Alert, CsvImporter, MetricCard — its trend colours were literal `green-600` / `red-600` before) read the roles, not `--status-*` or `--alert-*`. To recolour them, override the roles (`--{intent}-*`); overriding `--status-*` or `--alert-*` no longer changes the components. `--status-*` and `--alert-*` remain for existing consumer code and are not used by components any more. - cream, bloom and `_template.css` values are not edited. The header comments now list `--{intent}-{role}` among the inherited tokens. + +## Update 2026-10-06: danger action + +The "Danger action" row of the target shape is done. `--destructive` and `--destructive-foreground` are aliases of `--danger-solid` and `--danger-contrast` in `default.css`, light and dark. This supersedes the first item under "Out of scope" and the Consequences bullet that says Button and `text-destructive` still follow `--destructive`. + +- Names stay. `Button variant="destructive"`, `text-destructive`, `bg-destructive` and `border-destructive` keep working and are not deprecated: they are the shadcn vocabulary consumers write. `text-danger-text` is the role to prefer for text. +- Shipped components read the danger roles directly: text uses `text-danger-text`; invalid borders use `border-danger-solid`; focus rings use `ring-danger-solid/20` (dark `/40`, toolbar `/30`); tints use `bg-danger-surface`; the static error box border uses `border-danger-border`. Nothing in `packages/core/src` uses a `destructive` utility. +- Invalid inputs keep the solid. `border-danger-border` (step a6, 1.67:1 on the card) and step 8 (2.62:1 in light mode) are below the 3:1 non-text minimum. The solid is 4.83:1 in light mode and 3.71:1 in dark mode on the card. A unit test holds the solid at 3:1 or more over `--card` and `--background`. +- Dark `Button variant="destructive"` is a full `#dc2626` fill (it was `#f87171` at 60% opacity). The white text on `#dc2626` is 4.83:1; hover is `--danger-solid-hover` (5.66:1). Radix and Primer use the mode's step 9 at full; shadcn uses `dark:bg-destructive/60`. +- `cream`, `bloom` and `_template.css` no longer set `--destructive` (their values equalled the default). `_template.css` lists it under DO NOT SET. A theme that recolours destructive actions overrides `--danger-solid`. +- Visible changes in the default palette: light `text-destructive` goes from `#dc2626` to `#ce0c17` through `text-danger-text`, and dark from `#f87171` to `#ff9083`; the dark invalid border goes from `#f87171` to `#dc2626`; CsvImporter error cells use `bg-danger-surface`, matching the warning cells (`bg-warning-surface`). +- Outside this repository: `cp-apps/apps/couturier/src/theme-tailor.css` still overrides `--destructive`, and the Theme Generator still emits `destructive` as a constant. Both stop reaching the shipped components until they move to `--danger-*`. diff --git a/docs-manifest.json b/docs-manifest.json index dbff2066a..79de7d836 100644 --- a/docs-manifest.json +++ b/docs-manifest.json @@ -65,7 +65,7 @@ "hashes": { "typeSurface": "7cb8a34c1c36eb02", "outline": "797e012e34847b80", - "snapshot": "791be2c6087af41c", + "snapshot": "5b69cb9a28bbcc2e", "outputMd": "824d1aa36bb7f194", "examples": null } @@ -225,7 +225,7 @@ "hashes": { "typeSurface": "16df9927f8394548", "outline": "200f12eb4bec959a", - "snapshot": "d607a315ffce3abc", + "snapshot": "985de4f9c817a3cf", "outputMd": "bce0e2fada1d1638", "examples": "1ee01b1e27764cd2" } @@ -259,7 +259,7 @@ "hashes": { "typeSurface": "3886bf7012f24283", "outline": "916645fe8e5baf10", - "snapshot": "5df639bf40a2bdd7", + "snapshot": "2d057b30b9401e12", "outputMd": "14f7ba23f441df38", "examples": "e8f09fba5acb116f" } @@ -673,7 +673,7 @@ "hashes": { "typeSurface": "7b374d45d53d4535", "outline": "79c9b0f08ca38027", - "snapshot": "4287a03df2854f9a", + "snapshot": "d22d9caf48bb1b3b", "outputMd": "91d901b17aed7781", "examples": null } @@ -894,7 +894,7 @@ "hashes": { "typeSurface": "7c5b64c579f508ee", "outline": "9b273bc9e509f1fe", - "snapshot": "7e0ed658f518fd9a", + "snapshot": "4742086e97cfbdee", "outputMd": "a3a6c9679af99d37", "examples": "fc036f7af21b9dd4" } @@ -1046,9 +1046,9 @@ "symbols": [], "hashes": { "typeSurface": null, - "outline": "45dcfe4383a87ea9", + "outline": "8f4b40e6eb2e5381", "snapshot": null, - "outputMd": "2fa0b6a249d8bd4f", + "outputMd": "80b53c137713bde8", "examples": null } }, @@ -1284,9 +1284,9 @@ "symbols": [], "hashes": { "typeSurface": null, - "outline": "c8a79ca086341d8c", + "outline": "0abe88ee82688f3d", "snapshot": null, - "outputMd": "1277e06bb82432e6", + "outputMd": "be7dd6881d489be5", "examples": "d3cca347b055a503" } }, @@ -1336,7 +1336,7 @@ "hashes": { "typeSurface": "3d9225f7356a29b2", "outline": "40952524e46c333e", - "snapshot": "40f4b39567ab051b", + "snapshot": "e775f88bb9fc2565", "outputMd": "c940bc01cd573334", "examples": "0712ee03ad6ce259" } @@ -1387,7 +1387,7 @@ "hashes": { "typeSurface": "2d88f0306efcaa77", "outline": "1257a84ccd91d5c3", - "snapshot": "3b402cab18147ef9", + "snapshot": "c85c431dcd58bc43", "outputMd": "91d70b140c7bb747", "examples": null } @@ -1656,7 +1656,7 @@ "packages/core/skills/app-shell-patterns/references/concepts/modules-and-resources.md": "bf15b5460f22d165", "packages/core/skills/app-shell-patterns/references/concepts/routing-navigation.md": "4becc2090217bfd2", "packages/core/skills/app-shell-patterns/references/concepts/sidebar-navigation.md": "dc6b3afc049b4148", - "packages/core/skills/app-shell-patterns/references/concepts/styling-theming.md": "1277e06bb82432e6", + "packages/core/skills/app-shell-patterns/references/concepts/styling-theming.md": "be7dd6881d489be5", "packages/core/skills/app-shell-patterns/references/components/action-panel.md": "71794d4b561e4d4b", "packages/core/skills/app-shell-patterns/references/components/activity-card.md": "4f386e49380fb0b0", "packages/core/skills/app-shell-patterns/references/components/ai-chat.md": "824d1aa36bb7f194", @@ -1735,7 +1735,7 @@ "packages/core/skills/app-shell-patterns/references/patterns/interaction-toast.md": "f68bdab855730f48", "packages/core/skills/app-shell-patterns/references/patterns/list-dense-scan.md": "8062ea0df9403fbb", "packages/core/skills/app-shell-patterns/references/pages/document-detail.md": "c3fb2588a0699c26", - "packages/core/skills/app-shell-patterns/references/migrations.md": "013c3fef2303b096", + "packages/core/skills/app-shell-patterns/references/migrations.md": "d5ca48664fedae18", "packages/core/skills/app-shell-patterns/SKILL.md": "814325f4ea06ffae" } } diff --git a/docs-src/concepts/styling-theming.docs.outline.md b/docs-src/concepts/styling-theming.docs.outline.md index dff987ad7..b8f05fc11 100644 --- a/docs-src/concepts/styling-theming.docs.outline.md +++ b/docs-src/concepts/styling-theming.docs.outline.md @@ -152,7 +152,9 @@ Surfaces are named by **role**, not by depth — there is no numbered `surface-1 | `--primary` / `--primary-foreground` | primary buttons, emphasis | `bg-primary` / `text-primary-foreground` | | `--secondary` / `--secondary-foreground` | secondary buttons, neutral chips | `bg-secondary` / `text-secondary-foreground` | | `--accent` / `--accent-foreground` | hover and selected nav states | `bg-accent` / `text-accent-foreground` | -| `--destructive` / `--destructive-foreground` | destructive actions, errors | `bg-destructive` / `text-destructive` | +| `--destructive` / `--destructive-foreground` | alias of danger solid / contrast | `bg-destructive` / `text-destructive` | + +`--destructive` and `--destructive-foreground` are aliases of the danger `solid` and `contrast` roles ([semantic color roles](#semantic-color-roles)), so `bg-destructive` is the danger solid fill in both modes and `Button variant="destructive"` reads the danger roles directly. `text-destructive` still works and resolves to the solid. For text, prefer `text-danger-text`, which meets 4.5:1 on `--card` and `--background`; the solid is `#dc2626` in dark mode, 3.71:1 on the dark card. To recolour destructive actions, override `--danger-solid` (and the other danger roles), not `--destructive`. There are no `-hover` or `-active` brand tokens. Express interaction states with Tailwind variants and opacity — `hover:bg-primary/90`, `active:bg-primary/80` — which is what AppShell's own components do. @@ -195,7 +197,7 @@ In the default palette, `text` on `surface` and `contrast` on `solid` and `solid Most `surface` and `border` values are translucent tints, so an opacity modifier compounds rather than replaces — `bg-info-surface/50` halves the tint's alpha instead of setting it to 50%. Some light-mode values are opaque instead of translucent: the `warning` surface, surface-hover and border, the `info` border and the `danger` surface-hover. They do not blend with a tinted parent. All dark-mode values are translucent. -`Badge`, `Alert`, `CsvImporter` and `MetricCard` read these roles directly, not `--status-*` or `--alert-*`. To recolour them, override the roles. `Badge` `error` and `subtle-error`, and `Alert` `error`, follow `--danger-*`. They no longer follow `--destructive`, which still drives `Button` and `text-destructive`. +`Badge`, `Alert`, `CsvImporter` and `MetricCard` read these roles directly, not `--status-*` or `--alert-*`. To recolour them, override the roles. `Badge` `error` and `subtle-error`, and `Alert` `error`, follow `--danger-*`. `Button variant="destructive"`, invalid input borders and error text follow `--danger-*` too, and `--destructive` is an alias of `--danger-solid`. #### Sidebar & charts @@ -576,7 +578,7 @@ These are visual-composition rules every screen must follow, regardless of patte | Intent | Pick | | ---------------------------------- | ------------------------------------------------------------------------------------------------------- | -| Destructive action (delete, void) | `Button variant="destructive"`; `bg-destructive` on custom surfaces; confirm in a dialog at `shadow-lg` | +| Destructive action (delete, void) | `Button variant="destructive"`; `bg-danger-solid` on custom surface; confirm in a dialog at `shadow-lg` | | Non-blocking caution | `Badge variant="warning"`, or `bg-warning-surface text-warning-text` | | Confirmation / completed state | `Badge variant="success"`, or `bg-success-surface text-success-text` | | Informational callout | `Badge variant="info"`, or `Alert variant="info"` | diff --git a/docs-src/guides/migrations.docs.outline.md b/docs-src/guides/migrations.docs.outline.md index cca666b24..47b4b637b 100644 --- a/docs-src/guides/migrations.docs.outline.md +++ b/docs-src/guides/migrations.docs.outline.md @@ -15,6 +15,7 @@ Each entry states which versions are affected, what breaks, how to detect it, an | Version | Change | | ------------- | ---------------------------------------------------------------------------------------------------------------------------- | +| 1.18.0 | [Destructive colours now resolve to the danger roles](#1180-destructive-colours-now-resolve-to-the-danger-roles) | | 1.17.0 | [Status and alert tokens now resolve to the colour roles](#1170-status-and-alert-tokens-now-resolve-to-the-colour-roles) | | 1.15.0 | [Base UI 1.8.0 breaks closed-`Select` and dialog-click tests](#1150-base-ui-180-breaks-closed-select-and-dialog-click-tests) | | 1.12.0 | [`DateField` / `DatePicker` field chrome moved to `Field.Root`](#1120-datefield--datepicker-field-chrome-moved-to-fieldroot) | @@ -27,6 +28,26 @@ Each entry states which versions are affected, what breaks, how to detect it, an | 1.0.2 | [`Toaster` no longer accepts `richColors`](#102-toaster-no-longer-accepts-richcolors) | | before 1.0 | [Pre-1.0 breaking changes](#before-10) | +## 1.18.0: destructive colours now resolve to the danger roles + +**Applies to:** apps that show `Button variant="destructive"` in dark mode, apps that use `text-destructive`, `border-destructive` or `bg-destructive` on their own elements, and apps whose theme overrides `--destructive` or `--destructive-foreground`. + +1.18.0 makes `--destructive` and `--destructive-foreground` aliases of `--danger-solid` and `--danger-contrast`. The names, `Button variant="destructive"` and `text-destructive` keep working and are not deprecated. Nothing errors. Three things change silently: + +- **Dark-mode `Button variant="destructive"` is a full `#dc2626` fill.** It was `#f87171` at 60% opacity. The text is `--danger-contrast` (white) and the hover fill is `--danger-solid-hover` instead of 90% opacity. Light mode keeps `#dc2626`. +- **`text-destructive` resolves to the solid.** In dark mode it changes from `#f87171` to `#dc2626`, which is 3.71:1 on the dark card, below 4.5:1 for text. Components shipped with AppShell now use `text-danger-text` for error text, and invalid borders and focus rings read `--danger-solid`. In dark mode the invalid border changes from `#f87171` to `#dc2626`. +- **Themes that override `--destructive` no longer reach the components.** `Button`, invalid `Input`, `Textarea`, `Checkbox`, `Field` and `DateField` borders and rings, error text and the `CsvImporter` error cells read `--danger-*`. An override of `--destructive` still changes `bg-destructive` and `text-destructive` on your own elements, so the two reds can drift apart. The `cream` and `bloom` palettes no longer set `--destructive`; their values were the same as the default. + +How to detect it: search the app for `destructive` in class names and in theme CSS, including Theme Generator output that sets `--destructive`. Compare `Button variant="destructive"`, an invalid `Input` and an error message in light and dark mode after upgrading. + +What to change: + +- For text, replace `text-destructive` with `text-danger-text`. `text-destructive` still works; use it only where the solid colour is intended. +- For tints and borders, use `bg-danger-surface` and `border-danger-border` instead of `bg-destructive/10` and `border-destructive/30`. +- In a theme that recolours destructive actions, set `--danger-solid` (and `--danger-solid-hover`, `--danger-text`, `--danger-contrast` as needed) instead of `--destructive`. Remove `--destructive` and `--destructive-foreground` from the theme. + +The roles are documented in [Styling & theming](./concepts/styling-theming.md#brand--action). The decision record is `decisions/semantic-color-roles.md`. + ## 1.17.0: status and alert tokens now resolve to the colour roles **Applies to:** apps that use `bg-status-*`, `text-status-*` or `bg-alert-*` utilities, and apps whose theme overrides `--status-*`, `--alert-*` or `--destructive` to recolour `Badge`, `Alert`, `CsvImporter` or `MetricCard`. diff --git a/docs/concepts/styling-theming.md b/docs/concepts/styling-theming.md index b553fc46d..78aeee1f1 100644 --- a/docs/concepts/styling-theming.md +++ b/docs/concepts/styling-theming.md @@ -169,7 +169,9 @@ Surfaces are named by **role**, not by depth — there is no numbered `surface-1 | `--primary` / `--primary-foreground` | primary buttons, emphasis | `bg-primary` / `text-primary-foreground` | | `--secondary` / `--secondary-foreground` | secondary buttons, neutral chips | `bg-secondary` / `text-secondary-foreground` | | `--accent` / `--accent-foreground` | hover and selected nav states | `bg-accent` / `text-accent-foreground` | -| `--destructive` / `--destructive-foreground` | destructive actions, errors | `bg-destructive` / `text-destructive` | +| `--destructive` / `--destructive-foreground` | alias of danger solid / contrast | `bg-destructive` / `text-destructive` | + +`--destructive` and `--destructive-foreground` are aliases of the danger `solid` and `contrast` roles ([semantic color roles](#semantic-color-roles)), so `bg-destructive` is the danger solid fill in both modes and `Button variant="destructive"` reads the danger roles directly. `text-destructive` still works and resolves to the solid. For text, prefer `text-danger-text`, which meets 4.5:1 on `--card` and `--background`; the solid is `#dc2626` in dark mode, 3.71:1 on the dark card. To recolour destructive actions, override `--danger-solid` (and the other danger roles), not `--destructive`. There are no `-hover` or `-active` brand tokens. Express interaction states with Tailwind variants and opacity — `hover:bg-primary/90`, `active:bg-primary/80` — which is what AppShell's own components do. @@ -212,7 +214,7 @@ In the default palette, `text` on `surface` and `contrast` on `solid` and `solid Most `surface` and `border` values are translucent tints, so an opacity modifier compounds rather than replaces — `bg-info-surface/50` halves the tint's alpha instead of setting it to 50%. Some light-mode values are opaque instead of translucent: the `warning` surface, surface-hover and border, the `info` border and the `danger` surface-hover. They do not blend with a tinted parent. All dark-mode values are translucent. -`Badge`, `Alert`, `CsvImporter` and `MetricCard` read these roles directly, not `--status-*` or `--alert-*`. To recolour them, override the roles. `Badge` `error` and `subtle-error`, and `Alert` `error`, follow `--danger-*`. They no longer follow `--destructive`, which still drives `Button` and `text-destructive`. +`Badge`, `Alert`, `CsvImporter` and `MetricCard` read these roles directly, not `--status-*` or `--alert-*`. To recolour them, override the roles. `Badge` `error` and `subtle-error`, and `Alert` `error`, follow `--danger-*`. `Button variant="destructive"`, invalid input borders and error text follow `--danger-*` too, and `--destructive` is an alias of `--danger-solid`. #### Sidebar & charts @@ -593,7 +595,7 @@ These are visual-composition rules every screen must follow, regardless of patte | Intent | Pick | | ---------------------------------- | ------------------------------------------------------------------------------------------------------- | -| Destructive action (delete, void) | `Button variant="destructive"`; `bg-destructive` on custom surfaces; confirm in a dialog at `shadow-lg` | +| Destructive action (delete, void) | `Button variant="destructive"`; `bg-danger-solid` on custom surface; confirm in a dialog at `shadow-lg` | | Non-blocking caution | `Badge variant="warning"`, or `bg-warning-surface text-warning-text` | | Confirmation / completed state | `Badge variant="success"`, or `bg-success-surface text-success-text` | | Informational callout | `Badge variant="info"`, or `Alert variant="info"` | diff --git a/docs/migrations.md b/docs/migrations.md index 4ffc91542..a0849155f 100644 --- a/docs/migrations.md +++ b/docs/migrations.md @@ -15,6 +15,7 @@ Each entry states which versions are affected, what breaks, how to detect it, an | Version | Change | | ------------- | ---------------------------------------------------------------------------------------------------------------------------- | +| 1.18.0 | [Destructive colours now resolve to the danger roles](#1180-destructive-colours-now-resolve-to-the-danger-roles) | | 1.17.0 | [Status and alert tokens now resolve to the colour roles](#1170-status-and-alert-tokens-now-resolve-to-the-colour-roles) | | 1.15.0 | [Base UI 1.8.0 breaks closed-`Select` and dialog-click tests](#1150-base-ui-180-breaks-closed-select-and-dialog-click-tests) | | 1.12.0 | [`DateField` / `DatePicker` field chrome moved to `Field.Root`](#1120-datefield--datepicker-field-chrome-moved-to-fieldroot) | @@ -27,6 +28,26 @@ Each entry states which versions are affected, what breaks, how to detect it, an | 1.0.2 | [`Toaster` no longer accepts `richColors`](#102-toaster-no-longer-accepts-richcolors) | | before 1.0 | [Pre-1.0 breaking changes](#before-10) | +## 1.18.0: destructive colours now resolve to the danger roles + +**Applies to:** apps that show `Button variant="destructive"` in dark mode, apps that use `text-destructive`, `border-destructive` or `bg-destructive` on their own elements, and apps whose theme overrides `--destructive` or `--destructive-foreground`. + +1.18.0 makes `--destructive` and `--destructive-foreground` aliases of `--danger-solid` and `--danger-contrast`. The names, `Button variant="destructive"` and `text-destructive` keep working and are not deprecated. Nothing errors. Three things change silently: + +- **Dark-mode `Button variant="destructive"` is a full `#dc2626` fill.** It was `#f87171` at 60% opacity. The text is `--danger-contrast` (white) and the hover fill is `--danger-solid-hover` instead of 90% opacity. Light mode keeps `#dc2626`. +- **`text-destructive` resolves to the solid.** In dark mode it changes from `#f87171` to `#dc2626`, which is 3.71:1 on the dark card, below 4.5:1 for text. Components shipped with AppShell now use `text-danger-text` for error text, and invalid borders and focus rings read `--danger-solid`. In dark mode the invalid border changes from `#f87171` to `#dc2626`. +- **Themes that override `--destructive` no longer reach the components.** `Button`, invalid `Input`, `Textarea`, `Checkbox`, `Field` and `DateField` borders and rings, error text and the `CsvImporter` error cells read `--danger-*`. An override of `--destructive` still changes `bg-destructive` and `text-destructive` on your own elements, so the two reds can drift apart. The `cream` and `bloom` palettes no longer set `--destructive`; their values were the same as the default. + +How to detect it: search the app for `destructive` in class names and in theme CSS, including Theme Generator output that sets `--destructive`. Compare `Button variant="destructive"`, an invalid `Input` and an error message in light and dark mode after upgrading. + +What to change: + +- For text, replace `text-destructive` with `text-danger-text`. `text-destructive` still works; use it only where the solid colour is intended. +- For tints and borders, use `bg-danger-surface` and `border-danger-border` instead of `bg-destructive/10` and `border-destructive/30`. +- In a theme that recolours destructive actions, set `--danger-solid` (and `--danger-solid-hover`, `--danger-text`, `--danger-contrast` as needed) instead of `--destructive`. Remove `--destructive` and `--destructive-foreground` from the theme. + +The roles are documented in [Styling & theming](./concepts/styling-theming.md#brand--action). The decision record is `decisions/semantic-color-roles.md`. + ## 1.17.0: status and alert tokens now resolve to the colour roles **Applies to:** apps that use `bg-status-*`, `text-status-*` or `bg-alert-*` utilities, and apps whose theme overrides `--status-*`, `--alert-*` or `--destructive` to recolour `Badge`, `Alert`, `CsvImporter` or `MetricCard`. From 39f68e2dc3fd36a5ec8c23c9bdc5199a207c40fc Mon Sep 17 00:00:00 2001 From: Hiroki Uemura Date: Tue, 6 Oct 2026 20:52:11 +0900 Subject: [PATCH 09/20] chore: add changeset for the destructive danger alias Co-Authored-By: Claude Fable 5.1 --- .changeset/danger-destructive-alias.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .changeset/danger-destructive-alias.md diff --git a/.changeset/danger-destructive-alias.md b/.changeset/danger-destructive-alias.md new file mode 100644 index 000000000..fdfae7647 --- /dev/null +++ b/.changeset/danger-destructive-alias.md @@ -0,0 +1,13 @@ +--- +"@tailor-platform/app-shell": minor +--- + +`--destructive` and `--destructive-foreground` are now aliases of `--danger-solid` and `--danger-contrast`, and the shipped components read the danger roles instead of `destructive` utilities. The names, `Button variant="destructive"` and `text-destructive` keep working. + +Visible changes in the default palette: + +- Dark-mode `Button variant="destructive"` is a full `#dc2626` fill with white text. It was `#f87171` at 60% opacity. Hover uses `--danger-solid-hover`. +- `text-destructive` now resolves to the solid colour: `#dc2626` in dark mode (was `#f87171`), which is below 4.5:1 as text on the dark card. Error text in components uses `text-danger-text` (light `#ce0c17`, dark `#ff9083`). Use `text-danger-text` for text in your own code. +- Invalid borders and focus rings on `Input`, `Textarea`, `Checkbox`, `Field` and `DateField` read `--danger-solid`. The dark invalid border changes from `#f87171` to `#dc2626` (3.71:1 on the card). +- `CsvImporter` error cells use `bg-danger-surface`, matching the warning cells. +- A theme that overrides `--destructive` no longer changes the shipped components; override `--danger-solid` (and the other danger roles) instead. `cream`, `bloom` and the theme template no longer set `--destructive`; their values were the same as the default. From 3fee8a95d352049fbc37dc3c87e9f46f907fbe64 Mon Sep 17 00:00:00 2001 From: Hiroki Uemura Date: Mon, 12 Oct 2026 17:04:48 +0900 Subject: [PATCH 10/20] refactor(core): default palette surfaces read the gray, white and alpha scales --- packages/core/src/assets/themes/default.css | 94 ++++++++++++++------- 1 file changed, 62 insertions(+), 32 deletions(-) diff --git a/packages/core/src/assets/themes/default.css b/packages/core/src/assets/themes/default.css index 0a76bc497..af56a5ab7 100644 --- a/packages/core/src/assets/themes/default.css +++ b/packages/core/src/assets/themes/default.css @@ -8,12 +8,12 @@ color-scheme: light; /* ── 1. BRAND ─────────────────────────────────────────────────────────── */ - --primary: rgba(23, 23, 23, 1); - --primary-foreground: rgba(250, 250, 250, 1); - --secondary: rgba(245, 245, 245, 1); - --secondary-foreground: rgba(23, 23, 23, 1); + --primary: var(--primitive-gray-900); + --primary-foreground: var(--primitive-gray-50); + --secondary: var(--primitive-gray-100); + --secondary-foreground: var(--primitive-gray-900); --accent: rgb(237, 237, 237); - --accent-foreground: rgba(10, 10, 10, 1); + --accent-foreground: var(--primitive-gray-950); /* ── 2. SHELL GRADIENT — not used (flat palette) ──────────────────────── */ @@ -26,19 +26,19 @@ --sidebar-ring: var(--ring); /* ── 4. SYSTEM ────────────────────────────────────────────────────────── */ - --background: rgba(250, 250, 250, 1); + --background: var(--primitive-gray-50); --foreground: var(--primitive-gray-950); - --card: rgba(255, 255, 255, 1); - --card-foreground: rgba(10, 10, 10, 1); - --popover: rgba(255, 255, 255, 1); - --popover-foreground: rgba(10, 10, 10, 1); - --muted: rgba(245, 245, 245, 1); + --card: var(--primitive-white); + --card-foreground: var(--primitive-gray-950); + --popover: var(--primitive-white); + --popover-foreground: var(--primitive-gray-950); + --muted: var(--primitive-gray-100); --muted-foreground: var(--primitive-gray-500); --border: var(--primitive-gray-200); --input: var(--primitive-gray-200); - --sidebar: rgba(250, 250, 250, 1); - --sidebar-foreground: rgba(10, 10, 10, 1); - --sidebar-border: rgba(229, 229, 229, 1); + --sidebar: var(--primitive-gray-50); + --sidebar-foreground: var(--primitive-gray-950); + --sidebar-border: var(--primitive-gray-200); /* ── 5. PALETTE ───────────────────────────────────────────────────────── */ --destructive: rgba(220, 38, 38, 1); @@ -65,27 +65,27 @@ color-scheme: dark; /* ── 1. BRAND (dark) ──────────────────────────────────────────────────── */ - --primary: rgba(229, 229, 229, 1); - --primary-foreground: rgba(23, 23, 23, 1); - --secondary: rgba(38, 38, 38, 1); - --secondary-foreground: rgba(250, 250, 250, 1); + --primary: var(--primitive-gray-200); + --primary-foreground: var(--primitive-gray-900); + --secondary: var(--primitive-gray-800); + --secondary-foreground: var(--primitive-gray-50); --accent: rgb(48, 48, 48); - --accent-foreground: rgba(250, 250, 250, 1); + --accent-foreground: var(--primitive-gray-50); /* ── 4. SYSTEM (dark) ─────────────────────────────────────────────────── */ - --background: rgba(10, 10, 10, 1); + --background: var(--primitive-gray-950); --foreground: var(--primitive-gray-50); - --card: rgba(23, 23, 23, 1); - --card-foreground: rgba(250, 250, 250, 1); - --popover: rgba(38, 38, 38, 1); - --popover-foreground: rgba(250, 250, 250, 1); - --muted: rgba(38, 38, 38, 1); + --card: var(--primitive-gray-900); + --card-foreground: var(--primitive-gray-50); + --popover: var(--primitive-gray-800); + --popover-foreground: var(--primitive-gray-50); + --muted: var(--primitive-gray-800); --muted-foreground: var(--primitive-gray-400); - --border: rgba(255, 255, 255, 0.1); - --input: rgba(255, 255, 255, 0.15); - --sidebar: rgba(10, 10, 10, 1); - --sidebar-foreground: rgba(250, 250, 250, 1); - --sidebar-border: rgba(255, 255, 255, 0.1); + --border: var(--primitive-white-a2); + --input: var(--primitive-white-a3); + --sidebar: var(--primitive-gray-950); + --sidebar-foreground: var(--primitive-gray-50); + --sidebar-border: var(--primitive-white-a2); /* ── 5. PALETTE (dark) ────────────────────────────────────────────────── */ --destructive: rgba(248, 113, 113, 1); @@ -112,8 +112,11 @@ * resolve to opaque hex. * * Gray is the Tailwind `neutral` family (the values this palette already used) - * and is the same in both modes, so it is declared once; the SYSTEM text and - * line tokens (4) read it. Surfaces stay literal: they are theme inputs. + * and is the same in both modes, so it is declared once. White and the white / + * black alpha scales are declared with it. The BRAND (1) and SYSTEM (4) tokens + * of this palette read these scales. Other palettes still set their own + * literal values; the scales are not a theme input yet. `--accent` and the + * shadows have no step and stay literal. */ :root { /* gray — both modes */ @@ -129,6 +132,33 @@ --primitive-gray-900: #171717; --primitive-gray-950: #0a0a0a; + /* white, and white / black alpha — both modes (Radix whiteA / blackA) */ + --primitive-white: #ffffff; + --primitive-white-a1: rgba(255, 255, 255, 0.05); + --primitive-white-a2: rgba(255, 255, 255, 0.1); + --primitive-white-a3: rgba(255, 255, 255, 0.15); + --primitive-white-a4: rgba(255, 255, 255, 0.2); + --primitive-white-a5: rgba(255, 255, 255, 0.3); + --primitive-white-a6: rgba(255, 255, 255, 0.4); + --primitive-white-a7: rgba(255, 255, 255, 0.5); + --primitive-white-a8: rgba(255, 255, 255, 0.6); + --primitive-white-a9: rgba(255, 255, 255, 0.7); + --primitive-white-a10: rgba(255, 255, 255, 0.8); + --primitive-white-a11: rgba(255, 255, 255, 0.9); + --primitive-white-a12: rgba(255, 255, 255, 0.95); + --primitive-black-a1: rgba(0, 0, 0, 0.05); + --primitive-black-a2: rgba(0, 0, 0, 0.1); + --primitive-black-a3: rgba(0, 0, 0, 0.15); + --primitive-black-a4: rgba(0, 0, 0, 0.2); + --primitive-black-a5: rgba(0, 0, 0, 0.3); + --primitive-black-a6: rgba(0, 0, 0, 0.4); + --primitive-black-a7: rgba(0, 0, 0, 0.5); + --primitive-black-a8: rgba(0, 0, 0, 0.6); + --primitive-black-a9: rgba(0, 0, 0, 0.7); + --primitive-black-a10: rgba(0, 0, 0, 0.8); + --primitive-black-a11: rgba(0, 0, 0, 0.9); + --primitive-black-a12: rgba(0, 0, 0, 0.95); + /* hues — light */ --primitive-blue-1: #f7f8f9; --primitive-blue-2: #f0f4fa; From 668e1bcf0caefd8cd70134a56efca0d6850fb4a6 Mon Sep 17 00:00:00 2001 From: Hiroki Uemura Date: Mon, 12 Oct 2026 17:19:38 +0900 Subject: [PATCH 11/20] feat(core): add a --backdrop role for modal backdrops --- .../src__components__dialog__dialog.test.tsx.snap | 4 ++-- .../src__components__sheet__sheet.test.tsx.snap | 6 +++--- packages/core/src/assets/theme.bridge.css | 1 + packages/core/src/assets/theme.css | 2 +- packages/core/src/assets/themes/_template.css | 2 +- packages/core/src/assets/themes/default.css | 1 + packages/core/src/components/csv-importer/CsvImporter.tsx | 2 +- packages/core/src/components/dialog/dialog.tsx | 2 +- packages/core/src/components/sheet/sheet.tsx | 2 +- 9 files changed, 12 insertions(+), 10 deletions(-) diff --git a/packages/core/__snapshots__/src__components__dialog__dialog.test.tsx.snap b/packages/core/__snapshots__/src__components__dialog__dialog.test.tsx.snap index 8a18066e3..2023c299b 100644 --- a/packages/core/__snapshots__/src__components__dialog__dialog.test.tsx.snap +++ b/packages/core/__snapshots__/src__components__dialog__dialog.test.tsx.snap @@ -2,6 +2,6 @@ exports[`Dialog > snapshots > closed dialog (trigger only) 1`] = `""`; -exports[`Dialog > snapshots > open dialog with header 1`] = `"
"`; +exports[`Dialog > snapshots > open dialog with header 1`] = `"
"`; -exports[`Dialog > snapshots > open dialog with header and footer 1`] = `"
"`; +exports[`Dialog > snapshots > open dialog with header and footer 1`] = `"
"`; diff --git a/packages/core/__snapshots__/src__components__sheet__sheet.test.tsx.snap b/packages/core/__snapshots__/src__components__sheet__sheet.test.tsx.snap index c7e5b7147..10e75cc78 100644 --- a/packages/core/__snapshots__/src__components__sheet__sheet.test.tsx.snap +++ b/packages/core/__snapshots__/src__components__sheet__sheet.test.tsx.snap @@ -4,8 +4,8 @@ exports[`Sheet > snapshots > closed sheet (trigger only) 1`] = `"
"`; -exports[`Sheet > snapshots > open sheet (left side) 1`] = `"
"`; +exports[`Sheet > snapshots > open sheet (left side) 1`] = `"
"`; -exports[`Sheet > snapshots > open sheet (right side, default) 1`] = `"
"`; +exports[`Sheet > snapshots > open sheet (right side, default) 1`] = `"
"`; -exports[`Sheet > snapshots > open sheet with header and footer 1`] = `"
"`; +exports[`Sheet > snapshots > open sheet with header and footer 1`] = `"
"`; diff --git a/packages/core/src/assets/theme.bridge.css b/packages/core/src/assets/theme.bridge.css index 31d9cc0b5..ba691f90c 100644 --- a/packages/core/src/assets/theme.bridge.css +++ b/packages/core/src/assets/theme.bridge.css @@ -105,6 +105,7 @@ --color-border: var(--border); --color-input: var(--input); --color-ring: var(--ring); + --color-backdrop: var(--backdrop); --color-chart-1: var(--chart-1); --color-chart-2: var(--chart-2); --color-chart-3: var(--chart-3); diff --git a/packages/core/src/assets/theme.css b/packages/core/src/assets/theme.css index fac3798e7..7ab38be85 100644 --- a/packages/core/src/assets/theme.css +++ b/packages/core/src/assets/theme.css @@ -16,7 +16,7 @@ * 4. PALETTE — optional: radius, charts, shadows, destructive. * * Inherited from default.css — do not duplicate in new palettes: - * --ring, --sidebar-*, --primitive-* (colour scales), --status-*, --alert-*, + * --ring, --sidebar-*, --backdrop, --primitive-* (colour scales), --status-*, --alert-*, * --app-shell-type-* (typography roles). * The colour roles --{intent}-{role} inherit too; a palette overrides them * only to recolour the status components. Typography roles are inherited diff --git a/packages/core/src/assets/themes/_template.css b/packages/core/src/assets/themes/_template.css index a4dd7e390..6b007ff25 100644 --- a/packages/core/src/assets/themes/_template.css +++ b/packages/core/src/assets/themes/_template.css @@ -17,7 +17,7 @@ * `@tailor-platform/app-shell/styles` in the consuming app. * * DO NOT SET (inherited from `:root` in default.css): - * --ring, --sidebar-*, --primitive-* (colour scales), --status-*, --alert-*, + * --ring, --sidebar-*, --backdrop, --primitive-* (colour scales), --status-*, --alert-*, * --app-shell-type-* * The colour roles --{info|success|warning|danger|neutral}-{role} also inherit. * Override a role only to recolour the status components (Badge, Alert, ...) diff --git a/packages/core/src/assets/themes/default.css b/packages/core/src/assets/themes/default.css index af56a5ab7..9f5c6b17f 100644 --- a/packages/core/src/assets/themes/default.css +++ b/packages/core/src/assets/themes/default.css @@ -24,6 +24,7 @@ --sidebar-accent: var(--accent); --sidebar-accent-foreground: var(--accent-foreground); --sidebar-ring: var(--ring); + --backdrop: var(--primitive-black-a7); /* ── 4. SYSTEM ────────────────────────────────────────────────────────── */ --background: var(--primitive-gray-50); diff --git a/packages/core/src/components/csv-importer/CsvImporter.tsx b/packages/core/src/components/csv-importer/CsvImporter.tsx index 3cc9b9253..9e1d4c509 100644 --- a/packages/core/src/components/csv-importer/CsvImporter.tsx +++ b/packages/core/src/components/csv-importer/CsvImporter.tsx @@ -711,7 +711,7 @@ export function CsvImporter({ return ( - + Date: Mon, 12 Oct 2026 17:19:44 +0900 Subject: [PATCH 12/20] docs: document --backdrop and the default palette reading the neutral scales --- .changeset/neutral-surfaces-scale.md | 7 +++++++ decisions/semantic-color-roles.md | 11 +++++++++++ docs-manifest.json | 10 +++++----- docs-src/concepts/styling-theming.docs.outline.md | 5 +++++ docs/concepts/styling-theming.md | 5 +++++ 5 files changed, 33 insertions(+), 5 deletions(-) create mode 100644 .changeset/neutral-surfaces-scale.md diff --git a/.changeset/neutral-surfaces-scale.md b/.changeset/neutral-surfaces-scale.md new file mode 100644 index 000000000..5c75d8a71 --- /dev/null +++ b/.changeset/neutral-surfaces-scale.md @@ -0,0 +1,7 @@ +--- +"@tailor-platform/app-shell": minor +--- + +Add a `--backdrop` colour token and the `bg-backdrop` utility for the dim layer behind modals. `Dialog`, `Sheet` and the CsvImporter drawer use it instead of `bg-black/50`. The value is the same (black at 50%), so nothing changes on screen. Override `--backdrop` on `:root` to change every backdrop at once. + +In the default palette, the surface, border and brand colours now read the internal gray scale, a white step, and white / black alpha steps. All values are unchanged. The `cream` and `bloom` palettes are not changed. diff --git a/decisions/semantic-color-roles.md b/decisions/semantic-color-roles.md index 23927ead4..1c48ad4ea 100644 --- a/decisions/semantic-color-roles.md +++ b/decisions/semantic-color-roles.md @@ -108,3 +108,14 @@ The component variant names (`Badge` and `Alert` `error`, Badge `neutral`) are p - Alert description text is the same colour as the Alert title in the default palette. - Consumers who override `--status-*` or `--alert-*` keep working for their own `bg-status-*` and `bg-alert-*` utilities, which read those tokens. The shipped components (Badge, Alert, CsvImporter, MetricCard — its trend colours were literal `green-600` / `red-600` before) read the roles, not `--status-*` or `--alert-*`. To recolour them, override the roles (`--{intent}-*`); overriding `--status-*` or `--alert-*` no longer changes the components. `--status-*` and `--alert-*` remain for existing consumer code and are not used by components any more. - cream, bloom and `_template.css` values are not edited. The header comments now list `--{intent}-{role}` among the inherited tokens. + +## Update 2026-10-12: default surfaces, white and alpha scales + +The default palette's surfaces and brand tokens read scale steps. For the default palette, this supersedes "all surfaces stay literal" and "Gray has no alpha steps" under Hue scales, and the second item under "Out of scope". + +- New primitives, declared once with gray because they are the same in both modes: `--primitive-white` and `--primitive-{white,black}-a1` to `-a12`. The alpha values are Radix `whiteA` / `blackA` (0.05, 0.1, 0.15, 0.2, 0.3, then 0.1 steps to 0.9, and 0.95). They are not bridged, like the other primitives. +- In `default.css`, every BRAND and SYSTEM colour with an exact step now reads it. Light: background and sidebar 50, secondary and muted 100, sidebar border 200, primary 900, text 950, card and popover white. Dark: primary 200, secondary, popover and muted 800, card 900, background and sidebar 950, foregrounds 50, border and sidebar border white a2, input white a3. No values change: every token resolves to the same colour as before in light and dark mode. +- White is its own step. The light page is gray 50 and cards are white; putting cards on gray 50 would remove the difference. +- New role `--backdrop`, `var(--primitive-black-a7)` (50%) in both modes, bridged as `bg-backdrop`. `Dialog`, `Sheet` and the CsvImporter drawer use it instead of `bg-black/50`; the value is the same. The name follows the CSS `::backdrop` pseudo-element and the Base UI `Backdrop` part. It is not `overlay`, because `--z-overlay` already names the modal layer, which holds both the backdrop and the popup. +- Still literal: `--accent` (light 237, dark 48) has no step; moving it to gray 200 / 700 changes the hover surface, so it belongs to the theme step. The shadows (alpha varies by size, and the light tint is slate), the chart colours and `--destructive` also stay literal. +- `cream`, `bloom` and `_template.css` keep their own literal values. Their lines (black 8% in light, white 8% and 12% in dark) do not match an alpha step. Moving the themes onto scales is the theme step, together with the Theme Generator. The header comments now list `--backdrop` among the inherited tokens. diff --git a/docs-manifest.json b/docs-manifest.json index 163a15912..7911ef6dc 100644 --- a/docs-manifest.json +++ b/docs-manifest.json @@ -608,7 +608,7 @@ "hashes": { "typeSurface": "b1614970f950ceb6", "outline": "bda40376d6e3e008", - "snapshot": "b845d4c54453b0f0", + "snapshot": "ac70f5a7004d3bab", "outputMd": "ec6a0ac098d1d87a", "examples": "934c52d4e8761404" } @@ -1186,7 +1186,7 @@ "hashes": { "typeSurface": "e760949efcbe4c38", "outline": "464d91acc48f7c9d", - "snapshot": "40d15c3f1003fdb0", + "snapshot": "2c9114c5a81198be", "outputMd": "1f81ebd562c50371", "examples": "b0eebb8c5e2219dc" } @@ -1287,9 +1287,9 @@ "symbols": [], "hashes": { "typeSurface": null, - "outline": "e63d837190e71ff1", + "outline": "7369a064bd4d422b", "snapshot": null, - "outputMd": "112b43c697d30369", + "outputMd": "fcfff0b2584c2e74", "examples": "4d19f4f688e0f0ac" } }, @@ -1659,7 +1659,7 @@ "packages/core/skills/app-shell-patterns/references/concepts/modules-and-resources.md": "bf15b5460f22d165", "packages/core/skills/app-shell-patterns/references/concepts/routing-navigation.md": "8034c7f885994b29", "packages/core/skills/app-shell-patterns/references/concepts/sidebar-navigation.md": "dc6b3afc049b4148", - "packages/core/skills/app-shell-patterns/references/concepts/styling-theming.md": "112b43c697d30369", + "packages/core/skills/app-shell-patterns/references/concepts/styling-theming.md": "fcfff0b2584c2e74", "packages/core/skills/app-shell-patterns/references/components/action-panel.md": "71794d4b561e4d4b", "packages/core/skills/app-shell-patterns/references/components/activity-card.md": "4f386e49380fb0b0", "packages/core/skills/app-shell-patterns/references/components/ai-chat.md": "824d1aa36bb7f194", diff --git a/docs-src/concepts/styling-theming.docs.outline.md b/docs-src/concepts/styling-theming.docs.outline.md index c2d5d721e..ed2312cea 100644 --- a/docs-src/concepts/styling-theming.docs.outline.md +++ b/docs-src/concepts/styling-theming.docs.outline.md @@ -142,9 +142,14 @@ Colors follow shadcn-style semantic naming: a surface token and its matching `-f | `--border` | hairlines, dividers | `border-border` | | `--input` | form control borders | `border-input` | | `--ring` | focus rings | `ring-ring` / `outline-ring` | +| `--backdrop` | dim layer behind a modal | `bg-backdrop` | Surfaces are named by **role**, not by depth — there is no numbered `surface-1/2/3` ladder, and no third tier of body text below `muted-foreground`. Stack depth with `background` → `card` → `muted` plus a border or shadow, and reach for `popover` when the surface actually floats. +`--backdrop` is the dim layer behind `Dialog` and `Sheet` (black at 50% in both modes). Use `bg-backdrop` for your own modal layers instead of `bg-black/50`. Palettes inherit it; override it on `:root` to change every backdrop at once. + +In the default palette these tokens read the internal gray scale, white, and white alpha steps, so their values match the gray used by text and lines. The other palettes set their own values. + #### Brand & action | Token | Use | Tailwind | diff --git a/docs/concepts/styling-theming.md b/docs/concepts/styling-theming.md index 61a21f8dc..1ad9a592a 100644 --- a/docs/concepts/styling-theming.md +++ b/docs/concepts/styling-theming.md @@ -159,9 +159,14 @@ Colors follow shadcn-style semantic naming: a surface token and its matching `-f | `--border` | hairlines, dividers | `border-border` | | `--input` | form control borders | `border-input` | | `--ring` | focus rings | `ring-ring` / `outline-ring` | +| `--backdrop` | dim layer behind a modal | `bg-backdrop` | Surfaces are named by **role**, not by depth — there is no numbered `surface-1/2/3` ladder, and no third tier of body text below `muted-foreground`. Stack depth with `background` → `card` → `muted` plus a border or shadow, and reach for `popover` when the surface actually floats. +`--backdrop` is the dim layer behind `Dialog` and `Sheet` (black at 50% in both modes). Use `bg-backdrop` for your own modal layers instead of `bg-black/50`. Palettes inherit it; override it on `:root` to change every backdrop at once. + +In the default palette these tokens read the internal gray scale, white, and white alpha steps, so their values match the gray used by text and lines. The other palettes set their own values. + #### Brand & action | Token | Use | Tailwind | From 37599915af55eb8d9b08215ac74b23592609ebc3 Mon Sep 17 00:00:00 2001 From: Hiroki Uemura Date: Mon, 12 Oct 2026 17:54:56 +0900 Subject: [PATCH 13/20] test(core): pin the default neutral token values and the --backdrop bridge --- .../core/src/assets/semantic-contrast.test.ts | 53 +++++++++++++++++++ packages/core/src/assets/theme-bridge.test.ts | 7 +++ 2 files changed, 60 insertions(+) diff --git a/packages/core/src/assets/semantic-contrast.test.ts b/packages/core/src/assets/semantic-contrast.test.ts index 61974dd16..f6e4be462 100644 --- a/packages/core/src/assets/semantic-contrast.test.ts +++ b/packages/core/src/assets/semantic-contrast.test.ts @@ -152,3 +152,56 @@ describe.each(modes)("semantic colour contrast (%s)", (mode) => { }); }); }); + +describe("default palette neutral tokens", () => { + // Pinned when the BRAND and SYSTEM tokens moved from literals onto the gray, + // white and alpha scales. That move was meant to keep every value, so a step + // change that alters one of these colours fails here. + const expected: Record> = { + light: { + "--primary": "#171717", + "--primary-foreground": "#fafafa", + "--secondary": "#f5f5f5", + "--secondary-foreground": "#171717", + "--accent-foreground": "#0a0a0a", + "--background": "#fafafa", + "--card": "#ffffff", + "--card-foreground": "#0a0a0a", + "--popover": "#ffffff", + "--popover-foreground": "#0a0a0a", + "--muted": "#f5f5f5", + "--border": "#e5e5e5", + "--input": "#e5e5e5", + "--sidebar": "#fafafa", + "--sidebar-foreground": "#0a0a0a", + "--sidebar-border": "#e5e5e5", + "--backdrop": "rgba(0, 0, 0, 0.5)", + }, + dark: { + "--primary": "#e5e5e5", + "--primary-foreground": "#171717", + "--secondary": "#262626", + "--secondary-foreground": "#fafafa", + "--accent-foreground": "#fafafa", + "--background": "#0a0a0a", + "--card": "#171717", + "--card-foreground": "#fafafa", + "--popover": "#262626", + "--popover-foreground": "#fafafa", + "--muted": "#262626", + "--border": "rgba(255, 255, 255, 0.1)", + "--input": "rgba(255, 255, 255, 0.15)", + "--sidebar": "#0a0a0a", + "--sidebar-foreground": "#fafafa", + "--sidebar-border": "rgba(255, 255, 255, 0.1)", + "--backdrop": "rgba(0, 0, 0, 0.5)", + }, + }; + + describe.each(["light", "dark"] as const)("%s mode", (mode) => { + const vars = variablesFor(mode); + it.each(Object.entries(expected[mode]))("%s resolves to %s", (token, value) => { + expect(parseColor(`var(${token})`, vars)).toEqual(parseColor(value, vars)); + }); + }); +}); diff --git a/packages/core/src/assets/theme-bridge.test.ts b/packages/core/src/assets/theme-bridge.test.ts index 86fb5ef0c..f8036da02 100644 --- a/packages/core/src/assets/theme-bridge.test.ts +++ b/packages/core/src/assets/theme-bridge.test.ts @@ -156,4 +156,11 @@ describe("theme bridge", () => { } }); }); + + it("defines --backdrop in the default palette and bridges it as bg-backdrop", () => { + // Dialog, Sheet and the CsvImporter drawer use `bg-backdrop`; without both + // lines the backdrop compiles to an undefined variable and turns transparent. + expect(defaultTheme).toMatch(/^\s*--backdrop:\s*var\(--primitive-black-a7\);/m); + expect(bridge).toMatch(/^\s*--color-backdrop:\s*var\(--backdrop\);/m); + }); }); From f599e2c0547977fde1aaa9b4d390addbf3c33539 Mon Sep 17 00:00:00 2001 From: Hiroki Uemura Date: Mon, 12 Oct 2026 17:54:59 +0900 Subject: [PATCH 14/20] docs: correct the token step list and computed-value notes for the neutral scales --- .changeset/neutral-surfaces-scale.md | 2 +- decisions/semantic-color-roles.md | 2 +- docs-manifest.json | 12 ++++++------ docs-src/concepts/styling-theming.docs.outline.md | 2 +- docs-src/guides/migrations.docs.outline.md | 2 +- docs/concepts/styling-theming.md | 2 +- docs/migrations.md | 2 +- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.changeset/neutral-surfaces-scale.md b/.changeset/neutral-surfaces-scale.md index 5c75d8a71..d6b8db91a 100644 --- a/.changeset/neutral-surfaces-scale.md +++ b/.changeset/neutral-surfaces-scale.md @@ -4,4 +4,4 @@ Add a `--backdrop` colour token and the `bg-backdrop` utility for the dim layer behind modals. `Dialog`, `Sheet` and the CsvImporter drawer use it instead of `bg-black/50`. The value is the same (black at 50%), so nothing changes on screen. Override `--backdrop` on `:root` to change every backdrop at once. -In the default palette, the surface, border and brand colours now read the internal gray scale, a white step, and white / black alpha steps. All values are unchanged. The `cream` and `bloom` palettes are not changed. +In the default palette, the surface, border and brand colours now read the internal gray scale, a white step, and white alpha steps. All colours are unchanged. If your code reads these tokens with `getComputedStyle(...).getPropertyValue()`, the string is now the step's notation, for example `#171717` instead of `rgba(23, 23, 23, 1)`. The `cream` and `bloom` palettes are not changed. diff --git a/decisions/semantic-color-roles.md b/decisions/semantic-color-roles.md index 1c48ad4ea..66a413434 100644 --- a/decisions/semantic-color-roles.md +++ b/decisions/semantic-color-roles.md @@ -114,7 +114,7 @@ The component variant names (`Badge` and `Alert` `error`, Badge `neutral`) are p The default palette's surfaces and brand tokens read scale steps. For the default palette, this supersedes "all surfaces stay literal" and "Gray has no alpha steps" under Hue scales, and the second item under "Out of scope". - New primitives, declared once with gray because they are the same in both modes: `--primitive-white` and `--primitive-{white,black}-a1` to `-a12`. The alpha values are Radix `whiteA` / `blackA` (0.05, 0.1, 0.15, 0.2, 0.3, then 0.1 steps to 0.9, and 0.95). They are not bridged, like the other primitives. -- In `default.css`, every BRAND and SYSTEM colour with an exact step now reads it. Light: background and sidebar 50, secondary and muted 100, sidebar border 200, primary 900, text 950, card and popover white. Dark: primary 200, secondary, popover and muted 800, card 900, background and sidebar 950, foregrounds 50, border and sidebar border white a2, input white a3. No values change: every token resolves to the same colour as before in light and dark mode. +- In `default.css`, every BRAND and SYSTEM colour with an exact step now reads it. Light: background, sidebar and primary foreground 50, secondary and muted 100, sidebar border 200, primary and secondary foreground 900, the other foregrounds 950, card and popover white. Dark: primary 200, secondary, popover and muted 800, card and primary foreground 900, background and sidebar 950, the other foregrounds 50, border and sidebar border white a2, input white a3. No values change: every token resolves to the same colour as before in light and dark mode. - White is its own step. The light page is gray 50 and cards are white; putting cards on gray 50 would remove the difference. - New role `--backdrop`, `var(--primitive-black-a7)` (50%) in both modes, bridged as `bg-backdrop`. `Dialog`, `Sheet` and the CsvImporter drawer use it instead of `bg-black/50`; the value is the same. The name follows the CSS `::backdrop` pseudo-element and the Base UI `Backdrop` part. It is not `overlay`, because `--z-overlay` already names the modal layer, which holds both the backdrop and the popup. - Still literal: `--accent` (light 237, dark 48) has no step; moving it to gray 200 / 700 changes the hover surface, so it belongs to the theme step. The shadows (alpha varies by size, and the light tint is slate), the chart colours and `--destructive` also stay literal. diff --git a/docs-manifest.json b/docs-manifest.json index 7911ef6dc..024b4e8b4 100644 --- a/docs-manifest.json +++ b/docs-manifest.json @@ -1049,9 +1049,9 @@ "symbols": [], "hashes": { "typeSurface": null, - "outline": "45dcfe4383a87ea9", + "outline": "36cdcfab060a0d1c", "snapshot": null, - "outputMd": "2fa0b6a249d8bd4f", + "outputMd": "86fe04e370ecdff5", "examples": null } }, @@ -1287,9 +1287,9 @@ "symbols": [], "hashes": { "typeSurface": null, - "outline": "7369a064bd4d422b", + "outline": "18061fd0dd60d63d", "snapshot": null, - "outputMd": "fcfff0b2584c2e74", + "outputMd": "bce2991c993061b6", "examples": "4d19f4f688e0f0ac" } }, @@ -1659,7 +1659,7 @@ "packages/core/skills/app-shell-patterns/references/concepts/modules-and-resources.md": "bf15b5460f22d165", "packages/core/skills/app-shell-patterns/references/concepts/routing-navigation.md": "8034c7f885994b29", "packages/core/skills/app-shell-patterns/references/concepts/sidebar-navigation.md": "dc6b3afc049b4148", - "packages/core/skills/app-shell-patterns/references/concepts/styling-theming.md": "fcfff0b2584c2e74", + "packages/core/skills/app-shell-patterns/references/concepts/styling-theming.md": "bce2991c993061b6", "packages/core/skills/app-shell-patterns/references/components/action-panel.md": "71794d4b561e4d4b", "packages/core/skills/app-shell-patterns/references/components/activity-card.md": "4f386e49380fb0b0", "packages/core/skills/app-shell-patterns/references/components/ai-chat.md": "824d1aa36bb7f194", @@ -1738,7 +1738,7 @@ "packages/core/skills/app-shell-patterns/references/patterns/interaction-toast.md": "f68bdab855730f48", "packages/core/skills/app-shell-patterns/references/patterns/list-dense-scan.md": "916baa3cd2b9cbf1", "packages/core/skills/app-shell-patterns/references/pages/document-detail.md": "717ef4e7ae835aff", - "packages/core/skills/app-shell-patterns/references/migrations.md": "013c3fef2303b096", + "packages/core/skills/app-shell-patterns/references/migrations.md": "1918f794d1bd4e99", "packages/core/skills/app-shell-patterns/SKILL.md": "12527965a47cf42f" } } diff --git a/docs-src/concepts/styling-theming.docs.outline.md b/docs-src/concepts/styling-theming.docs.outline.md index ed2312cea..1d5cd1ee5 100644 --- a/docs-src/concepts/styling-theming.docs.outline.md +++ b/docs-src/concepts/styling-theming.docs.outline.md @@ -148,7 +148,7 @@ Surfaces are named by **role**, not by depth — there is no numbered `surface-1 `--backdrop` is the dim layer behind `Dialog` and `Sheet` (black at 50% in both modes). Use `bg-backdrop` for your own modal layers instead of `bg-black/50`. Palettes inherit it; override it on `:root` to change every backdrop at once. -In the default palette these tokens read the internal gray scale, white, and white alpha steps, so their values match the gray used by text and lines. The other palettes set their own values. +In the default palette, the surface, text and line tokens in this table read the internal gray scale, a white step, and white alpha steps (dark-mode lines), so their values match the gray used across the palette. `--backdrop` reads a black alpha step, and `--ring` is mixed from `--primary`. The other palettes set their own values. #### Brand & action diff --git a/docs-src/guides/migrations.docs.outline.md b/docs-src/guides/migrations.docs.outline.md index cca666b24..12ed007ca 100644 --- a/docs-src/guides/migrations.docs.outline.md +++ b/docs-src/guides/migrations.docs.outline.md @@ -184,7 +184,7 @@ body { Toggle dark mode and confirm a real surface changes: inspect a `Card` and watch its computed `background-color` go from `rgb(255, 255, 255)` to `rgb(23, 23, 23)` on the default palette. -Reading the token directly also works — `getComputedStyle(document.documentElement).getPropertyValue("--card")` returns the winning declaration, so a stale copy shows up as its own value. Just compare against the authored notation: AppShell writes `rgba(23, 23, 23, 1)`, not `#171717`, and the computed value preserves that form. +Reading the token directly also works — `getComputedStyle(document.documentElement).getPropertyValue("--card")` returns the winning declaration, so a stale copy shows up as its own value. Compare against the value the default palette resolves to: in dark mode `--card` returns `#171717`, the gray step it reads. (Releases before the default palette moved onto its colour scales returned `rgba(23, 23, 23, 1)`.) A copy in your own CSS shows up in the notation you wrote. ## 1.5.0: `loader` removed from file-based page definitions diff --git a/docs/concepts/styling-theming.md b/docs/concepts/styling-theming.md index 1ad9a592a..122488282 100644 --- a/docs/concepts/styling-theming.md +++ b/docs/concepts/styling-theming.md @@ -165,7 +165,7 @@ Surfaces are named by **role**, not by depth — there is no numbered `surface-1 `--backdrop` is the dim layer behind `Dialog` and `Sheet` (black at 50% in both modes). Use `bg-backdrop` for your own modal layers instead of `bg-black/50`. Palettes inherit it; override it on `:root` to change every backdrop at once. -In the default palette these tokens read the internal gray scale, white, and white alpha steps, so their values match the gray used by text and lines. The other palettes set their own values. +In the default palette, the surface, text and line tokens in this table read the internal gray scale, a white step, and white alpha steps (dark-mode lines), so their values match the gray used across the palette. `--backdrop` reads a black alpha step, and `--ring` is mixed from `--primary`. The other palettes set their own values. #### Brand & action diff --git a/docs/migrations.md b/docs/migrations.md index 4ffc91542..df276264a 100644 --- a/docs/migrations.md +++ b/docs/migrations.md @@ -184,7 +184,7 @@ body { Toggle dark mode and confirm a real surface changes: inspect a `Card` and watch its computed `background-color` go from `rgb(255, 255, 255)` to `rgb(23, 23, 23)` on the default palette. -Reading the token directly also works — `getComputedStyle(document.documentElement).getPropertyValue("--card")` returns the winning declaration, so a stale copy shows up as its own value. Just compare against the authored notation: AppShell writes `rgba(23, 23, 23, 1)`, not `#171717`, and the computed value preserves that form. +Reading the token directly also works — `getComputedStyle(document.documentElement).getPropertyValue("--card")` returns the winning declaration, so a stale copy shows up as its own value. Compare against the value the default palette resolves to: in dark mode `--card` returns `#171717`, the gray step it reads. (Releases before the default palette moved onto its colour scales returned `rgba(23, 23, 23, 1)`.) A copy in your own CSS shows up in the notation you wrote. ## 1.5.0: `loader` removed from file-based page definitions From 2b4765affcf55dedc316c652ca2c38867e49dcb8 Mon Sep 17 00:00:00 2001 From: Hiroki Uemura Date: Mon, 12 Oct 2026 18:00:53 +0900 Subject: [PATCH 15/20] test(core): pin --foreground and --muted-foreground with the other neutral tokens --- packages/core/src/assets/semantic-contrast.test.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/core/src/assets/semantic-contrast.test.ts b/packages/core/src/assets/semantic-contrast.test.ts index f6e4be462..c9e431aba 100644 --- a/packages/core/src/assets/semantic-contrast.test.ts +++ b/packages/core/src/assets/semantic-contrast.test.ts @@ -159,6 +159,8 @@ describe("default palette neutral tokens", () => { // change that alters one of these colours fails here. const expected: Record> = { light: { + "--foreground": "#0a0a0a", + "--muted-foreground": "#737373", "--primary": "#171717", "--primary-foreground": "#fafafa", "--secondary": "#f5f5f5", @@ -178,6 +180,8 @@ describe("default palette neutral tokens", () => { "--backdrop": "rgba(0, 0, 0, 0.5)", }, dark: { + "--foreground": "#fafafa", + "--muted-foreground": "#a3a3a3", "--primary": "#e5e5e5", "--primary-foreground": "#171717", "--secondary": "#262626", From 60965fed3b467ce0efbb034003b7dedac245718e Mon Sep 17 00:00:00 2001 From: Hiroki Uemura Date: Mon, 12 Oct 2026 18:17:59 +0900 Subject: [PATCH 16/20] docs(decisions): keep the update to what this change does --- decisions/semantic-color-roles.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/decisions/semantic-color-roles.md b/decisions/semantic-color-roles.md index 66a413434..20a488f88 100644 --- a/decisions/semantic-color-roles.md +++ b/decisions/semantic-color-roles.md @@ -117,5 +117,5 @@ The default palette's surfaces and brand tokens read scale steps. For the defaul - In `default.css`, every BRAND and SYSTEM colour with an exact step now reads it. Light: background, sidebar and primary foreground 50, secondary and muted 100, sidebar border 200, primary and secondary foreground 900, the other foregrounds 950, card and popover white. Dark: primary 200, secondary, popover and muted 800, card and primary foreground 900, background and sidebar 950, the other foregrounds 50, border and sidebar border white a2, input white a3. No values change: every token resolves to the same colour as before in light and dark mode. - White is its own step. The light page is gray 50 and cards are white; putting cards on gray 50 would remove the difference. - New role `--backdrop`, `var(--primitive-black-a7)` (50%) in both modes, bridged as `bg-backdrop`. `Dialog`, `Sheet` and the CsvImporter drawer use it instead of `bg-black/50`; the value is the same. The name follows the CSS `::backdrop` pseudo-element and the Base UI `Backdrop` part. It is not `overlay`, because `--z-overlay` already names the modal layer, which holds both the backdrop and the popup. -- Still literal: `--accent` (light 237, dark 48) has no step; moving it to gray 200 / 700 changes the hover surface, so it belongs to the theme step. The shadows (alpha varies by size, and the light tint is slate), the chart colours and `--destructive` also stay literal. -- `cream`, `bloom` and `_template.css` keep their own literal values. Their lines (black 8% in light, white 8% and 12% in dark) do not match an alpha step. Moving the themes onto scales is the theme step, together with the Theme Generator. The header comments now list `--backdrop` among the inherited tokens. +- `--accent`, the shadows, the chart colours and `--destructive` stay literal. They have no matching step. +- `cream`, `bloom` and `_template.css` keep their own literal values. The header comments now list `--backdrop` among the inherited tokens. From 0aa1390b2fb32c26a8e718594777539d2fc888e5 Mon Sep 17 00:00:00 2001 From: Hiroki Uemura Date: Wed, 14 Oct 2026 09:02:14 +0900 Subject: [PATCH 17/20] docs(core): mark destructive tokens and utilities as deprecated --destructive, --destructive-foreground and their utilities will be removed in the next major (tailor-inc/platform-planning#2084), the same as --status-* and --alert-*. Button variant="destructive" stays undeprecated until a danger variant exists. Co-Authored-By: Claude Opus 5.5 --- .changeset/danger-destructive-alias.md | 2 ++ decisions/semantic-color-roles.md | 2 +- docs-manifest.json | 12 ++++++------ .../concepts/styling-theming.docs.outline.md | 18 ++++++++++-------- docs-src/guides/migrations.docs.outline.md | 4 ++-- docs/concepts/styling-theming.md | 18 ++++++++++-------- docs/migrations.md | 4 ++-- 7 files changed, 33 insertions(+), 27 deletions(-) diff --git a/.changeset/danger-destructive-alias.md b/.changeset/danger-destructive-alias.md index fdfae7647..1022c0da4 100644 --- a/.changeset/danger-destructive-alias.md +++ b/.changeset/danger-destructive-alias.md @@ -4,6 +4,8 @@ `--destructive` and `--destructive-foreground` are now aliases of `--danger-solid` and `--danger-contrast`, and the shipped components read the danger roles instead of `destructive` utilities. The names, `Button variant="destructive"` and `text-destructive` keep working. +`--destructive`, `--destructive-foreground` and their utilities (`bg-destructive`, `text-destructive`, `border-destructive`, `text-destructive-foreground`) are deprecated and will be removed in the next major. They keep working in 1.x; new code uses the danger roles (`bg-danger-solid`, `text-danger-text`, `border-danger-border`, `text-danger-contrast`). `Button variant="destructive"` has no replacement yet, so it is not deprecated in this release; the next major renames it to `danger`. + Visible changes in the default palette: - Dark-mode `Button variant="destructive"` is a full `#dc2626` fill with white text. It was `#f87171` at 60% opacity. Hover uses `--danger-solid-hover`. diff --git a/decisions/semantic-color-roles.md b/decisions/semantic-color-roles.md index 0250fe1b9..8a8735789 100644 --- a/decisions/semantic-color-roles.md +++ b/decisions/semantic-color-roles.md @@ -113,7 +113,7 @@ The component variant names (`Badge` and `Alert` `error`, Badge `neutral`) are p The "Danger action" row of the target shape is done. `--destructive` and `--destructive-foreground` are aliases of `--danger-solid` and `--danger-contrast` in `default.css`, light and dark. This supersedes the first item under "Out of scope" and the Consequences bullet that says Button and `text-destructive` still follow `--destructive`. -- Names stay. `Button variant="destructive"`, `text-destructive`, `bg-destructive` and `border-destructive` keep working and are not deprecated: they are the shadcn vocabulary consumers write. `text-danger-text` is the role to prefer for text. +- Names stay in 1.x but are deprecated. `--destructive`, `--destructive-foreground` and their utilities (`bg-destructive`, `text-destructive`, `border-destructive`, `text-destructive-foreground`) keep working and will be removed in the next major, the same as `--status-*` and `--alert-*` ([platform-planning#2084](https://github.com/tailor-inc/platform-planning/issues/2084)). Keeping a second name for one colour leaves two vocabularies, and `text-destructive` is below 4.5:1 as text on the dark card. `text-danger-text` is the role to prefer for text. `Button variant="destructive"` is not deprecated yet because there is no `danger` variant to move to; the next major renames it. - Shipped components read the danger roles directly: text uses `text-danger-text`; invalid borders use `border-danger-solid`; focus rings use `ring-danger-solid/20` (dark `/40`, toolbar `/30`); tints use `bg-danger-surface`; the static error box border uses `border-danger-border`. Nothing in `packages/core/src` uses a `destructive` utility. - Invalid inputs keep the solid. `border-danger-border` (step a6, 1.67:1 on the card) and step 8 (2.62:1 in light mode) are below the 3:1 non-text minimum. The solid is 4.83:1 in light mode and 3.71:1 in dark mode on the card. A unit test holds the solid at 3:1 or more over `--card` and `--background`. - Dark `Button variant="destructive"` is a full `#dc2626` fill (it was `#f87171` at 60% opacity). The white text on `#dc2626` is 4.83:1; hover is `--danger-solid-hover` (5.66:1). Radix and Primer use the mode's step 9 at full; shadcn uses `dark:bg-destructive/60`. diff --git a/docs-manifest.json b/docs-manifest.json index d1bcf9695..7f588adbb 100644 --- a/docs-manifest.json +++ b/docs-manifest.json @@ -1049,9 +1049,9 @@ "symbols": [], "hashes": { "typeSurface": null, - "outline": "8f4b40e6eb2e5381", + "outline": "16c1723900750d80", "snapshot": null, - "outputMd": "80b53c137713bde8", + "outputMd": "f095aa7055b83481", "examples": null } }, @@ -1304,9 +1304,9 @@ "symbols": [], "hashes": { "typeSurface": null, - "outline": "ef379deb65bcba3e", + "outline": "dcf033e2a63f0f03", "snapshot": null, - "outputMd": "74855a46c0284abc", + "outputMd": "3ebd9f5828c49ed7", "examples": "4d19f4f688e0f0ac" } }, @@ -1676,7 +1676,7 @@ "packages/core/skills/app-shell-patterns/references/concepts/modules-and-resources.md": "bf15b5460f22d165", "packages/core/skills/app-shell-patterns/references/concepts/routing-navigation.md": "8034c7f885994b29", "packages/core/skills/app-shell-patterns/references/concepts/sidebar-navigation.md": "e342ef02dc2b9b86", - "packages/core/skills/app-shell-patterns/references/concepts/styling-theming.md": "74855a46c0284abc", + "packages/core/skills/app-shell-patterns/references/concepts/styling-theming.md": "3ebd9f5828c49ed7", "packages/core/skills/app-shell-patterns/references/components/action-panel.md": "71794d4b561e4d4b", "packages/core/skills/app-shell-patterns/references/components/activity-card.md": "4f386e49380fb0b0", "packages/core/skills/app-shell-patterns/references/components/ai-chat.md": "824d1aa36bb7f194", @@ -1756,7 +1756,7 @@ "packages/core/skills/app-shell-patterns/references/patterns/interaction-toast.md": "f68bdab855730f48", "packages/core/skills/app-shell-patterns/references/patterns/list-dense-scan.md": "916baa3cd2b9cbf1", "packages/core/skills/app-shell-patterns/references/pages/document-detail.md": "717ef4e7ae835aff", - "packages/core/skills/app-shell-patterns/references/migrations.md": "d5ca48664fedae18", + "packages/core/skills/app-shell-patterns/references/migrations.md": "afad0935852bec9f", "packages/core/skills/app-shell-patterns/SKILL.md": "526decbfd2db6555" } } diff --git a/docs-src/concepts/styling-theming.docs.outline.md b/docs-src/concepts/styling-theming.docs.outline.md index 6acb9b572..70f6ad9f1 100644 --- a/docs-src/concepts/styling-theming.docs.outline.md +++ b/docs-src/concepts/styling-theming.docs.outline.md @@ -147,14 +147,16 @@ Surfaces are named by **role**, not by depth — there is no numbered `surface-1 #### Brand & action -| Token | Use | Tailwind | -| -------------------------------------------- | -------------------------------- | -------------------------------------------- | -| `--primary` / `--primary-foreground` | primary buttons, emphasis | `bg-primary` / `text-primary-foreground` | -| `--secondary` / `--secondary-foreground` | secondary buttons, neutral chips | `bg-secondary` / `text-secondary-foreground` | -| `--accent` / `--accent-foreground` | hover and selected nav states | `bg-accent` / `text-accent-foreground` | -| `--destructive` / `--destructive-foreground` | alias of danger solid / contrast | `bg-destructive` / `text-destructive` | - -`--destructive` and `--destructive-foreground` are aliases of the danger `solid` and `contrast` roles ([semantic color roles](#semantic-color-roles)), so `bg-destructive` is the danger solid fill in both modes and `Button variant="destructive"` reads the danger roles directly. `text-destructive` still works and resolves to the solid. For text, prefer `text-danger-text`, which meets 4.5:1 on `--card` and `--background`; the solid is `#dc2626` in dark mode, 3.71:1 on the dark card. To recolour destructive actions, override `--danger-solid` (and the other danger roles), not `--destructive`. +| Token | Use | Tailwind | +| -------------------------------------------- | ------------------------------------------- | -------------------------------------------- | +| `--primary` / `--primary-foreground` | primary buttons, emphasis | `bg-primary` / `text-primary-foreground` | +| `--secondary` / `--secondary-foreground` | secondary buttons, neutral chips | `bg-secondary` / `text-secondary-foreground` | +| `--accent` / `--accent-foreground` | hover and selected nav states | `bg-accent` / `text-accent-foreground` | +| `--destructive` / `--destructive-foreground` | deprecated alias of danger solid / contrast | `bg-destructive` / `text-destructive` | + +**Deprecated.** `--destructive`, `--destructive-foreground` and their utilities remain for existing code and will be removed in the next major. New code uses the danger roles: `bg-danger-solid`, `text-danger-contrast`, and `text-danger-text` for text. `Button variant="destructive"` is not deprecated yet; the next major renames it to `danger`. + +`--destructive` and `--destructive-foreground` are aliases of the danger `solid` and `contrast` roles ([semantic color roles](#semantic-color-roles)), so `bg-destructive` is the danger solid fill in both modes and `Button variant="destructive"` reads the danger roles directly. `text-destructive` still works and resolves to the solid. For text, use `text-danger-text`, which meets 4.5:1 on `--card` and `--background`; the solid is `#dc2626` in dark mode, 3.71:1 on the dark card. To recolour destructive actions, override `--danger-solid` (and the other danger roles), not `--destructive`. There are no `-hover` or `-active` brand tokens. Express interaction states with Tailwind variants and opacity — `hover:bg-primary/90`, `active:bg-primary/80` — which is what AppShell's own components do. diff --git a/docs-src/guides/migrations.docs.outline.md b/docs-src/guides/migrations.docs.outline.md index 47b4b637b..f11e727ed 100644 --- a/docs-src/guides/migrations.docs.outline.md +++ b/docs-src/guides/migrations.docs.outline.md @@ -32,7 +32,7 @@ Each entry states which versions are affected, what breaks, how to detect it, an **Applies to:** apps that show `Button variant="destructive"` in dark mode, apps that use `text-destructive`, `border-destructive` or `bg-destructive` on their own elements, and apps whose theme overrides `--destructive` or `--destructive-foreground`. -1.18.0 makes `--destructive` and `--destructive-foreground` aliases of `--danger-solid` and `--danger-contrast`. The names, `Button variant="destructive"` and `text-destructive` keep working and are not deprecated. Nothing errors. Three things change silently: +1.18.0 makes `--destructive` and `--destructive-foreground` aliases of `--danger-solid` and `--danger-contrast`. The names, `Button variant="destructive"` and `text-destructive` keep working. Nothing errors. `--destructive`, `--destructive-foreground` and their utilities are deprecated and will be removed in the next major; `Button variant="destructive"` will be renamed to `danger` then. Three things change silently: - **Dark-mode `Button variant="destructive"` is a full `#dc2626` fill.** It was `#f87171` at 60% opacity. The text is `--danger-contrast` (white) and the hover fill is `--danger-solid-hover` instead of 90% opacity. Light mode keeps `#dc2626`. - **`text-destructive` resolves to the solid.** In dark mode it changes from `#f87171` to `#dc2626`, which is 3.71:1 on the dark card, below 4.5:1 for text. Components shipped with AppShell now use `text-danger-text` for error text, and invalid borders and focus rings read `--danger-solid`. In dark mode the invalid border changes from `#f87171` to `#dc2626`. @@ -42,7 +42,7 @@ How to detect it: search the app for `destructive` in class names and in theme C What to change: -- For text, replace `text-destructive` with `text-danger-text`. `text-destructive` still works; use it only where the solid colour is intended. +- For text, replace `text-destructive` with `text-danger-text`. For fills, replace `bg-destructive` with `bg-danger-solid`, and `text-destructive-foreground` with `text-danger-contrast`. - For tints and borders, use `bg-danger-surface` and `border-danger-border` instead of `bg-destructive/10` and `border-destructive/30`. - In a theme that recolours destructive actions, set `--danger-solid` (and `--danger-solid-hover`, `--danger-text`, `--danger-contrast` as needed) instead of `--destructive`. Remove `--destructive` and `--destructive-foreground` from the theme. diff --git a/docs/concepts/styling-theming.md b/docs/concepts/styling-theming.md index af168cc80..3c7380969 100644 --- a/docs/concepts/styling-theming.md +++ b/docs/concepts/styling-theming.md @@ -164,14 +164,16 @@ Surfaces are named by **role**, not by depth — there is no numbered `surface-1 #### Brand & action -| Token | Use | Tailwind | -| -------------------------------------------- | -------------------------------- | -------------------------------------------- | -| `--primary` / `--primary-foreground` | primary buttons, emphasis | `bg-primary` / `text-primary-foreground` | -| `--secondary` / `--secondary-foreground` | secondary buttons, neutral chips | `bg-secondary` / `text-secondary-foreground` | -| `--accent` / `--accent-foreground` | hover and selected nav states | `bg-accent` / `text-accent-foreground` | -| `--destructive` / `--destructive-foreground` | alias of danger solid / contrast | `bg-destructive` / `text-destructive` | - -`--destructive` and `--destructive-foreground` are aliases of the danger `solid` and `contrast` roles ([semantic color roles](#semantic-color-roles)), so `bg-destructive` is the danger solid fill in both modes and `Button variant="destructive"` reads the danger roles directly. `text-destructive` still works and resolves to the solid. For text, prefer `text-danger-text`, which meets 4.5:1 on `--card` and `--background`; the solid is `#dc2626` in dark mode, 3.71:1 on the dark card. To recolour destructive actions, override `--danger-solid` (and the other danger roles), not `--destructive`. +| Token | Use | Tailwind | +| -------------------------------------------- | ------------------------------------------- | -------------------------------------------- | +| `--primary` / `--primary-foreground` | primary buttons, emphasis | `bg-primary` / `text-primary-foreground` | +| `--secondary` / `--secondary-foreground` | secondary buttons, neutral chips | `bg-secondary` / `text-secondary-foreground` | +| `--accent` / `--accent-foreground` | hover and selected nav states | `bg-accent` / `text-accent-foreground` | +| `--destructive` / `--destructive-foreground` | deprecated alias of danger solid / contrast | `bg-destructive` / `text-destructive` | + +**Deprecated.** `--destructive`, `--destructive-foreground` and their utilities remain for existing code and will be removed in the next major. New code uses the danger roles: `bg-danger-solid`, `text-danger-contrast`, and `text-danger-text` for text. `Button variant="destructive"` is not deprecated yet; the next major renames it to `danger`. + +`--destructive` and `--destructive-foreground` are aliases of the danger `solid` and `contrast` roles ([semantic color roles](#semantic-color-roles)), so `bg-destructive` is the danger solid fill in both modes and `Button variant="destructive"` reads the danger roles directly. `text-destructive` still works and resolves to the solid. For text, use `text-danger-text`, which meets 4.5:1 on `--card` and `--background`; the solid is `#dc2626` in dark mode, 3.71:1 on the dark card. To recolour destructive actions, override `--danger-solid` (and the other danger roles), not `--destructive`. There are no `-hover` or `-active` brand tokens. Express interaction states with Tailwind variants and opacity — `hover:bg-primary/90`, `active:bg-primary/80` — which is what AppShell's own components do. diff --git a/docs/migrations.md b/docs/migrations.md index a0849155f..9f25f361e 100644 --- a/docs/migrations.md +++ b/docs/migrations.md @@ -32,7 +32,7 @@ Each entry states which versions are affected, what breaks, how to detect it, an **Applies to:** apps that show `Button variant="destructive"` in dark mode, apps that use `text-destructive`, `border-destructive` or `bg-destructive` on their own elements, and apps whose theme overrides `--destructive` or `--destructive-foreground`. -1.18.0 makes `--destructive` and `--destructive-foreground` aliases of `--danger-solid` and `--danger-contrast`. The names, `Button variant="destructive"` and `text-destructive` keep working and are not deprecated. Nothing errors. Three things change silently: +1.18.0 makes `--destructive` and `--destructive-foreground` aliases of `--danger-solid` and `--danger-contrast`. The names, `Button variant="destructive"` and `text-destructive` keep working. Nothing errors. `--destructive`, `--destructive-foreground` and their utilities are deprecated and will be removed in the next major; `Button variant="destructive"` will be renamed to `danger` then. Three things change silently: - **Dark-mode `Button variant="destructive"` is a full `#dc2626` fill.** It was `#f87171` at 60% opacity. The text is `--danger-contrast` (white) and the hover fill is `--danger-solid-hover` instead of 90% opacity. Light mode keeps `#dc2626`. - **`text-destructive` resolves to the solid.** In dark mode it changes from `#f87171` to `#dc2626`, which is 3.71:1 on the dark card, below 4.5:1 for text. Components shipped with AppShell now use `text-danger-text` for error text, and invalid borders and focus rings read `--danger-solid`. In dark mode the invalid border changes from `#f87171` to `#dc2626`. @@ -42,7 +42,7 @@ How to detect it: search the app for `destructive` in class names and in theme C What to change: -- For text, replace `text-destructive` with `text-danger-text`. `text-destructive` still works; use it only where the solid colour is intended. +- For text, replace `text-destructive` with `text-danger-text`. For fills, replace `bg-destructive` with `bg-danger-solid`, and `text-destructive-foreground` with `text-danger-contrast`. - For tints and borders, use `bg-danger-surface` and `border-danger-border` instead of `bg-destructive/10` and `border-destructive/30`. - In a theme that recolours destructive actions, set `--danger-solid` (and `--danger-solid-hover`, `--danger-text`, `--danger-contrast` as needed) instead of `--destructive`. Remove `--destructive` and `--destructive-foreground` from the theme. From 1bc524a8d844b4bb7d93c080d58a7ccde04de16a Mon Sep 17 00:00:00 2001 From: Hiroki Uemura Date: Wed, 14 Oct 2026 12:43:03 +0900 Subject: [PATCH 18/20] docs(core): list the destructive migration under 1.17.0 and fix the border replacement The destructive change ships in the same release as the colour roles, so the migration entry is 1.17.0. In the changeset, border-destructive maps to border-danger-solid for invalid states; border-danger-border is 1.67:1 on the card and is for decorative borders only. Co-Authored-By: Claude Opus 5.5 --- .changeset/danger-destructive-alias.md | 2 +- docs-manifest.json | 6 +++--- docs-src/guides/migrations.docs.outline.md | 6 +++--- docs/migrations.md | 6 +++--- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.changeset/danger-destructive-alias.md b/.changeset/danger-destructive-alias.md index 1022c0da4..0d41a57d4 100644 --- a/.changeset/danger-destructive-alias.md +++ b/.changeset/danger-destructive-alias.md @@ -4,7 +4,7 @@ `--destructive` and `--destructive-foreground` are now aliases of `--danger-solid` and `--danger-contrast`, and the shipped components read the danger roles instead of `destructive` utilities. The names, `Button variant="destructive"` and `text-destructive` keep working. -`--destructive`, `--destructive-foreground` and their utilities (`bg-destructive`, `text-destructive`, `border-destructive`, `text-destructive-foreground`) are deprecated and will be removed in the next major. They keep working in 1.x; new code uses the danger roles (`bg-danger-solid`, `text-danger-text`, `border-danger-border`, `text-danger-contrast`). `Button variant="destructive"` has no replacement yet, so it is not deprecated in this release; the next major renames it to `danger`. +`--destructive`, `--destructive-foreground` and their utilities (`bg-destructive`, `text-destructive`, `border-destructive`, `text-destructive-foreground`) are deprecated and will be removed in the next major. They keep working in 1.x; new code uses the danger roles: `bg-danger-solid` for fills, `text-danger-text` for text, `text-danger-contrast` for text on the solid, and `border-danger-solid` for invalid-state borders (`border-danger-border` is for decorative borders only; it is 1.67:1 on the card). `Button variant="destructive"` has no replacement yet, so it is not deprecated in this release; the next major renames it to `danger`. Visible changes in the default palette: diff --git a/docs-manifest.json b/docs-manifest.json index e5e711622..b38cdc2ed 100644 --- a/docs-manifest.json +++ b/docs-manifest.json @@ -1049,9 +1049,9 @@ "symbols": [], "hashes": { "typeSurface": null, - "outline": "16c1723900750d80", + "outline": "7d63cce8e5d0428b", "snapshot": null, - "outputMd": "f095aa7055b83481", + "outputMd": "6fefe6caff23f403", "examples": null } }, @@ -1756,7 +1756,7 @@ "packages/core/skills/app-shell-patterns/references/patterns/interaction-toast.md": "f68bdab855730f48", "packages/core/skills/app-shell-patterns/references/patterns/list-dense-scan.md": "916baa3cd2b9cbf1", "packages/core/skills/app-shell-patterns/references/pages/document-detail.md": "717ef4e7ae835aff", - "packages/core/skills/app-shell-patterns/references/migrations.md": "afad0935852bec9f", + "packages/core/skills/app-shell-patterns/references/migrations.md": "188801b60d8e5a76", "packages/core/skills/app-shell-patterns/SKILL.md": "526decbfd2db6555" } } diff --git a/docs-src/guides/migrations.docs.outline.md b/docs-src/guides/migrations.docs.outline.md index f11e727ed..24d978693 100644 --- a/docs-src/guides/migrations.docs.outline.md +++ b/docs-src/guides/migrations.docs.outline.md @@ -15,7 +15,7 @@ Each entry states which versions are affected, what breaks, how to detect it, an | Version | Change | | ------------- | ---------------------------------------------------------------------------------------------------------------------------- | -| 1.18.0 | [Destructive colours now resolve to the danger roles](#1180-destructive-colours-now-resolve-to-the-danger-roles) | +| 1.17.0 | [Destructive colours now resolve to the danger roles](#1170-destructive-colours-now-resolve-to-the-danger-roles) | | 1.17.0 | [Status and alert tokens now resolve to the colour roles](#1170-status-and-alert-tokens-now-resolve-to-the-colour-roles) | | 1.15.0 | [Base UI 1.8.0 breaks closed-`Select` and dialog-click tests](#1150-base-ui-180-breaks-closed-select-and-dialog-click-tests) | | 1.12.0 | [`DateField` / `DatePicker` field chrome moved to `Field.Root`](#1120-datefield--datepicker-field-chrome-moved-to-fieldroot) | @@ -28,11 +28,11 @@ Each entry states which versions are affected, what breaks, how to detect it, an | 1.0.2 | [`Toaster` no longer accepts `richColors`](#102-toaster-no-longer-accepts-richcolors) | | before 1.0 | [Pre-1.0 breaking changes](#before-10) | -## 1.18.0: destructive colours now resolve to the danger roles +## 1.17.0: destructive colours now resolve to the danger roles **Applies to:** apps that show `Button variant="destructive"` in dark mode, apps that use `text-destructive`, `border-destructive` or `bg-destructive` on their own elements, and apps whose theme overrides `--destructive` or `--destructive-foreground`. -1.18.0 makes `--destructive` and `--destructive-foreground` aliases of `--danger-solid` and `--danger-contrast`. The names, `Button variant="destructive"` and `text-destructive` keep working. Nothing errors. `--destructive`, `--destructive-foreground` and their utilities are deprecated and will be removed in the next major; `Button variant="destructive"` will be renamed to `danger` then. Three things change silently: +1.17.0 makes `--destructive` and `--destructive-foreground` aliases of `--danger-solid` and `--danger-contrast`. The names, `Button variant="destructive"` and `text-destructive` keep working. Nothing errors. `--destructive`, `--destructive-foreground` and their utilities are deprecated and will be removed in the next major; `Button variant="destructive"` will be renamed to `danger` then. Three things change silently: - **Dark-mode `Button variant="destructive"` is a full `#dc2626` fill.** It was `#f87171` at 60% opacity. The text is `--danger-contrast` (white) and the hover fill is `--danger-solid-hover` instead of 90% opacity. Light mode keeps `#dc2626`. - **`text-destructive` resolves to the solid.** In dark mode it changes from `#f87171` to `#dc2626`, which is 3.71:1 on the dark card, below 4.5:1 for text. Components shipped with AppShell now use `text-danger-text` for error text, and invalid borders and focus rings read `--danger-solid`. In dark mode the invalid border changes from `#f87171` to `#dc2626`. diff --git a/docs/migrations.md b/docs/migrations.md index 9f25f361e..b27f0ef99 100644 --- a/docs/migrations.md +++ b/docs/migrations.md @@ -15,7 +15,7 @@ Each entry states which versions are affected, what breaks, how to detect it, an | Version | Change | | ------------- | ---------------------------------------------------------------------------------------------------------------------------- | -| 1.18.0 | [Destructive colours now resolve to the danger roles](#1180-destructive-colours-now-resolve-to-the-danger-roles) | +| 1.17.0 | [Destructive colours now resolve to the danger roles](#1170-destructive-colours-now-resolve-to-the-danger-roles) | | 1.17.0 | [Status and alert tokens now resolve to the colour roles](#1170-status-and-alert-tokens-now-resolve-to-the-colour-roles) | | 1.15.0 | [Base UI 1.8.0 breaks closed-`Select` and dialog-click tests](#1150-base-ui-180-breaks-closed-select-and-dialog-click-tests) | | 1.12.0 | [`DateField` / `DatePicker` field chrome moved to `Field.Root`](#1120-datefield--datepicker-field-chrome-moved-to-fieldroot) | @@ -28,11 +28,11 @@ Each entry states which versions are affected, what breaks, how to detect it, an | 1.0.2 | [`Toaster` no longer accepts `richColors`](#102-toaster-no-longer-accepts-richcolors) | | before 1.0 | [Pre-1.0 breaking changes](#before-10) | -## 1.18.0: destructive colours now resolve to the danger roles +## 1.17.0: destructive colours now resolve to the danger roles **Applies to:** apps that show `Button variant="destructive"` in dark mode, apps that use `text-destructive`, `border-destructive` or `bg-destructive` on their own elements, and apps whose theme overrides `--destructive` or `--destructive-foreground`. -1.18.0 makes `--destructive` and `--destructive-foreground` aliases of `--danger-solid` and `--danger-contrast`. The names, `Button variant="destructive"` and `text-destructive` keep working. Nothing errors. `--destructive`, `--destructive-foreground` and their utilities are deprecated and will be removed in the next major; `Button variant="destructive"` will be renamed to `danger` then. Three things change silently: +1.17.0 makes `--destructive` and `--destructive-foreground` aliases of `--danger-solid` and `--danger-contrast`. The names, `Button variant="destructive"` and `text-destructive` keep working. Nothing errors. `--destructive`, `--destructive-foreground` and their utilities are deprecated and will be removed in the next major; `Button variant="destructive"` will be renamed to `danger` then. Three things change silently: - **Dark-mode `Button variant="destructive"` is a full `#dc2626` fill.** It was `#f87171` at 60% opacity. The text is `--danger-contrast` (white) and the hover fill is `--danger-solid-hover` instead of 90% opacity. Light mode keeps `#dc2626`. - **`text-destructive` resolves to the solid.** In dark mode it changes from `#f87171` to `#dc2626`, which is 3.71:1 on the dark card, below 4.5:1 for text. Components shipped with AppShell now use `text-danger-text` for error text, and invalid borders and focus rings read `--danger-solid`. In dark mode the invalid border changes from `#f87171` to `#dc2626`. From 342942a90a3b7ba02475c30d44a47d5344534a68 Mon Sep 17 00:00:00 2001 From: Hiroki Uemura Date: Sun, 18 Oct 2026 17:30:41 +0900 Subject: [PATCH 19/20] fix(core): move the destructive migration to 1.18.0 and use the danger role in the selection bar 1.17.0 shipped without this change, so the migration entry now lists it under 1.18.0. The DataTable selection bar (added in 1.17.0) used `text-destructive` for destructive overflow actions. It now uses `text-danger-text`, the same as ActionPanel. Co-Authored-By: Claude Opus 5.5 --- docs-src/guides/migrations.docs.outline.md | 6 +++--- docs/migrations.md | 6 +++--- packages/core/src/components/data-table/selection-bar.tsx | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs-src/guides/migrations.docs.outline.md b/docs-src/guides/migrations.docs.outline.md index 24d978693..f11e727ed 100644 --- a/docs-src/guides/migrations.docs.outline.md +++ b/docs-src/guides/migrations.docs.outline.md @@ -15,7 +15,7 @@ Each entry states which versions are affected, what breaks, how to detect it, an | Version | Change | | ------------- | ---------------------------------------------------------------------------------------------------------------------------- | -| 1.17.0 | [Destructive colours now resolve to the danger roles](#1170-destructive-colours-now-resolve-to-the-danger-roles) | +| 1.18.0 | [Destructive colours now resolve to the danger roles](#1180-destructive-colours-now-resolve-to-the-danger-roles) | | 1.17.0 | [Status and alert tokens now resolve to the colour roles](#1170-status-and-alert-tokens-now-resolve-to-the-colour-roles) | | 1.15.0 | [Base UI 1.8.0 breaks closed-`Select` and dialog-click tests](#1150-base-ui-180-breaks-closed-select-and-dialog-click-tests) | | 1.12.0 | [`DateField` / `DatePicker` field chrome moved to `Field.Root`](#1120-datefield--datepicker-field-chrome-moved-to-fieldroot) | @@ -28,11 +28,11 @@ Each entry states which versions are affected, what breaks, how to detect it, an | 1.0.2 | [`Toaster` no longer accepts `richColors`](#102-toaster-no-longer-accepts-richcolors) | | before 1.0 | [Pre-1.0 breaking changes](#before-10) | -## 1.17.0: destructive colours now resolve to the danger roles +## 1.18.0: destructive colours now resolve to the danger roles **Applies to:** apps that show `Button variant="destructive"` in dark mode, apps that use `text-destructive`, `border-destructive` or `bg-destructive` on their own elements, and apps whose theme overrides `--destructive` or `--destructive-foreground`. -1.17.0 makes `--destructive` and `--destructive-foreground` aliases of `--danger-solid` and `--danger-contrast`. The names, `Button variant="destructive"` and `text-destructive` keep working. Nothing errors. `--destructive`, `--destructive-foreground` and their utilities are deprecated and will be removed in the next major; `Button variant="destructive"` will be renamed to `danger` then. Three things change silently: +1.18.0 makes `--destructive` and `--destructive-foreground` aliases of `--danger-solid` and `--danger-contrast`. The names, `Button variant="destructive"` and `text-destructive` keep working. Nothing errors. `--destructive`, `--destructive-foreground` and their utilities are deprecated and will be removed in the next major; `Button variant="destructive"` will be renamed to `danger` then. Three things change silently: - **Dark-mode `Button variant="destructive"` is a full `#dc2626` fill.** It was `#f87171` at 60% opacity. The text is `--danger-contrast` (white) and the hover fill is `--danger-solid-hover` instead of 90% opacity. Light mode keeps `#dc2626`. - **`text-destructive` resolves to the solid.** In dark mode it changes from `#f87171` to `#dc2626`, which is 3.71:1 on the dark card, below 4.5:1 for text. Components shipped with AppShell now use `text-danger-text` for error text, and invalid borders and focus rings read `--danger-solid`. In dark mode the invalid border changes from `#f87171` to `#dc2626`. diff --git a/docs/migrations.md b/docs/migrations.md index b27f0ef99..9f25f361e 100644 --- a/docs/migrations.md +++ b/docs/migrations.md @@ -15,7 +15,7 @@ Each entry states which versions are affected, what breaks, how to detect it, an | Version | Change | | ------------- | ---------------------------------------------------------------------------------------------------------------------------- | -| 1.17.0 | [Destructive colours now resolve to the danger roles](#1170-destructive-colours-now-resolve-to-the-danger-roles) | +| 1.18.0 | [Destructive colours now resolve to the danger roles](#1180-destructive-colours-now-resolve-to-the-danger-roles) | | 1.17.0 | [Status and alert tokens now resolve to the colour roles](#1170-status-and-alert-tokens-now-resolve-to-the-colour-roles) | | 1.15.0 | [Base UI 1.8.0 breaks closed-`Select` and dialog-click tests](#1150-base-ui-180-breaks-closed-select-and-dialog-click-tests) | | 1.12.0 | [`DateField` / `DatePicker` field chrome moved to `Field.Root`](#1120-datefield--datepicker-field-chrome-moved-to-fieldroot) | @@ -28,11 +28,11 @@ Each entry states which versions are affected, what breaks, how to detect it, an | 1.0.2 | [`Toaster` no longer accepts `richColors`](#102-toaster-no-longer-accepts-richcolors) | | before 1.0 | [Pre-1.0 breaking changes](#before-10) | -## 1.17.0: destructive colours now resolve to the danger roles +## 1.18.0: destructive colours now resolve to the danger roles **Applies to:** apps that show `Button variant="destructive"` in dark mode, apps that use `text-destructive`, `border-destructive` or `bg-destructive` on their own elements, and apps whose theme overrides `--destructive` or `--destructive-foreground`. -1.17.0 makes `--destructive` and `--destructive-foreground` aliases of `--danger-solid` and `--danger-contrast`. The names, `Button variant="destructive"` and `text-destructive` keep working. Nothing errors. `--destructive`, `--destructive-foreground` and their utilities are deprecated and will be removed in the next major; `Button variant="destructive"` will be renamed to `danger` then. Three things change silently: +1.18.0 makes `--destructive` and `--destructive-foreground` aliases of `--danger-solid` and `--danger-contrast`. The names, `Button variant="destructive"` and `text-destructive` keep working. Nothing errors. `--destructive`, `--destructive-foreground` and their utilities are deprecated and will be removed in the next major; `Button variant="destructive"` will be renamed to `danger` then. Three things change silently: - **Dark-mode `Button variant="destructive"` is a full `#dc2626` fill.** It was `#f87171` at 60% opacity. The text is `--danger-contrast` (white) and the hover fill is `--danger-solid-hover` instead of 90% opacity. Light mode keeps `#dc2626`. - **`text-destructive` resolves to the solid.** In dark mode it changes from `#f87171` to `#dc2626`, which is 3.71:1 on the dark card, below 4.5:1 for text. Components shipped with AppShell now use `text-danger-text` for error text, and invalid borders and focus rings read `--danger-solid`. In dark mode the invalid border changes from `#f87171` to `#dc2626`. diff --git a/packages/core/src/components/data-table/selection-bar.tsx b/packages/core/src/components/data-table/selection-bar.tsx index 88a437e52..98279c220 100644 --- a/packages/core/src/components/data-table/selection-bar.tsx +++ b/packages/core/src/components/data-table/selection-bar.tsx @@ -150,7 +150,7 @@ export function DataTableSelectionBar>() { key={action.id} disabled={busy} onClick={() => run(action)} - className={cn(action.variant === "destructive" && "astw:text-destructive")} + className={cn(action.variant === "destructive" && "astw:text-danger-text")} > {action.icon} {action.label} From 8ee79430a0d941dbe90177225d0eedb0df843cfe Mon Sep 17 00:00:00 2001 From: Hiroki Uemura Date: Sun, 18 Oct 2026 17:59:57 +0900 Subject: [PATCH 20/20] docs(core): list the tokens whose computed string changes, and name the version in the migration note - The changeset said the border colours "now read" the gray scale. `--foreground`, `--muted-foreground`, `--border` and `--input` already did in 1.17.0, and the dark-mode lines keep the same `rgba(...)` string. It now lists the tokens whose `getPropertyValue()` string changes. - The migration note now says "1.17.0 and earlier" instead of "releases before the default palette moved onto its colour scales". Co-Authored-By: Claude Opus 5.5 --- .changeset/neutral-surfaces-scale.md | 2 +- docs-manifest.json | 6 +++--- docs-src/guides/migrations.docs.outline.md | 2 +- docs/migrations.md | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.changeset/neutral-surfaces-scale.md b/.changeset/neutral-surfaces-scale.md index d6b8db91a..1e14bc118 100644 --- a/.changeset/neutral-surfaces-scale.md +++ b/.changeset/neutral-surfaces-scale.md @@ -4,4 +4,4 @@ Add a `--backdrop` colour token and the `bg-backdrop` utility for the dim layer behind modals. `Dialog`, `Sheet` and the CsvImporter drawer use it instead of `bg-black/50`. The value is the same (black at 50%), so nothing changes on screen. Override `--backdrop` on `:root` to change every backdrop at once. -In the default palette, the surface, border and brand colours now read the internal gray scale, a white step, and white alpha steps. All colours are unchanged. If your code reads these tokens with `getComputedStyle(...).getPropertyValue()`, the string is now the step's notation, for example `#171717` instead of `rgba(23, 23, 23, 1)`. The `cream` and `bloom` palettes are not changed. +In the default palette, the surface and brand colours now read the internal gray scale and a white step, and the dark-mode lines read white alpha steps. All colours are unchanged. If your code reads these tokens with `getComputedStyle(...).getPropertyValue()`, the string changes notation, for example `#171717` instead of `rgba(23, 23, 23, 1)`, for `--primary`, `--primary-foreground`, `--secondary`, `--secondary-foreground`, `--accent-foreground`, `--background`, `--card`, `--card-foreground`, `--popover`, `--popover-foreground`, `--muted`, `--sidebar`, `--sidebar-foreground`, and light-mode `--sidebar-border`. `--foreground`, `--muted-foreground`, `--border` and `--input` already read the gray scale in 1.17.0, and the dark-mode lines keep the same `rgba(...)` string. The `cream` and `bloom` palettes are not changed. diff --git a/docs-manifest.json b/docs-manifest.json index 2dcf4a3d4..9da4f2103 100644 --- a/docs-manifest.json +++ b/docs-manifest.json @@ -1067,9 +1067,9 @@ "symbols": [], "hashes": { "typeSurface": null, - "outline": "36cdcfab060a0d1c", + "outline": "109717602ca7db40", "snapshot": null, - "outputMd": "86fe04e370ecdff5", + "outputMd": "08839e97a37c2909", "examples": null } }, @@ -1775,7 +1775,7 @@ "packages/core/skills/app-shell-patterns/references/patterns/interaction-toast.md": "f68bdab855730f48", "packages/core/skills/app-shell-patterns/references/patterns/list-dense-scan.md": "d2c10cdbb00590a2", "packages/core/skills/app-shell-patterns/references/pages/document-detail.md": "717ef4e7ae835aff", - "packages/core/skills/app-shell-patterns/references/migrations.md": "1918f794d1bd4e99", + "packages/core/skills/app-shell-patterns/references/migrations.md": "b4c2044472bda5f7", "packages/core/skills/app-shell-patterns/SKILL.md": "4650734e91fbc858" } } diff --git a/docs-src/guides/migrations.docs.outline.md b/docs-src/guides/migrations.docs.outline.md index 12ed007ca..7fad63753 100644 --- a/docs-src/guides/migrations.docs.outline.md +++ b/docs-src/guides/migrations.docs.outline.md @@ -184,7 +184,7 @@ body { Toggle dark mode and confirm a real surface changes: inspect a `Card` and watch its computed `background-color` go from `rgb(255, 255, 255)` to `rgb(23, 23, 23)` on the default palette. -Reading the token directly also works — `getComputedStyle(document.documentElement).getPropertyValue("--card")` returns the winning declaration, so a stale copy shows up as its own value. Compare against the value the default palette resolves to: in dark mode `--card` returns `#171717`, the gray step it reads. (Releases before the default palette moved onto its colour scales returned `rgba(23, 23, 23, 1)`.) A copy in your own CSS shows up in the notation you wrote. +Reading the token directly also works — `getComputedStyle(document.documentElement).getPropertyValue("--card")` returns the winning declaration, so a stale copy shows up as its own value. Compare against the value the default palette resolves to: in dark mode `--card` returns `#171717`, the gray step it reads. (1.17.0 and earlier return `rgba(23, 23, 23, 1)`.) A copy in your own CSS shows up in the notation you wrote. ## 1.5.0: `loader` removed from file-based page definitions diff --git a/docs/migrations.md b/docs/migrations.md index df276264a..70c4c226f 100644 --- a/docs/migrations.md +++ b/docs/migrations.md @@ -184,7 +184,7 @@ body { Toggle dark mode and confirm a real surface changes: inspect a `Card` and watch its computed `background-color` go from `rgb(255, 255, 255)` to `rgb(23, 23, 23)` on the default palette. -Reading the token directly also works — `getComputedStyle(document.documentElement).getPropertyValue("--card")` returns the winning declaration, so a stale copy shows up as its own value. Compare against the value the default palette resolves to: in dark mode `--card` returns `#171717`, the gray step it reads. (Releases before the default palette moved onto its colour scales returned `rgba(23, 23, 23, 1)`.) A copy in your own CSS shows up in the notation you wrote. +Reading the token directly also works — `getComputedStyle(document.documentElement).getPropertyValue("--card")` returns the winning declaration, so a stale copy shows up as its own value. Compare against the value the default palette resolves to: in dark mode `--card` returns `#171717`, the gray step it reads. (1.17.0 and earlier return `rgba(23, 23, 23, 1)`.) A copy in your own CSS shows up in the notation you wrote. ## 1.5.0: `loader` removed from file-based page definitions