@jasonsdevspace/design-kit 0.1.0-next.3 → 0.1.0-next.4
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 +38 -0
- package/README.md +138 -88
- package/dist/base.css +22 -0
- package/dist/components.css +21 -3
- package/dist/react.d.ts +38 -29
- package/dist/react.js +24 -12
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,44 @@ keeping a copy. Each version from `0.1.0-next.1` on is staged on npm from a tag
|
|
|
7
7
|
workflow, with provenance, goes live when it is promoted, and has a release on GitHub with the
|
|
8
8
|
entry below as its notes.
|
|
9
9
|
|
|
10
|
+
## 0.1.0-next.4
|
|
11
|
+
|
|
12
|
+
The strip's first link says whose work it leads to, and the footer's sentence holds to the measure.
|
|
13
|
+
Every optional prop takes `undefined`, the callout takes the props of a `<div>` and is padded as
|
|
14
|
+
steps and code blocks are, and every empty slot is read alike. The sheets show a pointer on what
|
|
15
|
+
takes a click, mark an invalid field in forced colors and keep a token's name whole in a table.
|
|
16
|
+
|
|
17
|
+
- **Page chrome.** The strip's first link reads "More work by Jason Poteet", where it read "A demo
|
|
18
|
+
by Jason Poteet", which fits a demo and the kit's own site alike and says where the link goes. A
|
|
19
|
+
page that writes the strip by hand changes the link's words itself. The footer's sentence holds
|
|
20
|
+
to the measure, while its rule still runs the page's width.
|
|
21
|
+
- **Types.** Every optional prop of every component takes `undefined`, as if it were left out, so a
|
|
22
|
+
wrapper that passes on a prop it may not have been given compiles under
|
|
23
|
+
`exactOptionalPropertyTypes`. Only the types widen, so nothing that compiled before breaks.
|
|
24
|
+
- **Callouts.** `Callout` takes the props of a `<div>` but `style`, `role`, the `title` attribute,
|
|
25
|
+
inner HTML and the live attributes, among them an `id`, a page's class after the kit's, and a
|
|
26
|
+
`ref` that holds the callout drawn, so a page can link to an error summary, focus it after a form
|
|
27
|
+
fails, or lay it out. Its props are exported as `CalloutProps`. Its message takes its role from
|
|
28
|
+
`live` alone. A tone or a heading level the types refuse, as plain JavaScript can pass, draws news
|
|
29
|
+
and a paragraph. A callout is padded 0.75rem by 1rem, from 0.5rem by 0.75rem, as steps and code
|
|
30
|
+
blocks are.
|
|
31
|
+
- **Empty slots.** Every slot a component leaves out when it is empty reads an empty string, or a
|
|
32
|
+
list or a fragment that holds only false, null or empty strings, as nothing. The strip's switches
|
|
33
|
+
and the footer's additions now read an empty string so too. A field's hint and error and a
|
|
34
|
+
callout's title, message and actions now read such a list or fragment as nothing, where before
|
|
35
|
+
they drew an empty paragraph, which a hint or an error also listed in `aria-describedby`.
|
|
36
|
+
- **Pointers.** `base.css` shows a pointer on a disclosure's summary and on a checkbox or a radio
|
|
37
|
+
with the label that holds it, and the arrow on one that is unavailable. The label's pointer reads
|
|
38
|
+
`:has()`, so in Firefox 120 only the box or the dot shows it.
|
|
39
|
+
- **Fields.** In forced colors, where an invalid control's red edge is gone, it gains a dashed line
|
|
40
|
+
around its edge in the system's text color, `CanvasText`, which moves nothing as the error comes
|
|
41
|
+
and goes. With focus, the ring takes its place.
|
|
42
|
+
- **Tables.** Code in a table cell within its frame stays on one line, so a token's name never
|
|
43
|
+
breaks after `--dk-`, and a long expression scrolls the frame as a wide table does.
|
|
44
|
+
- **Docs.** The README shows the strip's switch as a kit button and an unavailable button with the
|
|
45
|
+
text that says why, says why a field uses `disabled` where a button stays in the tab order, and
|
|
46
|
+
says the strip's and the footer's link props take only URLs the demo's author writes.
|
|
47
|
+
|
|
10
48
|
## 0.1.0-next.3
|
|
11
49
|
|
|
12
50
|
The page chrome holds still and shows where a reader is, the controls a page moves focus to take a
|
package/README.md
CHANGED
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
# @jasonsdevspace/design-kit
|
|
2
2
|
|
|
3
|
-
Design tokens, base styles, a theme script,
|
|
4
|
-
explainer
|
|
5
|
-
in
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
page chrome a demo draws around its page, a skip link, a strip and a footer, and the buttons,
|
|
10
|
-
text fields, selects and callouts on the page itself, as markup a sheet styles and as React
|
|
11
|
-
components that draw it. The explainer that says how a demo works, with its steps, panels, tables
|
|
12
|
-
and code blocks, is markup a page writes by hand, with no component.
|
|
3
|
+
Design tokens, base styles, a theme script, page chrome, buttons, fields and callouts, and an
|
|
4
|
+
explainer layout, for the demos on [jasonsdevspace.com](https://jasonsdevspace.com). The styles are
|
|
5
|
+
plain CSS in cascade layers, so a page's own rules outside a layer win without extra weight. The
|
|
6
|
+
theme script sets the theme a visitor chose on the portfolio before the page paints. The chrome and
|
|
7
|
+
the controls come as markup a sheet styles and as React components that draw it. The explainer is
|
|
8
|
+
markup a page writes by hand.
|
|
13
9
|
|
|
14
10
|
```sh
|
|
15
11
|
npm install @jasonsdevspace/design-kit
|
|
@@ -21,6 +17,10 @@ components. Prereleases are under npm's `next` tag, as `@jasonsdevspace/design-k
|
|
|
21
17
|
changed in each version is in
|
|
22
18
|
[CHANGELOG.md](https://github.com/jasonsdevspace/design-kit/blob/main/packages/design-kit/CHANGELOG.md).
|
|
23
19
|
|
|
20
|
+
A page with cascade layers of its own must name where the kit's layers go, as [Layers and
|
|
21
|
+
palettes](#layers-and-palettes) describes. The cut to motion for a visitor who asks for less holds
|
|
22
|
+
over a page's ordinary styles, as that section explains.
|
|
23
|
+
|
|
24
24
|
A page loads the sheets, tokens first, through a bundler that resolves package exports, such as
|
|
25
25
|
Vite.
|
|
26
26
|
|
|
@@ -52,15 +52,16 @@ export default defineConfig({ plugins: [themeScriptPlugin()] });
|
|
|
52
52
|
`--dk-surface`, `--dk-raised`, `--dk-fg`, `--dk-muted`, `--dk-line`, `--dk-control`,
|
|
53
53
|
`--dk-accent`, `--dk-on-accent`, `--dk-highlight`, `--dk-success`, `--dk-success-bg`,
|
|
54
54
|
`--dk-danger`, `--dk-danger-bg` and the scrim, `--dk-scrim`. Beside them are steps of space, text
|
|
55
|
-
size and corners, a code size in em that follows the text around it,
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
55
|
+
size and corners, a code size in em that follows the text around it, line heights, weights, a sans
|
|
56
|
+
and a mono font stack, the focus ring's width and offset, a field's height and inline padding, the
|
|
57
|
+
size of a touch target, the height of the portfolio's strip, the measure and column widths, a
|
|
58
|
+
shadow and a short duration.
|
|
59
59
|
- `@jasonsdevspace/design-kit/base.css` gives the page its type and colors, one focus ring in the
|
|
60
|
-
accent for every link and control the keyboard reaches, code in running text a step under the
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
accent for every link and control the keyboard reaches, code in running text a step under the text
|
|
61
|
+
around it, the `.dk-visually-hidden` class for a label a screen reader reads and the eye does not,
|
|
62
|
+
a pointer on a disclosure's summary and on a box or a dot with the label that holds it, and less
|
|
63
|
+
motion for a visitor who asks for it. It loads after the tokens, since the tokens' first statement
|
|
64
|
+
declares the kit's layer order.
|
|
64
65
|
- `@jasonsdevspace/design-kit/components.css` styles the page chrome, the skip link, the strip and
|
|
65
66
|
the footer, the buttons, fields and callouts, and the explainer page with its steps, panels,
|
|
66
67
|
tables and code blocks, by the classes in the markup the sections below give. It loads after
|
|
@@ -71,10 +72,11 @@ export default defineConfig({ plugins: [themeScriptPlugin()] });
|
|
|
71
72
|
- `@jasonsdevspace/design-kit/react` exports `SkipLink`, `PortfolioStrip` and `DemoFooter`, which
|
|
72
73
|
draw the chrome's markup, and `Button`, `InputField`, `SelectField` and `Callout`, which draw the
|
|
73
74
|
markup of the buttons, fields and callouts, with the types `ButtonProps`, `InputFieldProps`,
|
|
74
|
-
`SelectFieldProps` and `CalloutTone`. It also exports `PORTFOLIO_URL`,
|
|
75
|
-
`GITHUB_PROFILE_URL`, the portfolio, its list of work and the GitHub
|
|
76
|
-
It has no component for the explainer or the pieces in it, which a
|
|
77
|
-
React `^19.3.0` as an optional peer, which a page that writes the
|
|
75
|
+
`SelectFieldProps`, `CalloutProps` and `CalloutTone`. It also exports `PORTFOLIO_URL`,
|
|
76
|
+
`PORTFOLIO_WORK_URL` and `GITHUB_PROFILE_URL`, the portfolio, its list of work and the GitHub
|
|
77
|
+
profile the chrome links to. It has no component for the explainer or the pieces in it, which a
|
|
78
|
+
page writes by hand. It takes React `^19.3.0` as an optional peer, which a page that writes the
|
|
79
|
+
markup itself never needs.
|
|
78
80
|
- `@jasonsdevspace/design-kit/vite` exports `themeScriptPlugin()`, the Vite plugin that puts the
|
|
79
81
|
script inline in place of `THEME_PLACEHOLDER`, the comment `<!-- theme-script -->`, and
|
|
80
82
|
`themeScriptHash`, the script's hash as a Content Security Policy source. It runs in Node, and
|
|
@@ -102,31 +104,36 @@ The skip link is the keyboard's first stop, out of sight until it takes focus, a
|
|
|
102
104
|
column's inset, where the strip's links start. Following it moves focus to the page's main content,
|
|
103
105
|
which has a tabindex of -1 so it can take focus.
|
|
104
106
|
|
|
107
|
+
<!-- example: skipLink -->
|
|
108
|
+
|
|
105
109
|
```html
|
|
106
110
|
<a class="dk-skip" href="#main-content">Skip to content</a>
|
|
107
111
|
<main id="main-content" tabindex="-1">...</main>
|
|
108
112
|
```
|
|
109
113
|
|
|
110
|
-
A skip link
|
|
114
|
+
A skip link can also sit inside a box of the page's own, such as one at the top of a column of
|
|
111
115
|
filters that leads past them to the results. It waits in its own place as the visually hidden class
|
|
112
116
|
does, one pixel clipped to nothing, so it draws nothing and takes no click wherever the box starts
|
|
113
117
|
on the page. A box that is positioned holds it, and it shows at the top of that box, inset as it is
|
|
114
118
|
at the top of a page, when a keyboard reaches it.
|
|
115
119
|
|
|
116
|
-
The strip holds a navigation landmark named Portfolio, with
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
+
The strip holds a navigation landmark named Portfolio, with links in this order: the author's other
|
|
121
|
+
work, which names who made the demo, how it works and the source. It reads "More work by Jason
|
|
122
|
+
Poteet", which fits a demo and the kit's own site alike. The strip on the demo's explainer marks the
|
|
123
|
+
explainer's link with `aria-current="page"`. A demo with switches of its own puts them in a section
|
|
124
|
+
after the links, named for them, and a demo with none leaves the section out.
|
|
125
|
+
|
|
126
|
+
<!-- example: strip -->
|
|
120
127
|
|
|
121
128
|
```html
|
|
122
129
|
<div class="dk-strip">
|
|
123
130
|
<nav class="dk-strip-links" aria-label="Portfolio">
|
|
124
|
-
<a href="https://jasonsdevspace.com/#work">
|
|
131
|
+
<a href="https://jasonsdevspace.com/#work">More work by Jason Poteet</a>
|
|
125
132
|
<a href="./about/">How it works</a>
|
|
126
133
|
<a href="https://github.com/jasonsdevspace/example">Source on GitHub</a>
|
|
127
134
|
</nav>
|
|
128
135
|
<section class="dk-strip-tools" aria-label="Demo">
|
|
129
|
-
<button type="button">Tour</button>
|
|
136
|
+
<button type="button" class="dk-button">Tour</button>
|
|
130
137
|
</section>
|
|
131
138
|
</div>
|
|
132
139
|
```
|
|
@@ -136,6 +143,8 @@ room so nothing below the strip moves when they come. The section is drawn with
|
|
|
136
143
|
only the room, with no text, so it is no landmark and a screen reader finds nothing in it. When the
|
|
137
144
|
switches come they take the room's place in the same section, and the section takes its name.
|
|
138
145
|
|
|
146
|
+
<!-- example: stripWaiting -->
|
|
147
|
+
|
|
139
148
|
```html
|
|
140
149
|
<section class="dk-strip-tools"><span class="dk-strip-room"></span></section>
|
|
141
150
|
```
|
|
@@ -152,11 +161,14 @@ of their own below them.
|
|
|
152
161
|
The footer is one paragraph that says whose project the demo is, with the author's name linking the
|
|
153
162
|
portfolio, and links how it works, the source, the rest of the work and the license. The footer on
|
|
154
163
|
the demo's explainer marks the link to how it works with `aria-current="page"`, as the strip does.
|
|
155
|
-
Anything the demo adds follows the license, as sentences of its own.
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
164
|
+
Anything the demo adds follows the license, as sentences of its own. The sentence holds to the
|
|
165
|
+
measure, as prose does, while the footer's rule runs the page's width. Every link in the chrome
|
|
166
|
+
keeps its underline, and in the footer's sentence the underline is what tells a link from the text
|
|
167
|
+
around it. The link marked as the current page, in the strip or the footer, stands in the page's
|
|
168
|
+
text at the heading's weight, so it is told from the links around it by more than its color, in
|
|
169
|
+
forced colors too, and still reads as a link.
|
|
170
|
+
|
|
171
|
+
<!-- example: footer -->
|
|
160
172
|
|
|
161
173
|
```html
|
|
162
174
|
<footer class="dk-footer">
|
|
@@ -177,18 +189,14 @@ is another width sets `--dk-column` to it, as it sets a color role.
|
|
|
177
189
|
A page with React draws the same markup from the components.
|
|
178
190
|
|
|
179
191
|
```tsx
|
|
180
|
-
import { DemoFooter, PortfolioStrip, SkipLink } from '@jasonsdevspace/design-kit/react';
|
|
192
|
+
import { Button, DemoFooter, PortfolioStrip, SkipLink } from '@jasonsdevspace/design-kit/react';
|
|
181
193
|
|
|
182
194
|
const source = 'https://github.com/jasonsdevspace/example';
|
|
183
195
|
|
|
184
196
|
export const Page = () => (
|
|
185
197
|
<>
|
|
186
198
|
<SkipLink />
|
|
187
|
-
<PortfolioStrip
|
|
188
|
-
about="./about/"
|
|
189
|
-
source={source}
|
|
190
|
-
tools={<button type="button">Tour</button>}
|
|
191
|
-
/>
|
|
199
|
+
<PortfolioStrip about="./about/" source={source} tools={<Button>Tour</Button>} />
|
|
192
200
|
<main id="main-content" tabIndex={-1}>
|
|
193
201
|
<h1>Example</h1>
|
|
194
202
|
</main>
|
|
@@ -199,18 +207,23 @@ export const Page = () => (
|
|
|
199
207
|
|
|
200
208
|
- `SkipLink` leads to `main-content` unless `target` names another id, and its children replace
|
|
201
209
|
the words "Skip to content".
|
|
202
|
-
- `PortfolioStrip` takes `about`, the demo's explainer, and `source`, its repository. `current`
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
`toolsWaiting` keeps the room of one row of the kit's
|
|
207
|
-
page drawn before the switches can be, in the same
|
|
210
|
+
- `PortfolioStrip` takes `about`, the demo's explainer, and `source`, its repository. `current` set
|
|
211
|
+
to `about` marks the explainer's link as the current page. `tools` holds the demo's own switches,
|
|
212
|
+
and `toolsLabel` names their landmark, `Demo` unless given. With `tools` undefined, null, false,
|
|
213
|
+
an empty string, or a list or a fragment of only those, the strip draws no section for them, so a
|
|
214
|
+
demo can pass `hasTour && <Tour />`. `toolsWaiting` keeps the room of one row of the kit's
|
|
215
|
+
controls while `tools` draws nothing, for a page drawn before the switches can be, in the same
|
|
216
|
+
section unnamed until they come.
|
|
208
217
|
- `DemoFooter` takes `name`, the demo's name as the sentence opens with it, `about` and `source`.
|
|
209
218
|
`current` set to `about` marks the explainer's link as the current page, as on the strip.
|
|
210
219
|
`license` is the link to the license, `LICENSE` on the main branch of `source` unless given, with
|
|
211
|
-
any trailing slash on `source` dropped first. `licenseName` is the license's name in the
|
|
212
|
-
|
|
213
|
-
|
|
220
|
+
any trailing slash on `source` dropped first. `licenseName` is the license's name in the sentence,
|
|
221
|
+
`MIT license` unless given. Its children follow the license, and children that are undefined,
|
|
222
|
+
null, false, an empty string, or a list or a fragment of only those, add nothing.
|
|
223
|
+
|
|
224
|
+
The strip's and the footer's `about`, `source` and `license` go into their links unchecked, so each
|
|
225
|
+
is a URL the demo's author writes, never one taken from the page's address or a visitor's input,
|
|
226
|
+
since one taken from either could send a visitor anywhere.
|
|
214
227
|
|
|
215
228
|
## Buttons, fields and callouts
|
|
216
229
|
|
|
@@ -225,11 +238,16 @@ wins over all of them.
|
|
|
225
238
|
|
|
226
239
|
### Buttons
|
|
227
240
|
|
|
241
|
+
<!-- example: buttons -->
|
|
242
|
+
|
|
228
243
|
```html
|
|
229
244
|
<button type="button" class="dk-button">Filters</button>
|
|
230
245
|
<button type="button" class="dk-button dk-button-accent">Save rule</button>
|
|
231
246
|
<button type="button" class="dk-button" aria-pressed="true">Members only</button>
|
|
232
|
-
<button type="button" class="dk-button" aria-disabled="true">
|
|
247
|
+
<button aria-describedby="previous-why" type="button" class="dk-button" aria-disabled="true">
|
|
248
|
+
Previous
|
|
249
|
+
</button>
|
|
250
|
+
<p id="previous-why" class="dk-field-hint">This is the first page.</p>
|
|
233
251
|
```
|
|
234
252
|
|
|
235
253
|
A button is `type="button"` unless the page gives it another type, so it never submits a form by
|
|
@@ -239,15 +257,16 @@ toggle that stands alone carries `aria-pressed`, `"true"` or `"false"`, and keep
|
|
|
239
257
|
both states, since the attribute already says which it is. An action carries no `aria-pressed` at
|
|
240
258
|
all. A button with `aria-expanded="true"`, whose panel is open, is drawn as a pressed one.
|
|
241
259
|
|
|
242
|
-
|
|
243
|
-
has focus drops it, and leaves the tab order, while an aria-disabled one stays
|
|
244
|
-
can reach it and find out why. The attribute does nothing by itself, so a page
|
|
245
|
-
markup makes its own click handler check `aria-disabled` and return early, calling
|
|
260
|
+
An unavailable button, `off` in React, carries `aria-disabled="true"` rather than `disabled`. A
|
|
261
|
+
disabled button that has focus drops it, and leaves the tab order, while an aria-disabled one stays
|
|
262
|
+
where the keyboard can reach it and find out why. The attribute does nothing by itself, so a page
|
|
263
|
+
that writes this markup makes its own click handler check `aria-disabled` and return early, calling
|
|
246
264
|
`preventDefault()` so a submit button does not submit. The sheet draws a `disabled` button, and one
|
|
247
|
-
inside a disabled fieldset, the same as an off one, and an accent button that is off keeps its
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
265
|
+
inside a disabled fieldset, the same as an off one, and an accent button that is off keeps its class
|
|
266
|
+
and is drawn as any off button is. A page says why a button is off in text beside it, such as a
|
|
267
|
+
`.dk-field-hint` paragraph the button's `aria-describedby` names, a class that works beside any
|
|
268
|
+
control. A form's submit button is better left on while the form is incomplete, so a press shows the
|
|
269
|
+
errors rather than doing nothing.
|
|
251
270
|
|
|
252
271
|
A button is 2.25rem tall, `--dk-field-height`, as tall as a field, and 2.75rem, `--dk-target`, at
|
|
253
272
|
48rem wide or less, and it is never narrower than it is tall. It has no ground of its own, so it
|
|
@@ -262,22 +281,24 @@ off toggle that is on an edge in `Highlight`.
|
|
|
262
281
|
|
|
263
282
|
### Fields
|
|
264
283
|
|
|
284
|
+
<!-- example: fields -->
|
|
285
|
+
|
|
265
286
|
```html
|
|
266
287
|
<div class="dk-field">
|
|
267
288
|
<label class="dk-field-label" for="email">Email</label>
|
|
268
289
|
<p class="dk-field-hint" id="email-hint">Where the receipt goes</p>
|
|
269
290
|
<p class="dk-field-error" id="email-error">
|
|
270
|
-
<span class="dk-visually-hidden">Error: </span>
|
|
291
|
+
<span class="dk-visually-hidden">Error: </span>
|
|
292
|
+
Enter an email address
|
|
271
293
|
</p>
|
|
272
294
|
<input
|
|
273
295
|
class="dk-input"
|
|
274
|
-
id="email"
|
|
275
296
|
type="email"
|
|
297
|
+
id="email"
|
|
276
298
|
aria-describedby="email-hint email-error"
|
|
277
299
|
aria-invalid="true"
|
|
278
300
|
/>
|
|
279
301
|
</div>
|
|
280
|
-
|
|
281
302
|
<div class="dk-field">
|
|
282
303
|
<label class="dk-field-label" for="sort">Sort by</label>
|
|
283
304
|
<select class="dk-select" id="sort">
|
|
@@ -303,24 +324,31 @@ error, best after a submit is tried rather than while someone is typing.
|
|
|
303
324
|
A control is as tall as a button, at both widths, and its text is never under 16px, so a phone does
|
|
304
325
|
not zoom the page when the field is tapped. It sits on the page's own ground, so it reads as a well
|
|
305
326
|
on a card. The hint and the error are in the caption size, the error in the danger color, and an
|
|
306
|
-
invalid control's edge in the danger color too.
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
327
|
+
invalid control's edge in the danger color too. In forced colors, where that color is gone, an
|
|
328
|
+
invalid control gains a dashed line around its edge in `CanvasText`, which moves nothing as the
|
|
329
|
+
error comes and goes, and gives way to the focus ring when it has focus. A disabled control is drawn
|
|
330
|
+
on the surface ground in the muted text. The select is a native `<select>`, its caret drawn as a
|
|
331
|
+
background, and in forced colors the browser's own arrow comes back. A select that shows its options
|
|
332
|
+
as a list, with `multiple` or a `size`, is left as the browser draws it, with no caret. A field a
|
|
333
|
+
visitor cannot change uses `disabled`, unlike a button, which stays in the tab order when it is off.
|
|
334
|
+
The field's hint says why it cannot be changed, and a screen reader reads that hint wherever it
|
|
335
|
+
announces the field. A button's reason sits beside it in text its `aria-describedby` names, read
|
|
336
|
+
when the keyboard reaches the button.
|
|
310
337
|
|
|
311
338
|
### Callouts
|
|
312
339
|
|
|
340
|
+
<!-- example: callout -->
|
|
341
|
+
|
|
313
342
|
```html
|
|
314
343
|
<div class="dk-callout dk-callout-danger">
|
|
315
344
|
<div class="dk-callout-body">
|
|
316
345
|
<p class="dk-callout-title">
|
|
317
|
-
<span class="dk-visually-hidden">Error: </span>
|
|
346
|
+
<span class="dk-visually-hidden">Error: </span>
|
|
347
|
+
The products could not be loaded
|
|
318
348
|
</p>
|
|
319
349
|
<p>Check the file, then load it again.</p>
|
|
320
350
|
</div>
|
|
321
|
-
<div class="dk-callout-actions">
|
|
322
|
-
<button type="button" class="dk-button">Try again</button>
|
|
323
|
-
</div>
|
|
351
|
+
<div class="dk-callout-actions"><button type="button" class="dk-button">Try again</button></div>
|
|
324
352
|
</div>
|
|
325
353
|
```
|
|
326
354
|
|
|
@@ -382,6 +410,9 @@ export const Checkout = ({ error, saved }: Props) => {
|
|
|
382
410
|
};
|
|
383
411
|
```
|
|
384
412
|
|
|
413
|
+
Every optional prop of every component takes `undefined` as if it were left out, so a wrapper that
|
|
414
|
+
passes on a prop it may not have been given compiles under `exactOptionalPropertyTypes` too.
|
|
415
|
+
|
|
385
416
|
- `Button` takes the props of a `<button>` but `style`, `disabled`, `aria-disabled` and
|
|
386
417
|
`aria-pressed`, and is `type="button"` unless `type` says otherwise. `accent` adds
|
|
387
418
|
`dk-button-accent`. `pressed` writes `aria-pressed`, `false` included, and left undefined makes
|
|
@@ -392,11 +423,12 @@ export const Checkout = ({ error, saved }: Props) => {
|
|
|
392
423
|
`className` follows the kit's classes. A `ref` holds the `<button>` drawn, for a page that moves
|
|
393
424
|
focus to it, such as the button that opened a panel when the panel closes.
|
|
394
425
|
- `InputField` takes `label`, and `hint` and `error`, each drawn only when given, an empty string,
|
|
395
|
-
null or
|
|
396
|
-
a page gives one when something links to the field, such as
|
|
397
|
-
goes to the `<input>` but `style` and `aria-invalid`. A
|
|
398
|
-
`aria-describedby` of the page's own is listed after the
|
|
399
|
-
`<input>` drawn, for a page that moves focus to the field,
|
|
426
|
+
null, false, or a list or a fragment of only those drawing nothing. `id` is the control's, made
|
|
427
|
+
with React's `useId` unless given, and a page gives one when something links to the field, such as
|
|
428
|
+
an error summary. Every other prop goes to the `<input>` but `style` and `aria-invalid`. A
|
|
429
|
+
`className` follows `dk-input`, and an `aria-describedby` of the page's own is listed after the
|
|
430
|
+
hint and the error. A `ref` holds the `<input>` drawn, for a page that moves focus to the field,
|
|
431
|
+
such as a form that fails.
|
|
400
432
|
- `SelectField` takes the same props, with its children as the options of the `<select>` and its
|
|
401
433
|
`ref` holding the `<select>`.
|
|
402
434
|
- `Callout` takes `tone`, `info` unless given, `success` or `danger`, and `title`, drawn at the
|
|
@@ -405,8 +437,11 @@ export const Checkout = ({ error, saved }: Props) => {
|
|
|
405
437
|
before the title or the message, the tone's own unless given, and an empty string drops it.
|
|
406
438
|
`live`, `status` or `alert`, puts that role on the message and draws the message even when it is
|
|
407
439
|
empty, and a live callout whose message draws nothing, with no title or actions, draws nothing
|
|
408
|
-
until it is filled.
|
|
409
|
-
|
|
440
|
+
until it is filled. Every other prop goes to the callout's `<div>` but `style`, `role` and the
|
|
441
|
+
`title` attribute, inner HTML and the live attributes, since its message takes its role from
|
|
442
|
+
`live` alone, so a page gives it an `id`, such as one an error summary is linked by, and a
|
|
443
|
+
`className`, which follows the kit's. A `ref` holds the `<div>` drawn, for a page that moves focus
|
|
444
|
+
to it, as to a summary of what went wrong in a form that fails.
|
|
410
445
|
|
|
411
446
|
## Explainer pages
|
|
412
447
|
|
|
@@ -420,6 +455,8 @@ class would outweigh every piece set inside it.
|
|
|
420
455
|
|
|
421
456
|
### The page
|
|
422
457
|
|
|
458
|
+
<!-- example: explainerPage -->
|
|
459
|
+
|
|
423
460
|
```html
|
|
424
461
|
<a class="dk-skip" href="#main-content">Skip to content</a>
|
|
425
462
|
<div class="dk-strip">
|
|
@@ -468,11 +505,13 @@ caption size, its heading in the title size, and its text, with an h3 at the bod
|
|
|
468
505
|
|
|
469
506
|
### Steps
|
|
470
507
|
|
|
508
|
+
<!-- example: steps -->
|
|
509
|
+
|
|
471
510
|
```html
|
|
472
511
|
<ol class="dk-steps" role="list">
|
|
473
512
|
<li>
|
|
474
513
|
<strong class="dk-steps-title">The order names a product.</strong>
|
|
475
|
-
<span class="dk-steps-note">
|
|
514
|
+
<span class="dk-steps-note">It says how many it wants.</span>
|
|
476
515
|
</li>
|
|
477
516
|
<li>
|
|
478
517
|
<span class="dk-steps-arrow" aria-hidden="true">↓</span>
|
|
@@ -492,10 +531,15 @@ drops a list's meaning when its markers are gone.
|
|
|
492
531
|
|
|
493
532
|
### Panels
|
|
494
533
|
|
|
534
|
+
<!-- example: panel -->
|
|
535
|
+
|
|
495
536
|
```html
|
|
496
537
|
<div class="dk-panel">
|
|
497
538
|
<p class="dk-panel-title">How the numbers were taken</p>
|
|
498
|
-
<p
|
|
539
|
+
<p>
|
|
540
|
+
Each strategy ran three times on the same laptop with the same orders, and the table shows
|
|
541
|
+
the middle run.
|
|
542
|
+
</p>
|
|
499
543
|
</div>
|
|
500
544
|
```
|
|
501
545
|
|
|
@@ -514,6 +558,8 @@ complementary to it. Nor is it a named `section`, which would be a region landma
|
|
|
514
558
|
|
|
515
559
|
### Tables
|
|
516
560
|
|
|
561
|
+
<!-- example: table -->
|
|
562
|
+
|
|
517
563
|
```html
|
|
518
564
|
<p class="dk-table-title" id="bench-title">Orders a second under each strategy</p>
|
|
519
565
|
<div class="dk-table-scroll" role="region" aria-labelledby="bench-title" tabindex="0">
|
|
@@ -552,13 +598,16 @@ WebKit. It scrolls in its frame instead.
|
|
|
552
598
|
|
|
553
599
|
A table is in the small text, with each row ruled in the line color. Its column headers are the
|
|
554
600
|
muted text at medium weight, and a row's header is at medium weight too. The outer cells keep no
|
|
555
|
-
padding on their outer side, so the table's text lines up with the prose around it. Figures sit
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
601
|
+
padding on their outer side, so the table's text lines up with the prose around it. Figures sit to
|
|
602
|
+
the right and never wrap, while text in a cell wraps and keeps its first line level with the rest of
|
|
603
|
+
its row. Code in a cell stays on one line, hyphens and spaces alike, so a token's name never breaks
|
|
604
|
+
after `--dk-`, and a long expression scrolls the frame as a wide table does. A header gives its unit
|
|
605
|
+
in words a screen reader says well, "Slowest 1%, ms" rather than "p99 ms".
|
|
559
606
|
|
|
560
607
|
### Code blocks
|
|
561
608
|
|
|
609
|
+
<!-- example: code -->
|
|
610
|
+
|
|
562
611
|
```html
|
|
563
612
|
<p class="dk-code-title" id="lock-title">The statement that holds the stock</p>
|
|
564
613
|
<pre
|
|
@@ -586,11 +635,11 @@ script, so a block has no copy button.
|
|
|
586
635
|
### Code in running text
|
|
587
636
|
|
|
588
637
|
`base.css` sets code in running text on every page, not only an explainer, at `--dk-text-code`,
|
|
589
|
-
0.9em, so it follows the size of the text around it, a step under it since a mono face looks
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
block's size and keeps its lines whole. It has no ground of
|
|
593
|
-
link's underline.
|
|
638
|
+
0.9em, so it follows the size of the text around it, a step under it since a mono face looks larger
|
|
639
|
+
at the same size. It breaks anywhere it must, with `overflow-wrap: anywhere`, since a long name has
|
|
640
|
+
no spaces to break at and would push a narrow column wider, except in a table cell, as Tables
|
|
641
|
+
describes. Inside a `pre` it takes the block's size and keeps its lines whole. It has no ground of
|
|
642
|
+
its own, and in a link it keeps the link's underline.
|
|
594
643
|
|
|
595
644
|
## Layers and palettes
|
|
596
645
|
|
|
@@ -655,7 +704,8 @@ from the package rather than keeping a copy.
|
|
|
655
704
|
|
|
656
705
|
The colors are written with `light-dark()`, which sets the floor: Chrome and Edge 123, Firefox 120
|
|
657
706
|
and Safari 17.5. An empty live callout draws nothing by `:has()`, which Firefox has from 121, so in
|
|
658
|
-
Firefox 120 it draws its ground until it is filled
|
|
707
|
+
Firefox 120 it draws its ground until it is filled, and a label that holds a checkbox or a radio
|
|
708
|
+
shows no pointer there, though the box or the dot does. The kit's browser tests run in Chromium.
|
|
659
709
|
|
|
660
710
|
## License
|
|
661
711
|
|
package/dist/base.css
CHANGED
|
@@ -114,6 +114,28 @@
|
|
|
114
114
|
accent-color: var(--dk-accent);
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
+
/*
|
|
118
|
+
* What takes a click shows a pointer: a disclosure's summary, and a box or a dot with the label
|
|
119
|
+
* that holds it, which ticks it from anywhere on its words. One that is unavailable keeps the
|
|
120
|
+
* arrow. A label for a text field only moves focus to it, so it keeps the browser's own.
|
|
121
|
+
*/
|
|
122
|
+
:where(
|
|
123
|
+
summary,
|
|
124
|
+
input[type='checkbox'],
|
|
125
|
+
input[type='radio'],
|
|
126
|
+
label:has(> input[type='checkbox'], > input[type='radio'])
|
|
127
|
+
) {
|
|
128
|
+
cursor: pointer;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
:where(
|
|
132
|
+
input[type='checkbox']:disabled,
|
|
133
|
+
input[type='radio']:disabled,
|
|
134
|
+
label:has(> input[type='checkbox']:disabled, > input[type='radio']:disabled)
|
|
135
|
+
) {
|
|
136
|
+
cursor: default;
|
|
137
|
+
}
|
|
138
|
+
|
|
117
139
|
/*
|
|
118
140
|
* Out of sight but still read by a screen reader, as for a label the eye takes from what is
|
|
119
141
|
* around it. It keeps one pixel, since a screen reader may skip an element with no size at all.
|
package/dist/components.css
CHANGED
|
@@ -100,7 +100,9 @@
|
|
|
100
100
|
font-size: var(--dk-text-small);
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
+
/* The footer sentence holds to the measure, as prose does, while its rule runs full width. */
|
|
103
104
|
:where(.dk-footer p) {
|
|
105
|
+
max-width: var(--dk-measure);
|
|
104
106
|
margin: 0;
|
|
105
107
|
}
|
|
106
108
|
|
|
@@ -350,7 +352,7 @@
|
|
|
350
352
|
:where(.dk-callout) {
|
|
351
353
|
display: grid;
|
|
352
354
|
gap: var(--dk-space-2);
|
|
353
|
-
padding: var(--dk-space-
|
|
355
|
+
padding: var(--dk-space-3) var(--dk-space-4);
|
|
354
356
|
border: 1px solid transparent;
|
|
355
357
|
border-radius: var(--dk-radius);
|
|
356
358
|
background: var(--dk-highlight);
|
|
@@ -440,6 +442,22 @@
|
|
|
440
442
|
padding-inline-end: var(--dk-field-inline);
|
|
441
443
|
background-image: none;
|
|
442
444
|
}
|
|
445
|
+
|
|
446
|
+
/*
|
|
447
|
+
* An invalid field loses its red edge to the system's colors, so it gains a dashed line
|
|
448
|
+
* around its edge in the text color, which keeps its contrast in every theme and moves
|
|
449
|
+
* nothing as the error comes and goes. With focus, the ring takes its place, so a
|
|
450
|
+
* keyboard sees where it landed.
|
|
451
|
+
*/
|
|
452
|
+
:where(.dk-input[aria-invalid='true'], .dk-select[aria-invalid='true']) {
|
|
453
|
+
outline: var(--dk-ring-width) dashed CanvasText;
|
|
454
|
+
outline-offset: 1px;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
:where(.dk-input[aria-invalid='true'], .dk-select[aria-invalid='true']):focus-visible {
|
|
458
|
+
outline: var(--dk-ring-width) solid var(--dk-accent);
|
|
459
|
+
outline-offset: var(--dk-ring-offset);
|
|
460
|
+
}
|
|
443
461
|
}
|
|
444
462
|
|
|
445
463
|
/*
|
|
@@ -643,8 +661,8 @@
|
|
|
643
661
|
}
|
|
644
662
|
|
|
645
663
|
/* Code in a cell keeps its name whole, so the frame scrolls rather than the name breaking. */
|
|
646
|
-
:where(.dk-table code) {
|
|
647
|
-
|
|
664
|
+
:where(.dk-table-scroll .dk-table code) {
|
|
665
|
+
white-space: nowrap;
|
|
648
666
|
}
|
|
649
667
|
|
|
650
668
|
/*
|
package/dist/react.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export declare const GITHUB_PROFILE_URL = "https://github.com/jasonsdevspace";
|
|
|
11
11
|
*/
|
|
12
12
|
export declare const SkipLink: ({ target, children, }: {
|
|
13
13
|
/** The id of the element the link leads to. */
|
|
14
|
-
readonly target?: string;
|
|
14
|
+
readonly target?: string | undefined;
|
|
15
15
|
readonly children?: ReactNode;
|
|
16
16
|
}) => import("react").JSX.Element;
|
|
17
17
|
/** A page of the demo's that the chrome links to, and can mark as the one it sits on. */
|
|
@@ -24,22 +24,22 @@ type ChromePage = 'about';
|
|
|
24
24
|
* unnamed and so no landmark.
|
|
25
25
|
*/
|
|
26
26
|
export declare const PortfolioStrip: ({ about, source, current, tools, toolsLabel, toolsWaiting, }: {
|
|
27
|
-
/** The demo's explainer, relative to the page or absolute. */
|
|
27
|
+
/** The demo's explainer, relative to the page or absolute, written by the demo's author. */
|
|
28
28
|
readonly about: string;
|
|
29
|
-
/** The demo's repository. */
|
|
29
|
+
/** The demo's repository, written by the demo's author. */
|
|
30
30
|
readonly source: string;
|
|
31
31
|
/** The link that is the page the strip sits on, marked as the current page. */
|
|
32
|
-
readonly current?: ChromePage;
|
|
32
|
+
readonly current?: ChromePage | undefined;
|
|
33
33
|
/** The demo's own switches, drawn beside the links. */
|
|
34
34
|
readonly tools?: ReactNode;
|
|
35
35
|
/** The name a screen reader gives the switches' landmark. */
|
|
36
|
-
readonly toolsLabel?: string;
|
|
36
|
+
readonly toolsLabel?: string | undefined;
|
|
37
37
|
/**
|
|
38
38
|
* The switches are on their way, so the strip keeps the room of one row of the kit's controls
|
|
39
39
|
* in their section, empty and unnamed, so no landmark, until `tools` holds them and the section
|
|
40
40
|
* takes its name.
|
|
41
41
|
*/
|
|
42
|
-
readonly toolsWaiting?: boolean;
|
|
42
|
+
readonly toolsWaiting?: boolean | undefined;
|
|
43
43
|
}) => import("react").JSX.Element;
|
|
44
44
|
/**
|
|
45
45
|
* The exit after a long scroll: whose project this is, how it works, where the source and the rest
|
|
@@ -48,16 +48,19 @@ export declare const PortfolioStrip: ({ about, source, current, tools, toolsLabe
|
|
|
48
48
|
export declare const DemoFooter: ({ name, about, source, current, license, licenseName, children, }: {
|
|
49
49
|
/** The demo's name, as the footer's sentence opens with it. */
|
|
50
50
|
readonly name: string;
|
|
51
|
-
/** The demo's explainer, relative to the page or absolute. */
|
|
51
|
+
/** The demo's explainer, relative to the page or absolute, written by the demo's author. */
|
|
52
52
|
readonly about: string;
|
|
53
|
-
/** The demo's repository. */
|
|
53
|
+
/** The demo's repository, written by the demo's author. */
|
|
54
54
|
readonly source: string;
|
|
55
55
|
/** The link that is the page the footer sits on, marked as the current page. */
|
|
56
|
-
readonly current?: ChromePage;
|
|
57
|
-
/**
|
|
58
|
-
|
|
56
|
+
readonly current?: ChromePage | undefined;
|
|
57
|
+
/**
|
|
58
|
+
* The demo's license, written by the demo's author, or `LICENSE` on the main branch of `source`
|
|
59
|
+
* unless given.
|
|
60
|
+
*/
|
|
61
|
+
readonly license?: string | undefined;
|
|
59
62
|
/** What the license is called, as the footer's sentence names it. */
|
|
60
|
-
readonly licenseName?: string;
|
|
63
|
+
readonly licenseName?: string | undefined;
|
|
61
64
|
/** What the demo adds after the license, as sentences of its own. */
|
|
62
65
|
readonly children?: ReactNode;
|
|
63
66
|
}) => import("react").JSX.Element;
|
|
@@ -70,14 +73,14 @@ type NativeButton = Omit<ComponentPropsWithRef<'button'>, 'style' | 'disabled' |
|
|
|
70
73
|
*/
|
|
71
74
|
export type ButtonProps = NativeButton & {
|
|
72
75
|
/** Off, but still in the tab order, so a keyboard keeps its place and finds out why. */
|
|
73
|
-
readonly off?: boolean;
|
|
76
|
+
readonly off?: boolean | undefined;
|
|
74
77
|
} & ({
|
|
75
78
|
readonly accent: true;
|
|
76
|
-
readonly pressed?:
|
|
79
|
+
readonly pressed?: undefined;
|
|
77
80
|
} | {
|
|
78
|
-
readonly accent?: false;
|
|
81
|
+
readonly accent?: false | undefined;
|
|
79
82
|
/** Whether the toggle is on. False still marks it a toggle, undefined an action. */
|
|
80
|
-
readonly pressed?: boolean;
|
|
83
|
+
readonly pressed?: boolean | undefined;
|
|
81
84
|
});
|
|
82
85
|
/**
|
|
83
86
|
* A button, of type button unless a page gives another, so it never submits a form by surprise.
|
|
@@ -95,7 +98,7 @@ type FieldParts = {
|
|
|
95
98
|
/** What is wrong with the value, drawn only when given, which marks the control invalid. */
|
|
96
99
|
readonly error?: ReactNode;
|
|
97
100
|
/** The control's id, made with useId unless a page gives one, as it does for a link to it. */
|
|
98
|
-
readonly id?: string;
|
|
101
|
+
readonly id?: string | undefined;
|
|
99
102
|
};
|
|
100
103
|
export type InputFieldProps = FieldParts & Omit<ComponentPropsWithRef<'input'>, 'style' | 'id' | 'aria-invalid' | 'children'>;
|
|
101
104
|
/**
|
|
@@ -113,25 +116,31 @@ export declare const SelectField: (props: SelectFieldProps) => import("react").J
|
|
|
113
116
|
/** What a callout is about: news, something that went right, or something that went wrong. */
|
|
114
117
|
export type CalloutTone = 'info' | 'success' | 'danger';
|
|
115
118
|
/**
|
|
116
|
-
* A
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
* first and fill it after, which is when a screen reader reads it. The role sits on the message,
|
|
120
|
-
* which holds the title and the tone's word too, so they are read with it, and the actions are
|
|
121
|
-
* not.
|
|
119
|
+
* A callout's own props, beside a `<div>`'s own, its `id`, a page's class, drawn after the kit's,
|
|
120
|
+
* and its `ref` among them, which holds the callout drawn for a page that moves focus to it, such
|
|
121
|
+
* as a summary of what went wrong in a form. Its `title` is the callout's, not the attribute.
|
|
122
122
|
*/
|
|
123
|
-
export
|
|
124
|
-
readonly tone?: CalloutTone;
|
|
123
|
+
export type CalloutProps = Omit<ComponentPropsWithRef<'div'>, 'style' | 'role' | 'title' | 'children' | 'dangerouslySetInnerHTML' | 'aria-live' | 'aria-atomic' | 'aria-relevant'> & {
|
|
124
|
+
readonly tone?: CalloutTone | undefined;
|
|
125
125
|
/** What the callout is about, drawn before its message. */
|
|
126
126
|
readonly title?: ReactNode;
|
|
127
127
|
/** The title's heading level, where the callout is a section a visitor would jump to. */
|
|
128
|
-
readonly headingLevel?: 2 | 3 | 4 | 5 | 6;
|
|
128
|
+
readonly headingLevel?: 2 | 3 | 4 | 5 | 6 | undefined;
|
|
129
129
|
/** A message a screen reader reads when it changes, politely or at once. */
|
|
130
|
-
readonly live?:
|
|
130
|
+
readonly live?: 'status' | 'alert' | undefined;
|
|
131
131
|
/** The word read before the title or the message, the tone's own unless given. */
|
|
132
|
-
readonly prefix?: string;
|
|
132
|
+
readonly prefix?: string | undefined;
|
|
133
133
|
/** Buttons or links that act on the message. */
|
|
134
134
|
readonly actions?: ReactNode;
|
|
135
135
|
readonly children?: ReactNode;
|
|
136
|
-
}
|
|
136
|
+
};
|
|
137
|
+
/**
|
|
138
|
+
* A message set apart from the page, with an optional title before it and actions after it. It has
|
|
139
|
+
* no role unless the page gives `live`, whatever its tone, since a message on the page when it
|
|
140
|
+
* loads is read in its place. A live message is drawn even when empty, so the page can mount it
|
|
141
|
+
* first and fill it after, which is when a screen reader reads it. The role sits on the message,
|
|
142
|
+
* which holds the title and the tone's word too, so they are read with it, and the actions are
|
|
143
|
+
* not.
|
|
144
|
+
*/
|
|
145
|
+
export declare const Callout: ({ tone, title, headingLevel, live, prefix, actions, className, children, ...native }: CalloutProps) => import("react").JSX.Element;
|
|
137
146
|
export {};
|
package/dist/react.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { useId } from 'react';
|
|
2
|
+
import { Children, Fragment, isValidElement, useId, } from 'react';
|
|
3
3
|
/*
|
|
4
4
|
* The page chrome a demo draws around its page, a skip link, a strip above the page that says
|
|
5
5
|
* whose demo it is and a footer below it, and the buttons, fields and callouts on the page itself.
|
|
@@ -8,8 +8,15 @@ import { useId } from 'react';
|
|
|
8
8
|
* page whose policy allows styles only from its own origin draws it. A control's state is a
|
|
9
9
|
* native or ARIA attribute, never a class of the kit's.
|
|
10
10
|
*/
|
|
11
|
-
/**
|
|
12
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Whether a slot holds something to draw: a demo's `cond && <X />` leaves false or null, an empty
|
|
13
|
+
* string draws nothing either, and nor does a list or a fragment of only those. Every slot a
|
|
14
|
+
* component leaves out when it is empty is read the same way. A number draws, 0 included, as React
|
|
15
|
+
* draws it, so a page that writes `count && <X />` writes `count > 0 && <X />` instead.
|
|
16
|
+
*/
|
|
17
|
+
const filled = (node) => Children.toArray(node).some((child) => isValidElement(child) && child.type === Fragment
|
|
18
|
+
? filled(child.props.children)
|
|
19
|
+
: child !== '');
|
|
13
20
|
/** Where the portfolio is, and the section of it that lists the work. */
|
|
14
21
|
export const PORTFOLIO_URL = 'https://jasonsdevspace.com/';
|
|
15
22
|
export const PORTFOLIO_WORK_URL = 'https://jasonsdevspace.com/#work';
|
|
@@ -30,14 +37,12 @@ const currentAs = (current, page) => current === page ? 'page' : undefined;
|
|
|
30
37
|
* named for them, and while they are on their way the strip keeps their room in that section,
|
|
31
38
|
* unnamed and so no landmark.
|
|
32
39
|
*/
|
|
33
|
-
export const PortfolioStrip = ({ about, source, current, tools, toolsLabel = 'Demo', toolsWaiting = false, }) => (_jsxs("div", { className: "dk-strip", children: [_jsxs("nav", { className: "dk-strip-links", "aria-label": "Portfolio", children: [_jsx("a", { href: PORTFOLIO_WORK_URL, children: "
|
|
40
|
+
export const PortfolioStrip = ({ about, source, current, tools, toolsLabel = 'Demo', toolsWaiting = false, }) => (_jsxs("div", { className: "dk-strip", children: [_jsxs("nav", { className: "dk-strip-links", "aria-label": "Portfolio", children: [_jsx("a", { href: PORTFOLIO_WORK_URL, children: "More work by Jason Poteet" }), _jsx("a", { href: about, "aria-current": currentAs(current, 'about'), children: "How it works" }), _jsx("a", { href: source, children: "Source on GitHub" })] }), filled(tools) ? (_jsx("section", { className: "dk-strip-tools", "aria-label": toolsLabel, children: tools })) : toolsWaiting ? (_jsx("section", { className: "dk-strip-tools", children: _jsx("span", { className: "dk-strip-room" }) })) : null] }));
|
|
34
41
|
/**
|
|
35
42
|
* The exit after a long scroll: whose project this is, how it works, where the source and the rest
|
|
36
43
|
* of the work are, and the license, with anything the demo adds of its own after them.
|
|
37
44
|
*/
|
|
38
|
-
export const DemoFooter = ({ name, about, source, current, license = `${source.endsWith('/') ? source.slice(0, -1) : source}/blob/main/LICENSE`, licenseName = 'MIT license', children, }) => (_jsx("footer", { className: "dk-footer", children: _jsxs("p", { children: [name, " is a portfolio project by ", _jsx("a", { href: PORTFOLIO_URL, children: "Jason Poteet" }), ".", ' ', _jsx("a", { href: about, "aria-current": currentAs(current, 'about'), children: "How it works" }), ", in plain words. The ", _jsx("a", { href: source, children: "source" }), " and", ' ', _jsx("a", { href: GITHUB_PROFILE_URL, children: "more work" }), " are on GitHub, under the", ' ', _jsx("a", { href: license, children: licenseName }), ".",
|
|
39
|
-
/** Whether a slot holds words to draw, an empty string being none. */
|
|
40
|
-
const filled = (node) => drawn(node) && node !== '';
|
|
45
|
+
export const DemoFooter = ({ name, about, source, current, license = `${source.endsWith('/') ? source.slice(0, -1) : source}/blob/main/LICENSE`, licenseName = 'MIT license', children, }) => (_jsx("footer", { className: "dk-footer", children: _jsxs("p", { children: [name, " is a portfolio project by ", _jsx("a", { href: PORTFOLIO_URL, children: "Jason Poteet" }), ".", ' ', _jsx("a", { href: about, "aria-current": currentAs(current, 'about'), children: "How it works" }), ", in plain words. The ", _jsx("a", { href: source, children: "source" }), " and", ' ', _jsx("a", { href: GITHUB_PROFILE_URL, children: "more work" }), " are on GitHub, under the", ' ', _jsx("a", { href: license, children: licenseName }), ".", filled(children) ? _jsxs(_Fragment, { children: [" ", children] }) : null] }) }));
|
|
41
46
|
/**
|
|
42
47
|
* A list of names an attribute holds, such as the classes a component draws, its own first and a
|
|
43
48
|
* page's after them, or the ids that describe a control, with any left out or empty dropped.
|
|
@@ -112,6 +117,12 @@ const TONE_WORDS = {
|
|
|
112
117
|
};
|
|
113
118
|
/** The element a callout's title is drawn as, at each heading level a page may give it. */
|
|
114
119
|
const HEADINGS = { 2: 'h2', 3: 'h3', 4: 'h4', 5: 'h5', 6: 'h6' };
|
|
120
|
+
/**
|
|
121
|
+
* A tone or a heading level the types refuse, as a page in plain JavaScript can pass, draws news
|
|
122
|
+
* and a paragraph, rather than a class the sheet has no rule for or an element React refuses.
|
|
123
|
+
*/
|
|
124
|
+
const toneOf = (tone) => Object.hasOwn(TONE_WORDS, tone) ? tone : 'info';
|
|
125
|
+
const titleOf = (level) => level !== undefined && Object.hasOwn(HEADINGS, level) ? HEADINGS[level] : 'p';
|
|
115
126
|
/**
|
|
116
127
|
* A message set apart from the page, with an optional title before it and actions after it. It has
|
|
117
128
|
* no role unless the page gives `live`, whatever its tone, since a message on the page when it
|
|
@@ -120,11 +131,12 @@ const HEADINGS = { 2: 'h2', 3: 'h3', 4: 'h4', 5: 'h5', 6: 'h6' };
|
|
|
120
131
|
* which holds the title and the tone's word too, so they are read with it, and the actions are
|
|
121
132
|
* not.
|
|
122
133
|
*/
|
|
123
|
-
export const Callout = ({ tone = 'info', title, headingLevel, live, prefix
|
|
124
|
-
|
|
125
|
-
const
|
|
134
|
+
export const Callout = ({ tone = 'info', title, headingLevel, live, prefix, actions, className, children, ...native }) => {
|
|
135
|
+
const kind = toneOf(tone);
|
|
136
|
+
const spoken = prefix ?? TONE_WORDS[kind];
|
|
137
|
+
const Title = titleOf(headingLevel);
|
|
126
138
|
const titled = filled(title);
|
|
127
139
|
const said = filled(children);
|
|
128
|
-
const word =
|
|
129
|
-
return (_jsxs("div", { className: spaced('dk-callout',
|
|
140
|
+
const word = spoken === '' ? null : _jsx("span", { className: "dk-visually-hidden", children: spoken });
|
|
141
|
+
return (_jsxs("div", { ...native, role: undefined, style: undefined, dangerouslySetInnerHTML: undefined, "aria-live": undefined, "aria-atomic": undefined, "aria-relevant": undefined, className: spaced('dk-callout', kind !== 'info' && `dk-callout-${kind}`, className), children: [titled || said || live !== undefined ? (_jsxs("div", { className: "dk-callout-body", role: live, children: [titled ? (_jsxs(Title, { className: "dk-callout-title", children: [word, title] })) : null, !titled && said ? word : null, said ? children : null] })) : null, filled(actions) ? _jsx("div", { className: "dk-callout-actions", children: actions }) : null] }));
|
|
130
142
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jasonsdevspace/design-kit",
|
|
3
|
-
"version": "0.1.0-next.
|
|
3
|
+
"version": "0.1.0-next.4",
|
|
4
4
|
"description": "Design tokens, base styles, a theme script, page chrome, buttons, fields, callouts and an explainer layout for the demos on jasonsdevspace.com.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"design-tokens",
|