@repobit/dex-system-design 1.0.1 → 2.0.1
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 +89 -0
- package/README.md +43 -12
- package/package.json +6 -4
- 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
|
@@ -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: {
|
|
@@ -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
|
|
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:
|
|
140
|
-
|
|
159
|
+
outline: var(--bd-button-focus-ring);
|
|
160
|
+
outline-offset: var(--button-shared-focus-outlineOffset);
|
|
141
161
|
}
|
|
142
162
|
|
|
143
|
-
/*
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
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
|
-
|
|
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
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
a
|
|
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
|
|
46
|
-
size
|
|
47
|
-
variant
|
|
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
|
|
51
|
-
fullWidth
|
|
52
|
-
strong
|
|
53
|
-
disabled
|
|
54
|
-
loading
|
|
55
|
-
fontSize
|
|
56
|
-
fontWeight: { type: String, attribute: "font-weight" },
|
|
57
|
-
align
|
|
58
|
-
surface
|
|
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
|
|
68
|
+
shape : { type: String, reflect: true },
|
|
61
69
|
/** The accent variant reveals a trailing arrow on hover; this opts out. */
|
|
62
|
-
noArrow
|
|
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
|
|
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(
|
|
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(
|
|
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
|
|
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\` | \`
|
|
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 \`
|
|
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 : ['
|
|
49
|
+
options : ['md', 'sm'],
|
|
50
50
|
description: 'Type size of the caption.',
|
|
51
|
-
table : { type: { summary: 'string' }, defaultValue: { summary:
|
|
51
|
+
table : { type: { summary: 'string' }, defaultValue: { summary: 'md' }, category: 'Appearance' }
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
};
|