@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.
- package/CHANGELOG.md +81 -0
- package/README.md +43 -12
- package/package.json +4 -2
- package/schemas/component-manifest.json +207 -49
- package/schemas/manifest-omissions.json +2 -0
- package/src/components/accordion/accordion.css.js +12 -1
- package/src/components/agent-endpoints/agent-endpoints.js +1 -1
- package/src/components/alert/alert.css.js +22 -1
- package/src/components/anchor/anchor-nav.css.js +6 -1
- package/src/components/annotation/annotation.css.js +6 -1
- package/src/components/architecture.stories.js +6 -4
- package/src/components/avatar/avatar.js +37 -11
- package/src/components/avatar/avatar.stories.js +10 -6
- package/src/components/awards/awards-flex.css.js +31 -129
- package/src/components/awards/awards-flex.js +66 -116
- package/src/components/awards/awards-flex.stories.js +8 -6
- package/src/components/back/back.css.js +6 -1
- package/src/components/back/back.js +24 -12
- package/src/components/back/back.stories.js +3 -3
- package/src/components/badge/badge.js +6 -3
- package/src/components/badge/badge.stories.js +13 -9
- package/src/components/bento-grid/bento-grid.js +1 -1
- package/src/components/blockquote/blockquote.css.js +12 -3
- package/src/components/blockquote/blockquote.js +12 -5
- package/src/components/blockquote/blockquote.stories.js +6 -4
- package/src/components/button/button.css.js +46 -25
- package/src/components/button/button.js +44 -43
- package/src/components/button/button.stories.js +2 -2
- package/src/components/caption/caption.css.js +10 -0
- package/src/components/caption/caption.stories.js +4 -4
- package/src/components/carousel/carousel.css.js +19 -116
- package/src/components/carousel/carousel.js +70 -74
- package/src/components/carousel/carousel.stories.js +9 -8
- package/src/components/checkbox/checkbox.css.js +6 -1
- package/src/components/code/code.css.js +6 -1
- package/src/components/code/code.js +48 -7
- package/src/components/code/code.stories.js +7 -4
- package/src/components/divider/divider-horizontal.js +11 -3
- package/src/components/divider/divider-vertical.js +12 -5
- package/src/components/divider/divider.css.js +4 -1
- package/src/components/divider/divider.stories.js +6 -6
- package/src/components/dropdown/dropdown.js +67 -13
- package/src/components/dropdown/dropdown.stories.js +15 -14
- package/src/components/fieldset/fieldset.stories.js +41 -16
- package/src/components/footer/footer-nav-menu.css.js +6 -1
- package/src/components/footer/footer.css.js +6 -1
- package/src/components/header/header-code.js +1 -1
- package/src/components/header/header.css.js +9 -4
- package/src/components/heading/heading.stories.js +64 -31
- package/src/components/highlight/highlight.js +74 -34
- package/src/components/highlight/highlight.stories.js +5 -4
- package/src/components/icons/icon-element.js +49 -6
- package/src/components/icons/icons.stories.js +7 -0
- package/src/components/image/image.css.js +9 -98
- package/src/components/image/image.js +7 -2
- package/src/components/input/custom-form.stories.js +14 -19
- package/src/components/input/input-clipboard.css.js +6 -1
- package/src/components/input/input-clipboard.js +56 -8
- package/src/components/input/input.js +19 -2
- package/src/components/label/label.js +87 -1
- package/src/components/label/label.stories.js +21 -12
- package/src/components/link/link.css.js +52 -3
- package/src/components/link/link.js +52 -17
- package/src/components/link/link.stories.js +114 -3
- package/src/components/list/list-item/list-item.css.js +4 -6
- package/src/components/list/list-item/list-item.js +53 -5
- package/src/components/list/list-item/list-item.stories.js +22 -0
- package/src/components/list/list.js +27 -0
- package/src/components/modal/modal.css.js +6 -1
- package/src/components/pagination-dots/pagination-dots.css.js +161 -0
- package/src/components/pagination-dots/pagination-dots.js +202 -0
- package/src/components/pagination-dots/pagination-dots.stories.js +171 -0
- package/src/components/paragraph/paragraph.css.js +37 -1
- package/src/components/paragraph/paragraph.js +19 -2
- package/src/components/paragraph/paragraph.stories.js +40 -6
- package/src/components/picture/picture.css.js +8 -67
- package/src/components/picture/picture.js +15 -8
- package/src/components/pricing-cards/pricing-card.css.js +12 -2
- package/src/components/pricing-cards/pricing-card.js +4 -4
- package/src/components/progress/progress.js +24 -4
- package/src/components/progress/progress.stories.js +8 -6
- package/src/components/radio/radio.css.js +6 -1
- package/src/components/radio/radio.js +48 -3
- package/src/components/radio/radio.stories.js +1 -1
- package/src/components/shared/form-field.css.js +30 -4
- package/src/components/shared/token-scales.js +16 -0
- package/src/components/spinner/spinner.css.js +31 -4
- package/src/components/spinner/spinner.js +26 -4
- package/src/components/spinner/spinner.stories.js +10 -7
- package/src/components/switch/switch.css.js +21 -3
- package/src/components/switch/switch.js +8 -1
- package/src/components/switch/switch.stories.js +2 -1
- package/src/components/tab-list/tab-list.css.js +6 -1
- package/src/components/terminal/terminal-cursor.js +11 -0
- package/src/components/terminal/terminal-message.js +1 -1
- package/src/components/termsOfUse/terms.css.js +6 -1
- package/src/components/textarea/textarea.stories.js +23 -0
- package/src/components/tooltip/tooltip.css.js +24 -1
- package/src/components/tooltip/tooltip.js +135 -5
- package/src/components/tooltip/tooltip.stories.js +20 -14
- package/src/components/trust-panel/trust-panel.js +2 -2
- package/src/components/video/video.js +38 -3
- package/src/components/video/video.stories.js +10 -5
- package/src/components/video-player/video-player.css.js +6 -1
- package/src/tokens/elevation.stories.js +2 -3
- package/src/tokens/focus.stories.js +81 -15
- package/src/tokens/motion.stories.js +0 -6
- package/src/tokens/tokens.css +174 -97
- package/src/tokens/tokens.js +174 -97
- package/src/tokens/vertical-rhythm.stories.js +1 -1
- package/src/stories/Button.js +0 -20
- package/src/stories/Button.stories.js +0 -46
- package/src/stories/Configure.mdx +0 -364
- package/src/stories/Header.js +0 -45
- package/src/stories/Header.stories.js +0 -24
- package/src/stories/Page.js +0 -62
- package/src/stories/Page.stories.js +0 -20
- package/src/stories/assets/accessibility.png +0 -0
- package/src/stories/assets/accessibility.svg +0 -1
- package/src/stories/assets/addon-library.png +0 -0
- package/src/stories/assets/assets.png +0 -0
- package/src/stories/assets/avif-test-image.avif +0 -0
- package/src/stories/assets/context.png +0 -0
- package/src/stories/assets/discord.svg +0 -1
- package/src/stories/assets/docs.png +0 -0
- package/src/stories/assets/figma-plugin.png +0 -0
- package/src/stories/assets/github.svg +0 -1
- package/src/stories/assets/share.png +0 -0
- package/src/stories/assets/styling.png +0 -0
- package/src/stories/assets/testing.png +0 -0
- package/src/stories/assets/theming.png +0 -0
- package/src/stories/assets/tutorials.svg +0 -1
- package/src/stories/assets/youtube.svg +0 -1
- package/src/stories/button.css +0 -30
- package/src/stories/header.css +0 -32
- package/src/stories/page.css +0 -68
|
@@ -10,11 +10,19 @@ import "../icons/pc-mag-readers-icon.js";
|
|
|
10
10
|
import "../icons/top-product-icon.js";
|
|
11
11
|
import "../heading/heading.js";
|
|
12
12
|
import "../paragraph/paragraph.js";
|
|
13
|
+
import "../pagination-dots/pagination-dots.js";
|
|
13
14
|
import { awardsFlexCSS } from "./awards-flex.css.js";
|
|
14
15
|
import { ensembleCSS } from "../shared/ensemble.css.js";
|
|
16
|
+
import { below } from "../shared/token-scales.js";
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
/* How many quote cards one carousel slide holds: three side by side, and one
|
|
19
|
+
below sm. Three used to stack in a column on a phone, so a slide was about
|
|
20
|
+
1065px tall with every card on screen at once, and the dots paged between
|
|
21
|
+
two such stacks. One card a slide gives the phone a carousel again -- six
|
|
22
|
+
awards are six slides and six dots -- at about half the height. Drawn in the
|
|
23
|
+
foundation Figma file as Awards carousel, breakpoint=mobile. */
|
|
24
|
+
const GROUP_SIZE = 3;
|
|
25
|
+
const GROUP_SIZE_NARROW = 1;
|
|
18
26
|
|
|
19
27
|
export class AwardsFlex extends LitElement {
|
|
20
28
|
static properties = {
|
|
@@ -27,9 +35,10 @@ export class AwardsFlex extends LitElement {
|
|
|
27
35
|
subtext : { type: String },
|
|
28
36
|
badges : { type: Array },
|
|
29
37
|
_slide : { state: true },
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
38
|
+
_groupSize : { state: true },
|
|
39
|
+
_hovered : { state: true },
|
|
40
|
+
_focusWithin : { state: true },
|
|
41
|
+
_live : { state: true }
|
|
33
42
|
};
|
|
34
43
|
|
|
35
44
|
static styles = [tokens, ensembleCSS, awardsFlexCSS];
|
|
@@ -45,33 +54,43 @@ export class AwardsFlex extends LitElement {
|
|
|
45
54
|
this.subtext = "";
|
|
46
55
|
this.badges = [];
|
|
47
56
|
this._slide = 0;
|
|
48
|
-
this.
|
|
49
|
-
/*
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
this.
|
|
53
|
-
this.
|
|
54
|
-
|
|
57
|
+
this._groupSize = GROUP_SIZE;
|
|
58
|
+
/* The pointer or focus on the slides holds the autoplay -- a hold, which
|
|
59
|
+
lifts by itself. Stopping it for good is the atom's: a visitor who picks
|
|
60
|
+
a dot has chosen a slide, and it stays. */
|
|
61
|
+
this._hovered = false;
|
|
62
|
+
this._focusWithin = false;
|
|
63
|
+
/* The slides' live region. Off while they change on their own, so a
|
|
64
|
+
screen reader is not read a new set of quotes every few seconds; polite
|
|
65
|
+
once they are still, so a change the visitor asks for is announced.
|
|
66
|
+
bd-pagination-dots says which, through bd-toggle. */
|
|
67
|
+
this._live = "polite";
|
|
55
68
|
}
|
|
56
69
|
|
|
57
70
|
connectedCallback() {
|
|
58
71
|
super.connectedCallback();
|
|
59
|
-
/*
|
|
60
|
-
|
|
61
|
-
read once, so a reader who changes the setting is honoured live. */
|
|
72
|
+
/* Watched rather than read once: a slide is a layout, and a phone turned
|
|
73
|
+
sideways crosses the breakpoint. */
|
|
62
74
|
if (typeof window !== "undefined" && window.matchMedia) {
|
|
63
|
-
this.
|
|
64
|
-
this.
|
|
65
|
-
this.
|
|
66
|
-
this.
|
|
75
|
+
this._narrowQuery = window.matchMedia(below("sm"));
|
|
76
|
+
this._onNarrowChange = () => this._setGroupSize(this._narrowQuery.matches ? GROUP_SIZE_NARROW : GROUP_SIZE);
|
|
77
|
+
this._narrowQuery.addEventListener("change", this._onNarrowChange);
|
|
78
|
+
this._onNarrowChange();
|
|
67
79
|
}
|
|
68
|
-
this._maybeStartAutoplay();
|
|
69
80
|
}
|
|
70
81
|
|
|
71
82
|
disconnectedCallback() {
|
|
72
83
|
super.disconnectedCallback();
|
|
73
|
-
this.
|
|
74
|
-
|
|
84
|
+
this._narrowQuery?.removeEventListener("change", this._onNarrowChange);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* Changing the slide size keeps the visitor on the slide that holds the card
|
|
88
|
+
they were looking at: its first card, re-paged. */
|
|
89
|
+
_setGroupSize(size) {
|
|
90
|
+
if (size === this._groupSize) return;
|
|
91
|
+
const firstCard = this._slide * this._groupSize;
|
|
92
|
+
this._groupSize = size;
|
|
93
|
+
this._slide = Math.floor(firstCard / size);
|
|
75
94
|
}
|
|
76
95
|
|
|
77
96
|
updated(changed) {
|
|
@@ -80,23 +99,20 @@ export class AwardsFlex extends LitElement {
|
|
|
80
99
|
changed.has("carousel") ||
|
|
81
100
|
changed.has("layout") ||
|
|
82
101
|
changed.has("badges") ||
|
|
83
|
-
changed.has("
|
|
84
|
-
changed.has("_userStopped") ||
|
|
85
|
-
changed.has("_reduceMotion")
|
|
102
|
+
changed.has("_groupSize")
|
|
86
103
|
) {
|
|
87
104
|
// if the badge set shrank, make sure the current slide index is still valid
|
|
88
105
|
if (this._slide >= this._slidesCount) {
|
|
89
106
|
this._slide = 0;
|
|
90
107
|
}
|
|
91
|
-
this._maybeStartAutoplay();
|
|
92
108
|
}
|
|
93
109
|
}
|
|
94
110
|
|
|
95
111
|
get _groups() {
|
|
96
|
-
// chunk badges into
|
|
112
|
+
// chunk badges into slides of _groupSize for the carousel
|
|
97
113
|
const groups = [];
|
|
98
|
-
for (let i = 0; i < this.badges.length; i +=
|
|
99
|
-
groups.push(this.badges.slice(i, i +
|
|
114
|
+
for (let i = 0; i < this.badges.length; i += this._groupSize) {
|
|
115
|
+
groups.push(this.badges.slice(i, i + this._groupSize));
|
|
100
116
|
}
|
|
101
117
|
return groups;
|
|
102
118
|
}
|
|
@@ -105,52 +121,11 @@ export class AwardsFlex extends LitElement {
|
|
|
105
121
|
return this.layout === "quotes" ? this._groups.length : 0;
|
|
106
122
|
}
|
|
107
123
|
|
|
108
|
-
|
|
109
|
-
this._stopAutoplay();
|
|
110
|
-
const canAutoplay =
|
|
111
|
-
this.autoplay &&
|
|
112
|
-
this.carousel &&
|
|
113
|
-
this.layout === "quotes" &&
|
|
114
|
-
this._slidesCount > 1 &&
|
|
115
|
-
!this._paused &&
|
|
116
|
-
!this._userStopped &&
|
|
117
|
-
!this._reduceMotion;
|
|
118
|
-
|
|
119
|
-
if (!canAutoplay) return;
|
|
120
|
-
|
|
121
|
-
this._timer = setInterval(() => {
|
|
122
|
-
this._slide = (this._slide + 1) % this._slidesCount;
|
|
123
|
-
}, Math.max(1000, this.autoplayDelay));
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
_stopAutoplay() {
|
|
127
|
-
if (this._timer) {
|
|
128
|
-
clearInterval(this._timer);
|
|
129
|
-
this._timer = null;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
_onMouseEnter() {
|
|
134
|
-
if (this.autoplay) this._paused = true;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
_onMouseLeave() {
|
|
138
|
-
if (this.autoplay) this._paused = false;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
_toggleAutoplay() {
|
|
142
|
-
this._userStopped = !this._userStopped;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/** True when the carousel is actually able to move on its own. */
|
|
124
|
+
/** True when the carousel is able to move on its own. */
|
|
146
125
|
get _autoplayOffered() {
|
|
147
126
|
return this.autoplay && this.carousel && this.layout === "quotes" && this._slidesCount > 1;
|
|
148
127
|
}
|
|
149
128
|
|
|
150
|
-
_goTo(i) {
|
|
151
|
-
this._slide = i;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
129
|
_iconHeight() {
|
|
155
130
|
if (this.layout === "grid") return 64;
|
|
156
131
|
if (this.layout === "quotes") return 56;
|
|
@@ -202,48 +177,22 @@ export class AwardsFlex extends LitElement {
|
|
|
202
177
|
`;
|
|
203
178
|
}
|
|
204
179
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
* paused it, but that is not a mechanism a keyboard or touch user can rely
|
|
209
|
-
* on, and it cannot express "leave it stopped".
|
|
210
|
-
*
|
|
211
|
-
* Its name states the action it performs, which is what a screen reader
|
|
212
|
-
* announces on focus. It sits beside the dots rather than inside them: that
|
|
213
|
-
* container is a tablist and a plain button is not a valid child of one.
|
|
214
|
-
*/
|
|
215
|
-
_renderAutoplayToggle() {
|
|
216
|
-
if (!this._autoplayOffered) return nothing;
|
|
217
|
-
const stopped = this._userStopped || this._reduceMotion;
|
|
218
|
-
return html`
|
|
219
|
-
<button
|
|
220
|
-
type="button"
|
|
221
|
-
class="bd-awardsflex-autoplay-toggle"
|
|
222
|
-
@click=${this._toggleAutoplay}
|
|
223
|
-
aria-label=${stopped ? "Play slideshow" : "Pause slideshow"}
|
|
224
|
-
>${stopped ? "Play" : "Pause"}</button>
|
|
225
|
-
`;
|
|
226
|
-
}
|
|
227
|
-
|
|
180
|
+
/* bd-pagination-dots, which runs the clock too, so the progress in the
|
|
181
|
+
current pill and the change of slide are one thing. WCAG 2.2.2's way to
|
|
182
|
+
stop it is the dots themselves: picking one stops autoplay for good. */
|
|
228
183
|
_renderDots() {
|
|
229
184
|
if (!this.carousel || this._slidesCount < 2) return nothing;
|
|
230
185
|
return html`
|
|
231
|
-
<
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
></button>
|
|
242
|
-
`
|
|
243
|
-
)}
|
|
244
|
-
</div>
|
|
245
|
-
${this._renderAutoplayToggle()}
|
|
246
|
-
</div>
|
|
186
|
+
<bd-pagination-dots
|
|
187
|
+
count=${this._slidesCount}
|
|
188
|
+
current=${this._slide + 1}
|
|
189
|
+
label="Awards"
|
|
190
|
+
?autoplay=${this._autoplayOffered}
|
|
191
|
+
delay=${this.autoplayDelay}
|
|
192
|
+
?held=${this._hovered || this._focusWithin}
|
|
193
|
+
@bd-change=${e => { this._slide = e.detail.current - 1; }}
|
|
194
|
+
@bd-toggle=${e => { this._live = e.detail.moving ? "off" : "polite"; }}
|
|
195
|
+
></bd-pagination-dots>
|
|
247
196
|
`;
|
|
248
197
|
}
|
|
249
198
|
|
|
@@ -252,18 +201,19 @@ export class AwardsFlex extends LitElement {
|
|
|
252
201
|
return html`
|
|
253
202
|
<div
|
|
254
203
|
class="bd-awardsflex-carousel-viewport"
|
|
255
|
-
@mouseenter=${this.
|
|
256
|
-
@mouseleave=${this.
|
|
257
|
-
@focusin=${this.
|
|
258
|
-
@focusout=${this.
|
|
204
|
+
@mouseenter=${() => { this._hovered = true; }}
|
|
205
|
+
@mouseleave=${() => { this._hovered = false; }}
|
|
206
|
+
@focusin=${() => { this._focusWithin = true; }}
|
|
207
|
+
@focusout=${e => { this._focusWithin = e.currentTarget.contains(e.relatedTarget); }}
|
|
259
208
|
>
|
|
260
209
|
<div
|
|
261
210
|
class="bd-awardsflex-carousel-track"
|
|
262
211
|
style="transform: translateX(${offset}%);"
|
|
212
|
+
aria-live=${this._live}
|
|
263
213
|
>
|
|
264
214
|
${this._groups.map(
|
|
265
|
-
(group) => html`
|
|
266
|
-
<div class="bd-awardsflex-carousel-slide">
|
|
215
|
+
(group, i) => html`
|
|
216
|
+
<div class="bd-awardsflex-carousel-slide" ?inert=${i !== this._slide}>
|
|
267
217
|
${group.map((a) => this._renderQuoteCard(a))}
|
|
268
218
|
</div>
|
|
269
219
|
`
|
|
@@ -22,9 +22,11 @@ Badges render real icon components from \`components/icons\` (e.g. \`<bd-pc-mag-
|
|
|
22
22
|
\`<bd-av-comparatives-icon>\`) — pass the icon's slug via the \`icon\` field on each badge
|
|
23
23
|
object, and \`AwardsFlex\` maps it to the matching \`<bd-*-icon>\` custom element internally.
|
|
24
24
|
|
|
25
|
-
When \`layout="quotes"\` and \`carousel\` is set, badges are paginated 3 per
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
When \`layout="quotes"\` and \`carousel\` is set, badges are paginated 3 per slide, and
|
|
26
|
+
1 per slide below the \`sm\` breakpoint (576px). With 6 badges that's 2 slides on a wide
|
|
27
|
+
screen and 6 on a phone — the dots follow the count. The carousel slides a full slide at
|
|
28
|
+
a time; crossing the breakpoint keeps the visitor on the slide that holds the card they
|
|
29
|
+
were looking at.
|
|
28
30
|
|
|
29
31
|
### Usage
|
|
30
32
|
\`\`\`html
|
|
@@ -45,7 +47,7 @@ of 3 cards at a time.
|
|
|
45
47
|
|-------------------|------------------------------------|-------------|----------------------------------------------------------------------|
|
|
46
48
|
| \`background\` | \`"white" \\| "tint"\` | \`"white"\` | Section background |
|
|
47
49
|
| \`layout\` | \`"compact" \\| "grid" \\| "quotes"\` | \`"compact"\` | Badge size/arrangement, or badges paired with quote cards |
|
|
48
|
-
| \`carousel\` | Boolean | \`false\` | Only for \`layout="quotes"\` —
|
|
50
|
+
| \`carousel\` | Boolean | \`false\` | Only for \`layout="quotes"\` — 3 cards a slide, 1 below \`sm\` |
|
|
49
51
|
| \`autoplay\` | Boolean | \`false\` | Auto-advance carousel pages. Requires \`carousel\` + \`layout="quotes"\` and 2+ pages (7+ badges is unnecessary — 2 pages start at 4 badges) |
|
|
50
52
|
| \`autoplay-delay\` | Number (ms) | \`4000\` | Time between auto-advances. Minimum enforced: 1000ms |
|
|
51
53
|
| \`headline\` | String | \`""\` | Optional heading |
|
|
@@ -86,7 +88,7 @@ Autoplay pauses automatically on mouse hover or keyboard focus within the carous
|
|
|
86
88
|
},
|
|
87
89
|
carousel: {
|
|
88
90
|
control : "boolean",
|
|
89
|
-
description: "Paginate quote cards 3
|
|
91
|
+
description: "Paginate quote cards 3 a slide, 1 below sm (layout=\"quotes\" only)",
|
|
90
92
|
table : { category: "Appearance" }
|
|
91
93
|
},
|
|
92
94
|
autoplay: {
|
|
@@ -170,7 +172,7 @@ const threeBadgesWithQuotes = [
|
|
|
170
172
|
}
|
|
171
173
|
];
|
|
172
174
|
|
|
173
|
-
// Six badges with quotes — needed so the carousel has 2+
|
|
175
|
+
// Six badges with quotes — needed so the carousel has 2+ slides (3 a slide, 1 below sm)
|
|
174
176
|
const sixBadgesWithQuotes = [
|
|
175
177
|
...threeBadgesWithQuotes,
|
|
176
178
|
{
|
|
@@ -47,7 +47,12 @@ export default css`
|
|
|
47
47
|
4px for as long as it was focused. That token is the default for
|
|
48
48
|
elements with no radius, which is what Foundations/Focus says. */
|
|
49
49
|
.bd-back:focus-visible {
|
|
50
|
-
|
|
50
|
+
/* forced-colors mode does not paint box-shadow, so the ring
|
|
51
|
+
vanishes there; it does paint an outline, in a system colour. This one
|
|
52
|
+
is transparent everywhere else, and the offset lands it on the ring's
|
|
53
|
+
outer band. */
|
|
54
|
+
outline: var(--border-width-2) solid transparent;
|
|
55
|
+
outline-offset: var(--focus-outline-offset);
|
|
51
56
|
box-shadow: var(--focus-outline-primary);
|
|
52
57
|
}
|
|
53
58
|
|
|
@@ -30,27 +30,39 @@ export class BdBack extends LitElement {
|
|
|
30
30
|
this.ariaLabel = null;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
/** Without an href there is no destination to link to, only history. */
|
|
33
34
|
handleClick() {
|
|
34
|
-
|
|
35
|
-
window.location.href = this.href;
|
|
36
|
-
} else {
|
|
37
|
-
window.history.back();
|
|
38
|
-
}
|
|
35
|
+
window.history.back();
|
|
39
36
|
}
|
|
40
37
|
|
|
38
|
+
/* A real link when there is somewhere to go. With href this used to be a
|
|
39
|
+
<button> that set window.location.href on click, so the destination was
|
|
40
|
+
invisible to everything that reads links: no middle-click or
|
|
41
|
+
Cmd/Ctrl-click into a new tab, no "copy link", no status-bar URL, and a
|
|
42
|
+
screen reader announced a button and listed it under none of the page's
|
|
43
|
+
links. Without href it stays a button, because history.back() is an
|
|
44
|
+
action, not a place. */
|
|
41
45
|
render() {
|
|
42
|
-
|
|
46
|
+
const content = html`<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" aria-hidden="true" class="mi-solid mi-arrow-back" viewBox="0 0 24 24">
|
|
47
|
+
<path d="M19 11H7.83l4.88-4.88c.39-.39.39-1.03 0-1.42a.996.996 0 0 0-1.41 0l-6.59 6.59a.996.996 0 0 0 0 1.41l6.59 6.59a.996.996 0 1 0 1.41-1.41L7.83 13H19c.55 0 1-.45 1-1s-.45-1-1-1"/>
|
|
48
|
+
</svg>
|
|
49
|
+
<span class="bd-back-label">${this.label}</span>`;
|
|
50
|
+
|
|
51
|
+
return this.href
|
|
52
|
+
? html`
|
|
53
|
+
<a
|
|
54
|
+
class="bd-back"
|
|
55
|
+
href="${this.href}"
|
|
56
|
+
aria-label="${this.ariaLabel ?? nothing}"
|
|
57
|
+
>${content}</a>
|
|
58
|
+
`
|
|
59
|
+
: html`
|
|
43
60
|
<button
|
|
44
61
|
type="button"
|
|
45
62
|
class="bd-back"
|
|
46
63
|
aria-label="${this.ariaLabel ?? nothing}"
|
|
47
64
|
@click="${this.handleClick}"
|
|
48
|
-
>
|
|
49
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" aria-hidden="true" class="mi-solid mi-arrow-back" viewBox="0 0 24 24">
|
|
50
|
-
<path d="M19 11H7.83l4.88-4.88c.39-.39.39-1.03 0-1.42a.996.996 0 0 0-1.41 0l-6.59 6.59a.996.996 0 0 0 0 1.41l6.59 6.59a.996.996 0 1 0 1.41-1.41L7.83 13H19c.55 0 1-.45 1-1s-.45-1-1-1"/>
|
|
51
|
-
</svg>
|
|
52
|
-
<span class="bd-back-label">${this.label}</span>
|
|
53
|
-
</button>
|
|
65
|
+
>${content}</button>
|
|
54
66
|
`;
|
|
55
67
|
}
|
|
56
68
|
|
|
@@ -20,8 +20,8 @@ export default {
|
|
|
20
20
|
\`\`\`
|
|
21
21
|
|
|
22
22
|
### Behavior
|
|
23
|
-
- If \`href\` is provided,
|
|
24
|
-
- If \`href\` is empty,
|
|
23
|
+
- If \`href\` is provided, it renders a real link to that URL, so it opens in a new tab, can be copied, and is announced as a link
|
|
24
|
+
- If \`href\` is empty, it renders a button that calls \`window.history.back()\`
|
|
25
25
|
- Always renders a left-pointing arrow SVG icon alongside the label
|
|
26
26
|
`
|
|
27
27
|
}
|
|
@@ -69,7 +69,7 @@ export const WithHref = {
|
|
|
69
69
|
parameters: {
|
|
70
70
|
docs: {
|
|
71
71
|
description: {
|
|
72
|
-
story: '`href` is set to
|
|
72
|
+
story: '`href` is set to `/`, so it renders a link to the home page.'
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
}
|
|
@@ -71,12 +71,15 @@ export class BdBadge extends LitElement {
|
|
|
71
71
|
if (!this.icon || this.mode === 'dot') return '';
|
|
72
72
|
|
|
73
73
|
// currentColor makes the icon follow the tone's text colour, so there is
|
|
74
|
-
// no per-tone icon colour to keep in step
|
|
74
|
+
// no per-tone icon colour to keep in step.
|
|
75
|
+
// size is a step on the icon scale, not pixels. This said size="16", which
|
|
76
|
+
// is no step, so both icons fell back to the xl they were drawn at and a
|
|
77
|
+
// badge with an icon measured 40px tall beside 23px ones; sm is the 16.
|
|
75
78
|
switch (this.icon) {
|
|
76
79
|
case 'individual':
|
|
77
|
-
return html`<bd-individual-icon size="
|
|
80
|
+
return html`<bd-individual-icon size="sm" color="currentColor"></bd-individual-icon>`;
|
|
78
81
|
case 'family':
|
|
79
|
-
return html`<bd-family-icon size="
|
|
82
|
+
return html`<bd-family-icon size="sm" color="currentColor"></bd-family-icon>`;
|
|
80
83
|
default:
|
|
81
84
|
return '';
|
|
82
85
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { html } from 'lit';
|
|
2
2
|
import './badge.js';
|
|
3
3
|
|
|
4
|
-
const TONES = ['
|
|
4
|
+
const TONES = ['neutral', 'brand', 'success', 'warning', 'error', 'info'];
|
|
5
5
|
const SIZES = ['sm', 'md', 'lg'];
|
|
6
6
|
const VARIANTS = ['shade', 'fill'];
|
|
7
7
|
|
|
@@ -35,10 +35,10 @@ matched to the token that already carries it.
|
|
|
35
35
|
### Usage
|
|
36
36
|
\`\`\`html
|
|
37
37
|
<bd-badge tone="success" variant="shade">Save 44%</bd-badge>
|
|
38
|
-
<bd-badge tone="
|
|
38
|
+
<bd-badge tone="brand" variant="fill" size="sm">New</bd-badge>
|
|
39
39
|
<bd-badge mode="count" tone="error" variant="fill">3</bd-badge>
|
|
40
40
|
<bd-badge mode="dot" tone="warning" variant="fill"></bd-badge>
|
|
41
|
-
<bd-badge tone="
|
|
41
|
+
<bd-badge tone="brand" variant="fill" icon="family">Family Pack</bd-badge>
|
|
42
42
|
\`\`\`
|
|
43
43
|
|
|
44
44
|
### Props
|
|
@@ -46,7 +46,7 @@ matched to the token that already carries it.
|
|
|
46
46
|
|---|---|---|
|
|
47
47
|
| \`mode\` | \`text\`, \`dot\`, \`count\` | \`text\` |
|
|
48
48
|
| \`size\` | \`sm\`, \`md\`, \`lg\` | \`md\` |
|
|
49
|
-
| \`tone\` | \`
|
|
49
|
+
| \`tone\` | \`neutral\`, \`brand\`, \`success\`, \`warning\`, \`error\`, \`info\` | \`neutral\` |
|
|
50
50
|
| \`variant\` | \`shade\`, \`fill\` | \`shade\` |
|
|
51
51
|
| \`icon\` | \`individual\`, \`family\`, \`''\` | \`''\` |
|
|
52
52
|
| \`label\` | any string — accessible name for \`mode="dot"\` | tone name |
|
|
@@ -107,7 +107,7 @@ matched to the token that already carries it.
|
|
|
107
107
|
control : { type: 'select' },
|
|
108
108
|
options : TONES,
|
|
109
109
|
description: 'Semantic colour pairing.',
|
|
110
|
-
table : { type: { summary: 'string' }, defaultValue: { summary: '
|
|
110
|
+
table : { type: { summary: 'string' }, defaultValue: { summary: 'neutral' }, category: 'Appearance' }
|
|
111
111
|
},
|
|
112
112
|
variant: {
|
|
113
113
|
control : { type: 'inline-radio' },
|
|
@@ -135,7 +135,7 @@ matched to the token that already carries it.
|
|
|
135
135
|
args: {
|
|
136
136
|
mode : 'text',
|
|
137
137
|
size : 'md',
|
|
138
|
-
tone : '
|
|
138
|
+
tone : 'neutral',
|
|
139
139
|
variant: 'shade',
|
|
140
140
|
icon : '',
|
|
141
141
|
label : '',
|
|
@@ -172,7 +172,7 @@ export const Sizes = {
|
|
|
172
172
|
render : () => html`
|
|
173
173
|
${VARIANTS.map(variant => html`
|
|
174
174
|
${row(variant, SIZES.map(size => html`
|
|
175
|
-
<bd-badge size="${size}" tone="
|
|
175
|
+
<bd-badge size="${size}" tone="brand" variant="${variant}">${size}</bd-badge>
|
|
176
176
|
`))}
|
|
177
177
|
`)}
|
|
178
178
|
`
|
|
@@ -212,7 +212,7 @@ export const WithIcon = {
|
|
|
212
212
|
<bd-badge tone="success" icon="family">Family Pack</bd-badge>
|
|
213
213
|
`)}
|
|
214
214
|
${row('fill', html`
|
|
215
|
-
<bd-badge variant="fill" tone="
|
|
215
|
+
<bd-badge variant="fill" tone="brand" icon="individual">Individual</bd-badge>
|
|
216
216
|
<bd-badge variant="fill" tone="success" icon="family">Family Pack</bd-badge>
|
|
217
217
|
`)}
|
|
218
218
|
`
|
|
@@ -220,11 +220,15 @@ export const WithIcon = {
|
|
|
220
220
|
|
|
221
221
|
export const Legacy = {
|
|
222
222
|
name : 'Legacy variants',
|
|
223
|
-
parameters: { docs: { description: { story: 'Pre-`ds-badge` markup still renders, mapped onto the new axes.' } } },
|
|
223
|
+
parameters: { docs: { description: { story: 'Pre-`ds-badge` markup still renders, mapped onto the new axes: the old variants, and the two retired tone names, `default` (now `neutral`) and `primary` (now `brand`). Write the new names; these are here so a page still using the old ones is shown to work.' } } },
|
|
224
224
|
render : () => html`
|
|
225
225
|
${row('mapped', html`
|
|
226
226
|
<bd-badge variant="bd-light-blue">bd-light-blue</bd-badge>
|
|
227
227
|
<bd-badge variant="bd-light-green">bd-light-green</bd-badge>
|
|
228
228
|
`)}
|
|
229
|
+
${row('tones', html`
|
|
230
|
+
<bd-badge tone="default">tone="default" → neutral</bd-badge>
|
|
231
|
+
<bd-badge tone="primary">tone="primary" → brand</bd-badge>
|
|
232
|
+
`)}
|
|
229
233
|
`
|
|
230
234
|
};
|
|
@@ -220,7 +220,7 @@ class BentoGrid extends LitElement {
|
|
|
220
220
|
<div class="bd-bentogrid-header">
|
|
221
221
|
${this.eyebrow
|
|
222
222
|
? html`
|
|
223
|
-
<bd-p variant="
|
|
223
|
+
<bd-p variant="eyebrow" class="bd-bentogrid-eyebrow">
|
|
224
224
|
<slot name="eyebrow">${this.eyebrow}</slot>
|
|
225
225
|
</bd-p>
|
|
226
226
|
`
|
|
@@ -4,8 +4,17 @@ import { css } from 'lit';
|
|
|
4
4
|
export const blockquoteCSS = css`
|
|
5
5
|
:host { display: block; }
|
|
6
6
|
|
|
7
|
+
/* .quote is the outer element: the <figure> when there is an attribution,
|
|
8
|
+
the bare <blockquote> when there is not. The bar and the type live on it,
|
|
9
|
+
so the caption inherits the quotation's family and leading the way the
|
|
10
|
+
<footer> did when it sat inside the <blockquote> -- the rhythm the note
|
|
11
|
+
below is about. */
|
|
12
|
+
figure,
|
|
7
13
|
blockquote {
|
|
8
14
|
margin: 0;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.quote {
|
|
9
18
|
padding-left: var(--spacing-16);
|
|
10
19
|
border-left: var(--border-width-4) solid var(--color-border-brand);
|
|
11
20
|
font-family: var(--typography-body-large-fontFamily);
|
|
@@ -15,7 +24,7 @@ export const blockquoteCSS = css`
|
|
|
15
24
|
color: var(--color-text-primary);
|
|
16
25
|
}
|
|
17
26
|
|
|
18
|
-
|
|
27
|
+
figcaption {
|
|
19
28
|
margin-top: var(--spacing-8);
|
|
20
29
|
font-size: var(--typography-caption-default-fontSize);
|
|
21
30
|
/* no role: two roles are each a near-miss, from opposite directions, and
|
|
@@ -24,8 +33,8 @@ export const blockquoteCSS = css`
|
|
|
24
33
|
.
|
|
25
34
|
label-small matches this text's size and weight exactly -- but adopting
|
|
26
35
|
it would add letterSpacing-wide, which has no job on mixed-case sentence
|
|
27
|
-
text because nothing here crowds, and would replace the
|
|
28
|
-
|
|
36
|
+
text because nothing here crowds, and would replace the leading this
|
|
37
|
+
inherits from the quote it attributes with 1.5. That inherited
|
|
29
38
|
rhythm is the one thing in this rule that means something: the credit
|
|
30
39
|
line sits on the same measure as the quotation it belongs to, where 1.5
|
|
31
40
|
merely comes attached to the role.
|
|
@@ -17,12 +17,19 @@ export class BdBlockquote extends LitElement {
|
|
|
17
17
|
this.attribution = '';
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
/* WHERE THE ATTRIBUTION GOES.
|
|
21
|
+
It used to be a <footer> inside the <blockquote>, and everything inside a
|
|
22
|
+
blockquote is the quotation, so the name of the person quoted was read as
|
|
23
|
+
part of what they said. The HTML spec's pattern puts the credit outside
|
|
24
|
+
the quote: a <figure> holding the <blockquote> and a <figcaption>. With
|
|
25
|
+
no attribution there is no caption, so the bare <blockquote> renders, as
|
|
26
|
+
before -- a figure with nothing to name it would only add an empty
|
|
27
|
+
"figure" for a screen reader to step through. */
|
|
20
28
|
render() {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
`;
|
|
29
|
+
const quote = html`<blockquote class=${this.attribution ? nothing : 'quote'} cite=${this.cite || nothing}><slot></slot></blockquote>`;
|
|
30
|
+
return this.attribution
|
|
31
|
+
? html`<figure class="quote">${quote}<figcaption>\u2014 ${this.attribution}</figcaption></figure>`
|
|
32
|
+
: quote;
|
|
26
33
|
}
|
|
27
34
|
}
|
|
28
35
|
|
|
@@ -28,9 +28,11 @@ optional attribution.
|
|
|
28
28
|
| \`cite\` | URL of the source | \`''\` |
|
|
29
29
|
|
|
30
30
|
### Notes
|
|
31
|
-
\`attribution\` renders as a \`<
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
\`attribution\` renders as a \`<figcaption>\`, prefixed with an em dash, beside
|
|
32
|
+
the \`<blockquote>\` inside a \`<figure>\`. That is the HTML spec's pattern for
|
|
33
|
+
crediting a quotation: everything inside a \`<blockquote>\` is the quote, so the
|
|
34
|
+
name of the person quoted belongs outside it. Without an attribution there is
|
|
35
|
+
no figure, only the \`<blockquote>\`.
|
|
34
36
|
|
|
35
37
|
\`cite\` maps to the native \`cite\` attribute. It is **metadata only** — browsers
|
|
36
38
|
do not render it and it is not a link, so if the source should be visible or
|
|
@@ -42,7 +44,7 @@ clickable, put it in \`attribution\` or in the slotted content.
|
|
|
42
44
|
argTypes: {
|
|
43
45
|
attribution: {
|
|
44
46
|
control : 'text',
|
|
45
|
-
description: 'Who said it. Renders as a
|
|
47
|
+
description: 'Who said it. Renders as a figcaption beside the blockquote in a figure, prefixed with an em dash.',
|
|
46
48
|
table : { type: { summary: 'string' }, defaultValue: { summary: "" }, category: 'Content' }
|
|
47
49
|
},
|
|
48
50
|
cite: {
|