@jasonsdevspace/design-kit 0.1.0-next.2 → 0.1.0-next.3
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 +32 -0
- package/README.md +57 -16
- package/dist/components.css +52 -7
- package/dist/react.d.ts +31 -11
- package/dist/react.js +16 -6
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,38 @@ 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.3
|
|
11
|
+
|
|
12
|
+
The page chrome holds still and shows where a reader is, the controls a page moves focus to take a
|
|
13
|
+
`ref`, and an empty live callout draws nothing. The strip keeps room for a demo's switches before
|
|
14
|
+
they arrive, the footer marks the current page as the strip does, the link to the current page
|
|
15
|
+
stands out by more than its color, and the skip link stays hidden wherever it sits.
|
|
16
|
+
|
|
17
|
+
- **Page chrome.** `PortfolioStrip` takes `toolsWaiting`, which keeps room for one row of the kit's
|
|
18
|
+
controls while a demo's switches are on their way, so nothing below the strip moves when they
|
|
19
|
+
arrive. The section that holds the room is left unnamed, so it is no landmark until the switches
|
|
20
|
+
fill it. A page without React writes `.dk-strip-room` in the unnamed section, and names the
|
|
21
|
+
section when it fills it.
|
|
22
|
+
- **Page chrome.** `DemoFooter` takes `current`, as `PortfolioStrip` does, and set to `about` marks
|
|
23
|
+
the link to how it works as the current page, for the footer on a demo's explainer. In the strip
|
|
24
|
+
or the footer, the link marked as the current page stands in the page's text at the heading's
|
|
25
|
+
weight, so a sighted reader sees that "How it works" leads where they already are. It keeps its
|
|
26
|
+
underline, and the weight holds in forced colors, where the color does not.
|
|
27
|
+
- **Page chrome.** The skip link waits in its own place as the visually hidden class does, one pixel
|
|
28
|
+
clipped to nothing, rather than 10rem above it, so one inside a positioned box lower on the page
|
|
29
|
+
no longer shows over what is above the box or takes its clicks. It shows where it did when a
|
|
30
|
+
keyboard focuses it.
|
|
31
|
+
- **Buttons.** `Button`'s props take a `ref`, which holds the `<button>` drawn, so a page that
|
|
32
|
+
moves focus to a button, such as the one that opened a panel when the panel closes, uses the
|
|
33
|
+
kit's component. React 19 already passed the ref through, and only the type refused it.
|
|
34
|
+
- **Fields.** `InputField` and `SelectField` take a `ref` in the same way, which holds the
|
|
35
|
+
`<input>` or the `<select>` drawn, so a page that moves focus to a field, such as a form that
|
|
36
|
+
fails, uses the kit's component rather than finding the control by its id.
|
|
37
|
+
- **Callouts.** A live callout whose message holds nothing at all, with no actions, draws nothing,
|
|
38
|
+
no ground, padding or edge, and stays in the page so a screen reader reads what fills it. A page
|
|
39
|
+
no longer needs a holder of its own to hide one it mounts before it has words. The rule uses
|
|
40
|
+
`:has()`, which Firefox has from 121, so in Firefox 120 such a callout still draws its ground.
|
|
41
|
+
|
|
10
42
|
## 0.1.0-next.2
|
|
11
43
|
|
|
12
44
|
The page chrome every demo draws, and buttons, text fields, selects and callouts, styled by
|
package/README.md
CHANGED
|
@@ -107,6 +107,12 @@ which has a tabindex of -1 so it can take focus.
|
|
|
107
107
|
<main id="main-content" tabindex="-1">...</main>
|
|
108
108
|
```
|
|
109
109
|
|
|
110
|
+
A skip link may also sit inside a box of the page's own, such as one at the top of a column of
|
|
111
|
+
filters that leads past them to the results. It waits in its own place as the visually hidden class
|
|
112
|
+
does, one pixel clipped to nothing, so it draws nothing and takes no click wherever the box starts
|
|
113
|
+
on the page. A box that is positioned holds it, and it shows at the top of that box, inset as it is
|
|
114
|
+
at the top of a page, when a keyboard reaches it.
|
|
115
|
+
|
|
110
116
|
The strip holds a navigation landmark named Portfolio, with three links in this order: whose demo
|
|
111
117
|
it is, how it works and the source. The strip on the demo's explainer marks the explainer's link
|
|
112
118
|
with `aria-current="page"`. A demo with switches of its own puts them in a section after the links,
|
|
@@ -125,15 +131,32 @@ named for them, and a demo with none leaves the section out.
|
|
|
125
131
|
</div>
|
|
126
132
|
```
|
|
127
133
|
|
|
134
|
+
A demo whose switches are still on their way, drawn by a script that has not run yet, keeps their
|
|
135
|
+
room so nothing below the strip moves when they come. The section is drawn with no name and holds
|
|
136
|
+
only the room, with no text, so it is no landmark and a screen reader finds nothing in it. When the
|
|
137
|
+
switches come they take the room's place in the same section, and the section takes its name.
|
|
138
|
+
|
|
139
|
+
```html
|
|
140
|
+
<section class="dk-strip-tools"><span class="dk-strip-room"></span></section>
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
The room holds the height of one row of the kit's buttons or fields, beside the links on a wide
|
|
144
|
+
screen and on a row of their own on a phone. Switches of another height, or more than fit on one
|
|
145
|
+
row, still move what is below them when they come.
|
|
146
|
+
|
|
128
147
|
On a wide screen the strip is one 2.5rem row over a 1px rule, in the small text, with its links
|
|
129
148
|
1.5rem apart and the switches on the right. At 48rem wide or less the text steps down to the
|
|
130
149
|
caption size, the links close to 1rem apart and wrap where they must, and the switches take a row
|
|
131
150
|
of their own below them.
|
|
132
151
|
|
|
133
152
|
The footer is one paragraph that says whose project the demo is, with the author's name linking the
|
|
134
|
-
portfolio, and links how it works, the source, the rest of the work and the license.
|
|
135
|
-
demo
|
|
136
|
-
|
|
153
|
+
portfolio, and links how it works, the source, the rest of the work and the license. The footer on
|
|
154
|
+
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. Every link in the chrome keeps
|
|
156
|
+
its underline, and in the footer's sentence the underline is what tells a link from the text around
|
|
157
|
+
it. The link marked as the current page, in the strip or the footer, stands in the page's text at
|
|
158
|
+
the heading's weight, so it is told from the links around it by more than its color, in forced
|
|
159
|
+
colors too, and still reads as a link.
|
|
137
160
|
|
|
138
161
|
```html
|
|
139
162
|
<footer class="dk-footer">
|
|
@@ -180,7 +203,10 @@ export const Page = () => (
|
|
|
180
203
|
set to `about` marks the explainer's link as the current page. `tools` holds the demo's own
|
|
181
204
|
switches, and `toolsLabel` names their landmark, `Demo` unless given. With `tools` undefined,
|
|
182
205
|
null or false the strip draws no section for them, so a demo can pass `hasTour && <Tour />`.
|
|
206
|
+
`toolsWaiting` keeps the room of one row of the kit's controls while `tools` draws nothing, for a
|
|
207
|
+
page drawn before the switches can be, in the same section unnamed until they come.
|
|
183
208
|
- `DemoFooter` takes `name`, the demo's name as the sentence opens with it, `about` and `source`.
|
|
209
|
+
`current` set to `about` marks the explainer's link as the current page, as on the strip.
|
|
184
210
|
`license` is the link to the license, `LICENSE` on the main branch of `source` unless given, with
|
|
185
211
|
any trailing slash on `source` dropped first. `licenseName` is the license's name in the
|
|
186
212
|
sentence, `MIT license` unless given. Its children follow the license, and children that are
|
|
@@ -314,8 +340,12 @@ at once, on `.dk-callout-body`, so the title and the tone's word are read with t
|
|
|
314
340
|
actions are not. The page puts a live callout on the page empty first and fills it after, which is
|
|
315
341
|
when a screen reader reads it. A page filling a success or danger callout puts the hidden word in
|
|
316
342
|
with the message, as `<span class="dk-visually-hidden">Success: </span>Saved.`, since the word is
|
|
317
|
-
part of what is read.
|
|
318
|
-
|
|
343
|
+
part of what is read. A live callout whose message holds nothing at all, not even a space, a line
|
|
344
|
+
break or the tone's word, and that has no actions draws nothing, no ground, padding or edge, until
|
|
345
|
+
it is filled, so a page puts it where its words will go with no holder of its own to hide it. It
|
|
346
|
+
still takes its place in the flow, so a margin or a gap around it stays. The rule reads the message
|
|
347
|
+
with `:has()`, which Firefox has from 121, so in Firefox 120 an empty live callout still draws its
|
|
348
|
+
ground.
|
|
319
349
|
|
|
320
350
|
A callout's edge is transparent, and shows only in forced colors, where the ground is dropped and
|
|
321
351
|
the edge marks where the callout starts and ends.
|
|
@@ -359,20 +389,24 @@ export const Checkout = ({ error, saved }: Props) => {
|
|
|
359
389
|
`aria-disabled="true"` and puts a click handler that calls `preventDefault()` in place of
|
|
360
390
|
`onClick`, so the page's click handler does not run and a submit button does not submit. Any
|
|
361
391
|
other handler the page passes, for keys or a pointer, still runs, and checks `off` itself. A
|
|
362
|
-
`className` follows the kit's classes.
|
|
392
|
+
`className` follows the kit's classes. A `ref` holds the `<button>` drawn, for a page that moves
|
|
393
|
+
focus to it, such as the button that opened a panel when the panel closes.
|
|
363
394
|
- `InputField` takes `label`, and `hint` and `error`, each drawn only when given, an empty string,
|
|
364
395
|
null or false drawing nothing. `id` is the control's, made with React's `useId` unless given, and
|
|
365
396
|
a page gives one when something links to the field, such as an error summary. Every other prop
|
|
366
397
|
goes to the `<input>` but `style` and `aria-invalid`. A `className` follows `dk-input`, and an
|
|
367
|
-
`aria-describedby` of the page's own is listed after the hint and the error.
|
|
368
|
-
|
|
398
|
+
`aria-describedby` of the page's own is listed after the hint and the error. A `ref` holds the
|
|
399
|
+
`<input>` drawn, for a page that moves focus to the field, such as a form that fails.
|
|
400
|
+
- `SelectField` takes the same props, with its children as the options of the `<select>` and its
|
|
401
|
+
`ref` holding the `<select>`.
|
|
369
402
|
- `Callout` takes `tone`, `info` unless given, `success` or `danger`, and `title`, drawn at the
|
|
370
|
-
start of the message as a paragraph or, with `headingLevel` from 2 to 6, as that heading, and as
|
|
371
|
-
|
|
372
|
-
|
|
403
|
+
start of the message as a paragraph or, with `headingLevel` from 2 to 6, as that heading, and as a
|
|
404
|
+
paragraph for any other level. `actions` are drawn after the message. `prefix` is the word read
|
|
405
|
+
before the title or the message, the tone's own unless given, and an empty string drops it.
|
|
373
406
|
`live`, `status` or `alert`, puts that role on the message and draws the message even when it is
|
|
374
|
-
empty
|
|
375
|
-
|
|
407
|
+
empty, and a live callout whose message draws nothing, with no title or actions, draws nothing
|
|
408
|
+
until it is filled. A callout takes no other props, so a page that needs a class or an id on one
|
|
409
|
+
puts it on an element around it.
|
|
376
410
|
|
|
377
411
|
## Explainer pages
|
|
378
412
|
|
|
@@ -388,7 +422,11 @@ class would outweigh every piece set inside it.
|
|
|
388
422
|
|
|
389
423
|
```html
|
|
390
424
|
<a class="dk-skip" href="#main-content">Skip to content</a>
|
|
391
|
-
<div class="dk-strip"
|
|
425
|
+
<div class="dk-strip">
|
|
426
|
+
<nav class="dk-strip-links" aria-label="Portfolio">
|
|
427
|
+
... <a href="./" aria-current="page">How it works</a> ...
|
|
428
|
+
</nav>
|
|
429
|
+
</div>
|
|
392
430
|
<header class="dk-explainer-header"><a href="../">Example</a></header>
|
|
393
431
|
<main id="main-content" tabindex="-1" class="dk-explainer">
|
|
394
432
|
<h1>How Example works</h1>
|
|
@@ -402,7 +440,9 @@ class would outweigh every piece set inside it.
|
|
|
402
440
|
<p>...</p>
|
|
403
441
|
</section>
|
|
404
442
|
</main>
|
|
405
|
-
<footer class="dk-footer"
|
|
443
|
+
<footer class="dk-footer">
|
|
444
|
+
<p>... <a href="./" aria-current="page">How it works</a>, in plain words. ...</p>
|
|
445
|
+
</footer>
|
|
406
446
|
```
|
|
407
447
|
|
|
408
448
|
The header holds a link back to the demo, outside `main`, so the skip link passes it and reading
|
|
@@ -614,7 +654,8 @@ from the package rather than keeping a copy.
|
|
|
614
654
|
## Browser support
|
|
615
655
|
|
|
616
656
|
The colors are written with `light-dark()`, which sets the floor: Chrome and Edge 123, Firefox 120
|
|
617
|
-
and Safari 17.5.
|
|
657
|
+
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. The kit's browser tests run in Chromium.
|
|
618
659
|
|
|
619
660
|
## License
|
|
620
661
|
|
package/dist/components.css
CHANGED
|
@@ -12,13 +12,22 @@
|
|
|
12
12
|
@layer dk.components {
|
|
13
13
|
/*
|
|
14
14
|
* Skip to content, out of sight until it takes focus, and then over the strip at the column's
|
|
15
|
-
* inset, where the strip's links start.
|
|
15
|
+
* inset, where the strip's links start. It waits in its own place as the visually hidden
|
|
16
|
+
* class does, one pixel clipped to nothing, rather than moved above it, so one inside a
|
|
17
|
+
* positioned box of the page's own draws nothing, takes no click and covers nothing a screen
|
|
18
|
+
* reader's touch finds wherever the box starts, and shows at the top of that box once a
|
|
19
|
+
* keyboard reaches it.
|
|
16
20
|
*/
|
|
17
21
|
.dk-skip {
|
|
18
22
|
position: absolute;
|
|
19
23
|
left: calc(max(0px, (100% - var(--dk-column)) / 2) + var(--dk-space-4));
|
|
20
|
-
top: -
|
|
21
|
-
|
|
24
|
+
top: var(--dk-space-4);
|
|
25
|
+
width: 1px;
|
|
26
|
+
height: 1px;
|
|
27
|
+
padding: 0;
|
|
28
|
+
overflow: hidden;
|
|
29
|
+
clip-path: inset(50%);
|
|
30
|
+
white-space: nowrap;
|
|
22
31
|
border-radius: var(--dk-radius);
|
|
23
32
|
background: var(--dk-accent);
|
|
24
33
|
color: var(--dk-on-accent);
|
|
@@ -26,7 +35,11 @@
|
|
|
26
35
|
}
|
|
27
36
|
|
|
28
37
|
:where(.dk-skip):focus-visible {
|
|
29
|
-
|
|
38
|
+
width: auto;
|
|
39
|
+
height: auto;
|
|
40
|
+
padding: var(--dk-space-2) var(--dk-space-4);
|
|
41
|
+
overflow: visible;
|
|
42
|
+
clip-path: none;
|
|
30
43
|
}
|
|
31
44
|
|
|
32
45
|
/*
|
|
@@ -93,8 +106,8 @@
|
|
|
93
106
|
|
|
94
107
|
/*
|
|
95
108
|
* The chrome's links step back in the muted text, and come forward to the page's text when
|
|
96
|
-
* pointed at or focused
|
|
97
|
-
* tells a link from the text around it.
|
|
109
|
+
* pointed at or focused, as the link to the current page always is. Every one keeps its
|
|
110
|
+
* underline, which in the footer's sentence is what tells a link from the text around it.
|
|
98
111
|
*/
|
|
99
112
|
:where(.dk-strip-links a, .dk-footer a) {
|
|
100
113
|
color: var(--dk-muted);
|
|
@@ -103,12 +116,23 @@
|
|
|
103
116
|
:where(
|
|
104
117
|
.dk-strip-links a:hover,
|
|
105
118
|
.dk-strip-links a:focus-visible,
|
|
119
|
+
.dk-strip-links a[aria-current='page'],
|
|
106
120
|
.dk-footer a:hover,
|
|
107
|
-
.dk-footer a:focus-visible
|
|
121
|
+
.dk-footer a:focus-visible,
|
|
122
|
+
.dk-footer a[aria-current='page']
|
|
108
123
|
) {
|
|
109
124
|
color: var(--dk-fg);
|
|
110
125
|
}
|
|
111
126
|
|
|
127
|
+
/*
|
|
128
|
+
* The link to the page a reader is on stands in the page's text, as above, and at the heading's
|
|
129
|
+
* weight, so it is told from the links around it by more than its color, which forced colors
|
|
130
|
+
* replace. It keeps its underline, so it still reads as a link.
|
|
131
|
+
*/
|
|
132
|
+
:where(.dk-strip-links a[aria-current='page'], .dk-footer a[aria-current='page']) {
|
|
133
|
+
font-weight: var(--dk-weight-strong);
|
|
134
|
+
}
|
|
135
|
+
|
|
112
136
|
/*
|
|
113
137
|
* On a phone the links are smaller and closer, and wrap to rows of their own where they must,
|
|
114
138
|
* with the switches on a row below them.
|
|
@@ -126,6 +150,15 @@
|
|
|
126
150
|
flex-basis: 100%;
|
|
127
151
|
padding-block: 0 var(--dk-space-2);
|
|
128
152
|
}
|
|
153
|
+
|
|
154
|
+
/*
|
|
155
|
+
* The room of switches on their way stands as tall as one of the kit's controls at the
|
|
156
|
+
* target, which is taller than the strip's row, so their row keeps the height the switches
|
|
157
|
+
* give it. On a wide screen the strip's row is taller than a control, and holds alone.
|
|
158
|
+
*/
|
|
159
|
+
.dk-strip-room {
|
|
160
|
+
min-height: var(--dk-target);
|
|
161
|
+
}
|
|
129
162
|
}
|
|
130
163
|
|
|
131
164
|
/*
|
|
@@ -333,6 +366,18 @@
|
|
|
333
366
|
background: var(--dk-danger-bg);
|
|
334
367
|
}
|
|
335
368
|
|
|
369
|
+
/*
|
|
370
|
+
* A callout whose message holds nothing, not even a space, and that has no actions draws
|
|
371
|
+
* nothing, no ground, padding or edge. That is a live callout a page mounts before it has
|
|
372
|
+
* words, and it stays in the page so a screen reader reads what fills it. It comes after the
|
|
373
|
+
* tones, so it wins over each of them by its order.
|
|
374
|
+
*/
|
|
375
|
+
:where(.dk-callout:has(> .dk-callout-body:empty):not(:has(> .dk-callout-actions))) {
|
|
376
|
+
padding: 0;
|
|
377
|
+
border-width: 0;
|
|
378
|
+
background: none;
|
|
379
|
+
}
|
|
380
|
+
|
|
336
381
|
:where(.dk-callout-title) {
|
|
337
382
|
margin: 0;
|
|
338
383
|
font-size: inherit;
|
package/dist/react.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type ComponentPropsWithRef, type ReactNode } from 'react';
|
|
2
2
|
/** Where the portfolio is, and the section of it that lists the work. */
|
|
3
3
|
export declare const PORTFOLIO_URL = "https://jasonsdevspace.com/";
|
|
4
4
|
export declare const PORTFOLIO_WORK_URL = "https://jasonsdevspace.com/#work";
|
|
@@ -14,35 +14,46 @@ export declare const SkipLink: ({ target, children, }: {
|
|
|
14
14
|
readonly target?: string;
|
|
15
15
|
readonly children?: ReactNode;
|
|
16
16
|
}) => import("react").JSX.Element;
|
|
17
|
+
/** A page of the demo's that the chrome links to, and can mark as the one it sits on. */
|
|
18
|
+
type ChromePage = 'about';
|
|
17
19
|
/**
|
|
18
20
|
* Whose demo this is, above the page, with three links together on the left. It is a navigation
|
|
19
21
|
* landmark in a plain div rather than a header, so the demo's own header stays the page's one
|
|
20
22
|
* banner. A demo with switches of its own puts them beside the links, in a landmark of their own
|
|
21
|
-
* named for them
|
|
23
|
+
* named for them, and while they are on their way the strip keeps their room in that section,
|
|
24
|
+
* unnamed and so no landmark.
|
|
22
25
|
*/
|
|
23
|
-
export declare const PortfolioStrip: ({ about, source, current, tools, toolsLabel, }: {
|
|
26
|
+
export declare const PortfolioStrip: ({ about, source, current, tools, toolsLabel, toolsWaiting, }: {
|
|
24
27
|
/** The demo's explainer, relative to the page or absolute. */
|
|
25
28
|
readonly about: string;
|
|
26
29
|
/** The demo's repository. */
|
|
27
30
|
readonly source: string;
|
|
28
31
|
/** The link that is the page the strip sits on, marked as the current page. */
|
|
29
|
-
readonly current?:
|
|
32
|
+
readonly current?: ChromePage;
|
|
30
33
|
/** The demo's own switches, drawn beside the links. */
|
|
31
34
|
readonly tools?: ReactNode;
|
|
32
35
|
/** The name a screen reader gives the switches' landmark. */
|
|
33
36
|
readonly toolsLabel?: string;
|
|
37
|
+
/**
|
|
38
|
+
* The switches are on their way, so the strip keeps the room of one row of the kit's controls
|
|
39
|
+
* in their section, empty and unnamed, so no landmark, until `tools` holds them and the section
|
|
40
|
+
* takes its name.
|
|
41
|
+
*/
|
|
42
|
+
readonly toolsWaiting?: boolean;
|
|
34
43
|
}) => import("react").JSX.Element;
|
|
35
44
|
/**
|
|
36
45
|
* The exit after a long scroll: whose project this is, how it works, where the source and the rest
|
|
37
46
|
* of the work are, and the license, with anything the demo adds of its own after them.
|
|
38
47
|
*/
|
|
39
|
-
export declare const DemoFooter: ({ name, about, source, license, licenseName, children, }: {
|
|
48
|
+
export declare const DemoFooter: ({ name, about, source, current, license, licenseName, children, }: {
|
|
40
49
|
/** The demo's name, as the footer's sentence opens with it. */
|
|
41
50
|
readonly name: string;
|
|
42
51
|
/** The demo's explainer, relative to the page or absolute. */
|
|
43
52
|
readonly about: string;
|
|
44
53
|
/** The demo's repository. */
|
|
45
54
|
readonly source: string;
|
|
55
|
+
/** The link that is the page the footer sits on, marked as the current page. */
|
|
56
|
+
readonly current?: ChromePage;
|
|
46
57
|
/** The demo's license, the LICENSE file on the repository's main branch unless given. */
|
|
47
58
|
readonly license?: string;
|
|
48
59
|
/** What the license is called, as the footer's sentence names it. */
|
|
@@ -50,10 +61,12 @@ export declare const DemoFooter: ({ name, about, source, license, licenseName, c
|
|
|
50
61
|
/** What the demo adds after the license, as sentences of its own. */
|
|
51
62
|
readonly children?: ReactNode;
|
|
52
63
|
}) => import("react").JSX.Element;
|
|
53
|
-
type NativeButton = Omit<
|
|
64
|
+
type NativeButton = Omit<ComponentPropsWithRef<'button'>, 'style' | 'disabled' | 'aria-disabled' | 'aria-pressed'>;
|
|
54
65
|
/**
|
|
55
66
|
* A button's own props: the accent for the one action a view leads with, `pressed` for a toggle
|
|
56
|
-
* that stands alone, and `off
|
|
67
|
+
* that stands alone, and `off`, beside a `<button>`'s own, its `ref` among them, which holds the
|
|
68
|
+
* button drawn for a page that moves focus to it. The accent is never a toggle, so the two do not
|
|
69
|
+
* go together.
|
|
57
70
|
*/
|
|
58
71
|
export type ButtonProps = NativeButton & {
|
|
59
72
|
/** Off, but still in the tab order, so a keyboard keeps its place and finds out why. */
|
|
@@ -84,11 +97,18 @@ type FieldParts = {
|
|
|
84
97
|
/** The control's id, made with useId unless a page gives one, as it does for a link to it. */
|
|
85
98
|
readonly id?: string;
|
|
86
99
|
};
|
|
87
|
-
export type InputFieldProps = FieldParts & Omit<
|
|
88
|
-
/**
|
|
100
|
+
export type InputFieldProps = FieldParts & Omit<ComponentPropsWithRef<'input'>, 'style' | 'id' | 'aria-invalid' | 'children'>;
|
|
101
|
+
/**
|
|
102
|
+
* A text input with its label, hint and error, the input taking every other prop given, its `ref`
|
|
103
|
+
* among them, which holds the input drawn for a page that moves focus to it.
|
|
104
|
+
*/
|
|
89
105
|
export declare const InputField: (props: InputFieldProps) => import("react").JSX.Element;
|
|
90
|
-
export type SelectFieldProps = FieldParts & Omit<
|
|
91
|
-
/**
|
|
106
|
+
export type SelectFieldProps = FieldParts & Omit<ComponentPropsWithRef<'select'>, 'style' | 'id' | 'aria-invalid'>;
|
|
107
|
+
/**
|
|
108
|
+
* A native select with its label, hint and error, its children the options, and the select taking
|
|
109
|
+
* every other prop given, its `ref` among them, which holds the select drawn for a page that moves
|
|
110
|
+
* focus to it.
|
|
111
|
+
*/
|
|
92
112
|
export declare const SelectField: (props: SelectFieldProps) => import("react").JSX.Element;
|
|
93
113
|
/** What a callout is about: news, something that went right, or something that went wrong. */
|
|
94
114
|
export type CalloutTone = 'info' | 'success' | 'danger';
|
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
|
|
2
|
+
import { 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.
|
|
@@ -21,18 +21,21 @@ export const GITHUB_PROFILE_URL = 'https://github.com/jasonsdevspace';
|
|
|
21
21
|
* content, which has a tabindex of -1 so it can take focus.
|
|
22
22
|
*/
|
|
23
23
|
export const SkipLink = ({ target = 'main-content', children = 'Skip to content', }) => (_jsx("a", { className: "dk-skip", href: `#${target}`, children: children }));
|
|
24
|
+
/** The mark a link to `page` takes when it is the page the chrome sits on, and none otherwise. */
|
|
25
|
+
const currentAs = (current, page) => current === page ? 'page' : undefined;
|
|
24
26
|
/**
|
|
25
27
|
* Whose demo this is, above the page, with three links together on the left. It is a navigation
|
|
26
28
|
* landmark in a plain div rather than a header, so the demo's own header stays the page's one
|
|
27
29
|
* banner. A demo with switches of its own puts them beside the links, in a landmark of their own
|
|
28
|
-
* named for them
|
|
30
|
+
* named for them, and while they are on their way the strip keeps their room in that section,
|
|
31
|
+
* unnamed and so no landmark.
|
|
29
32
|
*/
|
|
30
|
-
export const PortfolioStrip = ({ about, source, current, tools, toolsLabel = 'Demo', }) => (_jsxs("div", { className: "dk-strip", children: [_jsxs("nav", { className: "dk-strip-links", "aria-label": "Portfolio", children: [_jsx("a", { href: PORTFOLIO_WORK_URL, children: "A demo by Jason Poteet" }), _jsx("a", { href: about, "aria-current": current
|
|
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: "A demo by Jason Poteet" }), _jsx("a", { href: about, "aria-current": currentAs(current, 'about'), children: "How it works" }), _jsx("a", { href: source, children: "Source on GitHub" })] }), drawn(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] }));
|
|
31
34
|
/**
|
|
32
35
|
* The exit after a long scroll: whose project this is, how it works, where the source and the rest
|
|
33
36
|
* of the work are, and the license, with anything the demo adds of its own after them.
|
|
34
37
|
*/
|
|
35
|
-
export const DemoFooter = ({ name, about, source, 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, 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 }), ".", drawn(children) ? _jsxs(_Fragment, { children: [" ", children] }) : null] }) }));
|
|
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 }), ".", drawn(children) ? _jsxs(_Fragment, { children: [" ", children] }) : null] }) }));
|
|
36
39
|
/** Whether a slot holds words to draw, an empty string being none. */
|
|
37
40
|
const filled = (node) => drawn(node) && node !== '';
|
|
38
41
|
/**
|
|
@@ -80,14 +83,21 @@ const useField = ({ id, hint, error }, own) => {
|
|
|
80
83
|
* fields at once would speak over itself, so the page says when an error appears.
|
|
81
84
|
*/
|
|
82
85
|
const Field = ({ parts, field, children, }) => (_jsxs("div", { className: "dk-field", children: [_jsx("label", { className: "dk-field-label", htmlFor: field.id, children: parts.label }), field.hint ? (_jsx("p", { className: "dk-field-hint", id: `${field.id}-hint`, children: parts.hint })) : null, field.error ? (_jsxs("p", { className: "dk-field-error", id: `${field.id}-error`, children: [_jsx("span", { className: "dk-visually-hidden", children: "Error: " }), parts.error] })) : null, children] }));
|
|
83
|
-
/**
|
|
86
|
+
/**
|
|
87
|
+
* A text input with its label, hint and error, the input taking every other prop given, its `ref`
|
|
88
|
+
* among them, which holds the input drawn for a page that moves focus to it.
|
|
89
|
+
*/
|
|
84
90
|
export const InputField = (props) => {
|
|
85
91
|
const { label: _label, hint: _hint, error: _error, id: _id, className, ...rest } = props;
|
|
86
92
|
const { 'aria-describedby': own, ...native } = rest;
|
|
87
93
|
const field = useField(props, own);
|
|
88
94
|
return (_jsx(Field, { parts: props, field: field, children: _jsx("input", { className: spaced('dk-input', className), ...native, ...field.wired }) }));
|
|
89
95
|
};
|
|
90
|
-
/**
|
|
96
|
+
/**
|
|
97
|
+
* A native select with its label, hint and error, its children the options, and the select taking
|
|
98
|
+
* every other prop given, its `ref` among them, which holds the select drawn for a page that moves
|
|
99
|
+
* focus to it.
|
|
100
|
+
*/
|
|
91
101
|
export const SelectField = (props) => {
|
|
92
102
|
const { label: _label, hint: _hint, error: _error, id: _id, className, ...rest } = props;
|
|
93
103
|
const { 'aria-describedby': own, children, ...native } = rest;
|
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.3",
|
|
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",
|