Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .changeset/danger-destructive-alias.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
"@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.

`--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:

- 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.
14 changes: 13 additions & 1 deletion decisions/semantic-color-roles.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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 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`.
- `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-*`.
26 changes: 13 additions & 13 deletions docs-manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
"hashes": {
"typeSurface": "7cb8a34c1c36eb02",
"outline": "797e012e34847b80",
"snapshot": "791be2c6087af41c",
"snapshot": "5b69cb9a28bbcc2e",
"outputMd": "824d1aa36bb7f194",
"examples": null
}
Expand Down Expand Up @@ -225,7 +225,7 @@
"hashes": {
"typeSurface": "16df9927f8394548",
"outline": "200f12eb4bec959a",
"snapshot": "d607a315ffce3abc",
"snapshot": "985de4f9c817a3cf",
"outputMd": "bce0e2fada1d1638",
"examples": "1ee01b1e27764cd2"
}
Expand Down Expand Up @@ -259,7 +259,7 @@
"hashes": {
"typeSurface": "3886bf7012f24283",
"outline": "916645fe8e5baf10",
"snapshot": "5df639bf40a2bdd7",
"snapshot": "2d057b30b9401e12",
"outputMd": "14f7ba23f441df38",
"examples": "e8f09fba5acb116f"
}
Expand Down Expand Up @@ -676,7 +676,7 @@
"hashes": {
"typeSurface": "7b374d45d53d4535",
"outline": "79c9b0f08ca38027",
"snapshot": "4287a03df2854f9a",
"snapshot": "d22d9caf48bb1b3b",
"outputMd": "91d901b17aed7781",
"examples": null
}
Expand Down Expand Up @@ -897,7 +897,7 @@
"hashes": {
"typeSurface": "7c5b64c579f508ee",
"outline": "9b273bc9e509f1fe",
"snapshot": "7e0ed658f518fd9a",
"snapshot": "4742086e97cfbdee",
"outputMd": "a3a6c9679af99d37",
"examples": "fc036f7af21b9dd4"
}
Expand Down Expand Up @@ -1049,9 +1049,9 @@
"symbols": [],
"hashes": {
"typeSurface": null,
"outline": "45dcfe4383a87ea9",
"outline": "7d63cce8e5d0428b",
"snapshot": null,
"outputMd": "2fa0b6a249d8bd4f",
"outputMd": "6fefe6caff23f403",
"examples": null
}
},
Expand Down Expand Up @@ -1304,9 +1304,9 @@
"symbols": [],
"hashes": {
"typeSurface": null,
"outline": "e63d837190e71ff1",
"outline": "dcf033e2a63f0f03",
"snapshot": null,
"outputMd": "112b43c697d30369",
"outputMd": "3ebd9f5828c49ed7",
"examples": "4d19f4f688e0f0ac"
}
},
Expand Down Expand Up @@ -1356,7 +1356,7 @@
"hashes": {
"typeSurface": "3d9225f7356a29b2",
"outline": "40952524e46c333e",
"snapshot": "40f4b39567ab051b",
"snapshot": "e775f88bb9fc2565",
"outputMd": "c940bc01cd573334",
"examples": "0712ee03ad6ce259"
}
Expand Down Expand Up @@ -1407,7 +1407,7 @@
"hashes": {
"typeSurface": "2d88f0306efcaa77",
"outline": "1257a84ccd91d5c3",
"snapshot": "3b402cab18147ef9",
"snapshot": "c85c431dcd58bc43",
"outputMd": "91d70b140c7bb747",
"examples": null
}
Expand Down Expand Up @@ -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": "112b43c697d30369",
"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",
Expand Down Expand Up @@ -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": "013c3fef2303b096",
"packages/core/skills/app-shell-patterns/references/migrations.md": "188801b60d8e5a76",
"packages/core/skills/app-shell-patterns/SKILL.md": "526decbfd2db6555"
}
}
20 changes: 12 additions & 8 deletions docs-src/concepts/styling-theming.docs.outline.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,12 +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` | destructive actions, errors | `bg-destructive` / `text-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.

Expand Down Expand Up @@ -195,7 +199,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

Expand Down Expand Up @@ -654,7 +658,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"` |
Expand Down
Loading
Loading