@repobit/dex-system-design 1.0.1 → 2.0.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 (136) hide show
  1. package/CHANGELOG.md +81 -0
  2. package/README.md +43 -12
  3. package/package.json +4 -2
  4. package/schemas/component-manifest.json +207 -49
  5. package/schemas/manifest-omissions.json +2 -0
  6. package/src/components/accordion/accordion.css.js +12 -1
  7. package/src/components/agent-endpoints/agent-endpoints.js +1 -1
  8. package/src/components/alert/alert.css.js +22 -1
  9. package/src/components/anchor/anchor-nav.css.js +6 -1
  10. package/src/components/annotation/annotation.css.js +6 -1
  11. package/src/components/architecture.stories.js +6 -4
  12. package/src/components/avatar/avatar.js +37 -11
  13. package/src/components/avatar/avatar.stories.js +10 -6
  14. package/src/components/awards/awards-flex.css.js +31 -129
  15. package/src/components/awards/awards-flex.js +66 -116
  16. package/src/components/awards/awards-flex.stories.js +8 -6
  17. package/src/components/back/back.css.js +6 -1
  18. package/src/components/back/back.js +24 -12
  19. package/src/components/back/back.stories.js +3 -3
  20. package/src/components/badge/badge.js +6 -3
  21. package/src/components/badge/badge.stories.js +13 -9
  22. package/src/components/bento-grid/bento-grid.js +1 -1
  23. package/src/components/blockquote/blockquote.css.js +12 -3
  24. package/src/components/blockquote/blockquote.js +12 -5
  25. package/src/components/blockquote/blockquote.stories.js +6 -4
  26. package/src/components/button/button.css.js +46 -25
  27. package/src/components/button/button.js +44 -43
  28. package/src/components/button/button.stories.js +2 -2
  29. package/src/components/caption/caption.css.js +10 -0
  30. package/src/components/caption/caption.stories.js +4 -4
  31. package/src/components/carousel/carousel.css.js +19 -116
  32. package/src/components/carousel/carousel.js +70 -74
  33. package/src/components/carousel/carousel.stories.js +9 -8
  34. package/src/components/checkbox/checkbox.css.js +6 -1
  35. package/src/components/code/code.css.js +6 -1
  36. package/src/components/code/code.js +48 -7
  37. package/src/components/code/code.stories.js +7 -4
  38. package/src/components/divider/divider-horizontal.js +11 -3
  39. package/src/components/divider/divider-vertical.js +12 -5
  40. package/src/components/divider/divider.css.js +4 -1
  41. package/src/components/divider/divider.stories.js +6 -6
  42. package/src/components/dropdown/dropdown.js +67 -13
  43. package/src/components/dropdown/dropdown.stories.js +15 -14
  44. package/src/components/fieldset/fieldset.stories.js +41 -16
  45. package/src/components/footer/footer-nav-menu.css.js +6 -1
  46. package/src/components/footer/footer.css.js +6 -1
  47. package/src/components/header/header-code.js +1 -1
  48. package/src/components/header/header.css.js +9 -4
  49. package/src/components/heading/heading.stories.js +64 -31
  50. package/src/components/highlight/highlight.js +74 -34
  51. package/src/components/highlight/highlight.stories.js +5 -4
  52. package/src/components/icons/icon-element.js +49 -6
  53. package/src/components/icons/icons.stories.js +7 -0
  54. package/src/components/image/image.css.js +9 -98
  55. package/src/components/image/image.js +7 -2
  56. package/src/components/input/custom-form.stories.js +14 -19
  57. package/src/components/input/input-clipboard.css.js +6 -1
  58. package/src/components/input/input-clipboard.js +56 -8
  59. package/src/components/input/input.js +19 -2
  60. package/src/components/label/label.js +87 -1
  61. package/src/components/label/label.stories.js +21 -12
  62. package/src/components/link/link.css.js +52 -3
  63. package/src/components/link/link.js +52 -17
  64. package/src/components/link/link.stories.js +114 -3
  65. package/src/components/list/list-item/list-item.css.js +4 -6
  66. package/src/components/list/list-item/list-item.js +53 -5
  67. package/src/components/list/list-item/list-item.stories.js +22 -0
  68. package/src/components/list/list.js +27 -0
  69. package/src/components/modal/modal.css.js +6 -1
  70. package/src/components/pagination-dots/pagination-dots.css.js +161 -0
  71. package/src/components/pagination-dots/pagination-dots.js +202 -0
  72. package/src/components/pagination-dots/pagination-dots.stories.js +171 -0
  73. package/src/components/paragraph/paragraph.css.js +37 -1
  74. package/src/components/paragraph/paragraph.js +19 -2
  75. package/src/components/paragraph/paragraph.stories.js +40 -6
  76. package/src/components/picture/picture.css.js +8 -67
  77. package/src/components/picture/picture.js +15 -8
  78. package/src/components/pricing-cards/pricing-card.css.js +12 -2
  79. package/src/components/pricing-cards/pricing-card.js +4 -4
  80. package/src/components/progress/progress.js +24 -4
  81. package/src/components/progress/progress.stories.js +8 -6
  82. package/src/components/radio/radio.css.js +6 -1
  83. package/src/components/radio/radio.js +48 -3
  84. package/src/components/radio/radio.stories.js +1 -1
  85. package/src/components/shared/form-field.css.js +30 -4
  86. package/src/components/shared/token-scales.js +16 -0
  87. package/src/components/spinner/spinner.css.js +31 -4
  88. package/src/components/spinner/spinner.js +26 -4
  89. package/src/components/spinner/spinner.stories.js +10 -7
  90. package/src/components/switch/switch.css.js +21 -3
  91. package/src/components/switch/switch.js +8 -1
  92. package/src/components/switch/switch.stories.js +2 -1
  93. package/src/components/tab-list/tab-list.css.js +6 -1
  94. package/src/components/terminal/terminal-cursor.js +11 -0
  95. package/src/components/terminal/terminal-message.js +1 -1
  96. package/src/components/termsOfUse/terms.css.js +6 -1
  97. package/src/components/textarea/textarea.stories.js +23 -0
  98. package/src/components/tooltip/tooltip.css.js +24 -1
  99. package/src/components/tooltip/tooltip.js +135 -5
  100. package/src/components/tooltip/tooltip.stories.js +20 -14
  101. package/src/components/trust-panel/trust-panel.js +2 -2
  102. package/src/components/video/video.js +38 -3
  103. package/src/components/video/video.stories.js +10 -5
  104. package/src/components/video-player/video-player.css.js +6 -1
  105. package/src/tokens/elevation.stories.js +2 -3
  106. package/src/tokens/focus.stories.js +81 -15
  107. package/src/tokens/motion.stories.js +0 -6
  108. package/src/tokens/tokens.css +174 -97
  109. package/src/tokens/tokens.js +174 -97
  110. package/src/tokens/vertical-rhythm.stories.js +1 -1
  111. package/src/stories/Button.js +0 -20
  112. package/src/stories/Button.stories.js +0 -46
  113. package/src/stories/Configure.mdx +0 -364
  114. package/src/stories/Header.js +0 -45
  115. package/src/stories/Header.stories.js +0 -24
  116. package/src/stories/Page.js +0 -62
  117. package/src/stories/Page.stories.js +0 -20
  118. package/src/stories/assets/accessibility.png +0 -0
  119. package/src/stories/assets/accessibility.svg +0 -1
  120. package/src/stories/assets/addon-library.png +0 -0
  121. package/src/stories/assets/assets.png +0 -0
  122. package/src/stories/assets/avif-test-image.avif +0 -0
  123. package/src/stories/assets/context.png +0 -0
  124. package/src/stories/assets/discord.svg +0 -1
  125. package/src/stories/assets/docs.png +0 -0
  126. package/src/stories/assets/figma-plugin.png +0 -0
  127. package/src/stories/assets/github.svg +0 -1
  128. package/src/stories/assets/share.png +0 -0
  129. package/src/stories/assets/styling.png +0 -0
  130. package/src/stories/assets/testing.png +0 -0
  131. package/src/stories/assets/theming.png +0 -0
  132. package/src/stories/assets/tutorials.svg +0 -1
  133. package/src/stories/assets/youtube.svg +0 -1
  134. package/src/stories/button.css +0 -30
  135. package/src/stories/header.css +0 -32
  136. package/src/stories/page.css +0 -68
@@ -79,6 +79,11 @@ export default css`
79
79
  boundary, so the value is switched rather than the rule */
80
80
  --bd-button-accent-ring: var(--button-accent-ring-default);
81
81
 
82
+ /* the focus ring is switched the same way: surface="dark" swaps the blue
83
+ outline for a white one. white and outline-white do not read this; see
84
+ FOCUS. */
85
+ --bd-button-focus-ring: var(--button-shared-focus-outline);
86
+
82
87
  --bd-button-border-width: var(--border-width-2);
83
88
  }
84
89
 
@@ -121,14 +126,29 @@ export default css`
121
126
  FOCUS
122
127
  The design specifies no focus state, but 2.4.7 requires one.
123
128
 
129
+ The ring is an outline held 2px off the control, and the 2px between is
130
+ left transparent, so it is whatever surface the button sits on. That is
131
+ the one thing code can do here that Figma cannot, and it is why the two
132
+ differ. Figma builds a ring from spread shadows, every band of which is a
133
+ painted colour, so its library paints the gap and keeps one ring per
134
+ surface: focus/ring-primary on light, focus/ring-inverse on dark and
135
+ brand. Painted in code, that gap is what went wrong twice: a white band
136
+ merged into a white fill on surface="dark", and the neutral-900 band
137
+ that replaced it drew a black outline round every button on a brand
138
+ hero. A transparent gap cannot do either, on any surface.
139
+
140
+ So only the colour switches: blue on light, white under surface="dark",
141
+ which covers a brand hero and a neutral one alike; the numbers are on
142
+ --focus-outline-traditional. Every variant takes the same ring, ghost
143
+ included. An outline is also kept in forced-colors mode, where a
144
+ box-shadow ring is not drawn at all.
145
+
146
+ The rule leaves box-shadow alone, so an outlined variant's own boundary
147
+ in --bd-button-edge stays put while focused.
148
+
124
149
  This used to be driven from JavaScript: two requestAnimationFrame hops
125
150
  checked :focus-visible and set a data attribute the CSS then styled.
126
- :focus-visible does that natively, so the detection is gone and the ring
127
- is the shared one from Foundations/Focus — a two-colour ring whose white
128
- inner band keeps it visible on a coloured hero, which the old
129
- single-colour outline could not do. It is composed with
130
- --bd-button-edge rather than replacing it, so an outlined variant keeps
131
- its own boundary while focused; see the note on that property.
151
+ :focus-visible does that natively.
132
152
  ============================================ */
133
153
  button:focus,
134
154
  a:focus {
@@ -136,21 +156,17 @@ export default css`
136
156
  }
137
157
 
138
158
  .button:focus-visible {
139
- outline: none;
140
- box-shadow: var(--focus-outline-primary), var(--bd-button-edge);
159
+ outline: var(--bd-button-focus-ring);
160
+ outline-offset: var(--button-shared-focus-outlineOffset);
141
161
  }
142
162
 
143
- /* A white control needs the ring the other way up. --focus-outline-primary
144
- puts its WHITE band against the control, which is what keeps it visible on
145
- a coloured hero -- and is exactly wrong when the control is itself white:
146
- measured on Button — White / Outline-White, the inner band was 1.00:1
147
- against the button's own fill and the blue band 2.38:1 against the hero
148
- (1.75:1 at the gradient's light end), so neither reached the 3:1 WCAG
149
- 2.4.11 asks. outline-white is here too because restoring its boundary
150
- above puts a white border where the white band used to sit on the hero. */
163
+ /* white and outline-white are presets for a coloured banner and ignore
164
+ surface, so they take the white ring on their own: 4.53:1 on blue-500,
165
+ 7.95:1 at the light end of the banner gradient. A blue ring there was
166
+ 2.38:1, and 1.75:1 at that end. */
151
167
  .button--white:focus-visible,
152
168
  .button--outline-white:focus-visible {
153
- box-shadow: var(--focus-outline-inverse), var(--bd-button-edge);
169
+ outline: var(--focus-outline-traditional-inverse);
154
170
  }
155
171
 
156
172
  a.button {
@@ -175,14 +191,14 @@ export default css`
175
191
  vertical-align: middle;
176
192
  box-sizing: border-box;
177
193
 
178
- /* The variant's own boundary, held in a custom property so the focus ring
179
- can COMPOSE with it. box-shadow is a single list, not additive, and
180
- .button:focus-visible (0,2,0) outranks .button--ghost (0,1,0) -- so a
181
- focus rule that declared box-shadow directly erased the border of every
182
- outlined variant. A focused ghost button lost its dark outline and wore
183
- a blue ring instead, which read as a tertiary button. The default is a
184
- zero-spread transparent shadow rather than the keyword none, so the value
185
- a valid shadow the focus rule can append to. */
194
+ /* The variant's own boundary, held in a custom property so each state can
195
+ switch it. It was introduced while the focus ring was a box-shadow, so
196
+ the ring could COMPOSE with it: box-shadow is a single list, not
197
+ additive, and .button:focus-visible (0,2,0) outranks .button--ghost
198
+ (0,1,0) -- so a focus rule that declared box-shadow directly erased the
199
+ border of every outlined variant, and a focused ghost button read as a
200
+ tertiary one. The ring is an outline now and does not touch box-shadow;
201
+ a focus rule that goes back to declaring it brings that defect back. */
186
202
  --bd-button-edge: 0 0 0 0 transparent;
187
203
  box-shadow: var(--bd-button-edge);
188
204
 
@@ -557,6 +573,11 @@ export default css`
557
573
  /* unchanged red, plus a boundary against the hero */
558
574
  --bd-button-accent-ring: var(--color-neutral-0);
559
575
 
576
+ /* a white ring, the one colour that clears 3:1 on a brand hero and a
577
+ neutral one alike, which this surface has to cover without knowing
578
+ which it is on. The gap inside it is the hero itself; see FOCUS. */
579
+ --bd-button-focus-ring: var(--focus-outline-traditional-inverse);
580
+
560
581
  /* muted downward instead of upward: a neutral-100 fill would read as an
561
582
  active control on a dark hero. Exempt from contrast under WCAG 1.4.3. */
562
583
  --bd-button-disabled-bg: var(--color-neutral-700); /* no role: on-dark ground; see the block note */
@@ -29,6 +29,14 @@ const SIZE_CLASS = { sm: "button--small", md: "button--medium", lg: "button--lar
29
29
  const SURFACES = ["light", "dark"];
30
30
  const SHAPES = ["default", "rounded"];
31
31
 
32
+ /* custom-click bubbles and is composed, like every sibling event in the
33
+ package -- bd-checkbox's and bd-radio's change, bd-switch's and
34
+ bd-custom-input's bd-change. It was a plain Event, which does not bubble,
35
+ so it reached a listener on the button itself and nothing above it: a form
36
+ or a card delegating from a wrapper heard nothing. composed carries it out
37
+ of any shadow root the button is rendered inside, too. */
38
+ const clickEvent = () => new CustomEvent("custom-click", { bubbles: true, composed: true });
39
+
32
40
  /**
33
41
  * Everything bd-button and bd-button-link share.
34
42
  *
@@ -42,31 +50,37 @@ class BdButtonBase extends LitElement {
42
50
  static styles = [buttonsCSS];
43
51
 
44
52
  static properties = {
45
- label : { type: String },
46
- size : { type: String },
47
- variant : { type: String, reflect: true },
53
+ label : { type: String },
54
+ size : { type: String },
55
+ variant : { type: String, reflect: true },
48
56
  /** @deprecated pass `variant`. kind meant four different things across the
49
57
  atoms; on this one it always meant appearance. */
50
- kind : { type: String },
51
- fullWidth : { type: Boolean, attribute: "fullwidth" },
52
- strong : { type: Boolean },
53
- disabled : { type: Boolean, reflect: true },
54
- loading : { type: Boolean, reflect: true },
55
- fontSize : { type: String, attribute: "font-size" },
56
- fontWeight: { type: String, attribute: "font-weight" },
57
- align : { type: String },
58
- surface : { type: String, reflect: true },
58
+ kind : { type: String },
59
+ fullWidth : { type: Boolean, attribute: "fullwidth" },
60
+ strong : { type: Boolean },
61
+ disabled : { type: Boolean, reflect: true },
62
+ loading : { type: Boolean, reflect: true },
63
+ fontSize : { type: String, attribute: "font-size" },
64
+ fontWeight : { type: String, attribute: "font-weight" },
65
+ align : { type: String },
66
+ surface : { type: String, reflect: true },
59
67
  /** "rounded" takes the pill corner; see .button--rounded. */
60
- shape : { type: String, reflect: true },
68
+ shape : { type: String, reflect: true },
61
69
  /** The accent variant reveals a trailing arrow on hover; this opts out. */
62
- noArrow : { type: Boolean, attribute: "no-arrow" },
70
+ noArrow : { type: Boolean, attribute: "no-arrow" },
63
71
  /* A circle holding one icon instead of a label: the homepage's audience
64
72
  cards and the mobile menu toggle. A mode, not a variant, because every
65
73
  variant, size, surface and state applies to it unchanged -- which is how
66
74
  the Button icon sets in the foundation Figma file are drawn. With no
67
75
  visible text there is nothing to name the control, so label becomes
68
76
  required rather than optional. */
69
- iconOnly : { type: Boolean, attribute: "icon-only", reflect: true }
77
+ iconOnly : { type: Boolean, attribute: "icon-only", reflect: true },
78
+ /* Extra text a screen reader reads after the name, through
79
+ aria-describedby on the inner control. It has to be carried in here:
80
+ the control is in this shadow root, so an aria-describedby written on
81
+ the host, or pointing at an id outside, never reaches it. bd-tooltip
82
+ sets it on a bd-button trigger; a page can set it directly. */
83
+ description: { type: String }
70
84
  };
71
85
 
72
86
  constructor() {
@@ -90,6 +104,7 @@ class BdButtonBase extends LitElement {
90
104
  this.shape = "default";
91
105
  this.noArrow = false;
92
106
  this.iconOnly = false;
107
+ this.description = "";
93
108
  }
94
109
 
95
110
  willUpdate(changed) {
@@ -162,6 +177,14 @@ class BdButtonBase extends LitElement {
162
177
  * lg 24 against padding 8, 12 and 16 -- so the size passes straight through
163
178
  * and the circle comes out 32, 44 and 56.
164
179
  */
180
+ get _describedBy() {
181
+ return this.description ? "description" : nothing;
182
+ }
183
+
184
+ get _description() {
185
+ return this.description ? html`<span id="description" hidden>${this.description}</span>` : nothing;
186
+ }
187
+
165
188
  get _iconContent() {
166
189
  if (this.loading) return html`<span class="spinner" aria-hidden="true"></span>`;
167
190
  return html`<slot><bd-arrow-icon size="${this.size}" aria-hidden="true"></bd-arrow-icon></slot>`;
@@ -173,7 +196,7 @@ class BdButtonBase extends LitElement {
173
196
  e.stopPropagation();
174
197
  return;
175
198
  }
176
- this.dispatchEvent(new Event("custom-click"));
199
+ this.dispatchEvent(clickEvent());
177
200
  }
178
201
  }
179
202
 
@@ -186,10 +209,11 @@ class Button extends BdButtonBase {
186
209
  class="${this._classes}"
187
210
  aria-label="${this.label || nothing}"
188
211
  aria-busy="${this.loading ? "true" : nothing}"
212
+ aria-describedby="${this._describedBy}"
189
213
  ?disabled="${this.disabled}"
190
214
  style="${this._inlineStyle}"
191
215
  @click=${this._handleClick}
192
- >${this._content}</button>
216
+ >${this._content}</button>${this._description}
193
217
  `;
194
218
  }
195
219
  }
@@ -211,35 +235,11 @@ class ButtonLink extends BdButtonBase {
211
235
  super();
212
236
  this.href = "";
213
237
  this.buyLink = false;
214
- this.shadow = true;
215
238
  this.align = "left";
216
239
  this.target = "";
217
240
  this.rel = "";
218
241
  }
219
242
 
220
- _onFocusin(e) {
221
- const el = e.currentTarget;
222
- requestAnimationFrame(() =>
223
- requestAnimationFrame(() => {
224
- if (el.matches(':focus-visible')) {
225
- this.setAttribute('data-bd-button-focus', '');
226
- }
227
- })
228
- );
229
- }
230
-
231
- _onFocusout() {
232
- this.removeAttribute('data-bd-button-focus');
233
- }
234
-
235
- get fullWidthClass() {
236
- return this.fullWidth ? "max--width" : "";
237
- }
238
-
239
- get strongClass() {
240
- return this.strong ? "button--bold" : "";
241
- }
242
-
243
243
  /* Reverse tabnabbing is the reason rather than convention: a new tab opened
244
244
  from here keeps a window.opener handle back unless rel says otherwise. An
245
245
  explicit rel always wins, so a consumer can still opt out deliberately. */
@@ -265,11 +265,12 @@ class ButtonLink extends BdButtonBase {
265
265
  aria-label="${this.label || nothing}"
266
266
  aria-busy="${this.loading ? "true" : nothing}"
267
267
  aria-disabled="${this.disabled ? "true" : nothing}"
268
+ aria-describedby="${this._describedBy}"
268
269
  style="${this._inlineStyle}"
269
270
  @click=${this._handleClick}
270
271
  ?data-store-render="${this.buyLink}"
271
272
  ?data-store-buy-link="${this.buyLink}"
272
- >${this._content}</a>
273
+ >${this._content}</a>${this._description}
273
274
  `;
274
275
  }
275
276
  }
@@ -297,7 +298,7 @@ class ContactButton extends LitElement {
297
298
  }
298
299
 
299
300
  _handleClick() {
300
- this.dispatchEvent(new Event("custom-click"));
301
+ this.dispatchEvent(clickEvent());
301
302
  }
302
303
 
303
304
  render() {
@@ -100,7 +100,7 @@ variant, so every \`variant\`, \`size\`, \`surface\` and state applies unchanged
100
100
  | \`right\` | Aligns button to the right |
101
101
 
102
102
  ### Events
103
- - \`custom-click\` — dispatched on click or Enter/Space keypress
103
+ - \`custom-click\` — dispatched on click or Enter/Space keypress. It bubbles and is composed, so a wrapper can listen for it
104
104
  `
105
105
  }
106
106
  }
@@ -430,7 +430,7 @@ export const ButtonKeyboard = {
430
430
  <p style="font-size:14px; margin-bottom:8px;">Tab to focus, press Enter or Space to trigger.</p>
431
431
  <bd-button variant="primary" size="md" label="Keyboard test">Press me</bd-button>
432
432
  `,
433
- parameters: { docs: { description: { story: 'Demonstrates keyboard activation. Enter/Space trigger `custom-click` and toggle the `active` class briefly.' } } }
433
+ parameters: { docs: { description: { story: 'Demonstrates keyboard activation. Enter and Space trigger `custom-click`.' } } }
434
434
  };
435
435
 
436
436
  // ─── bd-button-link Stories ────────────────────────────────────────────────
@@ -13,7 +13,17 @@ export const captionCSS = css`
13
13
  color: var(--typography-caption-default-color);
14
14
  }
15
15
 
16
+ /* sm takes the caption-small role whole. Today it differs from
17
+ caption-default in tracking alone -- 0.025em against 0, the design's
18
+ caption/small -- so reading only letter-spacing drew the same thing, but
19
+ left the role's other four axes unread, and a change to any of them would
20
+ never have reached the element. The colour stays the default's: the role
21
+ carries none of its own. */
16
22
  :host([size='sm']) figcaption {
23
+ font-family: var(--typography-caption-small-fontFamily);
24
+ font-size: var(--typography-caption-small-fontSize);
25
+ font-weight: var(--typography-caption-small-fontWeight);
26
+ line-height: var(--typography-caption-small-lineHeight);
17
27
  letter-spacing: var(--typography-caption-small-letterSpacing);
18
28
  }
19
29
  `;
@@ -23,11 +23,11 @@ export default {
23
23
  ### Props
24
24
  | Prop | Values | Default |
25
25
  |---|---|---|
26
- | \`size\` | \`default\`, \`small\` | \`default\` |
26
+ | \`size\` | \`md\`, \`sm\` — \`sm\` is the same size tracked wider (\`caption-small\`); \`default\` / \`small\` still resolve | \`md\` |
27
27
 
28
28
  ### Contrast
29
29
  The caption is \`--color-neutral-600\`, which is 6.59:1 on white and stays above
30
- 4.5:1 at the \`small\` size too. Caption text is the most common place a design
30
+ 4.5:1 at the \`sm\` size too. Caption text is the most common place a design
31
31
  system slips below AA, because it is both small and grey — anything lighter
32
32
  than \`neutral-600\` would not clear it.
33
33
 
@@ -46,9 +46,9 @@ and reach for this only where the styling alone is wanted.
46
46
  argTypes: {
47
47
  size: {
48
48
  control : { type: 'select' },
49
- options : ['default', 'small'],
49
+ options : ['md', 'sm'],
50
50
  description: 'Type size of the caption.',
51
- table : { type: { summary: 'string' }, defaultValue: { summary: "default" }, category: 'Appearance' }
51
+ table : { type: { summary: 'string' }, defaultValue: { summary: 'md' }, category: 'Appearance' }
52
52
  }
53
53
  }
54
54
  };
@@ -12,7 +12,10 @@ export default css`
12
12
  display: block;
13
13
  align-items: center;
14
14
  position: relative;
15
- margin-bottom: 100px;
15
+ /* 100px used to stand here, room for two arrows positioned absolutely
16
+ 65px below the track. The dots are in the flow now, so the space below
17
+ the track is their margin, their 24px and this: 96. */
18
+ margin-bottom: var(--spacing-48);
16
19
  max-width: 100%;
17
20
  flex-direction: column;
18
21
  }
@@ -27,17 +30,16 @@ export default css`
27
30
  hand while leaving its line-height unset, which is how the title came to
28
31
  render at the browser's default leading and with the browser's default h2
29
32
  margins. LightCarousel and CardFeatures declare no such rule either. */
30
- div.bd-arrow {
31
- padding: var(--spacing-2);
32
- visibility: visible;
33
- }
34
-
35
33
  .bd-box:focus-visible,
36
- .bd-arrow:focus-visible,
37
34
  .bd-plus-button:focus-visible,
38
35
  .bd-modal-content button:focus-visible {
39
36
  /* the one focus ring — see Foundations/Focus */
40
- outline: none;
37
+ /* forced-colors mode does not paint box-shadow, so the ring
38
+ vanishes there; it does paint an outline, in a system colour. This one
39
+ is transparent everywhere else, and the offset lands it on the ring's
40
+ outer band. */
41
+ outline: var(--border-width-2) solid transparent;
42
+ outline-offset: var(--focus-outline-offset);
41
43
  box-shadow: var(--focus-outline-primary);
42
44
  }
43
45
 
@@ -59,90 +61,16 @@ export default css`
59
61
  gap: 1rem;
60
62
  padding: 1rem;
61
63
  }
62
- .bd-arrow {
63
- border-radius: 50%;
64
- width: var(--spacing-44);
65
- height: var(--spacing-44);
64
+ /* The dots: bd-pagination-dots, the package's one set of slide controls.
65
+ Two <img role="button"> arrows stood here, painted by eight data-URI SVGs
66
+ with the blues and the disabled grey baked in as hex -- invisible to every
67
+ colour check -- and positioned absolutely 65px under the track. Where they
68
+ sat, the dots sit now: under the track, at its end. */
69
+ .bd-carousel-controls {
66
70
  display: flex;
67
- align-items: center;
68
- justify-content: center;
69
- cursor: pointer;
70
- box-shadow: var(--spacing-0) var(--spacing-2) var(--spacing-4) rgba(0, 0, 0, 0.1);
71
- position: absolute;
72
- bottom: -65px;
73
- background: var(--color-surface-brand);
74
- color: var(--color-text-inverse);
75
- /* no role: .bd-arrow is an <img> -- left-arrow.svg / right-arrow.svg in
76
- carousel.js. It renders no text, so this styles nothing but the alt
77
- text a browser shows when the SVG fails to load. Fallback typography
78
- is not a role decision. */
79
- font-size: var(--typography-fontSize-xl);
80
- /* no role: .bd-arrow is an <img> -- left-arrow.svg / right-arrow.svg in
81
- carousel.js. It renders no text, so this styles nothing but the alt
82
- text a browser shows when the SVG fails to load. Fallback typography
83
- is not a role decision. */
84
- line-height: var(--typography-lineHeight-none);
85
- text-align: center;
86
- }
87
-
88
- .bd-left-arrow {
89
- content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><circle cx="20" cy="20" r="20" transform="matrix(-1 0 0 1 40 0)" fill="%23006DFF"/><path d="M23.3431 14.4142L21.9289 13L14.8579 20.071L16.2721 21.4853L23.3431 14.4142Z" fill="white"/><path d="M16.2721 18.6568L14.8579 20.071L21.9289 27.1421L23.3431 25.7279L16.2721 18.6568Z" fill="white"/></svg>');
90
- }
91
-
92
-
93
- .bd-left-arrow:hover {
94
- content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><circle cx="20" cy="20" r="20" transform="matrix(-1 0 0 1 40 0)" fill="%230059D6"/><path d="M23.3431 14.4142L21.9289 13L14.8579 20.071L16.2721 21.4853L23.3431 14.4142Z" fill="white"/><path d="M16.2721 18.6568L14.8579 20.071L21.9289 27.1421L23.3431 25.7279L16.2721 18.6568Z" fill="white"/></svg>');
95
- }
96
-
97
- .bd-left-arrow:active {
98
- content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><circle cx="20" cy="20" r="20" transform="matrix(-1 0 0 1 40 0)" fill="%230045AD"/><path d="M23.3431 14.4142L21.9289 13L14.8579 20.071L16.2721 21.4853L23.3431 14.4142Z" fill="white"/><path d="M16.2721 18.6568L14.8579 20.071L21.9289 27.1421L23.3431 25.7279L16.2721 18.6568Z" fill="white"/></svg>');
99
- }
100
-
101
-
102
- .bd-left-arrow:disabled {
103
- content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><circle cx="20" cy="20" r="20" fill="%23F1F2F3"/><path d="M23.3431 14.4142L21.9289 13L14.8579 20.071L16.2721 21.4853L23.3431 14.4142Z" fill="%23A6ADB4"/><path d="M16.2721 18.6568L14.8579 20.071L21.9289 27.1421L23.3431 25.7279L16.2721 18.6568Z" fill="%23A6ADB4"/></svg>');
104
- }
105
-
106
- .bd-right-arrow {
107
- content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><circle cx="20" cy="20" r="20" fill="%23006DFF"/><path d="M16.6569 14.4142L18.0711 13L25.1421 20.071L23.7279 21.4853L16.6569 14.4142Z" fill="white"/><path d="M23.7279 18.6568L25.1421 20.071L18.0711 27.1421L16.6569 25.7279L23.7279 18.6568Z" fill="white"/></svg>');
108
- }
109
-
110
-
111
- .bd-right-arrow:hover {
112
- content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><circle cx="20" cy="20" r="20" fill="%230059D6"/><path d="M16.6569 14.4142L18.0711 13L25.1421 20.071L23.7279 21.4853L16.6569 14.4142Z" fill="white"/><path d="M23.7279 18.6568L25.1421 20.071L18.0711 27.1421L16.6569 25.7279L23.7279 18.6568Z" fill="white"/></svg>');
113
- }
114
-
115
-
116
- .bd-right-arrow:active {
117
- content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><circle cx="20" cy="20" r="20" fill="%230045AD"/><path d="M16.6569 14.4142L18.0711 13L25.1421 20.071L23.7279 21.4853L16.6569 14.4142Z" fill="white"/><path d="M23.7279 18.6568L25.1421 20.071L18.0711 27.1421L16.6569 25.7279L23.7279 18.6568Z" fill="white"/></svg>');
118
- }
119
-
120
-
121
- .bd-right-arrow:disabled {
122
- content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><circle cx="20" cy="20" r="20" fill="%23F1F2F3"/><path d="M16.6569 14.4142L18.0711 13L25.1421 20.071L23.7279 21.4853L16.6569 14.4142Z" fill="%23A6ADB4"/><path d="M23.7279 18.6568L25.1421 20.071L18.0711 27.1421L16.6569 25.7279L23.7279 18.6568Z" fill="%23A6ADB4"/></svg>');
123
- }
124
- .bd-right-arrow.bd-disabled {
125
- content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><circle cx="20" cy="20" r="20" fill="%23F1F2F3"/><path d="M16.6569 14.4142L18.0711 13L25.1421 20.071L23.7279 21.4853L16.6569 14.4142Z" fill="%23A6ADB4"/><path d="M23.7279 18.6568L25.1421 20.071L18.0711 27.1421L16.6569 25.7279L23.7279 18.6568Z" fill="%23A6ADB4"/></svg>');
126
- cursor: not-allowed;
127
- }
128
-
129
- .bd-left-arrow.bd-disabled {
130
- content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><circle cx="20" cy="20" r="20" fill="%23F1F2F3"/><path d="M23.3431 14.4142L21.9289 13L14.8579 20.071L16.2721 21.4853L23.3431 14.4142Z" fill="%23A6ADB4"/><path d="M16.2721 18.6568L14.8579 20.071L21.9289 27.1421L23.3431 25.7279L16.2721 18.6568Z" fill="%23A6ADB4"/></svg>');
131
- cursor: not-allowed;
132
- }
133
-
134
- .bd-disabled {
135
- background: var(--color-neutral-50);
136
- color: var(--color-text-muted-inverse);
137
- cursor: not-allowed;
138
- }
139
-
140
- .bd-arrow:first-of-type {
141
- right: 60px;
142
- }
143
-
144
- .bd-arrow:last-of-type {
145
- right: var(--spacing-10);
71
+ justify-content: flex-end;
72
+ margin-top: var(--spacing-24);
73
+ padding-inline: var(--spacing-10);
146
74
  }
147
75
 
148
76
  .bd-carousel-track {
@@ -259,14 +187,6 @@ export default css`
259
187
  content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40" fill="none"><circle cx="20" cy="20" r="19" fill="%23006DFF" stroke="%23006DFF" stroke-width="2"/><path d="M19 11H21V29H19V11Z" fill="white"/><path d="M29 19V21L11 21L11 19L29 19Z" fill="white"/></svg>');
260
188
  }
261
189
 
262
- .bd-left-arrow {
263
- margin-right: var(--spacing-6);
264
- }
265
-
266
- .bd-right-arrow {
267
- margin-left: var(--spacing-6);
268
- }
269
-
270
190
  @media ${unsafeCSS(below('md'))} {
271
191
  .bd-carousel-track {
272
192
  flex-wrap: nowrap;
@@ -284,23 +204,6 @@ export default css`
284
204
  max-width: 90%;
285
205
  padding: 1.5em;
286
206
  }
287
-
288
- .bd-arrow {
289
- bottom: -75px;
290
- /* no role: .bd-arrow is an <img> -- left-arrow.svg / right-arrow.svg in
291
- carousel.js. It renders no text, so this styles nothing but the alt
292
- text a browser shows when the SVG fails to load. Fallback typography
293
- is not a role decision. */
294
- font-size: var(--typography-fontSize-base);
295
- }
296
-
297
- .bd-arrow:first-of-type {
298
- right: 50px;
299
- }
300
-
301
- .bd-arrow:last-of-type {
302
- right: var(--spacing-6);
303
- }
304
207
  }
305
208
 
306
209
  @media (max-width: 480px) {