@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
@@ -4,6 +4,7 @@ import "../../tokens/tokens.js";
4
4
  import carouselCSS from "../carousel/carousel.css.js";
5
5
  import "../heading/heading.js";
6
6
  import "../modal/modal.js";
7
+ import "../pagination-dots/pagination-dots.js";
7
8
  export class CarouselItem extends LitElement {
8
9
  static properties = {
9
10
  icon : { type: String },
@@ -61,7 +62,9 @@ class CustomCarousel extends LitElement {
61
62
  static properties = {
62
63
  title : { type: String },
63
64
  headingLevel: { type: Number, attribute: "heading-level" },
64
- selectedItem: { type: Object }
65
+ selectedItem: { type: Object },
66
+ _pages : { state: true },
67
+ _page : { state: true }
65
68
  };
66
69
 
67
70
  constructor() {
@@ -69,6 +72,10 @@ class CustomCarousel extends LitElement {
69
72
  this.selectedItem = null;
70
73
  this.title = "";
71
74
  this.headingLevel = 2;
75
+ // scroll stops, 1-based, read from the track; see _measure
76
+ this._pages = 1;
77
+ this._page = 1;
78
+ this._seeking = 0;
72
79
  }
73
80
 
74
81
  /**
@@ -98,36 +105,22 @@ class CustomCarousel extends LitElement {
98
105
  firstUpdated() {
99
106
  this.track = this.shadowRoot.querySelector(".bd-carousel-track");
100
107
  this.slotElement = this.shadowRoot.querySelector("slot");
101
- this.leftArrow = this.shadowRoot.querySelector(".bd-left-arrow");
102
- this.rightArrow = this.shadowRoot.querySelector(".bd-right-arrow");
103
108
 
104
- this.track.addEventListener("scroll", () => this.updateArrowStates());
109
+ this.track.addEventListener("scroll", () => this._measure(), { passive: true });
110
+ this.track.addEventListener("scrollend", () => { this._seeking = 0; this._measure(); });
111
+ // a visitor taking hold of the track ends any scroll a control started
112
+ for (const type of ["pointerdown", "touchstart", "wheel"]) {
113
+ this.track.addEventListener(type, () => { this._seeking = 0; }, { passive: true });
114
+ }
105
115
 
106
116
  this.slotElement.addEventListener("slotchange", () => {
107
- this.waitForImagesToLoad().then(() => {
108
- this.updateArrowStates();
109
- });
117
+ this.waitForImagesToLoad().then(() => this._measure());
110
118
  });
111
119
 
112
- setTimeout(() => {
113
- this.waitForImagesToLoad().then(() => {
114
- this.updateArrowStates();
115
- });
116
- }, 0);
117
-
118
- const arrows = this.shadowRoot.querySelectorAll(".bd-arrow");
119
- arrows.forEach((arrow) => {
120
- arrow.addEventListener("mousedown", () =>
121
- arrow.classList.add("bd-active")
122
- );
123
- arrow.addEventListener("mouseup", () =>
124
- arrow.classList.remove("bd-active")
125
- );
126
- arrow.addEventListener("mouseleave", () =>
127
- arrow.classList.remove("bd-active")
128
- );
129
- arrow.addEventListener("keydown", (e) => this._onArrowKeydown(e, arrow));
130
- });
120
+ this._resizeObserver = new ResizeObserver(() => this._measure());
121
+ this._resizeObserver.observe(this.track);
122
+
123
+ this.waitForImagesToLoad().then(() => this._measure());
131
124
 
132
125
  this.addEventListener("open-modal", (e) => {
133
126
  this.openModal(e.detail);
@@ -135,6 +128,11 @@ class CustomCarousel extends LitElement {
135
128
  this.enableDragAndTouchScroll(this.track);
136
129
  }
137
130
 
131
+ disconnectedCallback() {
132
+ super.disconnectedCallback();
133
+ this._resizeObserver?.disconnect();
134
+ }
135
+
138
136
  waitForImagesToLoad() {
139
137
  const assignedElements = this.slotElement.assignedElements({
140
138
  flatten: true
@@ -155,40 +153,47 @@ class CustomCarousel extends LitElement {
155
153
  return Promise.all(promises);
156
154
  }
157
155
 
158
- scrollCarousel(direction) {
159
- const assignedElements = this.slotElement.assignedElements({
160
- flatten: true
161
- });
162
- if (!assignedElements.length) return;
163
- const itemWidth = assignedElements[0].offsetWidth || 300;
164
- if (!this.track) return;
165
-
166
- this.track.scrollBy({
167
- left : direction === "left" ? -itemWidth : itemWidth,
168
- behavior: "smooth"
169
- });
170
-
171
- setTimeout(() => this.updateArrowStates(), 300);
156
+ /* The distance from one item to the next: an item's width and the gap. A
157
+ scroll stop is one of these, and the controls count stops, not items --
158
+ the last stop is wherever the track ends, which is short of a full step
159
+ when the items do not divide the width evenly. */
160
+ _step() {
161
+ const first = this.slotElement?.assignedElements({ flatten: true })[0];
162
+ if (!first) return 0;
163
+ const gap = parseFloat(getComputedStyle(this.track).columnGap) || 0;
164
+ return first.offsetWidth + gap;
172
165
  }
173
166
 
174
- updateArrowStates() {
175
- const trackWidth = this.track.offsetWidth;
176
- const totalWidth = this.track.scrollWidth;
177
- const scrollPosition = this.track.scrollLeft;
178
- if (!this.leftArrow || !this.rightArrow) return;
179
-
180
- this.leftArrow.classList.toggle("bd-disabled", scrollPosition <= 0);
181
- this.rightArrow.classList.toggle(
182
- "bd-disabled",
183
- scrollPosition + trackWidth >= totalWidth - 1
184
- );
167
+ /* How many stops the track has and which one it is at, from where it has
168
+ scrolled to -- by a control, a drag, a wheel or a keyboard. While a
169
+ control's smooth scroll is on its way, the dot it asked for stays current
170
+ rather than stepping through every stop it passes. */
171
+ _measure() {
172
+ if (!this.track) return;
173
+ const max = this.track.scrollWidth - this.track.clientWidth;
174
+ const step = this._step();
175
+ if (max <= 1 || step <= 0) {
176
+ this._pages = 1;
177
+ this._page = 1;
178
+ return;
179
+ }
180
+ this._pages = Math.ceil((max - 1) / step) + 1;
181
+ const left = this.track.scrollLeft;
182
+ const page = left >= max - 1 ? this._pages : Math.min(this._pages, Math.round(left / step) + 1);
183
+ if (this._seeking && page !== this._seeking) return;
184
+ this._seeking = 0;
185
+ this._page = page;
185
186
  }
186
187
 
187
- _onArrowKeydown(e, arrow) {
188
- if (e.key === "Enter" || e.key === " ") {
189
- e.preventDefault();
190
- arrow.click();
191
- }
188
+ _goToPage(n) {
189
+ const max = this.track.scrollWidth - this.track.clientWidth;
190
+ const reduce = window.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
191
+ this._seeking = n;
192
+ this._page = n;
193
+ this.track.scrollTo({
194
+ left : Math.min((n - 1) * this._step(), max),
195
+ behavior: reduce ? "auto" : "smooth"
196
+ });
192
197
  }
193
198
 
194
199
  openModal(item) {
@@ -256,27 +261,18 @@ class CustomCarousel extends LitElement {
256
261
  </div>`
257
262
  : ""}
258
263
 
259
- <img
260
- src="left-arrow.svg"
261
- alt="Left Arrow"
262
- class="bd-arrow bd-left-arrow"
263
- role="button"
264
- aria-label="Scroll left"
265
- @click="${() => this.scrollCarousel("left")}"
266
- tabindex="0"
267
- />
268
264
  <div class="bd-carousel-track">
269
265
  <slot></slot>
270
266
  </div>
271
- <img
272
- src="right-arrow.svg"
273
- alt="Right Arrow"
274
- class="bd-arrow bd-right-arrow"
275
- role="button"
276
- aria-label="Scroll right"
277
- @click="${() => this.scrollCarousel("right")}"
278
- tabindex="0"
279
- />
267
+ <div class="bd-carousel-controls">
268
+ <bd-pagination-dots
269
+ count=${this._pages}
270
+ current=${this._page}
271
+ label=${this.title || "Slides"}
272
+ item-label="Go to item"
273
+ @bd-change=${e => this._goToPage(e.detail.current)}
274
+ ></bd-pagination-dots>
275
+ </div>
280
276
  </div>
281
277
 
282
278
  ${this.selectedItem
@@ -10,7 +10,7 @@ export default {
10
10
  component: `
11
11
  **CustomCarousel** and **CarouselItem** are Lit components for a scrollable, draggable card carousel with modal support.
12
12
 
13
- - \`<bd-carousel-section>\` — scrollable container with left/right arrows and optional title
13
+ - \`<bd-carousel-section>\` — scrollable container with optional title, and the package's slide dots (\`bd-pagination-dots\`) under the track
14
14
  - \`<bd-carousel-item>\` — individual card with icon, slotted content, and a \`+\` button to open a modal
15
15
 
16
16
  ### Usage
@@ -23,14 +23,15 @@ export default {
23
23
  \`\`\`
24
24
 
25
25
  ### Behavior
26
- - Left/right arrows scroll by one item width with smooth scroll
27
- - Arrows disable automatically at scroll boundaries
28
- - Click and drag (mouse + touch) supported for scrolling
26
+ - One dot per scroll stop: a stop is one item's width plus the gap, and the last is wherever the track ends
27
+ - A dot scrolls there smoothly (instantly under \`prefers-reduced-motion\`)
28
+ - When every item fits, there is nowhere to go and no controls render
29
+ - Click and drag (mouse + touch) supported for scrolling; the dots follow any scroll
29
30
  - Clicking \`+\` on an item fires \`open-modal\` custom event, caught by the carousel to show \`<bd-modal>\`
30
31
  - Modal closes via \`close-modal\` event
31
32
 
32
33
  ### Keyboard Support
33
- - Arrow buttons respond to Enter/Space
34
+ - The dots are real buttons
34
35
  - \`+\` button on items responds to Enter/Space
35
36
  `
36
37
  }
@@ -84,7 +85,7 @@ export const Default = {
84
85
  ${sampleItems.map(renderItem)}
85
86
  </bd-carousel-section>
86
87
  `,
87
- parameters: { docs: { description: { story: '6-item carousel with title. Scroll with arrows or drag.' } } }
88
+ parameters: { docs: { description: { story: '6-item carousel with title. Move with the dots under the track, or drag.' } } }
88
89
  };
89
90
 
90
91
  export const NoTitle = {
@@ -104,7 +105,7 @@ export const FewItems = {
104
105
  ${sampleItems.slice(0, 2).map(renderItem)}
105
106
  </bd-carousel-section>
106
107
  `,
107
- parameters: { docs: { description: { story: 'Only 2 items — right arrow should be disabled if all items fit in view.' } } }
108
+ parameters: { docs: { description: { story: 'Only 2 items. When every item fits there is nowhere to scroll, so no dots render.' } } }
108
109
  };
109
110
 
110
111
  export const SingleItem = {
@@ -114,7 +115,7 @@ export const SingleItem = {
114
115
  ${renderItem(sampleItems[0])}
115
116
  </bd-carousel-section>
116
117
  `,
117
- parameters: { docs: { description: { story: 'Edge case with a single item. Both arrows should be disabled.' } } }
118
+ parameters: { docs: { description: { story: 'Edge case with a single item: nothing to scroll, so no dots render.' } } }
118
119
  };
119
120
 
120
121
  export const NoIcon = {
@@ -40,7 +40,12 @@ export default css`
40
40
  /* the one focus ring — see Foundations/Focus. A two-colour ring whose
41
41
  white inner band keeps it visible on a coloured or dark surface, which a
42
42
  single-colour outline cannot do. */
43
- outline: none;
43
+ /* forced-colors mode does not paint box-shadow, so the ring
44
+ vanishes there; it does paint an outline, in a system colour. This one
45
+ is transparent everywhere else, and the offset lands it on the ring's
46
+ outer band. */
47
+ outline: var(--border-width-2) solid transparent;
48
+ outline-offset: var(--focus-outline-offset);
44
49
  box-shadow: var(--focus-outline-primary);
45
50
  border-radius: var(--focus-outline-radius);
46
51
  }
@@ -62,7 +62,12 @@ export const codeCSS = css`
62
62
  /* the one focus ring — see Foundations/Focus. A two-colour ring whose
63
63
  white inner band keeps it visible on a coloured or dark surface, which a
64
64
  single-colour outline cannot do. */
65
- outline: none;
65
+ /* forced-colors mode does not paint box-shadow, so the ring
66
+ vanishes there; it does paint an outline, in a system colour. This one
67
+ is transparent everywhere else, and the offset lands it on the ring's
68
+ outer band. */
69
+ outline: var(--border-width-2) solid transparent;
70
+ outline-offset: var(--focus-outline-offset);
66
71
  box-shadow: var(--focus-outline-primary);
67
72
  }
68
73
  `;
@@ -1,4 +1,4 @@
1
- import { LitElement, html } from 'lit';
1
+ import { LitElement, html, nothing } from 'lit';
2
2
  // Installs the design tokens into document scope; see src/tokens/tokens.js
3
3
  import '../../tokens/tokens.js';
4
4
  import { codeCSS } from './code.css.js';
@@ -17,20 +17,61 @@ export class BdCode extends LitElement {
17
17
  this.label = '';
18
18
  }
19
19
 
20
+ /* Whether the block is wider than its box. Only then is there anything to
21
+ scroll, and only then does it need to be a tab stop. The box changes
22
+ size with the page, and the content with the light DOM, so both are
23
+ watched. */
24
+ _measure() {
25
+ const pre = this.shadowRoot?.querySelector('pre');
26
+ const scrollable = !!pre && pre.scrollWidth > pre.clientWidth;
27
+ if (scrollable !== this._scrollable) {
28
+ this._scrollable = scrollable;
29
+ this.requestUpdate();
30
+ }
31
+ }
32
+
33
+ updated() {
34
+ const pre = this.shadowRoot.querySelector('pre');
35
+ if (pre && pre !== this._observed) {
36
+ this._resize?.disconnect();
37
+ this._resize = new ResizeObserver(() => this._measure());
38
+ this._resize.observe(pre);
39
+ this._observed = pre;
40
+ }
41
+ if (!pre) { this._resize?.disconnect(); this._observed = null; }
42
+ this._measure();
43
+ }
44
+
45
+ connectedCallback() {
46
+ super.connectedCallback();
47
+ this._content = new MutationObserver(() => this._measure());
48
+ this._content.observe(this, { childList: true, characterData: true, subtree: true });
49
+ }
50
+
51
+ disconnectedCallback() {
52
+ this._content?.disconnect();
53
+ this._resize?.disconnect();
54
+ this._observed = null;
55
+ super.disconnectedCallback();
56
+ }
57
+
20
58
  render() {
21
59
  // Block mode nests <code> inside <pre>: <pre> carries the preformatted
22
60
  // whitespace, <code> carries "this is code". A bare <pre> is only
23
61
  // preformatted text, so assistive tech got no signal it was code at all,
24
62
  // even though the inline variant gave one.
25
63
  //
26
- // The <pre> scrolls horizontally, and a scrollable region that cannot be
27
- // focused cannot be scrolled from the keyboard (WCAG 2.1.1). tabindex
28
- // makes it reachable; role and label give it something to announce, since
29
- // a focusable element with no accessible name says nothing.
64
+ // A <pre> that scrolls horizontally has to be focusable, or it cannot be
65
+ // scrolled from the keyboard (WCAG 2.1.1), and a focusable element needs
66
+ // a name. It is a group, not a region: a named region is a landmark, so
67
+ // every code block on a page joined the landmark list a screen reader
68
+ // navigates by, beside header, main and footer. And it is a tab stop only
69
+ // while it overflows; a snippet that fits has nothing to scroll and was a
70
+ // stop that did nothing.
30
71
  return this.block
31
72
  ? html`<pre
32
- tabindex="0"
33
- role="region"
73
+ tabindex="${this._scrollable ? '0' : nothing}"
74
+ role="group"
34
75
  aria-label="${this.label || 'Code'}"
35
76
  ><code><slot></slot></code></pre>`
36
77
  : html`<code><slot></slot></code>`;
@@ -33,10 +33,13 @@ Inline renders a single \`<code>\` with a tinted background. Block nests
33
33
  \`<code>\` carries "this is code", which is the spec's pairing for a code block.
34
34
 
35
35
  ### The block is keyboard-scrollable
36
- A long line scrolls horizontally, and a scrollable region that cannot be
37
- focused cannot be scrolled from the keyboard. The block therefore takes
38
- \`tabindex="0"\` with \`role="region"\` and an accessible name from \`label\`,
39
- defaulting to "Code", and shows a focus ring when reached by Tab.
36
+ A long line scrolls horizontally, and a scrollable box that cannot be focused
37
+ cannot be scrolled from the keyboard. So while the block is wider than its box
38
+ it takes \`tabindex="0"\` and shows a focus ring when reached by Tab; a block
39
+ that fits has nothing to scroll and is not a tab stop. It is a
40
+ \`role="group"\` named by \`label\`, defaulting to "Code" — a group rather
41
+ than a region, because a named region is a landmark, and a page of code
42
+ samples would otherwise fill the landmark list a screen reader navigates by.
40
43
 
41
44
  ### Notes
42
45
  The inline variant sets its colour explicitly rather than inheriting, so it
@@ -2,7 +2,7 @@ import { LitElement, html } from "lit";
2
2
  // Installs the design tokens into document scope; see src/tokens/tokens.js
3
3
  import "../../tokens/tokens.js";
4
4
  import { dividerHorizontal } from "./divider.css.js";
5
- import { borderColor } from "../shared/token-scales.js";
5
+ import { borderColor, opacity } from "../shared/token-scales.js";
6
6
 
7
7
  export class BdDividerHorizontal extends LitElement {
8
8
  static styles = [dividerHorizontal];
@@ -27,7 +27,14 @@ export class BdDividerHorizontal extends LitElement {
27
27
  ask for `brand-subtle`. The ink vocabulary is still accepted as a
28
28
  fallback for the names already in the markup. */
29
29
  this.color = "";
30
- this.opacity = "0.25";
30
+ /* No opacity by default. It was 0.25, laid over --color-border-default,
31
+ and that composite -- rgb(225,227,230), 1.286:1 on the page -- is
32
+ --color-border-container to within one unit (225,227,229, 1.287:1). So
33
+ every bare divider was spelling a role that exists by dimming a
34
+ different one. The stylesheet now draws that role at full strength,
35
+ which renders the same rule, and opacity is only what a caller asks
36
+ for. */
37
+ this.opacity = "";
31
38
  }
32
39
 
33
40
  render() {
@@ -35,13 +42,14 @@ export class BdDividerHorizontal extends LitElement {
35
42
  // stylesheet's --color-border-default stands rather than being overridden
36
43
  // with an empty value.
37
44
  const ink = borderColor(this.color);
45
+ const fade = opacity(this.opacity);
38
46
  return html`
39
47
  <hr
40
48
  class="hr-line"
41
49
  style="
42
50
  width: ${this.width};
43
51
  ${ink ? `border-top-color: ${ink};` : ''}
44
- opacity: ${this.opacity};
52
+ ${fade ? `opacity: ${fade};` : ''}
45
53
  "
46
54
  />
47
55
  `;
@@ -2,7 +2,7 @@ import { LitElement, html } from "lit";
2
2
  // Installs the design tokens into document scope; see src/tokens/tokens.js
3
3
  import "../../tokens/tokens.js";
4
4
  import { dividerVertical } from "./divider.css.js";
5
- import { borderColor, spacing } from "../shared/token-scales.js";
5
+ import { borderColor, spacing, opacity, borderWidth } from "../shared/token-scales.js";
6
6
 
7
7
  export class BdDividerVertical extends LitElement {
8
8
  static styles = [dividerVertical];
@@ -18,13 +18,19 @@ export class BdDividerVertical extends LitElement {
18
18
  constructor() {
19
19
  super();
20
20
  this.height = "16"; // a spacing step; was a 15px literal, off every scale
21
- this.thickness = "1px";
21
+ /* A border-width step, resolved to var(--border-width-N); "1px" and the
22
+ other px spellings are read as their step. It was the literal "1px",
23
+ written into the style as given -- out of lint:tokens' reach, so any
24
+ length, or any string at all, could stand in for a hairline. An
25
+ unknown value falls back to the 1px step. */
26
+ this.thickness = "1";
22
27
  /* No colour by default — see the note in divider-horizontal.js. The
23
28
  stylesheet's --color-border-default is the role for a hairline; "subtle"
24
29
  was a text role that happened to resolve to the same step. The prop
25
30
  resolves through BORDER_ROLES first; see token-scales.js. */
26
31
  this.color = "";
27
- this.opacity = "1.00";
32
+ // none by default, so none is written; a value is kept only from 0 to 1
33
+ this.opacity = "";
28
34
 
29
35
  }
30
36
 
@@ -32,14 +38,15 @@ export class BdDividerVertical extends LitElement {
32
38
  // See divider-horizontal.js: no colour means no declaration, so the
33
39
  // stylesheet's --color-border-default stands.
34
40
  const ink = borderColor(this.color);
41
+ const fade = opacity(this.opacity);
35
42
  return html`
36
43
  <div
37
44
  class="divider"
38
45
  style="
39
- width: ${this.thickness};
46
+ width: ${borderWidth(this.thickness) || borderWidth('1')};
40
47
  height: ${spacing(this.height)};
41
48
  ${ink ? `background-color: ${ink};` : ''}
42
- opacity: ${this.opacity};
49
+ ${fade ? `opacity: ${fade};` : ''}
43
50
  "
44
51
  ></div>
45
52
  `;
@@ -3,7 +3,10 @@ import { css } from "lit";
3
3
  export const dividerHorizontal = css`
4
4
  .hr-line {
5
5
  border: none;
6
- border-top: var(--border-width-1) solid var(--color-border-default);
6
+ /* --color-border-container, the rule every bare divider already rendered:
7
+ it used to be --color-border-default dimmed to 0.25, which composites to
8
+ the same colour. See divider-horizontal.js. */
9
+ border-top: var(--border-width-1) solid var(--color-border-container);
7
10
  margin: 0;
8
11
  width: 100%;
9
12
  margin-left: auto;
@@ -33,12 +33,12 @@ Two divider components are available:
33
33
  control : { type: 'select' },
34
34
  options : ['primary', 'strong', 'muted', 'subtle', 'inverse', 'success', 'warning', 'error'],
35
35
  description: '(Horizontal) A semantic colour role, not a CSS colour.',
36
- table : { type: { summary: 'string' }, defaultValue: { summary: 'subtle' }, category: 'Horizontal' }
36
+ table : { type: { summary: 'string' }, defaultValue: { summary: "'' (--color-border-container)" }, category: 'Horizontal' }
37
37
  },
38
38
  hOpacity: {
39
39
  control : 'text',
40
- description: '(Horizontal) Opacity value between 0 and 1',
41
- table : { type: { summary: 'string' }, defaultValue: { summary: '0.25' }, category: 'Horizontal' }
40
+ description: '(Horizontal) Opacity from 0 to 1. Anything else is ignored. None by default.',
41
+ table : { type: { summary: 'string' }, defaultValue: { summary: "''" }, category: 'Horizontal' }
42
42
  },
43
43
  height: {
44
44
  control: { type: 'select' },
@@ -49,8 +49,8 @@ Two divider components are available:
49
49
  },
50
50
  thickness: {
51
51
  control : 'text',
52
- description: '(Vertical) Width/thickness of the line e.g. "1px"',
53
- table : { type: { summary: 'string' }, defaultValue: { summary: '1px' }, category: 'Vertical' }
52
+ description: '(Vertical) A border-width step — 0, 1, 2, 3, 4 or 8, also written "1px" etc. Resolves to var(--border-width-N); anything else is the 1px step.',
53
+ table : { type: { summary: 'string' }, defaultValue: { summary: '1' }, category: 'Vertical' }
54
54
  },
55
55
  vColor: {
56
56
  control : { type: 'select' },
@@ -71,7 +71,7 @@ Two divider components are available:
71
71
  export const HorizontalDefault = {
72
72
  name : 'Horizontal — Default',
73
73
  render : () => html`<bd-divider-horizontal></bd-divider-horizontal>`,
74
- parameters: { docs: { description: { story: 'Default horizontal divider with default color and 0.25 opacity.' } } }
74
+ parameters: { docs: { description: { story: 'Default horizontal divider: --color-border-container at full strength, no opacity.' } } }
75
75
  };
76
76
 
77
77
  export const HorizontalFullWidth = {