@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
|
@@ -3,7 +3,10 @@ import { css } from "lit";
|
|
|
3
3
|
export const dividerHorizontal = css`
|
|
4
4
|
.hr-line {
|
|
5
5
|
border: none;
|
|
6
|
-
|
|
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: '
|
|
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
|
|
41
|
-
table : { type: { summary: 'string' }, defaultValue: { summary: '
|
|
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)
|
|
53
|
-
table : { type: { summary: 'string' }, defaultValue: { summary: '
|
|
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
|
|
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 = {
|
|
@@ -7,10 +7,26 @@ import dropdownCSS from "../dropdown/dropdown.css.js";
|
|
|
7
7
|
import { resolveSize } from '../shared/prop-vocabulary.js';
|
|
8
8
|
|
|
9
9
|
|
|
10
|
+
/* options is a JSON array, as a property or as an attribute. Anything else --
|
|
11
|
+
malformed JSON, a comma list, a JSON object, a removed attribute -- is no
|
|
12
|
+
options rather than a crash. type: Array could not say that: Lit's Array
|
|
13
|
+
converter turns unparseable text into null before the setter sees it, and
|
|
14
|
+
render() then threw on null.map, so the field drew nothing at all. */
|
|
15
|
+
const toOptions = value => {
|
|
16
|
+
if (Array.isArray(value)) return value;
|
|
17
|
+
if (typeof value !== "string") return [];
|
|
18
|
+
try {
|
|
19
|
+
const parsed = JSON.parse(value);
|
|
20
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
21
|
+
} catch {
|
|
22
|
+
return [];
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
10
26
|
class CustomDropdown extends LitElement {
|
|
11
27
|
static properties = {
|
|
12
28
|
label : { type: String },
|
|
13
|
-
options : {
|
|
29
|
+
options : { converter: { fromAttribute: toOptions } },
|
|
14
30
|
size : { type: String, reflect: true },
|
|
15
31
|
/** @deprecated pass `size`. It named the size, not the kind. */
|
|
16
32
|
kind : { type: String },
|
|
@@ -19,6 +35,12 @@ class CustomDropdown extends LitElement {
|
|
|
19
35
|
invalid : { type: Boolean },
|
|
20
36
|
validated: { type: Boolean },
|
|
21
37
|
disabled : { type: Boolean },
|
|
38
|
+
/* The chosen option, and what the events carry, so a form handler written
|
|
39
|
+
for bd-custom-input reads this one unchanged. It always names an option
|
|
40
|
+
that exists: unset, or set to one that is not there, it is the first. */
|
|
41
|
+
value : { type: String },
|
|
42
|
+
/** Carried on the event detail, as on bd-custom-input. */
|
|
43
|
+
name : { type: String },
|
|
22
44
|
/* Whether the native picker is showing. A best effort, not a fact: a
|
|
23
45
|
<select> renders its popup in the OS, so the page is never told when it
|
|
24
46
|
opens or closes. See _onSelectMouseDown. */
|
|
@@ -37,6 +59,8 @@ class CustomDropdown extends LitElement {
|
|
|
37
59
|
this.invalid = false;
|
|
38
60
|
this.validated = false;
|
|
39
61
|
this.disabled = false;
|
|
62
|
+
this.value = "";
|
|
63
|
+
this.name = "";
|
|
40
64
|
this._open = false;
|
|
41
65
|
}
|
|
42
66
|
|
|
@@ -97,15 +121,7 @@ class CustomDropdown extends LitElement {
|
|
|
97
121
|
}
|
|
98
122
|
|
|
99
123
|
set options(val) {
|
|
100
|
-
|
|
101
|
-
try {
|
|
102
|
-
this._options = JSON.parse(val);
|
|
103
|
-
} catch {
|
|
104
|
-
this._options = [];
|
|
105
|
-
}
|
|
106
|
-
} else {
|
|
107
|
-
this._options = val;
|
|
108
|
-
}
|
|
124
|
+
this._options = toOptions(val);
|
|
109
125
|
}
|
|
110
126
|
|
|
111
127
|
get options() {
|
|
@@ -116,6 +132,37 @@ class CustomDropdown extends LitElement {
|
|
|
116
132
|
// so it moved; see shared/prop-vocabulary.js
|
|
117
133
|
if (changed.has("kind") && this.kind) this.size = this.kind;
|
|
118
134
|
if (changed.has("size") || changed.has("kind")) this.size = resolveSize(this.size);
|
|
135
|
+
if (!this.options.map(String).includes(this.value)) this.value = this.options.length ? String(this.options[0]) : "";
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/* The select is told its value after its options exist. A binding on the
|
|
139
|
+
<select> would be committed before the options inside it rendered, and
|
|
140
|
+
an option's selected attribute stops counting once a visitor has chosen,
|
|
141
|
+
so neither could move the selection when value is set from outside. */
|
|
142
|
+
updated() {
|
|
143
|
+
const select = this.shadowRoot.querySelector("select");
|
|
144
|
+
if (select && select.value !== this.value) select.value = this.value;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/* WHY TWO EVENTS FOR ONE CHOICE.
|
|
148
|
+
This dispatched nothing: choosing an option changed nothing observable
|
|
149
|
+
from outside, and the chosen value could only be read by reaching into
|
|
150
|
+
the shadow root. The native change and input do not help -- change is not
|
|
151
|
+
composed, so it never leaves the shadow root, and input arrives
|
|
152
|
+
retargeted to the host with no value on it.
|
|
153
|
+
|
|
154
|
+
A select commits the moment an option is chosen, so its input and change
|
|
155
|
+
are the same moment. Both are sent anyway, with bd-custom-input's names,
|
|
156
|
+
detail and flags, so a handler written for a form of inputs -- live
|
|
157
|
+
validation on bd-input, saving on bd-change -- needs nothing special for
|
|
158
|
+
this field. */
|
|
159
|
+
_onChange(e) {
|
|
160
|
+
this._closePicker();
|
|
161
|
+
this.value = e.target.value;
|
|
162
|
+
const detail = { value: this.value, name: this.name };
|
|
163
|
+
for (const type of ["bd-input", "bd-change"]) {
|
|
164
|
+
this.dispatchEvent(new CustomEvent(type, { detail, bubbles: true, composed: true }));
|
|
165
|
+
}
|
|
119
166
|
}
|
|
120
167
|
|
|
121
168
|
render() {
|
|
@@ -145,6 +192,10 @@ class CustomDropdown extends LitElement {
|
|
|
145
192
|
? "form-help-text--success"
|
|
146
193
|
: "form-help-text--default";
|
|
147
194
|
|
|
195
|
+
// The hint is the select's description, and the state is announced, not
|
|
196
|
+
// only drawn: invalid used to be a red border alone and the hint was never
|
|
197
|
+
// pointed at, so the reason a field was invalid reached a screen reader
|
|
198
|
+
// not at all. Both as bd-custom-input does them.
|
|
148
199
|
return html`
|
|
149
200
|
<div class="input-container">
|
|
150
201
|
${this.label
|
|
@@ -161,13 +212,16 @@ class CustomDropdown extends LitElement {
|
|
|
161
212
|
class="form-input-bd ${inputClass} ${inputStateClass}"
|
|
162
213
|
?required="${this.required}"
|
|
163
214
|
?disabled="${this.disabled}"
|
|
215
|
+
aria-invalid="${this.invalid ? "true" : nothing}"
|
|
216
|
+
aria-describedby="${this.tooltip ? "custom-dropdown-help" : nothing}"
|
|
164
217
|
@mousedown="${this._onSelectMouseDown}"
|
|
165
218
|
@keydown="${this._onSelectKeyDown}"
|
|
166
|
-
|
|
219
|
+
name="${this.name || nothing}"
|
|
220
|
+
@change="${this._onChange}"
|
|
167
221
|
@blur="${this._closePicker}"
|
|
168
222
|
>
|
|
169
223
|
${this.options.map(
|
|
170
|
-
|
|
224
|
+
option => html`<option value="${option}" ?selected="${String(option) === this.value}">
|
|
171
225
|
${option}
|
|
172
226
|
</option>`
|
|
173
227
|
)}
|
|
@@ -178,7 +232,7 @@ class CustomDropdown extends LitElement {
|
|
|
178
232
|
></bd-arrow-down-icon>
|
|
179
233
|
</div>
|
|
180
234
|
${this.tooltip
|
|
181
|
-
? html`<div class="form-help-text ${helpTextClass}">
|
|
235
|
+
? html`<div id="custom-dropdown-help" class="form-help-text ${helpTextClass}">
|
|
182
236
|
${this.tooltip}
|
|
183
237
|
</div>`
|
|
184
238
|
: ""}
|
|
@@ -55,20 +55,21 @@ descender.
|
|
|
55
55
|
stories under **Atoms/Input**. They are one component; the Atoms/Input page
|
|
56
56
|
carries the fuller write-up.
|
|
57
57
|
|
|
58
|
-
###
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
58
|
+
### Value and events
|
|
59
|
+
\`value\` is the chosen option — read it, or set it to choose one; unset, or
|
|
60
|
+
set to an option that is not there, it is the first. Choosing an option fires
|
|
61
|
+
\`bd-input\` and then \`bd-change\`, both with \`detail\` \`{ value, name }\`,
|
|
62
|
+
bubbling and composed — the same names, shape and flags as
|
|
63
|
+
\`bd-custom-input\`, so one form handler reads both. A select commits on choice,
|
|
64
|
+
so the two arrive together.
|
|
65
|
+
|
|
66
|
+
### Accessibility
|
|
67
|
+
\`invalid\` sets \`aria-invalid="true"\` on the select as well as the red
|
|
68
|
+
border, and \`tooltip\` is the select's description through
|
|
69
|
+
\`aria-describedby\`, so the reason a field is invalid is announced, not only
|
|
70
|
+
drawn — as on \`bd-custom-input\`.
|
|
71
|
+
|
|
72
|
+
### Known gap
|
|
72
73
|
Being in a shadow root, the select also does not participate in an enclosing
|
|
73
74
|
\`<form>\` — a \`<form>\` containing it yields no \`FormData\` entry.
|
|
74
75
|
`
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { html } from 'lit';
|
|
2
2
|
import './fieldset.js';
|
|
3
3
|
import '../switch/switch.js';
|
|
4
|
-
import '../
|
|
5
|
-
import '../help-text/help-text.js';
|
|
4
|
+
import '../radio/radio.js';
|
|
6
5
|
import '../input/input.js';
|
|
7
6
|
|
|
8
7
|
export default {
|
|
@@ -37,6 +36,14 @@ they are choosing between. The \`legend\` is what supplies that.
|
|
|
37
36
|
Because slotted controls sit inside the \`<fieldset>\` in the flattened tree, the
|
|
38
37
|
native grouping does apply here — unlike a \`for\` reference, which cannot cross
|
|
39
38
|
the shadow boundary.
|
|
39
|
+
|
|
40
|
+
### Labelling the fields inside it
|
|
41
|
+
Give each field its own label and hint through its own props —
|
|
42
|
+
\`label\`, \`required\` and \`tooltip\` on \`bd-custom-input\` — not a
|
|
43
|
+
\`<bd-label for>\` and a \`<bd-help-text>\` beside it. The field keeps its
|
|
44
|
+
\`<input>\` in its own shadow root, and a \`for\` or an \`aria-describedby\`
|
|
45
|
+
in another tree resolves to nothing there: the field is then named only by its
|
|
46
|
+
placeholder, which disappears as soon as anyone types.
|
|
40
47
|
`
|
|
41
48
|
}
|
|
42
49
|
}
|
|
@@ -50,6 +57,22 @@ the shadow boundary.
|
|
|
50
57
|
}
|
|
51
58
|
};
|
|
52
59
|
|
|
60
|
+
/**
|
|
61
|
+
* The case the docs are about: a choice among radios, named by the legend.
|
|
62
|
+
* Each radio's own label says only "Basic" or "Premium"; the legend is what
|
|
63
|
+
* says they are plans. It renders the usage example above, so the page shows
|
|
64
|
+
* the grouping it describes rather than only asserting it.
|
|
65
|
+
*/
|
|
66
|
+
export const ChooseOne = {
|
|
67
|
+
name : 'Choose one — radios under a legend',
|
|
68
|
+
render: () => html`
|
|
69
|
+
<bd-fieldset legend="Choose your plan">
|
|
70
|
+
<bd-radio name="plan" value="basic" label="Basic"></bd-radio>
|
|
71
|
+
<bd-radio name="plan" value="premium" label="Premium"></bd-radio>
|
|
72
|
+
</bd-fieldset>
|
|
73
|
+
`
|
|
74
|
+
};
|
|
75
|
+
|
|
53
76
|
export const Default = () => html`
|
|
54
77
|
<bd-fieldset legend="Notifications">
|
|
55
78
|
<bd-switch label="Email"></bd-switch>
|
|
@@ -63,22 +86,24 @@ export const Default = () => html`
|
|
|
63
86
|
* `--form-help-text-default-marginTop`. None of those three is visible in a
|
|
64
87
|
* fieldset of bare switches, so this story is the one that shows them — it is
|
|
65
88
|
* where a change to the form spacing tokens actually appears.
|
|
89
|
+
*
|
|
90
|
+
* Each label and hint is the field's own, through its label / required /
|
|
91
|
+
* tooltip props. This story used to pair <bd-label for="line1"> and a
|
|
92
|
+
* <bd-help-text> with an unlabelled field, and no for in it resolved: the
|
|
93
|
+
* three fields were named by their placeholders and the hints described
|
|
94
|
+
* nothing. The field's own props wire the label and aria-describedby inside
|
|
95
|
+
* its shadow root, where the <input> is.
|
|
66
96
|
*/
|
|
67
97
|
export const FieldStack = () => html`
|
|
68
98
|
<bd-fieldset legend="Billing address">
|
|
69
|
-
<
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
<
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
<div>
|
|
79
|
-
<bd-label for="post">Postcode</bd-label>
|
|
80
|
-
<bd-custom-input label="" size="md" placeholder="NW1 6XE"></bd-custom-input>
|
|
81
|
-
<bd-help-text tone="error">We could not find that postcode.</bd-help-text>
|
|
82
|
-
</div>
|
|
99
|
+
<bd-custom-input
|
|
100
|
+
label="Street" required size="md" placeholder="221B Baker Street"
|
|
101
|
+
tooltip="Include the building number."
|
|
102
|
+
></bd-custom-input>
|
|
103
|
+
<bd-custom-input label="City" required size="md" placeholder="London"></bd-custom-input>
|
|
104
|
+
<bd-custom-input
|
|
105
|
+
label="Postcode" size="md" placeholder="NW1 6XE"
|
|
106
|
+
invalid tooltip="We could not find that postcode."
|
|
107
|
+
></bd-custom-input>
|
|
83
108
|
</bd-fieldset>
|
|
84
109
|
`;
|
|
@@ -37,7 +37,12 @@ export default css`
|
|
|
37
37
|
Spelled ::slotted, which is the only selector that crosses this boundary,
|
|
38
38
|
and :focus-visible so a mouse click does not draw it. */
|
|
39
39
|
::slotted(a:focus-visible) {
|
|
40
|
-
|
|
40
|
+
/* forced-colors mode does not paint box-shadow, so the ring
|
|
41
|
+
vanishes there; it does paint an outline, in a system colour. This one
|
|
42
|
+
is transparent everywhere else, and the offset lands it on the ring's
|
|
43
|
+
outer band. */
|
|
44
|
+
outline: var(--border-width-2) solid transparent;
|
|
45
|
+
outline-offset: var(--focus-outline-offset);
|
|
41
46
|
box-shadow: var(--focus-outline-primary);
|
|
42
47
|
border-radius: var(--focus-outline-radius);
|
|
43
48
|
}
|
|
@@ -36,7 +36,12 @@ export default css`
|
|
|
36
36
|
.country-toggle:focus,
|
|
37
37
|
.close-countries-button:focus,
|
|
38
38
|
.social-icons a:focus {
|
|
39
|
-
|
|
39
|
+
/* forced-colors mode does not paint box-shadow, so the ring
|
|
40
|
+
vanishes there; it does paint an outline, in a system colour. This one
|
|
41
|
+
is transparent everywhere else, and the offset lands it on the ring's
|
|
42
|
+
outer band. */
|
|
43
|
+
outline: var(--border-width-2) solid transparent;
|
|
44
|
+
outline-offset: var(--focus-outline-offset);
|
|
40
45
|
/* The ring is --focus-outline-primary alone, as in every other component.
|
|
41
46
|
The 2px border that stood here was redundant and, since these elements
|
|
42
47
|
have no border otherwise, it shifted the layout by 2px on focus. */
|
|
@@ -176,7 +176,7 @@ class HeaderCode extends LitElement {
|
|
|
176
176
|
</div>
|
|
177
177
|
|
|
178
178
|
${this.eyebrow
|
|
179
|
-
? html`<bd-p variant="
|
|
179
|
+
? html`<bd-p variant="eyebrow" class="bd-headercode-eyebrow">${this.eyebrow}</bd-p>`
|
|
180
180
|
: nothing}
|
|
181
181
|
|
|
182
182
|
<bd-h as="${this._headingTag}" visual-level="h1" class="bd-headercode-title">
|
|
@@ -36,7 +36,12 @@ export default css`
|
|
|
36
36
|
failure mode as the bare descendant selector this replaced. */
|
|
37
37
|
::slotted(a:focus-visible) {
|
|
38
38
|
/* the one focus ring — see Foundations/Focus */
|
|
39
|
-
|
|
39
|
+
/* forced-colors mode does not paint box-shadow, so the ring
|
|
40
|
+
vanishes there; it does paint an outline, in a system colour. This one
|
|
41
|
+
is transparent everywhere else, and the offset lands it on the ring's
|
|
42
|
+
outer band. */
|
|
43
|
+
outline: var(--border-width-2) solid transparent;
|
|
44
|
+
outline-offset: var(--focus-outline-offset);
|
|
40
45
|
box-shadow: var(--focus-outline-primary);
|
|
41
46
|
border-radius: var(--focus-outline-radius);
|
|
42
47
|
}
|
|
@@ -736,9 +741,9 @@ export default css`
|
|
|
736
741
|
:host { height: var(--bd-icon-size, ...) }, and a consuming rule and a
|
|
737
742
|
:host rule match the SAME element, where the outer tree wins -- the same
|
|
738
743
|
mechanism scripts/atom-inert-properties.json records for colour on a
|
|
739
|
-
bd-p host. --bd-icon-size
|
|
740
|
-
|
|
741
|
-
|
|
744
|
+
bd-p host. Setting --bd-icon-size here would work as well, since the
|
|
745
|
+
atom now sets it from a :host rule rather than inline; height is kept
|
|
746
|
+
because it was measured, and it says the job directly.
|
|
742
747
|
.
|
|
743
748
|
sm on the icon scale is 16px, so the mark renders 121 wide -- 47% of a
|
|
744
749
|
256px panel. --bd-header-mark-height above is set to the same step, so
|
|
@@ -8,25 +8,42 @@ export default {
|
|
|
8
8
|
docs: {
|
|
9
9
|
description: {
|
|
10
10
|
component: `
|
|
11
|
-
**BdH**
|
|
11
|
+
**BdH** renders a semantic heading, \`<h1>\`–\`<h6>\`, on the system's type scale.
|
|
12
12
|
|
|
13
13
|
### Usage
|
|
14
14
|
\`\`\`html
|
|
15
15
|
<bd-h as="h1">Page Title</bd-h>
|
|
16
|
+
<bd-h as="h2" visual-level="h3">A component section inside a page section</bd-h>
|
|
16
17
|
<bd-h as="h3" color="primary" fontWeight="bold" paddingBottom="16">Section Title</bd-h>
|
|
17
18
|
\`\`\`
|
|
18
19
|
|
|
19
|
-
###
|
|
20
|
-
\`
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
20
|
+
### Level and size are two questions
|
|
21
|
+
\`as\` is the document outline — which heading element is rendered — and
|
|
22
|
+
belongs to the composing page. \`visual-level\` is the type role — which step of
|
|
23
|
+
the heading scale it looks like — and follows \`as\` when omitted. Both take
|
|
24
|
+
\`h1\`–\`h6\` or \`1\`–\`6\`. An \`as\` that is not a heading level renders an
|
|
25
|
+
\`h2\`: still a heading, never the page's \`h1\`.
|
|
26
|
+
|
|
27
|
+
Change the size with \`visual-level\`, not with \`aria-level\`: an \`aria-level\`
|
|
28
|
+
that disagrees with the element tells assistive technology one level and the
|
|
29
|
+
outline another.
|
|
30
|
+
|
|
31
|
+
### Style props take steps, not CSS
|
|
32
|
+
Each resolves to a token; a value that is not a step is dropped and the
|
|
33
|
+
heading keeps its own styling.
|
|
34
|
+
|
|
35
|
+
- **fontWeight** — \`light\`, \`normal\`, \`medium\`, \`semibold\`, \`bold\`, each
|
|
36
|
+
\`var(--typography-fontWeight-*)\`. The scale stops at \`bold\`; numbers are
|
|
37
|
+
not accepted.
|
|
38
|
+
- **color** — a text role, not a CSS colour: \`primary\` (the brand text role,
|
|
39
|
+
\`--color-text-brand\`), \`strong\` (\`--color-text-primary\`), \`muted\`,
|
|
40
|
+
\`subtle\`, \`inverse\`, \`success\`, \`warning\`, \`error\`. Hex, \`rgb()\`,
|
|
41
|
+
\`var()\` and colour keywords are all dropped.
|
|
42
|
+
- **padding** — one to four spacing steps, e.g. \`24 40\`, each
|
|
43
|
+
\`var(--spacing-N)\`; if any part is off the scale the whole value is dropped.
|
|
44
|
+
**paddingBottom** — one spacing step.
|
|
45
|
+
|
|
46
|
+
The resolved values are applied as an inline style on the rendered heading.
|
|
30
47
|
`
|
|
31
48
|
}
|
|
32
49
|
}
|
|
@@ -35,12 +52,18 @@ export default {
|
|
|
35
52
|
as: {
|
|
36
53
|
control : { type: 'select' },
|
|
37
54
|
options : ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'],
|
|
38
|
-
description: 'Which heading
|
|
55
|
+
description: 'Which heading element to render — the document outline. Falls back to `h2` if it is not a heading level.',
|
|
39
56
|
table : { type: { summary: "'h1'|'h2'|'h3'|'h4'|'h5'|'h6'" }, defaultValue: { summary: 'h1' }, category: 'Semantic' }
|
|
40
57
|
},
|
|
58
|
+
visualLevel: {
|
|
59
|
+
control : { type: 'select' },
|
|
60
|
+
options : ['', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'],
|
|
61
|
+
description: 'Which step of the heading type scale to render. Follows `as` when empty.',
|
|
62
|
+
table : { type: { summary: "'h1'…'h6' | '1'…'6'" }, defaultValue: { summary: 'as' }, category: 'Semantic' }
|
|
63
|
+
},
|
|
41
64
|
ariaLevel: {
|
|
42
65
|
control : 'number',
|
|
43
|
-
description: 'Overrides the
|
|
66
|
+
description: 'Overrides the element\'s ARIA level. Rarely right: to make a heading look a different size, use `visual-level`; to change its place in the outline, change `as`.',
|
|
44
67
|
table : { type: { summary: 'number' }, defaultValue: { summary: 'null' }, category: 'Accessibility' }
|
|
45
68
|
},
|
|
46
69
|
padding: {
|
|
@@ -73,6 +96,7 @@ export default {
|
|
|
73
96
|
const render = (args) => html`
|
|
74
97
|
<bd-h
|
|
75
98
|
as="${args.as}"
|
|
99
|
+
visual-level="${args.visualLevel}"
|
|
76
100
|
padding="${args.padding}"
|
|
77
101
|
paddingBottom="${args.paddingBottom}"
|
|
78
102
|
fontWeight="${args.fontWeight}"
|
|
@@ -82,7 +106,7 @@ const render = (args) => html`
|
|
|
82
106
|
</bd-h>
|
|
83
107
|
`;
|
|
84
108
|
|
|
85
|
-
const defaultArgs = { as: 'h2', padding: '', paddingBottom: '', fontWeight: '', color: '' };
|
|
109
|
+
const defaultArgs = { as: 'h2', visualLevel: '', padding: '', paddingBottom: '', fontWeight: '', color: '' };
|
|
86
110
|
|
|
87
111
|
// ─── Stories ───────────────────────────────────────────────────────────────
|
|
88
112
|
|
|
@@ -111,13 +135,13 @@ export const CustomColor = {
|
|
|
111
135
|
name : 'Custom Color',
|
|
112
136
|
render: () => html`
|
|
113
137
|
<div style="display:flex; flex-direction:column; gap:12px;">
|
|
114
|
-
<bd-h as="h2" color="primary">primary —
|
|
115
|
-
<bd-h as="h2" color="error">error —
|
|
116
|
-
<bd-h as="h2" color="success">success —
|
|
117
|
-
<bd-h as="h2" color="strong">strong —
|
|
138
|
+
<bd-h as="h2" color="primary">primary — --color-text-brand</bd-h>
|
|
139
|
+
<bd-h as="h2" color="error">error — --color-text-error</bd-h>
|
|
140
|
+
<bd-h as="h2" color="success">success — --color-text-success</bd-h>
|
|
141
|
+
<bd-h as="h2" color="strong">strong — --color-text-primary</bd-h>
|
|
118
142
|
</div>
|
|
119
143
|
`,
|
|
120
|
-
parameters: { docs: { description: { story: '
|
|
144
|
+
parameters: { docs: { description: { story: '`color` takes a text role and resolves it to that role\'s token. Each label names the role it reads, not today\'s value.' } } }
|
|
121
145
|
};
|
|
122
146
|
|
|
123
147
|
export const FontWeights = {
|
|
@@ -131,7 +155,7 @@ export const FontWeights = {
|
|
|
131
155
|
<bd-h as="h3" fontWeight="bold">bold (700)</bd-h>
|
|
132
156
|
</div>
|
|
133
157
|
`,
|
|
134
|
-
parameters: { docs: { description: { story: '
|
|
158
|
+
parameters: { docs: { description: { story: 'The five weight steps, light (300) to bold (700); the scale has nothing heavier. A value that is not a step is ignored.' } } }
|
|
135
159
|
};
|
|
136
160
|
|
|
137
161
|
export const CustomPadding = {
|
|
@@ -144,37 +168,46 @@ export const CustomPadding = {
|
|
|
144
168
|
<bd-h as="h3" paddingBottom="32">With Padding Bottom Only</bd-h>
|
|
145
169
|
</div>
|
|
146
170
|
`,
|
|
147
|
-
parameters: { docs: { description: { story: '
|
|
171
|
+
parameters: { docs: { description: { story: '`padding` takes one to four spacing steps and `paddingBottom` one, applied inline.' } } }
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
export const VisualLevel = {
|
|
175
|
+
name : 'Level and size apart',
|
|
176
|
+
render: () => html`
|
|
177
|
+
<bd-h as="h2" visual-level="h4">An h2 in the outline, at the h4 size</bd-h>
|
|
178
|
+
<bd-h as="h4" visual-level="h2">An h4 in the outline, at the h2 size</bd-h>
|
|
179
|
+
`,
|
|
180
|
+
parameters: { docs: { description: { story: '`as` sets the element and `visual-level` the type step, so a heading keeps its place in the outline at whatever size the design needs. This is the tool for a level that has to look different — not `aria-level`.' } } }
|
|
148
181
|
};
|
|
149
182
|
|
|
150
183
|
export const AriaLevelOverride = {
|
|
151
184
|
name : 'ARIA Level Override',
|
|
152
185
|
render: () => html`
|
|
153
186
|
<p style="font-size:14px; margin-bottom:8px;">
|
|
154
|
-
|
|
187
|
+
An <code>h4</code> element carrying <code>aria-level="2"</code>.
|
|
155
188
|
</p>
|
|
156
189
|
<bd-h as="h4" aria-level="2" color="primary">
|
|
157
|
-
|
|
190
|
+
An h4 element, announced as level 2
|
|
158
191
|
</bd-h>
|
|
159
192
|
`,
|
|
160
|
-
parameters: { docs: { description: { story: '`ariaLevel` overrides the
|
|
193
|
+
parameters: { docs: { description: { story: '`ariaLevel` overrides the level assistive technology announces without changing the element. The element and the announced level then disagree, so prefer `as` with `visual-level` (see "Level and size apart"); this is for the rare page that cannot change the element.' } } }
|
|
161
194
|
};
|
|
162
195
|
|
|
163
196
|
export const InvalidTag = {
|
|
164
|
-
name : 'Invalid Tag (fallback to
|
|
197
|
+
name : 'Invalid Tag (fallback to h2)',
|
|
165
198
|
render: () => html`
|
|
166
|
-
<bd-h as="div">This falls back to
|
|
199
|
+
<bd-h as="div">This falls back to h2</bd-h>
|
|
167
200
|
`,
|
|
168
|
-
parameters: { docs: { description: { story: '`as="div"` is not a
|
|
201
|
+
parameters: { docs: { description: { story: '`as="div"` is not a heading level, so the component renders an `<h2>` — still a heading, never the page\'s `<h1>`.' } } }
|
|
169
202
|
};
|
|
170
203
|
|
|
171
204
|
export const InvalidColor = {
|
|
172
205
|
name : 'Invalid Color (silently ignored)',
|
|
173
206
|
render: () => html`
|
|
174
207
|
<bd-h as="h3" color="blue">A bare CSS keyword is not a role, so it is dropped and the heading keeps its own colour</bd-h>
|
|
175
|
-
<bd-h as="h3" color="primary">color="primary" resolves to
|
|
208
|
+
<bd-h as="h3" color="primary">color="primary" resolves to --color-text-brand</bd-h>
|
|
176
209
|
`,
|
|
177
|
-
parameters: { docs: { description: { story: '
|
|
210
|
+
parameters: { docs: { description: { story: '`color="blue"` is not a role, so it is dropped and the heading keeps its own colour. Hex values and `var()` are dropped the same way.' } } }
|
|
178
211
|
};
|
|
179
212
|
|
|
180
213
|
export const Combined = {
|
|
@@ -191,5 +224,5 @@ export const Playground = {
|
|
|
191
224
|
name : '🛝 Playground',
|
|
192
225
|
args : { ...defaultArgs },
|
|
193
226
|
render,
|
|
194
|
-
parameters: { docs: { description: { story: 'Interactive playground. Adjust `as`, `color`, `fontWeight`, `padding`, and `paddingBottom` via Controls.' } } }
|
|
227
|
+
parameters: { docs: { description: { story: 'Interactive playground. Adjust `as`, `visual-level`, `color`, `fontWeight`, `padding`, and `paddingBottom` via Controls.' } } }
|
|
195
228
|
};
|