From 4a15d0f31b5c6526f6a13742cf728d84988d6886 Mon Sep 17 00:00:00 2001 From: Tucker McCoy Date: Wed, 2 Sep 2026 11:24:31 -0400 Subject: [PATCH 1/4] feat(button): add a warning variant, and text and button warning tokens MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Buttons could say "destructive" but not "pause and read this". `danger` was the only cautionary variant, so reversible actions either borrowed red and overstated themselves, or went unmarked. `Button/Warning` mirrors `Button/Danger` — Fill at Yellow 500/400, Border at Yellow 600/500 — with one deliberate break: its text is black, not white. Yellow is far lighter than red, so white on Yellow 500 measures 2.43:1 and on Yellow 400 just 1.99:1, under the 3:1 threshold for UI text. Black reads 8.65:1 and 10.54:1. Darkening the fill to Yellow 600 would let white clear 4:1, but #A27630 is a brown-olive that stops reading as caution, so the asymmetry sits in the text rather than the fill. It is the only filled variant with dark text, and the docs say why. `Text/Warning` needs no such compromise and mirrors `Text/Danger` exactly, at Yellow 500/400. Rendered in both themes and read back from the DOM: dark is a #DCB169 fill with a #D39D39 border, light is #D39D39 with #A27630, black text in both. Co-Authored-By: Claude Opus 5 (1M context) --- .../demo/src/content/components/button.mdx | 45 ++++++++---- packages/tokens/equality-tokens.json | 68 +++++++++++++++++++ packages/tokens/equality-tokens.pinwheel | 44 ++++++++++++ packages/ui/package.json | 2 +- .../src/components/button/button.module.css | 10 +++ packages/ui/src/components/button/button.tsx | 1 + .../ui/src/theme/generated/color-vars.css | 8 +++ .../ui/src/theme/generated/dark-tokens.css | 4 ++ .../theme/generated/global-dark-tokens.css | 4 ++ .../theme/generated/global-light-tokens.css | 4 ++ .../ui/src/theme/generated/light-tokens.css | 4 ++ .../src/theme/generated/tailwind-tokens.css | 4 ++ 12 files changed, 185 insertions(+), 13 deletions(-) diff --git a/packages/demo/src/content/components/button.mdx b/packages/demo/src/content/components/button.mdx index 8cff569e..b9943cb2 100644 --- a/packages/demo/src/content/components/button.mdx +++ b/packages/demo/src/content/components/button.mdx @@ -106,6 +106,27 @@ Use the `danger` variant For destructive actions like delete or remove. } +### Warning + +Use the `warning` variant for actions the user should pause over but that are not destructive, such as archiving something that can be restored later. Reserve `danger` for actions that cannot be undone. + +It is the only filled variant with dark text. Yellow is too light to carry white legibly, so `button-warning-text` is black where the other fills use white. + +{ + +
+ + + +
+} + ### Link Use the `link` variant for buttons that should be styled as a link but with button semantics. @@ -243,15 +264,15 @@ Buttons tagged with the `disabled` property are non-interactive and visually mut ## Props -| Name | Description | Type | Default | Required | -| ---------- | ------------------------------------------------ | ------------------------------------------------------------------ | --------- | -------- | -| `variant` | The visual style of the button | `primary`, `secondary`, `tertiary`, `danger`, `link`, `navigation` | `primary` | ❌ | -| `size` | The size of the button | `sm`, `md`, `lg` | `md` | ❌ | -| `prefix` | Content to display before the button label | `ReactNode` | - | ❌ | -| `suffix` | Content to display after the button label | `ReactNode` | - | ❌ | -| `href` | URL for link buttons (renders as anchor element) | `string` | - | ❌ | -| `target` | Link target (when href is provided) | `string` | - | ❌ | -| `rel` | Link rel attribute (when href is provided) | `string` | - | ❌ | -| `download` | Makes link downloadable (when href is provided) | `string`, `boolean` | - | ❌ | -| `asChild` | Merge props onto child element | `boolean` | `false` | ❌ | -| `disabled` | Disables the button | `boolean` | `false` | ❌ | +| Name | Description | Type | Default | Required | +| ---------- | ------------------------------------------------ | ----------------------------------------------------------------------------- | --------- | -------- | +| `variant` | The visual style of the button | `primary`, `secondary`, `tertiary`, `danger`, `warning`, `link`, `navigation` | `primary` | ❌ | +| `size` | The size of the button | `sm`, `md`, `lg` | `md` | ❌ | +| `prefix` | Content to display before the button label | `ReactNode` | - | ❌ | +| `suffix` | Content to display after the button label | `ReactNode` | - | ❌ | +| `href` | URL for link buttons (renders as anchor element) | `string` | - | ❌ | +| `target` | Link target (when href is provided) | `string` | - | ❌ | +| `rel` | Link rel attribute (when href is provided) | `string` | - | ❌ | +| `download` | Makes link downloadable (when href is provided) | `string`, `boolean` | - | ❌ | +| `asChild` | Merge props onto child element | `boolean` | `false` | ❌ | +| `disabled` | Disables the button | `boolean` | `false` | ❌ | diff --git a/packages/tokens/equality-tokens.json b/packages/tokens/equality-tokens.json index c53a2d73..57fb098a 100644 --- a/packages/tokens/equality-tokens.json +++ b/packages/tokens/equality-tokens.json @@ -463,6 +463,32 @@ "hex": "#FFFFFF" } } + }, + "warning": { + "border": { + "$type": "color", + "$value": { + "colorSpace": "display-p3", + "components": [0.8263, 0.6154, 0.2247], + "hex": "#D39D39" + } + }, + "fill": { + "$type": "color", + "$value": { + "colorSpace": "display-p3", + "components": [0.863, 0.6958, 0.4108], + "hex": "#DCB169" + } + }, + "text": { + "$type": "color", + "$value": { + "colorSpace": "display-p3", + "components": [0, 0, 0], + "hex": "#000000" + } + } } }, "card": { @@ -915,6 +941,14 @@ "components": [0.5021, 0.5021, 0.5021], "hex": "#808080" } + }, + "warning": { + "$type": "color", + "$value": { + "colorSpace": "display-p3", + "components": [0.863, 0.6958, 0.4108], + "hex": "#DCB169" + } } } } @@ -1597,6 +1631,32 @@ "hex": "#000000" } } + }, + "warning": { + "border": { + "$type": "color", + "$value": { + "colorSpace": "display-p3", + "components": [0.6345, 0.463, 0.1863], + "hex": "#A27630" + } + }, + "fill": { + "$type": "color", + "$value": { + "colorSpace": "display-p3", + "components": [0.8263, 0.6154, 0.2247], + "hex": "#D39D39" + } + }, + "text": { + "$type": "color", + "$value": { + "colorSpace": "display-p3", + "components": [0, 0, 0], + "hex": "#000000" + } + } } }, "card": { @@ -2973,6 +3033,14 @@ "components": [0.5021, 0.5021, 0.5021], "hex": "#808080" } + }, + "warning": { + "$type": "color", + "$value": { + "colorSpace": "display-p3", + "components": [0.8263, 0.6154, 0.2247], + "hex": "#D39D39" + } } }, "white": { diff --git a/packages/tokens/equality-tokens.pinwheel b/packages/tokens/equality-tokens.pinwheel index 1eb20709..6a7b3a89 100644 --- a/packages/tokens/equality-tokens.pinwheel +++ b/packages/tokens/equality-tokens.pinwheel @@ -2615,6 +2615,39 @@ 86B9D7D8-F74A-4CAE-B006-65451BBEDAB9 + + 5A31F3CF-7D77-4209-8306-BE84DCD28B3C + Warning + 1 + + Fill + 3 + 3 + lightanddark + BB783E8C-1914-4094-BE1D-37E41EB42641 + 4 + BB783E8C-1914-4094-BE1D-37E41EB42641 + 3 + 86B9D7D8-F74A-4CAE-B006-65451BBEDAB9 + + + Border + 3 + 3 + lightanddark + BB783E8C-1914-4094-BE1D-37E41EB42641 + 5 + BB783E8C-1914-4094-BE1D-37E41EB42641 + 4 + 86B9D7D8-F74A-4CAE-B006-65451BBEDAB9 + + + Text + 1 + 68E685CF-C64F-4A8C-8DE3-886255A54EC0 + 86B9D7D8-F74A-4CAE-B006-65451BBEDAB9 + + 4EC67953-4547-4883-B947-D73CF2D3DF18 @@ -2674,6 +2707,17 @@ 3 86B9D7D8-F74A-4CAE-B006-65451BBEDAB9 + + Warning + 3 + 3 + lightanddark + BB783E8C-1914-4094-BE1D-37E41EB42641 + 4 + BB783E8C-1914-4094-BE1D-37E41EB42641 + 3 + 86B9D7D8-F74A-4CAE-B006-65451BBEDAB9 + 635BED27-D972-4FC5-BD7E-AE05C08B92FD diff --git a/packages/ui/package.json b/packages/ui/package.json index 2cab3f65..2d933c08 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -2,7 +2,7 @@ "name": "@eqtylab/equality", "description": "EQTYLab's component and token-based design system", "homepage": "https://equality.eqtylab.io/", - "version": "3.2.6", + "version": "3.3.0", "license": "Apache-2.0", "keywords": [ "component library", diff --git a/packages/ui/src/components/button/button.module.css b/packages/ui/src/components/button/button.module.css index a8e2b959..264b6366 100644 --- a/packages/ui/src/components/button/button.module.css +++ b/packages/ui/src/components/button/button.module.css @@ -29,6 +29,16 @@ @apply hover:not-disabled:bg-mixed-dark; } +/* Yellow is too light to carry white text, so this is the one filled variant with dark text */ +.button--warning { + --mix-color: var(--color-button-warning-fill); + @apply bg-(--mix-color); + @apply border-button-warning-border border; + @apply text-button-warning-text; + @apply no-underline; + @apply hover:not-disabled:bg-mixed-dark; +} + .button--secondary { --mix-color: var(--color-button-secondary-fill); @apply bg-(--mix-color); diff --git a/packages/ui/src/components/button/button.tsx b/packages/ui/src/components/button/button.tsx index 9f6e89ad..9b99ccb1 100644 --- a/packages/ui/src/components/button/button.tsx +++ b/packages/ui/src/components/button/button.tsx @@ -10,6 +10,7 @@ const buttonVariants = cva(styles['button'], { variant: { primary: styles['button--primary'], danger: styles['button--danger'], + warning: styles['button--warning'], secondary: styles['button--secondary'], tertiary: styles['button--tertiary'], link: styles['button--link'], diff --git a/packages/ui/src/theme/generated/color-vars.css b/packages/ui/src/theme/generated/color-vars.css index 8d38a97e..20bcd574 100644 --- a/packages/ui/src/theme/generated/color-vars.css +++ b/packages/ui/src/theme/generated/color-vars.css @@ -82,6 +82,9 @@ --color-button-tertiary-border: color(display-p3 0.7432 0.7432 0.7432); --color-button-tertiary-fill: color(display-p3 1 1 1); --color-button-tertiary-text: color(display-p3 0 0 0); + --color-button-warning-border: color(display-p3 0.6345 0.463 0.1863); + --color-button-warning-fill: color(display-p3 0.8263 0.6154 0.2247); + --color-button-warning-text: color(display-p3 0 0 0); --color-card-background: color(display-p3 1 1 1); --color-card-border: color(display-p3 0.8698 0.8698 0.8698); --color-cyan-100: color(display-p3 0.8736 0.9466 0.9942); @@ -250,6 +253,7 @@ --color-text-primary: color(display-p3 0 0 0); --color-text-secondary: color(display-p3 0.1792 0.1792 0.1792); --color-text-tertiary: color(display-p3 0.5021 0.5021 0.5021); + --color-text-warning: color(display-p3 0.8263 0.6154 0.2247); --color-white: color(display-p3 1 1 1); --color-yellow-100: color(display-p3 0.966 0.9299 0.8701); --color-yellow-200: color(display-p3 0.9327 0.8528 0.7204); @@ -318,6 +322,9 @@ --dark-color-button-tertiary-border: color(display-p3 0.2806 0.2806 0.2806); --dark-color-button-tertiary-fill: color(display-p3 0 0 0); --dark-color-button-tertiary-text: color(display-p3 1 1 1); + --dark-color-button-warning-border: color(display-p3 0.8263 0.6154 0.2247); + --dark-color-button-warning-fill: color(display-p3 0.863 0.6958 0.4108); + --dark-color-button-warning-text: color(display-p3 0 0 0); --dark-color-card-background: color(display-p3 0.0861 0.0861 0.0861); --dark-color-card-border: color(display-p3 0.1792 0.1792 0.1792); --dark-color-focus-ring: color(display-p3 0.7319 0.6108 0.8873); @@ -373,4 +380,5 @@ --dark-color-text-primary: color(display-p3 1 1 1); --dark-color-text-secondary: color(display-p3 0.7432 0.7432 0.7432); --dark-color-text-tertiary: color(display-p3 0.5021 0.5021 0.5021); + --dark-color-text-warning: color(display-p3 0.863 0.6958 0.4108); } diff --git a/packages/ui/src/theme/generated/dark-tokens.css b/packages/ui/src/theme/generated/dark-tokens.css index 763778b0..ed8bc0bd 100644 --- a/packages/ui/src/theme/generated/dark-tokens.css +++ b/packages/ui/src/theme/generated/dark-tokens.css @@ -56,6 +56,9 @@ --color-button-tertiary-border: color(display-p3 0.2806 0.2806 0.2806); --color-button-tertiary-fill: color(display-p3 0 0 0); --color-button-tertiary-text: color(display-p3 1 1 1); + --color-button-warning-border: color(display-p3 0.8263 0.6154 0.2247); + --color-button-warning-fill: color(display-p3 0.863 0.6958 0.4108); + --color-button-warning-text: color(display-p3 0 0 0); --color-card-background: color(display-p3 0.0861 0.0861 0.0861); --color-card-border: color(display-p3 0.1792 0.1792 0.1792); --color-focus-ring: color(display-p3 0.7319 0.6108 0.8873); @@ -111,4 +114,5 @@ --color-text-primary: color(display-p3 1 1 1); --color-text-secondary: color(display-p3 0.7432 0.7432 0.7432); --color-text-tertiary: color(display-p3 0.5021 0.5021 0.5021); + --color-text-warning: color(display-p3 0.863 0.6958 0.4108); } diff --git a/packages/ui/src/theme/generated/global-dark-tokens.css b/packages/ui/src/theme/generated/global-dark-tokens.css index 0b46c79d..37e9e2ea 100644 --- a/packages/ui/src/theme/generated/global-dark-tokens.css +++ b/packages/ui/src/theme/generated/global-dark-tokens.css @@ -56,6 +56,9 @@ html[data-equality-theme='dark'] { --color-button-tertiary-border: color(display-p3 0.2806 0.2806 0.2806); --color-button-tertiary-fill: color(display-p3 0 0 0); --color-button-tertiary-text: color(display-p3 1 1 1); + --color-button-warning-border: color(display-p3 0.8263 0.6154 0.2247); + --color-button-warning-fill: color(display-p3 0.863 0.6958 0.4108); + --color-button-warning-text: color(display-p3 0 0 0); --color-card-background: color(display-p3 0.0861 0.0861 0.0861); --color-card-border: color(display-p3 0.1792 0.1792 0.1792); --color-focus-ring: color(display-p3 0.7319 0.6108 0.8873); @@ -111,4 +114,5 @@ html[data-equality-theme='dark'] { --color-text-primary: color(display-p3 1 1 1); --color-text-secondary: color(display-p3 0.7432 0.7432 0.7432); --color-text-tertiary: color(display-p3 0.5021 0.5021 0.5021); + --color-text-warning: color(display-p3 0.863 0.6958 0.4108); } diff --git a/packages/ui/src/theme/generated/global-light-tokens.css b/packages/ui/src/theme/generated/global-light-tokens.css index 09930acb..f7e20607 100644 --- a/packages/ui/src/theme/generated/global-light-tokens.css +++ b/packages/ui/src/theme/generated/global-light-tokens.css @@ -86,6 +86,9 @@ html { --color-button-tertiary-border: color(display-p3 0.7432 0.7432 0.7432); --color-button-tertiary-fill: color(display-p3 1 1 1); --color-button-tertiary-text: color(display-p3 0 0 0); + --color-button-warning-border: color(display-p3 0.6345 0.463 0.1863); + --color-button-warning-fill: color(display-p3 0.8263 0.6154 0.2247); + --color-button-warning-text: color(display-p3 0 0 0); --color-card-background: color(display-p3 1 1 1); --color-card-border: color(display-p3 0.8698 0.8698 0.8698); --color-cyan-100: color(display-p3 0.8736 0.9466 0.9942); @@ -254,6 +257,7 @@ html { --color-text-primary: color(display-p3 0 0 0); --color-text-secondary: color(display-p3 0.1792 0.1792 0.1792); --color-text-tertiary: color(display-p3 0.5021 0.5021 0.5021); + --color-text-warning: color(display-p3 0.8263 0.6154 0.2247); --color-white: color(display-p3 1 1 1); --color-yellow-100: color(display-p3 0.966 0.9299 0.8701); --color-yellow-200: color(display-p3 0.9327 0.8528 0.7204); diff --git a/packages/ui/src/theme/generated/light-tokens.css b/packages/ui/src/theme/generated/light-tokens.css index 0ce4ba1d..dd6c032c 100644 --- a/packages/ui/src/theme/generated/light-tokens.css +++ b/packages/ui/src/theme/generated/light-tokens.css @@ -86,6 +86,9 @@ --color-button-tertiary-border: color(display-p3 0.7432 0.7432 0.7432); --color-button-tertiary-fill: color(display-p3 1 1 1); --color-button-tertiary-text: color(display-p3 0 0 0); + --color-button-warning-border: color(display-p3 0.6345 0.463 0.1863); + --color-button-warning-fill: color(display-p3 0.8263 0.6154 0.2247); + --color-button-warning-text: color(display-p3 0 0 0); --color-card-background: color(display-p3 1 1 1); --color-card-border: color(display-p3 0.8698 0.8698 0.8698); --color-cyan-100: color(display-p3 0.8736 0.9466 0.9942); @@ -254,6 +257,7 @@ --color-text-primary: color(display-p3 0 0 0); --color-text-secondary: color(display-p3 0.1792 0.1792 0.1792); --color-text-tertiary: color(display-p3 0.5021 0.5021 0.5021); + --color-text-warning: color(display-p3 0.8263 0.6154 0.2247); --color-white: color(display-p3 1 1 1); --color-yellow-100: color(display-p3 0.966 0.9299 0.8701); --color-yellow-200: color(display-p3 0.9327 0.8528 0.7204); diff --git a/packages/ui/src/theme/generated/tailwind-tokens.css b/packages/ui/src/theme/generated/tailwind-tokens.css index 960ba0b0..ebba3750 100644 --- a/packages/ui/src/theme/generated/tailwind-tokens.css +++ b/packages/ui/src/theme/generated/tailwind-tokens.css @@ -86,6 +86,9 @@ --color-button-tertiary-border: color(display-p3 0.7432 0.7432 0.7432); --color-button-tertiary-fill: color(display-p3 1 1 1); --color-button-tertiary-text: color(display-p3 0 0 0); + --color-button-warning-border: color(display-p3 0.6345 0.463 0.1863); + --color-button-warning-fill: color(display-p3 0.8263 0.6154 0.2247); + --color-button-warning-text: color(display-p3 0 0 0); --color-card-background: color(display-p3 1 1 1); --color-card-border: color(display-p3 0.8698 0.8698 0.8698); --color-cyan-100: color(display-p3 0.8736 0.9466 0.9942); @@ -254,6 +257,7 @@ --color-text-primary: color(display-p3 0 0 0); --color-text-secondary: color(display-p3 0.1792 0.1792 0.1792); --color-text-tertiary: color(display-p3 0.5021 0.5021 0.5021); + --color-text-warning: color(display-p3 0.8263 0.6154 0.2247); --color-white: color(display-p3 1 1 1); --color-yellow-100: color(display-p3 0.966 0.9299 0.8701); --color-yellow-200: color(display-p3 0.9327 0.8528 0.7204); From 707596f8f960583af6294d7b601e5bd1eecfed21 Mon Sep 17 00:00:00 2001 From: Henry Wilkinson Date: Wed, 2 Sep 2026 23:42:48 -0400 Subject: [PATCH 2/4] Rebuild tokens out of pinwheel - Change button text colour to white for consistency as it meets APCA standards --- packages/tokens/equality-tokens.json | 12 ++---------- packages/tokens/equality-tokens.pinwheel | 5 ++++- packages/ui/src/theme/generated/color-vars.css | 1 - packages/ui/src/theme/generated/dark-tokens.css | 1 - .../ui/src/theme/generated/global-dark-tokens.css | 1 - 5 files changed, 6 insertions(+), 14 deletions(-) diff --git a/packages/tokens/equality-tokens.json b/packages/tokens/equality-tokens.json index 57fb098a..56cbcd89 100644 --- a/packages/tokens/equality-tokens.json +++ b/packages/tokens/equality-tokens.json @@ -480,14 +480,6 @@ "components": [0.863, 0.6958, 0.4108], "hex": "#DCB169" } - }, - "text": { - "$type": "color", - "$value": { - "colorSpace": "display-p3", - "components": [0, 0, 0], - "hex": "#000000" - } } } }, @@ -1653,8 +1645,8 @@ "$type": "color", "$value": { "colorSpace": "display-p3", - "components": [0, 0, 0], - "hex": "#000000" + "components": [1, 1, 1], + "hex": "#FFFFFF" } } } diff --git a/packages/tokens/equality-tokens.pinwheel b/packages/tokens/equality-tokens.pinwheel index 6a7b3a89..48b4e812 100644 --- a/packages/tokens/equality-tokens.pinwheel +++ b/packages/tokens/equality-tokens.pinwheel @@ -2478,6 +2478,7 @@ 792303FE-20DD-4519-88B7-A3D74D43A65A Button + 1 1 AB837834-B6A2-4B55-BB11-D3A5D13EEE41 @@ -2585,6 +2586,7 @@ 65EF707B-201E-41DA-9727-B23B74706082 Danger + 1 1 Fill @@ -2618,6 +2620,7 @@ 5A31F3CF-7D77-4209-8306-BE84DCD28B3C Warning + 1 1 Fill @@ -2644,7 +2647,7 @@ Text 1 - 68E685CF-C64F-4A8C-8DE3-886255A54EC0 + 9FC81FBB-965B-40C5-A7D2-A5F365863FE4 86B9D7D8-F74A-4CAE-B006-65451BBEDAB9 diff --git a/packages/ui/src/theme/generated/color-vars.css b/packages/ui/src/theme/generated/color-vars.css index 20bcd574..c3114973 100644 --- a/packages/ui/src/theme/generated/color-vars.css +++ b/packages/ui/src/theme/generated/color-vars.css @@ -324,7 +324,6 @@ --dark-color-button-tertiary-text: color(display-p3 1 1 1); --dark-color-button-warning-border: color(display-p3 0.8263 0.6154 0.2247); --dark-color-button-warning-fill: color(display-p3 0.863 0.6958 0.4108); - --dark-color-button-warning-text: color(display-p3 0 0 0); --dark-color-card-background: color(display-p3 0.0861 0.0861 0.0861); --dark-color-card-border: color(display-p3 0.1792 0.1792 0.1792); --dark-color-focus-ring: color(display-p3 0.7319 0.6108 0.8873); diff --git a/packages/ui/src/theme/generated/dark-tokens.css b/packages/ui/src/theme/generated/dark-tokens.css index ed8bc0bd..6cc46d8c 100644 --- a/packages/ui/src/theme/generated/dark-tokens.css +++ b/packages/ui/src/theme/generated/dark-tokens.css @@ -58,7 +58,6 @@ --color-button-tertiary-text: color(display-p3 1 1 1); --color-button-warning-border: color(display-p3 0.8263 0.6154 0.2247); --color-button-warning-fill: color(display-p3 0.863 0.6958 0.4108); - --color-button-warning-text: color(display-p3 0 0 0); --color-card-background: color(display-p3 0.0861 0.0861 0.0861); --color-card-border: color(display-p3 0.1792 0.1792 0.1792); --color-focus-ring: color(display-p3 0.7319 0.6108 0.8873); diff --git a/packages/ui/src/theme/generated/global-dark-tokens.css b/packages/ui/src/theme/generated/global-dark-tokens.css index 37e9e2ea..e56c8484 100644 --- a/packages/ui/src/theme/generated/global-dark-tokens.css +++ b/packages/ui/src/theme/generated/global-dark-tokens.css @@ -58,7 +58,6 @@ html[data-equality-theme='dark'] { --color-button-tertiary-text: color(display-p3 1 1 1); --color-button-warning-border: color(display-p3 0.8263 0.6154 0.2247); --color-button-warning-fill: color(display-p3 0.863 0.6958 0.4108); - --color-button-warning-text: color(display-p3 0 0 0); --color-card-background: color(display-p3 0.0861 0.0861 0.0861); --color-card-border: color(display-p3 0.1792 0.1792 0.1792); --color-focus-ring: color(display-p3 0.7319 0.6108 0.8873); From b2e7cdd5a9369f2217a9d205ba152c07f2033b42 Mon Sep 17 00:00:00 2001 From: Henry Wilkinson Date: Thu, 3 Sep 2026 00:21:48 -0400 Subject: [PATCH 3/4] Increase button colour vibrance --- packages/tokens/equality-tokens.json | 16 ++++++++-------- packages/tokens/equality-tokens.pinwheel | 8 ++++---- packages/ui/src/theme/generated/color-vars.css | 10 +++++----- packages/ui/src/theme/generated/dark-tokens.css | 8 ++++---- .../src/theme/generated/global-dark-tokens.css | 8 ++++---- .../src/theme/generated/global-light-tokens.css | 2 +- packages/ui/src/theme/generated/light-tokens.css | 2 +- .../ui/src/theme/generated/tailwind-tokens.css | 2 +- 8 files changed, 28 insertions(+), 28 deletions(-) diff --git a/packages/tokens/equality-tokens.json b/packages/tokens/equality-tokens.json index 56cbcd89..7f6bfbd4 100644 --- a/packages/tokens/equality-tokens.json +++ b/packages/tokens/equality-tokens.json @@ -389,8 +389,8 @@ "$type": "color", "$value": { "colorSpace": "display-p3", - "components": [0.8114, 0.4167, 0.3761], - "hex": "#CF6A60" + "components": [0.7365, 0.2216, 0.2026], + "hex": "#BC3934" } } }, @@ -399,16 +399,16 @@ "$type": "color", "$value": { "colorSpace": "display-p3", - "components": [0.6266, 0.4562, 0.8302], - "hex": "#A074D4" + "components": [0.5242, 0.2903, 0.7705], + "hex": "#864AC4" } }, "fill": { "$type": "color", "$value": { "colorSpace": "display-p3", - "components": [0.6266, 0.4562, 0.8302], - "hex": "#A074D4" + "components": [0.5242, 0.2903, 0.7705], + "hex": "#864AC4" } } }, @@ -477,8 +477,8 @@ "$type": "color", "$value": { "colorSpace": "display-p3", - "components": [0.863, 0.6958, 0.4108], - "hex": "#DCB169" + "components": [0.8263, 0.6154, 0.2247], + "hex": "#D39D39" } } } diff --git a/packages/tokens/equality-tokens.pinwheel b/packages/tokens/equality-tokens.pinwheel index 48b4e812..e7ddc1b3 100644 --- a/packages/tokens/equality-tokens.pinwheel +++ b/packages/tokens/equality-tokens.pinwheel @@ -2493,7 +2493,7 @@ 68A1562E-D9B7-40A5-BFB2-31353FD45D3D 4 68A1562E-D9B7-40A5-BFB2-31353FD45D3D - 3 + 4 86B9D7D8-F74A-4CAE-B006-65451BBEDAB9 @@ -2504,7 +2504,7 @@ 68A1562E-D9B7-40A5-BFB2-31353FD45D3D 4 68A1562E-D9B7-40A5-BFB2-31353FD45D3D - 3 + 4 86B9D7D8-F74A-4CAE-B006-65451BBEDAB9 @@ -2596,7 +2596,7 @@ CCD80E1D-24ED-4193-A845-891C968555AF 4 CCD80E1D-24ED-4193-A845-891C968555AF - 3 + 4 86B9D7D8-F74A-4CAE-B006-65451BBEDAB9 @@ -2630,7 +2630,7 @@ BB783E8C-1914-4094-BE1D-37E41EB42641 4 BB783E8C-1914-4094-BE1D-37E41EB42641 - 3 + 4 86B9D7D8-F74A-4CAE-B006-65451BBEDAB9 diff --git a/packages/ui/src/theme/generated/color-vars.css b/packages/ui/src/theme/generated/color-vars.css index c3114973..124531d9 100644 --- a/packages/ui/src/theme/generated/color-vars.css +++ b/packages/ui/src/theme/generated/color-vars.css @@ -84,7 +84,7 @@ --color-button-tertiary-text: color(display-p3 0 0 0); --color-button-warning-border: color(display-p3 0.6345 0.463 0.1863); --color-button-warning-fill: color(display-p3 0.8263 0.6154 0.2247); - --color-button-warning-text: color(display-p3 0 0 0); + --color-button-warning-text: color(display-p3 1 1 1); --color-card-background: color(display-p3 1 1 1); --color-card-border: color(display-p3 0.8698 0.8698 0.8698); --color-cyan-100: color(display-p3 0.8736 0.9466 0.9942); @@ -313,9 +313,9 @@ --dark-color-border-top: color(display-p3 0.3328 0.3328 0.3426); --dark-color-border: color(display-p3 0.1792 0.1792 0.1792); --dark-color-button-danger-border: color(display-p3 0.7365 0.2216 0.2026); - --dark-color-button-danger-fill: color(display-p3 0.8114 0.4167 0.3761); - --dark-color-button-primary-border: color(display-p3 0.6266 0.4562 0.8302); - --dark-color-button-primary-fill: color(display-p3 0.6266 0.4562 0.8302); + --dark-color-button-danger-fill: color(display-p3 0.7365 0.2216 0.2026); + --dark-color-button-primary-border: color(display-p3 0.5242 0.2903 0.7705); + --dark-color-button-primary-fill: color(display-p3 0.5242 0.2903 0.7705); --dark-color-button-secondary-border: color(display-p3 1 1 1); --dark-color-button-secondary-fill: color(display-p3 1 1 1); --dark-color-button-secondary-text: color(display-p3 0 0 0); @@ -323,7 +323,7 @@ --dark-color-button-tertiary-fill: color(display-p3 0 0 0); --dark-color-button-tertiary-text: color(display-p3 1 1 1); --dark-color-button-warning-border: color(display-p3 0.8263 0.6154 0.2247); - --dark-color-button-warning-fill: color(display-p3 0.863 0.6958 0.4108); + --dark-color-button-warning-fill: color(display-p3 0.8263 0.6154 0.2247); --dark-color-card-background: color(display-p3 0.0861 0.0861 0.0861); --dark-color-card-border: color(display-p3 0.1792 0.1792 0.1792); --dark-color-focus-ring: color(display-p3 0.7319 0.6108 0.8873); diff --git a/packages/ui/src/theme/generated/dark-tokens.css b/packages/ui/src/theme/generated/dark-tokens.css index 6cc46d8c..ee36211f 100644 --- a/packages/ui/src/theme/generated/dark-tokens.css +++ b/packages/ui/src/theme/generated/dark-tokens.css @@ -47,9 +47,9 @@ --color-border-top: color(display-p3 0.3328 0.3328 0.3426); --color-border: color(display-p3 0.1792 0.1792 0.1792); --color-button-danger-border: color(display-p3 0.7365 0.2216 0.2026); - --color-button-danger-fill: color(display-p3 0.8114 0.4167 0.3761); - --color-button-primary-border: color(display-p3 0.6266 0.4562 0.8302); - --color-button-primary-fill: color(display-p3 0.6266 0.4562 0.8302); + --color-button-danger-fill: color(display-p3 0.7365 0.2216 0.2026); + --color-button-primary-border: color(display-p3 0.5242 0.2903 0.7705); + --color-button-primary-fill: color(display-p3 0.5242 0.2903 0.7705); --color-button-secondary-border: color(display-p3 1 1 1); --color-button-secondary-fill: color(display-p3 1 1 1); --color-button-secondary-text: color(display-p3 0 0 0); @@ -57,7 +57,7 @@ --color-button-tertiary-fill: color(display-p3 0 0 0); --color-button-tertiary-text: color(display-p3 1 1 1); --color-button-warning-border: color(display-p3 0.8263 0.6154 0.2247); - --color-button-warning-fill: color(display-p3 0.863 0.6958 0.4108); + --color-button-warning-fill: color(display-p3 0.8263 0.6154 0.2247); --color-card-background: color(display-p3 0.0861 0.0861 0.0861); --color-card-border: color(display-p3 0.1792 0.1792 0.1792); --color-focus-ring: color(display-p3 0.7319 0.6108 0.8873); diff --git a/packages/ui/src/theme/generated/global-dark-tokens.css b/packages/ui/src/theme/generated/global-dark-tokens.css index e56c8484..7516c2d6 100644 --- a/packages/ui/src/theme/generated/global-dark-tokens.css +++ b/packages/ui/src/theme/generated/global-dark-tokens.css @@ -47,9 +47,9 @@ html[data-equality-theme='dark'] { --color-border-top: color(display-p3 0.3328 0.3328 0.3426); --color-border: color(display-p3 0.1792 0.1792 0.1792); --color-button-danger-border: color(display-p3 0.7365 0.2216 0.2026); - --color-button-danger-fill: color(display-p3 0.8114 0.4167 0.3761); - --color-button-primary-border: color(display-p3 0.6266 0.4562 0.8302); - --color-button-primary-fill: color(display-p3 0.6266 0.4562 0.8302); + --color-button-danger-fill: color(display-p3 0.7365 0.2216 0.2026); + --color-button-primary-border: color(display-p3 0.5242 0.2903 0.7705); + --color-button-primary-fill: color(display-p3 0.5242 0.2903 0.7705); --color-button-secondary-border: color(display-p3 1 1 1); --color-button-secondary-fill: color(display-p3 1 1 1); --color-button-secondary-text: color(display-p3 0 0 0); @@ -57,7 +57,7 @@ html[data-equality-theme='dark'] { --color-button-tertiary-fill: color(display-p3 0 0 0); --color-button-tertiary-text: color(display-p3 1 1 1); --color-button-warning-border: color(display-p3 0.8263 0.6154 0.2247); - --color-button-warning-fill: color(display-p3 0.863 0.6958 0.4108); + --color-button-warning-fill: color(display-p3 0.8263 0.6154 0.2247); --color-card-background: color(display-p3 0.0861 0.0861 0.0861); --color-card-border: color(display-p3 0.1792 0.1792 0.1792); --color-focus-ring: color(display-p3 0.7319 0.6108 0.8873); diff --git a/packages/ui/src/theme/generated/global-light-tokens.css b/packages/ui/src/theme/generated/global-light-tokens.css index f7e20607..d43b0260 100644 --- a/packages/ui/src/theme/generated/global-light-tokens.css +++ b/packages/ui/src/theme/generated/global-light-tokens.css @@ -88,7 +88,7 @@ html { --color-button-tertiary-text: color(display-p3 0 0 0); --color-button-warning-border: color(display-p3 0.6345 0.463 0.1863); --color-button-warning-fill: color(display-p3 0.8263 0.6154 0.2247); - --color-button-warning-text: color(display-p3 0 0 0); + --color-button-warning-text: color(display-p3 1 1 1); --color-card-background: color(display-p3 1 1 1); --color-card-border: color(display-p3 0.8698 0.8698 0.8698); --color-cyan-100: color(display-p3 0.8736 0.9466 0.9942); diff --git a/packages/ui/src/theme/generated/light-tokens.css b/packages/ui/src/theme/generated/light-tokens.css index dd6c032c..cb0a0da2 100644 --- a/packages/ui/src/theme/generated/light-tokens.css +++ b/packages/ui/src/theme/generated/light-tokens.css @@ -88,7 +88,7 @@ --color-button-tertiary-text: color(display-p3 0 0 0); --color-button-warning-border: color(display-p3 0.6345 0.463 0.1863); --color-button-warning-fill: color(display-p3 0.8263 0.6154 0.2247); - --color-button-warning-text: color(display-p3 0 0 0); + --color-button-warning-text: color(display-p3 1 1 1); --color-card-background: color(display-p3 1 1 1); --color-card-border: color(display-p3 0.8698 0.8698 0.8698); --color-cyan-100: color(display-p3 0.8736 0.9466 0.9942); diff --git a/packages/ui/src/theme/generated/tailwind-tokens.css b/packages/ui/src/theme/generated/tailwind-tokens.css index ebba3750..2eb34f26 100644 --- a/packages/ui/src/theme/generated/tailwind-tokens.css +++ b/packages/ui/src/theme/generated/tailwind-tokens.css @@ -88,7 +88,7 @@ --color-button-tertiary-text: color(display-p3 0 0 0); --color-button-warning-border: color(display-p3 0.6345 0.463 0.1863); --color-button-warning-fill: color(display-p3 0.8263 0.6154 0.2247); - --color-button-warning-text: color(display-p3 0 0 0); + --color-button-warning-text: color(display-p3 1 1 1); --color-card-background: color(display-p3 1 1 1); --color-card-border: color(display-p3 0.8698 0.8698 0.8698); --color-cyan-100: color(display-p3 0.8736 0.9466 0.9942); From c33e4ad6e1cb7c7afcc0e6e4b1eb990f04a325a9 Mon Sep 17 00:00:00 2001 From: Henry Wilkinson Date: Thu, 3 Sep 2026 00:29:23 -0400 Subject: [PATCH 4/4] Add warning variant to dropdown-menu --- .../src/components/demo/dropdown-menu.tsx | 26 ++++++++++++++ .../demo/src/content/components/button.mdx | 2 -- .../src/content/components/dropdown-menu.mdx | 34 +++++++++++++++---- .../dropdown-menu/dropdown-menu.module.css | 12 +++++++ .../dropdown-menu/dropdown-menu.tsx | 3 +- 5 files changed, 67 insertions(+), 10 deletions(-) diff --git a/packages/demo/src/components/demo/dropdown-menu.tsx b/packages/demo/src/components/demo/dropdown-menu.tsx index 4e005dc0..d53718ce 100644 --- a/packages/demo/src/components/demo/dropdown-menu.tsx +++ b/packages/demo/src/components/demo/dropdown-menu.tsx @@ -47,6 +47,7 @@ export const DropdownMenuDemo = ({ }: { variant?: | "default" + | "item-variants" | "with-separators" | "with-checkboxes" | "with-radio" @@ -92,6 +93,31 @@ export const DropdownMenuDemo = ({ ); } + if (variant === "item-variants") { + return ( +
+ + + + + + View logs + Duplicate + + + Roll back to previous release + + + Delete deployment + + + +
+ ); + } + if (variant === "with-separators") { return (
diff --git a/packages/demo/src/content/components/button.mdx b/packages/demo/src/content/components/button.mdx index b9943cb2..453d1f1e 100644 --- a/packages/demo/src/content/components/button.mdx +++ b/packages/demo/src/content/components/button.mdx @@ -110,8 +110,6 @@ Use the `danger` variant For destructive actions like delete or remove. Use the `warning` variant for actions the user should pause over but that are not destructive, such as archiving something that can be restored later. Reserve `danger` for actions that cannot be undone. -It is the only filled variant with dark text. Yellow is too light to carry white legibly, so `button-warning-text` is black where the other fills use white. - {
diff --git a/packages/demo/src/content/components/dropdown-menu.mdx b/packages/demo/src/content/components/dropdown-menu.mdx index 328a3244..899a2fa1 100644 --- a/packages/demo/src/content/components/dropdown-menu.mdx +++ b/packages/demo/src/content/components/dropdown-menu.mdx @@ -44,6 +44,26 @@ Basic usage: ## Variants +### Item Variants + +`DropdownMenuItem` takes a `variant` to mark an action's weight. Use `danger` for destructive or irreversible actions, and `warning` for actions that are consequential but recoverable — something the user should pause over rather than avoid. Leave everything else `neutral`. + +Colour alone shouldn't carry the meaning, so keep the label explicit about what the action does. + + + +```jsx + + View logs + Duplicate + + + Roll back to previous release + + Delete deployment + +``` + ### With Separators and Labels Use `DropdownMenuLabel` to title a section and `DropdownMenuSeparator` to divide groups of items. Items accept icons as children alongside a `` label. @@ -321,13 +341,13 @@ Flattening only works when `DropdownMenuSubContent` is placed directly inside `D ### DropdownMenuItem -| Name | Description | Type | Default | Required | -| ----------- | ---------------------------------------------------------------------- | ------------------- | --------- | -------- | -| `variant` | Visual style; `danger` marks a destructive action | `neutral`, `danger` | `neutral` | ❌ | -| `inset` | Adds left padding to align with items that have icons | `boolean` | `false` | ❌ | -| `disabled` | Prevents interaction and dims the item | `boolean` | `false` | ❌ | -| `textValue` | Text used for search filtering; falls back to the item's rendered text | `string` | - | ❌ | -| `onSelect` | Called when the item is selected | `() => void` | - | ❌ | +| Name | Description | Type | Default | Required | +| ----------- | -------------------------------------------------------------------------------- | ------------------------------ | --------- | -------- | +| `variant` | Visual style; `warning` marks a consequential action, `danger` a destructive one | `neutral`, `warning`, `danger` | `neutral` | ❌ | +| `inset` | Adds left padding to align with items that have icons | `boolean` | `false` | ❌ | +| `disabled` | Prevents interaction and dims the item | `boolean` | `false` | ❌ | +| `textValue` | Text used for search filtering; falls back to the item's rendered text | `string` | - | ❌ | +| `onSelect` | Called when the item is selected | `() => void` | - | ❌ | ### DropdownMenuCheckboxItem diff --git a/packages/ui/src/components/dropdown-menu/dropdown-menu.module.css b/packages/ui/src/components/dropdown-menu/dropdown-menu.module.css index 86b30647..5742e613 100644 --- a/packages/ui/src/components/dropdown-menu/dropdown-menu.module.css +++ b/packages/ui/src/components/dropdown-menu/dropdown-menu.module.css @@ -38,6 +38,18 @@ @apply dark:hover:not-disabled:text-red-200! dark:focus:text-red-200!; } +/* Text/Warning is Yellow 500, which reads 2.4:1 on the light overlay - under the 4.5:1 + text threshold - so light steps down to Yellow 700 (7.0:1). Dark keeps the token, which + resolves to Yellow 400 and already clears the dark overlay at 8.5:1. */ +.dropdown-menu-item-warning { + @apply text-yellow-700!; + @apply dark:text-text-warning!; + @apply hover:not-disabled:text-yellow-800! focus:text-yellow-800!; + @apply hover:not-disabled:bg-yellow-300/50! focus:bg-yellow-300/50!; + @apply dark:hover:not-disabled:bg-yellow-600/50! dark:focus:bg-yellow-600/50!; + @apply dark:hover:not-disabled:text-yellow-200! dark:focus:text-yellow-200!; +} + .dropdown-menu-item-indicator-checkbox, .dropdown-menu-item-indicator-radio { @apply absolute left-2 size-3.5; diff --git a/packages/ui/src/components/dropdown-menu/dropdown-menu.tsx b/packages/ui/src/components/dropdown-menu/dropdown-menu.tsx index 457d365f..c1349506 100644 --- a/packages/ui/src/components/dropdown-menu/dropdown-menu.tsx +++ b/packages/ui/src/components/dropdown-menu/dropdown-menu.tsx @@ -620,7 +620,7 @@ const DropdownMenuItem = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef & { inset?: boolean; - variant?: 'neutral' | 'danger'; + variant?: 'neutral' | 'warning' | 'danger'; } >(({ className, inset, variant = 'neutral', textValue, children, ...props }, ref) => { const visible = useFilterableItem(textValue, children); @@ -633,6 +633,7 @@ const DropdownMenuItem = React.forwardRef< className={cn( styles['dropdown-menu-item'], inset && styles['dropdown-menu-inset'], + variant === 'warning' && styles['dropdown-menu-item-warning'], variant === 'danger' && styles['dropdown-menu-item-danger'], className )}