@boostdev/design-system-components 2.5.0 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/client.cjs +56 -56
  2. package/dist/client.css +625 -626
  3. package/dist/client.js +56 -56
  4. package/dist/index.cjs +56 -56
  5. package/dist/index.css +625 -626
  6. package/dist/index.js +56 -56
  7. package/dist/web-components/{chunk-N3TN6WCH.js → chunk-GER5UJOF.js} +22 -15
  8. package/dist/web-components/chunk-QWPACK5L.js +72 -0
  9. package/dist/web-components/{chunk-YRXCVKHV.js → chunk-RTOS7LLG.js} +9 -7
  10. package/dist/web-components/{chunk-BX3IPVOB.js → chunk-ZNKEZIYE.js} +20 -16
  11. package/dist/web-components/index.js +9 -9
  12. package/dist/web-components/interaction/bds-button.js +1 -1
  13. package/dist/web-components/ui/bds-alert.js +1 -1
  14. package/dist/web-components/ui/bds-badge.js +1 -1
  15. package/dist/web-components/ui/bds-card.js +1 -1
  16. package/package.json +1 -1
  17. package/src/components/interaction/Button/Button.mdx +87 -13
  18. package/src/components/interaction/Button/Button.module.css +22 -17
  19. package/src/components/interaction/Toast/Toast.mdx +22 -3
  20. package/src/components/interaction/Toast/Toast.module.css +13 -10
  21. package/src/components/layout/Card/Card.mdx +20 -8
  22. package/src/components/layout/Card/Card.module.css +5 -3
  23. package/src/components/layout/Grid/Grid.spec.tsx +19 -0
  24. package/src/components/layout/Grid/masonry.ts +12 -5
  25. package/src/components/ui/Alert/Alert.mdx +39 -5
  26. package/src/components/ui/Alert/Alert.module.css +21 -15
  27. package/src/components/ui/Badge/Badge.mdx +23 -3
  28. package/src/components/ui/Badge/Badge.module.css +15 -12
  29. package/src/components/ui/Link/Link.mdx +20 -2
  30. package/src/components/ui/Link/Link.module.css +12 -8
  31. package/src/components/ui/NotificationBanner/NotificationBanner.mdx +25 -6
  32. package/src/components/ui/NotificationBanner/NotificationBanner.module.css +18 -15
  33. package/src/components/ui/Pagination/Pagination.mdx +22 -9
  34. package/src/components/ui/Pagination/Pagination.module.css +10 -8
  35. package/src/components/ui/Tabs/Tabs.mdx +20 -5
  36. package/src/components/ui/Tabs/Tabs.module.css +8 -5
  37. package/src/web-components/interaction/bds-button.ts +22 -15
  38. package/src/web-components/ui/bds-alert.ts +20 -16
  39. package/src/web-components/ui/bds-badge.ts +16 -13
  40. package/src/web-components/ui/bds-card.ts +9 -7
  41. package/dist/web-components/chunk-OHUTLSIQ.js +0 -69
@@ -0,0 +1,72 @@
1
+ import {
2
+ T,
3
+ i,
4
+ i2
5
+ } from "./chunk-LGEZYFUU.js";
6
+
7
+ // src/web-components/ui/bds-badge.ts
8
+ var BdsBadge = class extends i2 {
9
+ static styles = i`
10
+ :host {
11
+ display: inline-flex;
12
+ }
13
+
14
+ .badge {
15
+ display: inline-flex;
16
+ align-items: center;
17
+ justify-content: center;
18
+ padding: var(--bds-space_xxs) var(--bds-space_xs);
19
+ border-radius: var(--badge_radius, 2em);
20
+ font-size: var(--badge_font-size, var(--bds-font_size--body--s));
21
+ font-weight: var(--bds-font_weight--semibold);
22
+ line-height: 1;
23
+ white-space: nowrap;
24
+ /* Layered cascade: public overrides (--badge_bg, --badge_text) win over
25
+ per-variant defaults. Variants write to the inner --badge_*--variant slot. */
26
+ background-color: var(--badge_bg, var(--badge_bg--variant, var(--bds-color_blue)));
27
+ color: var(--badge_text, var(--badge_text--variant, var(--bds-color_on-blue)));
28
+ }
29
+
30
+ /* Per-variant resting overrides — consumers can theme per-variant via
31
+ --badge_bg--primary / --badge_bg--success / ... */
32
+ :host([variant='primary']) .badge {
33
+ --badge_bg--variant: var(--badge_bg--primary, var(--bds-color_interactive));
34
+ --badge_text--variant: var(--badge_text--primary, var(--bds-color_on-interactive));
35
+ }
36
+
37
+ :host([variant='secondary']) .badge {
38
+ --badge_bg--variant: var(--badge_bg--secondary, var(--bds-color_bg));
39
+ --badge_text--variant: var(--badge_text--secondary, var(--bds-color_on-bg));
40
+ }
41
+
42
+ :host([variant='success']) .badge {
43
+ --badge_bg--variant: var(--badge_bg--success, var(--bds-color_success));
44
+ --badge_text--variant: var(--badge_text--success, var(--bds-color_on-success));
45
+ }
46
+
47
+ :host([variant='error']) .badge {
48
+ --badge_bg--variant: var(--badge_bg--error, var(--bds-color_error));
49
+ --badge_text--variant: var(--badge_text--error, var(--bds-color_on-error));
50
+ }
51
+
52
+ :host([variant='warning']) .badge {
53
+ --badge_bg--variant: var(--badge_bg--warning, var(--bds-color_warning));
54
+ --badge_text--variant: var(--badge_text--warning, var(--bds-color_on-warning));
55
+ }
56
+ `;
57
+ static properties = {
58
+ variant: { type: String, reflect: true }
59
+ };
60
+ constructor() {
61
+ super();
62
+ this.variant = "primary";
63
+ }
64
+ render() {
65
+ return T`<span class="badge"><slot></slot></span>`;
66
+ }
67
+ };
68
+ customElements.define("bds-badge", BdsBadge);
69
+
70
+ export {
71
+ BdsBadge
72
+ };
@@ -25,12 +25,14 @@ var BdsCard = class extends i2 {
25
25
  border: none;
26
26
  }
27
27
 
28
- .card.--default { box-shadow: var(--bds-shadow_s); }
29
- .card.--elevated { box-shadow: var(--bds-shadow_m); }
28
+ /* Resting shadows — consumers can override per-variant via
29
+ --card_shadow--default / --card_shadow--elevated / --card_shadow--outlined. */
30
+ .card.--default { box-shadow: var(--card_shadow--default, var(--bds-shadow_s)); }
31
+ .card.--elevated { box-shadow: var(--card_shadow--elevated, var(--bds-shadow_m)); }
30
32
 
31
33
  .card.--outlined {
32
34
  --bdc_color: currentcolor;
33
- box-shadow: inset 0 0 0 var(--bdc-outline_width) var(--bdc_color), var(--bds-shadow_s);
35
+ box-shadow: var(--card_shadow--outlined, inset 0 0 0 var(--bdc-outline_width) var(--bdc_color), var(--bds-shadow_s));
34
36
  }
35
37
 
36
38
  .card.--padding-none { padding: 0; }
@@ -60,12 +62,12 @@ var BdsCard = class extends i2 {
60
62
  @media (hover: hover) and (pointer: fine) {
61
63
  .card.--clickable:hover {
62
64
  transform: translateY(-2px);
63
- box-shadow: var(--bds-shadow_xl);
65
+ box-shadow: var(--card_shadow--clickable-hover, var(--bds-shadow_xl));
64
66
  }
65
67
 
66
- .card.--default:hover { box-shadow: var(--bds-shadow_m); }
67
- .card.--elevated:hover { box-shadow: var(--bds-shadow_2xl); }
68
- .card.--outlined:hover { box-shadow: var(--bds-shadow_s); }
68
+ .card.--default:hover { box-shadow: var(--card_shadow--default-hover, var(--bds-shadow_m)); }
69
+ .card.--elevated:hover { box-shadow: var(--card_shadow--elevated-hover, var(--bds-shadow_2xl)); }
70
+ .card.--outlined:hover { box-shadow: var(--card_shadow--outlined-hover, var(--bds-shadow_s)); }
69
71
  }
70
72
  `;
71
73
  static properties = {
@@ -18,36 +18,40 @@ var BdsAlert = class extends i2 {
18
18
  gap: var(--bds-space_s);
19
19
  padding: var(--bds-space_m);
20
20
  border-radius: var(--bds-border_radius--s);
21
- border-inline-start: var(--bds-space_s) solid var(--alert_border, var(--bds-color_blue));
22
- background-color: var(--alert_bg, var(--bds-color_blue--subtle));
23
- color: var(--alert_text, var(--bds-color_on-blue--subtle));
21
+ /* Layered cascade: public overrides (--alert_bg, --alert_text, --alert_border)
22
+ are read OUTERMOST so an ancestor-set value always wins over variant
23
+ defaults. Variants write to the inner --alert_*--variant slot. */
24
+ background-color: var(--alert_bg, var(--alert_bg--variant, var(--bds-color_blue--subtle)));
25
+ color: var(--alert_text, var(--alert_text--variant, var(--bds-color_on-blue--subtle)));
26
+ border-inline-start: var(--bds-space_s) solid var(--alert_border, var(--alert_border--variant, var(--bds-color_blue)));
24
27
  font-size: var(--bds-font_size--body);
25
28
  line-height: var(--bds-font_line-height--body);
26
29
  }
27
30
 
28
- /* Variant colours */
31
+ /* Per-variant resting overrides — consumers can theme per-variant via
32
+ --alert_bg--info / --alert_bg--success / ... */
29
33
  :host([variant='info']) .alert {
30
- --alert_bg: var(--bds-color_blue--subtle);
31
- --alert_text: var(--bds-color_on-blue--subtle);
32
- --alert_border: var(--bds-color_blue);
34
+ --alert_bg--variant: var(--alert_bg--info, var(--bds-color_blue--subtle));
35
+ --alert_text--variant: var(--alert_text--info, var(--bds-color_on-blue--subtle));
36
+ --alert_border--variant: var(--alert_border--info, var(--bds-color_blue));
33
37
  }
34
38
 
35
39
  :host([variant='success']) .alert {
36
- --alert_bg: var(--bds-color_success--subtle);
37
- --alert_text: var(--bds-color_on-success--subtle);
38
- --alert_border: var(--bds-color_success);
40
+ --alert_bg--variant: var(--alert_bg--success, var(--bds-color_success--subtle));
41
+ --alert_text--variant: var(--alert_text--success, var(--bds-color_on-success--subtle));
42
+ --alert_border--variant: var(--alert_border--success, var(--bds-color_success));
39
43
  }
40
44
 
41
45
  :host([variant='warning']) .alert {
42
- --alert_bg: var(--bds-color_orange--subtle);
43
- --alert_text: var(--bds-color_on-orange--subtle);
44
- --alert_border: var(--bds-color_warning);
46
+ --alert_bg--variant: var(--alert_bg--warning, var(--bds-color_orange--subtle));
47
+ --alert_text--variant: var(--alert_text--warning, var(--bds-color_on-orange--subtle));
48
+ --alert_border--variant: var(--alert_border--warning, var(--bds-color_warning));
45
49
  }
46
50
 
47
51
  :host([variant='error']) .alert {
48
- --alert_bg: rgb(from var(--bds-color_error) r g b / 12%);
49
- --alert_text: var(--bds-color_on-bg);
50
- --alert_border: var(--bds-color_error);
52
+ --alert_bg--variant: var(--alert_bg--error, rgb(from var(--bds-color_error) r g b / 12%));
53
+ --alert_text--variant: var(--alert_text--error, var(--bds-color_on-bg));
54
+ --alert_border--variant: var(--alert_border--error, var(--bds-color_error));
51
55
  }
52
56
 
53
57
  /* Icon slot wrapper — hidden unless slot has content */
@@ -69,7 +69,7 @@ import {
69
69
  } from "./chunk-UFFCKVZE.js";
70
70
  import {
71
71
  BdsButton
72
- } from "./chunk-N3TN6WCH.js";
72
+ } from "./chunk-GER5UJOF.js";
73
73
  import {
74
74
  BdsCollapsible
75
75
  } from "./chunk-6I2DBFQ7.js";
@@ -104,19 +104,19 @@ import "./chunk-QPOKHLMQ.js";
104
104
  import "./chunk-3S42DZ7W.js";
105
105
  import {
106
106
  BdsAlert
107
- } from "./chunk-BX3IPVOB.js";
107
+ } from "./chunk-ZNKEZIYE.js";
108
108
  import {
109
109
  BdsAvatar
110
110
  } from "./chunk-57TRNCWQ.js";
111
111
  import {
112
112
  BdsBadge
113
- } from "./chunk-OHUTLSIQ.js";
113
+ } from "./chunk-QWPACK5L.js";
114
114
  import {
115
115
  BdsBreadcrumb
116
116
  } from "./chunk-Q4T5BJT6.js";
117
117
  import {
118
118
  BdsCard
119
- } from "./chunk-YRXCVKHV.js";
119
+ } from "./chunk-RTOS7LLG.js";
120
120
  import {
121
121
  BdsDescriptionList
122
122
  } from "./chunk-AGKJTMJI.js";
@@ -200,11 +200,11 @@ function applyMasonryLayout(container, rawChildren, options = {}) {
200
200
  }
201
201
  const heights = children.map((child) => child.offsetHeight);
202
202
  const cols = countGridColumns(container) || 1;
203
- const totalRowSum = heights.reduce(
204
- (s, h) => s + Math.max(1, Math.ceil(h + targetGap)),
205
- 0
206
- );
207
- const projectedRows = totalRowSum / cols;
203
+ const projectedRows = children.reduce((sum, child, i3) => {
204
+ const rowSpan = Math.max(1, Math.ceil(heights[i3] + targetGap));
205
+ const colSpan = readExplicitSpan(child, cols);
206
+ return sum + rowSpan * colSpan;
207
+ }, 0) / cols;
208
208
  const autoUnit = Math.max(
209
209
  1,
210
210
  Math.ceil(projectedRows / TARGET_MAX_IMPLICIT_ROWS)
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  BdsButton
3
- } from "../chunk-N3TN6WCH.js";
3
+ } from "../chunk-GER5UJOF.js";
4
4
  import "../chunk-LGEZYFUU.js";
5
5
  export {
6
6
  BdsButton
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  BdsAlert
3
- } from "../chunk-BX3IPVOB.js";
3
+ } from "../chunk-ZNKEZIYE.js";
4
4
  import "../chunk-LGEZYFUU.js";
5
5
  export {
6
6
  BdsAlert
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  BdsBadge
3
- } from "../chunk-OHUTLSIQ.js";
3
+ } from "../chunk-QWPACK5L.js";
4
4
  import "../chunk-LGEZYFUU.js";
5
5
  export {
6
6
  BdsBadge
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  BdsCard
3
- } from "../chunk-YRXCVKHV.js";
3
+ } from "../chunk-RTOS7LLG.js";
4
4
  import "../chunk-LGEZYFUU.js";
5
5
  export {
6
6
  BdsCard
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@boostdev/design-system-components",
3
- "version": "2.5.0",
3
+ "version": "2.6.0",
4
4
  "description": "BoostDev React component library: accessible, token-driven components built on @boostdev/design-system-foundation",
5
5
  "keywords": [
6
6
  "React",
@@ -95,35 +95,109 @@ Provide `href` to render as an `<a>` with button styling. `disabled` on a link s
95
95
 
96
96
  ## CSS variables
97
97
 
98
+ The override surface is **layered** — pick the level of specificity you need. Every public token cascades through any ancestor, so you can theme one button, a section, or the whole app from the same selector.
99
+
100
+ ### Quick theme — set two tokens
101
+
102
+ For most re-themes, the **identity tokens** are all you need. They drive both variants at rest, plus outline-hover.
103
+
104
+ ```css
105
+ .brand-buttons {
106
+ --button_color: var(--color_viviq-green);
107
+ --button_on-color: var(--color_on-viviq-green);
108
+ }
109
+ ```
110
+
111
+ What that gets you, automatically:
112
+
113
+ | State | Default variant | Outline variant |
114
+ | --- | --- | --- |
115
+ | Resting | bg = `--button_color`, text = `--button_on-color` | bg = transparent, text/border = `--button_color--on-bg` |
116
+ | Hover | bg = page surface, text = `--bds-color_interactive_on-bg` | bg = `--button_color`, text = `--button_on-color` |
117
+
118
+ Outline-resting still uses the **text-safe** `--button_color--on-bg` (a darker-on-light-bg variant) by default — so dark-on-light readability stays intact even when your identity colour is bright. To make the identity colour itself flow through outline-resting, set `--button_color--on-bg` too:
119
+
120
+ ```css
121
+ .brand-buttons {
122
+ --button_color: var(--color_viviq-green);
123
+ --button_on-color: var(--color_on-viviq-green);
124
+ --button_color--on-bg: var(--color_viviq-green); /* outline-resting follows */
125
+ }
126
+ ```
127
+
128
+ ### Full token surface
129
+
130
+ Every public token below can be set on the button or any ancestor — the consumer-set value always wins over variant + state defaults. Tokens are grouped by **state × variant** so the symmetry is visible.
131
+
132
+ #### Identity & geometry
133
+
98
134
  <table>
99
135
  <thead>
100
136
  <tr><th>Variable</th><th>Default</th><th>Description</th></tr>
101
137
  </thead>
102
138
  <tbody>
103
- <tr><td>`--button_color`</td><td>`var(--bds-color_interactive)`</td><td>Identity colour fills default, outlines outline. Set this to re-theme.</td></tr>
104
- <tr><td>`--button_on-color`</td><td>`var(--bds-color_on-interactive)`</td><td>Text/icon colour on the button. Pair with `--button_color`.</td></tr>
105
- <tr><td>`--button_color--on-bg`</td><td>`var(--bds-color_interactive_on-bg)`</td><td>Text-safe variant of `--button_color` for outline variant text/border.</td></tr>
139
+ <tr><td>`--button_color`</td><td>`var(--bds-color_interactive)`</td><td>Identity colour. Fills default-resting; flows through outline-hover.</td></tr>
140
+ <tr><td>`--button_on-color`</td><td>`var(--bds-color_on-interactive)`</td><td>Text/icon colour on default-resting. Flows through outline-hover.</td></tr>
141
+ <tr><td>`--button_color--on-bg`</td><td>`var(--bds-color_interactive_on-bg)`</td><td>Text-safe variant of `--button_color` for outline-resting text + border.</td></tr>
142
+ <tr><td>`--button_pulse-color`</td><td>`var(--button_color)`</td><td>Halo colour for the pulse animation.</td></tr>
106
143
  <tr><td>`--button_radius`</td><td>`var(--bds-border_radius--full)`</td><td>Button border radius.</td></tr>
107
144
  <tr><td>`--button_height`</td><td>`3em` (medium)</td><td>Button height. Also drives the width when `isIconOnly` is set.</td></tr>
108
- <tr><td>`--button_bg--hover`</td><td>`var(--bds-color_bg)`</td><td>Default variant background on hover.</td></tr>
109
- <tr><td>`--button_text--hover`</td><td>`var(--bds-color_interactive_on-bg)`</td><td>Default variant text colour on hover.</td></tr>
110
- <tr><td>`--button_bg--outline-hover`</td><td>`var(--button_color)`</td><td>Outline variant background on hover.</td></tr>
111
- <tr><td>`--button_text--outline-hover`</td><td>`var(--button_on-color)`</td><td>Outline variant text colour on hover.</td></tr>
112
- <tr><td>`--button_pulse-color`</td><td>`var(--button_color)`</td><td>Halo colour for the pulse animation.</td></tr>
145
+ <tr><td>`--button_font-size`</td><td>`inherit` (medium)</td><td>Button font size.</td></tr>
146
+ </tbody>
147
+ </table>
148
+
149
+ #### Default variant
150
+
151
+ <table>
152
+ <thead>
153
+ <tr><th>State</th><th>Variable</th><th>Default</th></tr>
154
+ </thead>
155
+ <tbody>
156
+ <tr><td rowspan="3">Resting</td><td>`--button_bg`</td><td>`var(--button_color)`</td></tr>
157
+ <tr><td>`--button_text`</td><td>`var(--button_on-color)`</td></tr>
158
+ <tr><td>`--button_border`</td><td>`transparent`</td></tr>
159
+ <tr><td rowspan="4">Hover</td><td>`--button_bg--hover`</td><td>`var(--bds-color_bg)`</td></tr>
160
+ <tr><td>`--button_text--hover`</td><td>`var(--bds-color_interactive_on-bg)`</td></tr>
161
+ <tr><td>`--button_border--hover`</td><td>`currentcolor`</td></tr>
162
+ <tr><td>`--button_pulse-color--hover`</td><td>`var(--bds-color_interactive_on-bg)`</td></tr>
113
163
  </tbody>
114
164
  </table>
115
165
 
116
- ### Re-theming
166
+ #### Outline variant
117
167
 
118
- Change the two identity custom properties to re-theme both variants at once:
168
+ <table>
169
+ <thead>
170
+ <tr><th>State</th><th>Variable</th><th>Default</th></tr>
171
+ </thead>
172
+ <tbody>
173
+ <tr><td rowspan="3">Resting</td><td>`--button_bg--outline`</td><td>`transparent`</td></tr>
174
+ <tr><td>`--button_text--outline`</td><td>`var(--button_color--on-bg)`</td></tr>
175
+ <tr><td>`--button_border--outline`</td><td>`var(--button_color--on-bg)`</td></tr>
176
+ <tr><td rowspan="3">Hover</td><td>`--button_bg--outline-hover`</td><td>`var(--button_color)`</td></tr>
177
+ <tr><td>`--button_text--outline-hover`</td><td>`var(--button_on-color)`</td></tr>
178
+ <tr><td>`--button_border--outline-hover`</td><td>`transparent`</td></tr>
179
+ </tbody>
180
+ </table>
181
+
182
+ ### Worked example: brand re-theme with one fine-grained tweak
183
+
184
+ Most consumers stop at the quick-theme. If you need to override one specific state — for example, you want default-hover to stay branded instead of fading to the page surface — drop a state-specific override alongside the identity tokens:
119
185
 
120
186
  ```css
121
- .danger-zone .button {
122
- --button_color: var(--bds-color_error);
123
- --button_on-color: var(--bds-color_on-error);
187
+ .brand-buttons {
188
+ /* Identity — themes both variants at rest, plus outline-hover */
189
+ --button_color: var(--color_viviq-green);
190
+ --button_on-color: var(--color_on-viviq-green);
191
+ --button_color--on-bg: var(--color_viviq-green);
192
+
193
+ /* One fine-grained override: keep default-hover on-brand instead of fading */
194
+ --button_bg--hover: var(--color_viviq-green--dark);
195
+ --button_text--hover: var(--color_on-viviq-green);
124
196
  }
125
197
  ```
126
198
 
199
+ Outline-hover, default-resting, and outline-resting all derive from the identity tokens — you only spell out the override that diverges.
200
+
127
201
  ## Accessibility
128
202
 
129
203
  - Always provide children **or** `aria-label` — icon-only buttons without an accessible name are a WCAG failure.
@@ -46,17 +46,20 @@
46
46
  white-space: nowrap;
47
47
  line-height: 1;
48
48
 
49
- /* Colour model: override --button_color + --button_on-color to re-theme */
49
+ /* Identity tokens override --button_color + --button_on-color to re-theme */
50
50
  --button_color: var(--bds-color_interactive);
51
51
  --button_on-color: var(--bds-color_on-interactive);
52
52
 
53
- /* Text-safe variant of --button_color for use on page background (ghost text/border) */
53
+ /* Text-safe variant of --button_color for use on page background (outline text/border) */
54
54
  --button_color--on-bg: var(--bds-color_interactive_on-bg);
55
55
 
56
- background-color: var(--button_bg, var(--button_color));
57
- color: var(--button_text, var(--button_on-color));
58
- border: var(--button_border, none);
59
- --bdc_color: transparent;
56
+ /* Layered cascade: the consumer-facing overrides (--button_bg, --button_text,
57
+ --button_border) are read OUTERMOST so an ancestor-set value always wins
58
+ over variant + hover defaults. Variants and hover write to the inner
59
+ --button_*--variant slot, never to the public tokens. */
60
+ background-color: var(--button_bg, var(--button_bg--variant, var(--button_color)));
61
+ color: var(--button_text, var(--button_text--variant, var(--button_on-color)));
62
+ --bdc_color: var(--button_border, var(--button_border--variant, transparent));
60
63
 
61
64
  box-shadow: inset 0 0 0 var(--bdc-outline_width) var(--bdc_color);
62
65
  transition: var(--bds-animation_transition), --bdc_color var(--bds-animation_transition-duration) var(--bds-animation_easing);
@@ -65,12 +68,14 @@
65
68
  /* Default: filled with --button_color, no visible border */
66
69
  .button.--default { /* inherits base — no overrides needed */ }
67
70
 
68
- /* Outline: transparent background, border and text use text-safe --button_color--on-bg */
71
+ /* Outline: transparent background, border and text use text-safe --button_color--on-bg.
72
+ Resting overrides (--button_bg--outline, --button_text--outline, --button_border--outline)
73
+ are honored if the consumer sets them. */
69
74
  .button.--outline,
70
75
  .button.--ghost /* @deprecated — use variant="outline" */ {
71
- --button_bg: transparent;
72
- --button_text: var(--button_color--on-bg);
73
- --bdc_color: var(--button_color--on-bg);
76
+ --button_bg--variant: var(--button_bg--outline, transparent);
77
+ --button_text--variant: var(--button_text--outline, var(--button_color--on-bg));
78
+ --button_border--variant: var(--button_border--outline, var(--button_color--on-bg));
74
79
  }
75
80
 
76
81
  .button.--size_small {
@@ -164,10 +169,10 @@
164
169
  /* Default hover: outline appears, bg becomes page surface */
165
170
  @media (hover: hover) and (pointer: fine) {
166
171
  .button.--default:hover {
167
- --button_bg: var(--button_bg--hover, var(--bds-color_bg));
168
- --button_text: var(--button_text--hover, var(--bds-color_interactive_on-bg));
169
- --button_pulse-color: var(--button_pulse-color--hover, var(--bds-color_interactive_on-bg));
170
- --bdc_color: currentcolor;
172
+ --button_bg--variant: var(--button_bg--hover, var(--bds-color_bg));
173
+ --button_text--variant: var(--button_text--hover, var(--bds-color_interactive_on-bg));
174
+ --button_border--variant: var(--button_border--hover, currentcolor);
175
+ --button_pulse-color: var(--button_pulse-color--hover, var(--bds-color_interactive_on-bg));
171
176
  }
172
177
  }
173
178
 
@@ -175,9 +180,9 @@
175
180
  @media (hover: hover) and (pointer: fine) {
176
181
  .button.--outline:hover,
177
182
  .button.--ghost:hover {
178
- --button_bg: var(--button_bg--outline-hover, var(--button_color));
179
- --button_text: var(--button_text--outline-hover, var(--button_on-color));
180
- --bdc_color: transparent;
183
+ --button_bg--variant: var(--button_bg--outline-hover, var(--button_color));
184
+ --button_text--variant: var(--button_text--outline-hover, var(--button_on-color));
185
+ --button_border--variant: var(--button_border--outline-hover, transparent);
181
186
  }
182
187
  }
183
188
  }
@@ -67,17 +67,36 @@ showToast('New message received', 'info');
67
67
 
68
68
  ## CSS variables
69
69
 
70
+ The override surface is **layered**. Setting a public token on the toast (or any ancestor) themes every variant; setting a per-variant token themes only that one variant.
71
+
72
+ ### Public override tokens (win over variant defaults)
73
+
74
+ <table>
75
+ <thead>
76
+ <tr><th>Variable</th><th>Default</th><th>Description</th></tr>
77
+ </thead>
78
+ <tbody>
79
+ <tr><td>`--toast_color`</td><td>per variant</td><td>Toast background. Set to theme every variant at once.</td></tr>
80
+ <tr><td>`--toast_on-color`</td><td>per variant</td><td>Toast text colour.</td></tr>
81
+ </tbody>
82
+ </table>
83
+
84
+ ### Per-variant overrides
70
85
 
71
86
  <table>
72
87
  <thead>
73
- <tr><th>Variable</th><th>Description</th></tr>
88
+ <tr><th>Variant</th><th>Background</th><th>Text</th></tr>
74
89
  </thead>
75
90
  <tbody>
76
- <tr><td>`--toast_color`</td><td>Toast background</td></tr>
77
- <tr><td>`--toast_on-color`</td><td>Toast text colour</td></tr>
91
+ <tr><td>`info`</td><td>`--toast_color--info`</td><td>`--toast_on-color--info`</td></tr>
92
+ <tr><td>`success`</td><td>`--toast_color--success`</td><td>`--toast_on-color--success`</td></tr>
93
+ <tr><td>`warning`</td><td>`--toast_color--warning`</td><td>`--toast_on-color--warning`</td></tr>
94
+ <tr><td>`error`</td><td>`--toast_color--error`</td><td>`--toast_on-color--error`</td></tr>
78
95
  </tbody>
79
96
  </table>
80
97
 
98
+ Defaults resolve to the corresponding foundation tokens.
99
+
81
100
 
82
101
  ## Accessibility
83
102
 
@@ -16,8 +16,11 @@
16
16
 
17
17
  .toast {
18
18
  padding: var(--bds-space_m) var(--bds-space_l);
19
- background-color: var(--toast_color, var(--bds-color_bg));
20
- color: var(--toast_on-color, var(--bds-color_on-bg));
19
+
20
+ /* Layered cascade: public overrides (--toast_color, --toast_on-color) win
21
+ over per-variant defaults. Variants write to the inner --toast_*--variant slot. */
22
+ background-color: var(--toast_color, var(--toast_color--variant, var(--bds-color_bg)));
23
+ color: var(--toast_on-color, var(--toast_on-color--variant, var(--bds-color_on-bg)));
21
24
  border-radius: var(--bds-border_radius--s);
22
25
  box-shadow: var(--bds-shadow_s);
23
26
  display: flex;
@@ -28,23 +31,23 @@
28
31
  }
29
32
 
30
33
  .toast.--variant_success {
31
- --toast_color: var(--bds-color_success);
32
- --toast_on-color: var(--bds-color_on-success);
34
+ --toast_color--variant: var(--toast_color--success, var(--bds-color_success));
35
+ --toast_on-color--variant: var(--toast_on-color--success, var(--bds-color_on-success));
33
36
  }
34
37
 
35
38
  .toast.--variant_warning {
36
- --toast_color: var(--bds-color_warning);
37
- --toast_on-color: var(--bds-color_on-warning);
39
+ --toast_color--variant: var(--toast_color--warning, var(--bds-color_warning));
40
+ --toast_on-color--variant: var(--toast_on-color--warning, var(--bds-color_on-warning));
38
41
  }
39
42
 
40
43
  .toast.--variant_info {
41
- --toast_color: var(--bds-color_bg--subtle);
42
- --toast_on-color: var(--bds-color_on-bg--subtle);
44
+ --toast_color--variant: var(--toast_color--info, var(--bds-color_bg--subtle));
45
+ --toast_on-color--variant: var(--toast_on-color--info, var(--bds-color_on-bg--subtle));
43
46
  }
44
47
 
45
48
  .toast.--variant_error {
46
- --toast_color: var(--bds-color_error);
47
- --toast_on-color: var(--bds-color_on-error);
49
+ --toast_color--variant: var(--toast_color--error, var(--bds-color_error));
50
+ --toast_on-color--variant: var(--toast_on-color--error, var(--bds-color_on-error));
48
51
  }
49
52
 
50
53
  .message {
@@ -43,23 +43,35 @@ Contained surface for grouping related content. Three visual variants provide di
43
43
 
44
44
  ## CSS variables
45
45
 
46
+ ### Card surface
46
47
 
47
48
  <table>
48
49
  <thead>
49
50
  <tr><th>Variable</th><th>Default</th><th>Description</th></tr>
50
51
  </thead>
51
52
  <tbody>
52
- <tr><td>`--card_color`</td><td>`var(--bds-color_bg)`</td><td>Card background colour</td></tr>
53
- <tr><td>`--card_on-color`</td><td>`var(--bds-color_on-bg)`</td><td>Card text colour</td></tr>
54
- <tr><td>`--card_padding`</td><td>varies by `padding` prop</td><td>Card padding (overrides the variant default)</td></tr>
55
- <tr><td>`--card_border`</td><td>`none`</td><td>Border (outlined variant)</td></tr>
56
- <tr><td>`--card_shadow--clickable-hover`</td><td>`var(--bds-shadow_xl)`</td><td>Box shadow on hover for clickable cards</td></tr>
57
- <tr><td>`--card_shadow--default-hover`</td><td>`var(--bds-shadow_m)`</td><td>Box shadow on hover for default variant</td></tr>
58
- <tr><td>`--card_shadow--elevated-hover`</td><td>`var(--bds-shadow_2xl)`</td><td>Box shadow on hover for elevated variant</td></tr>
59
- <tr><td>`--card_shadow--outlined-hover`</td><td>`var(--bds-shadow_s)`</td><td>Box shadow on hover for outlined variant</td></tr>
53
+ <tr><td>`--card_color`</td><td>`var(--bds-color_bg)`</td><td>Card background colour.</td></tr>
54
+ <tr><td>`--card_on-color`</td><td>`var(--bds-color_on-bg)`</td><td>Card text colour.</td></tr>
55
+ <tr><td>`--card_padding`</td><td>varies by `padding` prop</td><td>Card padding (overrides the prop default).</td></tr>
56
+ <tr><td>`--card_border`</td><td>`none`</td><td>Border (outlined variant).</td></tr>
60
57
  </tbody>
61
58
  </table>
62
59
 
60
+ ### Per-variant box-shadow (resting + hover)
61
+
62
+ <table>
63
+ <thead>
64
+ <tr><th>Variant</th><th>Resting shadow</th><th>Hover shadow</th></tr>
65
+ </thead>
66
+ <tbody>
67
+ <tr><td>`default`</td><td>`--card_shadow--default` (`var(--bds-shadow_s)`)</td><td>`--card_shadow--default-hover` (`var(--bds-shadow_m)`)</td></tr>
68
+ <tr><td>`elevated`</td><td>`--card_shadow--elevated` (`var(--bds-shadow_m)`)</td><td>`--card_shadow--elevated-hover` (`var(--bds-shadow_2xl)`)</td></tr>
69
+ <tr><td>`outlined`</td><td>`--card_shadow--outlined` (inset border + `var(--bds-shadow_s)`)</td><td>`--card_shadow--outlined-hover` (`var(--bds-shadow_s)`)</td></tr>
70
+ </tbody>
71
+ </table>
72
+
73
+ Plus `--card_shadow--clickable-hover` (`var(--bds-shadow_xl)`) for hover on any clickable card.
74
+
63
75
 
64
76
  ## Accessibility
65
77
 
@@ -7,19 +7,21 @@
7
7
  position: relative;
8
8
  }
9
9
 
10
+ /* Resting shadows — consumers can override per-variant via
11
+ --card_shadow--default / --card_shadow--elevated / --card_shadow--outlined. */
10
12
  .card.--default {
11
- box-shadow: var(--bds-shadow_s);
13
+ box-shadow: var(--card_shadow--default, var(--bds-shadow_s));
12
14
  }
13
15
 
14
16
  .card.--elevated {
15
- box-shadow: var(--bds-shadow_m);
17
+ box-shadow: var(--card_shadow--elevated, var(--bds-shadow_m));
16
18
  }
17
19
 
18
20
  .card.--outlined {
19
21
  border: var(--card_border, none);
20
22
  --bdc_color: currentcolor;
21
23
 
22
- box-shadow: inset 0 0 0 var(--bdc-outline_width) var(--bdc_color), var(--bds-shadow_s);
24
+ box-shadow: var(--card_shadow--outlined, inset 0 0 0 var(--bdc-outline_width) var(--bdc_color), var(--bds-shadow_s));
23
25
  }
24
26
 
25
27
  .card.--clickable {
@@ -260,6 +260,25 @@ describe('applyMasonryLayout', () => {
260
260
  expect(items[0].style.gridRowEnd).toBe('span 100');
261
261
  });
262
262
 
263
+ it('accounts for column span when projecting implicit row count', () => {
264
+ // Regression: the projection has to weight each item by its column span,
265
+ // otherwise a gallery of half-span items reports half the true row
266
+ // count and the unit stays at 1. 30 items × 600px tall × 6-col span on
267
+ // a 12-col grid = 30 * 600 * 6 / 12 = 9,000 implicit rows; the naive
268
+ // sum(rowSpan)/cols formula would have said 1,500 → unit 1 → overlap in
269
+ // Firefox. Correct: ceil(9000/5000) = 2.
270
+ const container = makeContainer(0);
271
+ container.style.gridTemplateColumns = '100px '.repeat(12).trim();
272
+ const items = Array.from({ length: 30 }, () => {
273
+ const el = makeItem(600);
274
+ el.style.gridColumn = 'span 6';
275
+ return el;
276
+ });
277
+ container.append(...items);
278
+ applyMasonryLayout(container, items);
279
+ expect(container.style.gridAutoRows).toBe('2px');
280
+ });
281
+
263
282
  it('honors an explicit rowUnitPx override', () => {
264
283
  const container = makeContainer(10);
265
284
  const item = makeItem(100);