@karmo-co/drift-kit-reference 0.0.0-stage → 23.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +7 -0
- package/README.md +24 -2
- package/bin/drift-kit-ref.js +197 -0
- package/manifest/components/accordion.md +47 -0
- package/manifest/components/action-bar.md +51 -0
- package/manifest/components/alert.md +53 -0
- package/manifest/components/app-shell.md +43 -0
- package/manifest/components/avatar.md +42 -0
- package/manifest/components/body.md +48 -0
- package/manifest/components/box.md +58 -0
- package/manifest/components/breadcrumb.md +59 -0
- package/manifest/components/button.md +46 -0
- package/manifest/components/calendar.md +69 -0
- package/manifest/components/card.md +71 -0
- package/manifest/components/checkbox.md +43 -0
- package/manifest/components/choice-card.md +61 -0
- package/manifest/components/code-input.md +56 -0
- package/manifest/components/date-picker.md +66 -0
- package/manifest/components/day-of-month.md +54 -0
- package/manifest/components/detail-list.md +37 -0
- package/manifest/components/divider.md +33 -0
- package/manifest/components/dropdown.md +52 -0
- package/manifest/components/dropzone.md +56 -0
- package/manifest/components/eyebrow.md +45 -0
- package/manifest/components/feedback-badge.md +35 -0
- package/manifest/components/glider.md +34 -0
- package/manifest/components/heading.md +47 -0
- package/manifest/components/hero.md +46 -0
- package/manifest/components/icon-button.md +41 -0
- package/manifest/components/icon-tile.md +44 -0
- package/manifest/components/icon.md +30 -0
- package/manifest/components/input.md +63 -0
- package/manifest/components/link.md +42 -0
- package/manifest/components/loading-spinner.md +42 -0
- package/manifest/components/logo.md +38 -0
- package/manifest/components/modal.md +53 -0
- package/manifest/components/pagination.md +54 -0
- package/manifest/components/payment-form.md +29 -0
- package/manifest/components/price-summary.md +30 -0
- package/manifest/components/radio.md +42 -0
- package/manifest/components/section-heading.md +41 -0
- package/manifest/components/select.md +99 -0
- package/manifest/components/shield.md +34 -0
- package/manifest/components/sidebar.md +73 -0
- package/manifest/components/stepper.md +53 -0
- package/manifest/components/switch.md +45 -0
- package/manifest/components/table.md +77 -0
- package/manifest/components/tabs.md +43 -0
- package/manifest/components/tag.md +43 -0
- package/manifest/components/textarea.md +52 -0
- package/manifest/components/toast.md +52 -0
- package/manifest/components/tooltip.md +42 -0
- package/manifest/components/top-bar.md +60 -0
- package/manifest/components/vehicle.md +43 -0
- package/manifest/index.json +1171 -0
- package/manifest/migrations/1.0.0.md +50 -0
- package/manifest/migrations/10.0.0.md +154 -0
- package/manifest/migrations/11.0.0.md +24 -0
- package/manifest/migrations/12.0.0.md +38 -0
- package/manifest/migrations/13.0.0.md +50 -0
- package/manifest/migrations/14.0.0.md +148 -0
- package/manifest/migrations/15.0.0.md +140 -0
- package/manifest/migrations/16.0.0.md +196 -0
- package/manifest/migrations/17.0.0.md +19 -0
- package/manifest/migrations/18.0.0.md +23 -0
- package/manifest/migrations/19.0.0.md +15 -0
- package/manifest/migrations/2.0.0.md +18 -0
- package/manifest/migrations/20.0.0.md +43 -0
- package/manifest/migrations/21.0.0.md +97 -0
- package/manifest/migrations/22.0.0.md +17 -0
- package/manifest/migrations/23.0.0.md +86 -0
- package/manifest/migrations/3.0.0.md +34 -0
- package/manifest/migrations/4.0.0.md +147 -0
- package/manifest/migrations/5.0.0.md +73 -0
- package/manifest/migrations/6.0.0.md +67 -0
- package/manifest/migrations/7.0.0.md +71 -0
- package/manifest/migrations/8.0.0.md +16 -0
- package/manifest/migrations/9.0.0.md +107 -0
- package/manifest/rules.md +37 -0
- package/manifest/setup.md +70 -0
- package/manifest/tokens/border-width.md +12 -0
- package/manifest/tokens/border.md +147 -0
- package/manifest/tokens/font.md +90 -0
- package/manifest/tokens/icon.md +84 -0
- package/manifest/tokens/interactive.md +53 -0
- package/manifest/tokens/layout.md +8 -0
- package/manifest/tokens/motion.md +97 -0
- package/manifest/tokens/radius.md +20 -0
- package/manifest/tokens/shadow.md +28 -0
- package/manifest/tokens/spacing.md +35 -0
- package/manifest/tokens/surface.md +138 -0
- package/manifest/tokens/text.md +92 -0
- package/package.json +20 -4
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
## 16.0.0 - 2026-09-22
|
|
2
|
+
|
|
3
|
+
- **Select's keyboard exists.** Until now the arrow keys opened the menu and
|
|
4
|
+
then nothing worked: ArrowUp, ArrowDown, Home and typing all left focus on the
|
|
5
|
+
trigger with no way to reach an option, and Tab walked out of the page while
|
|
6
|
+
the menu stayed open. There was no way to choose a value without a mouse. It
|
|
7
|
+
is the APG's select-only combobox now, with focus staying on the trigger and
|
|
8
|
+
`aria-activedescendant` naming the highlighted option:
|
|
9
|
+
|
|
10
|
+
```text
|
|
11
|
+
ArrowDown / ArrowUp open, or move the active option
|
|
12
|
+
Alt+ArrowDown open without moving
|
|
13
|
+
Home / End first / last
|
|
14
|
+
PageDown / PageUp ten at a time
|
|
15
|
+
Enter / Space choose
|
|
16
|
+
Escape close, keep the value
|
|
17
|
+
Tab close and let the page have it
|
|
18
|
+
any printable key typeahead, 700ms buffer, cycles, skips disabled
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Opening lands on the CURRENT SELECTION, which is where a reader expects to be
|
|
22
|
+
and what a native select does. Hovering moves the active option rather than
|
|
23
|
+
painting a second highlight, so the pointer and the keyboard never light two
|
|
24
|
+
different rows.
|
|
25
|
+
- **Forge's `check:contract` asserts the keyboard**, for any component that
|
|
26
|
+
declares one: open with a key, move with a key, choose with a key, and leave
|
|
27
|
+
the value alone on Escape. The old behaviour passed every gate in the repo
|
|
28
|
+
while being unusable, which is the whole argument for adding it. Verified by
|
|
29
|
+
breaking the arrow keys on purpose and watching it fail.
|
|
30
|
+
- **Its own token collection: 74 where there were 12.** Eight of the twelve
|
|
31
|
+
pointed straight at `--dk-component-input-*` and the stylesheet read Input's
|
|
32
|
+
collection another eleven times, which is component-to-component, a tier the
|
|
33
|
+
kit does not have, and the same fault Input's label was moved off
|
|
34
|
+
`--dk-component-eyebrow-*` for. The rungs are derived from the same primitives
|
|
35
|
+
by the same arithmetic rather than quoted, so both move together, and
|
|
36
|
+
`check:contract`'s field cohort is what asserts a select and a text field
|
|
37
|
+
still paint the same box.
|
|
38
|
+
- **BREAKING: `.dk-component-select--disabled` is gone.** The last BEM-style
|
|
39
|
+
modifier in a kit that uses attributes everywhere else, on a component whose
|
|
40
|
+
trigger is a real `<button>`. It was worse than untidy: it painted a trigger
|
|
41
|
+
without disabling it, so a select that looked locked still opened, still took
|
|
42
|
+
focus and still changed its value. Use the native `disabled` attribute on
|
|
43
|
+
`.dk-component-select__trigger`.
|
|
44
|
+
- **The field grammar it never had**: a label, a caption, an error with
|
|
45
|
+
`aria-invalid`, and the required asterisk, so a select and a text field in one
|
|
46
|
+
form can say the same things.
|
|
47
|
+
- **The menu can carry more**: option groups under real headings (a labelled
|
|
48
|
+
`role="group"`, which the keyboard walks past), a second line on an option, a
|
|
49
|
+
disabled option that is actually skipped, and an empty state for a menu with
|
|
50
|
+
nothing in it.
|
|
51
|
+
- **The floating surface is named for the shape, not the component.**
|
|
52
|
+
`--dk-component-menu-*` replaces `--dk-component-select-menu-radius` and
|
|
53
|
+
`-shadow` and picks up the literals beside them. Forge hand-rolls this box
|
|
54
|
+
five times over (seven `role="menu"` popups across the prototypes, the card
|
|
55
|
+
actions menu, the icon picker), and a Dropdown built later is menu semantics
|
|
56
|
+
on a surface that already exists rather than a second stylesheet that drifts.
|
|
57
|
+
- **On a phone the menu is a bottom sheet, by default and not by request.**
|
|
58
|
+
Below the kit's mobile breakpoint it stops being a dropdown: full width, off
|
|
59
|
+
the bottom edge where the thumb already is, with a scrim, a heading taken from
|
|
60
|
+
the label, 44px rows, and the page behind it locked from scrolling. A dropdown
|
|
61
|
+
anchored to a 100px trigger on a 375px screen is the worst of both, flipping
|
|
62
|
+
when there is no room below and clipping against the viewport.
|
|
63
|
+
|
|
64
|
+
It is not an option because a design system's defaults are what ship: an
|
|
65
|
+
opt-in would mean most call sites never opt in and mobile users keep the
|
|
66
|
+
cramped menu. `data-sheet="never"` is the escape hatch for the one case that
|
|
67
|
+
is genuinely wrong, a select inside something that is already a sheet or a
|
|
68
|
+
full-screen modal, and `data-sheet="always"` forces it at any width, which is
|
|
69
|
+
how the docs demonstrate it on a desktop.
|
|
70
|
+
|
|
71
|
+
THE BREAKPOINT IS DECLARED ONCE, in the stylesheet, and the behaviour reads it
|
|
72
|
+
back as a custom property when the menu opens. A media query cannot switch
|
|
73
|
+
JavaScript on, and a width hardcoded in both languages drifts the first time
|
|
74
|
+
one of them moves.
|
|
75
|
+
- **The sheet leaves the way it arrived.** It slid up and then vanished on the
|
|
76
|
+
frame the selection landed, which reads as the screen glitching rather than as
|
|
77
|
+
a panel closing: the thing you just touched is gone before you have finished
|
|
78
|
+
touching it. It slides out now, faster than it came in, and the scrim fades
|
|
79
|
+
with it, because the scrim is the sheet's own shadow and fading the element
|
|
80
|
+
fades the dim in one gesture. `aria-expanded` flips to false immediately: the
|
|
81
|
+
state a screen reader reads should not wait on an animation.
|
|
82
|
+
- **A sheet on its way out answers "closed" to everything that asks.** Tapping
|
|
83
|
+
the trigger mid-exit was swallowed, because the click handler asked whether
|
|
84
|
+
the menu was open, got true from an attribute that is still set while the
|
|
85
|
+
sheet slides, and called close, which returned since it was already leaving.
|
|
86
|
+
Tapping something and having nothing happen is the worst answer available.
|
|
87
|
+
Now the exit is cancelled and the sheet comes back up rather than a second one
|
|
88
|
+
animating in behind it.
|
|
89
|
+
- **The scrim is a box-shadow, not an element**: a 100vmax spread paints the
|
|
90
|
+
whole viewport in one declaration, so there is no node to create, none to
|
|
91
|
+
leak, and nothing to clean up if a page tears the menu out from under us. It
|
|
92
|
+
is not a hit target and does not need to be, since the outside-click handler
|
|
93
|
+
that closes the menu was already listening on the document.
|
|
94
|
+
- **New: `--dk-surface-backdrop-soft`**, 40% black, beside upstream's 80%.
|
|
95
|
+
A modal is the page for as long as it is open and a backdrop that heavy says
|
|
96
|
+
so; a select's sheet is a menu that happens to be full width, and dimming the
|
|
97
|
+
form behind it to black takes away the context you are choosing for. Declared
|
|
98
|
+
below the upstream fence, in both themes.
|
|
99
|
+
- **The highlight belongs to whichever input moved last.** Hovering an option
|
|
100
|
+
and then moving the pointer out of the menu left the row lit: one rule painted
|
|
101
|
+
both the hovered option and the ACTIVE one, and the active one is the
|
|
102
|
+
keyboard's cursor, which must exist the whole time the menu is open because
|
|
103
|
+
`aria-activedescendant` has to point at something. So the pointer's position
|
|
104
|
+
became permanent. The menu records `data-nav` now: on `pointer` the `:hover`
|
|
105
|
+
paints and stops the moment the pointer leaves, with the active option still
|
|
106
|
+
set silently for the screen reader and the next keypress; on `keyboard` the
|
|
107
|
+
active option paints and `:hover` is suppressed, or arrowing past a row the
|
|
108
|
+
pointer happens to be resting on would light two at once. A menu opened by
|
|
109
|
+
click starts in pointer mode and paints nothing until you are over a row.
|
|
110
|
+
- **The options are inset and rounded**, rather than running edge to edge. The
|
|
111
|
+
menu carries the inset, `--dk-component-menu-padding-inline`, and the option
|
|
112
|
+
carries the rest, and the two are chosen so their SUM is unchanged: nothing
|
|
113
|
+
moves but the highlight, which now stops short of the menu's edge and rounds.
|
|
114
|
+
The radius is concentric rather than chosen by eye, `--dk-border-radius-100`
|
|
115
|
+
against the menu's `-300` across a `spacing-2` inset, so the two curves sit
|
|
116
|
+
parallel; a rounder option looks deliberate on its own and wrong inside the
|
|
117
|
+
menu's own corner. The group headings keep their text on the option text's
|
|
118
|
+
line.
|
|
119
|
+
- **`data-raised` on the trigger**, the same attribute Input carries and the
|
|
120
|
+
same trade: the shadow is the whole of it, since padding belongs to the size
|
|
121
|
+
rung. It stacks with the focus ring rather than replacing it, in both of the
|
|
122
|
+
states that draw one, focused and open, because a select has two where a field
|
|
123
|
+
has one. Readable on the root or on the trigger, so a lone trigger can be
|
|
124
|
+
raised too, and the forced states carry the lift so the docs' state control
|
|
125
|
+
does not show a raised select losing its shadow on hover. React takes
|
|
126
|
+
`raised`.
|
|
127
|
+
- **The type rides with the box: 14 / 16 / 18**, on the same rungs a field
|
|
128
|
+
reads. The trigger was a rung low at small and medium, so a select and a text
|
|
129
|
+
field in one form were the same height and not the same type; the heights
|
|
130
|
+
matching all along is what let it go unseen, since the box arithmetic reads a
|
|
131
|
+
line-height rung and the value reads a text rung, and only one of the two was
|
|
132
|
+
Input's. And an option is the text you are about to get, so it is set at the
|
|
133
|
+
rung the trigger will show it at: options were fixed at 16/24 at every size, a
|
|
134
|
+
rung larger than the trigger's value with four more pixels of leading, so a
|
|
135
|
+
14px control opened a 16px list. The menu is portalled, so `init()` mirrors
|
|
136
|
+
`data-size` onto it the way `dk.scope.adopt` mirrors the token legs.
|
|
137
|
+
- **The menu's width is its own, not the trigger's.** It took the trigger's
|
|
138
|
+
width outright, which reads fine on a 320px field and badly everywhere else:
|
|
139
|
+
Forge's own control rail opens 100px selects, and a 100px menu truncates every
|
|
140
|
+
option in it. A trigger only ever holds one line, so it is a poor guide to how
|
|
141
|
+
much room a list needs. The rule is now a floor, a fit and a cap: never
|
|
142
|
+
narrower than its trigger, as wide as its widest option needs,
|
|
143
|
+
`--dk-component-menu-max-width` (328px) and the viewport behind it.
|
|
144
|
+
- **`dk.position` gains `align`**, and finding that it needed one is the second
|
|
145
|
+
half of the same story. Alignment was inferred from `matchWidth`, which worked
|
|
146
|
+
only because a menu exactly as wide as its trigger centres and start-aligns to
|
|
147
|
+
the same number. The moment the menu could be wider, a 152px menu on a 117px
|
|
148
|
+
trigger sat 17px to the left of it. `align: 'start' | 'center' | 'end'` is
|
|
149
|
+
explicit now and `matchWidth` means only what its name says; Tooltip keeps the
|
|
150
|
+
centred default.
|
|
151
|
+
- **React `Select`**, which did not exist: the docs' TSX pointed at
|
|
152
|
+
`@karmo-co/component-library`, a different product. Uncontrolled by design,
|
|
153
|
+
because the menu is built and mutated by `behaviour.js` in DOM React does not
|
|
154
|
+
own; `defaultValue` seeds it and `onChange` reports it.
|
|
155
|
+
|
|
156
|
+
### Rename map: `migrations/16.0.0-select-keyboard.json`
|
|
157
|
+
|
|
158
|
+
```json
|
|
159
|
+
{
|
|
160
|
+
"version": "16.0.0",
|
|
161
|
+
"summary": "Select gains the keyboard it never had, its own token collection, and the field grammar (label, caption, error, required). The only breaking change is the removal of .dk-component-select--disabled.",
|
|
162
|
+
"breaking": {
|
|
163
|
+
"dk-component-select--disabled": {
|
|
164
|
+
"replacement": "the native disabled attribute on .dk-component-select__trigger",
|
|
165
|
+
"why": "It painted a trigger without disabling it, so a select that looked locked still opened, still took focus and still changed its value. It was also the last BEM-style modifier in a kit that uses attributes everywhere else.",
|
|
166
|
+
"before": "<div class=\"dk-component-select dk-component-select--disabled\">…<button class=\"dk-component-select__trigger\">…",
|
|
167
|
+
"after": "<div class=\"dk-component-select\">…<button class=\"dk-component-select__trigger\" disabled>…",
|
|
168
|
+
"javascript": "root.classList.toggle('dk-component-select--disabled', locked) → root.querySelector('.dk-component-select__trigger').disabled = locked"
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
"rename": {
|
|
172
|
+
"--dk-component-select-menu-radius": "--dk-component-menu-radius",
|
|
173
|
+
"--dk-component-select-menu-shadow": "--dk-component-menu-shadow"
|
|
174
|
+
},
|
|
175
|
+
"unchanged": {
|
|
176
|
+
"markup": "Every class is the same and every existing select keeps working: the root, __trigger, __value, __chevron, __menu and __option all mean what they meant, and the hidden input still carries the value. role=\"combobox\", aria-controls, aria-activedescendant and the option ids are written by init(), so markup from before 16.0.0 gets the keyboard without being touched.",
|
|
177
|
+
"api": "root.__dcSelect keeps setOptions / select / value / open / close, and the bubbling dk-component-select:change CustomEvent keeps its detail { value }. setOptions gained three optional fields per option: description, group and disabled.",
|
|
178
|
+
"sizes": "data-size=\"small|large\" mean what they meant, 38 and 52 against the unmarked 44."
|
|
179
|
+
},
|
|
180
|
+
"added": {
|
|
181
|
+
"keyboard": "Arrows, Home, End, PageUp, PageDown, Enter, Space, Escape, Tab and typeahead. Focus stays on the trigger; aria-activedescendant names the active option.",
|
|
182
|
+
"dk-component-select__label / __caption / __error": "The field grammar. Pair the error with aria-invalid=\"true\" on the trigger and aria-describedby pointing at the message.",
|
|
183
|
+
"dk-component-select__group / __group-label": "Options under a heading. The heading is a labelled role=\"group\", not an option, so the keyboard walks past it.",
|
|
184
|
+
"dk-component-select__option-text / __option-desc": "A second line on an option. The trigger shows the label alone.",
|
|
185
|
+
"dk-component-select__empty": "What a menu with nothing in it says.",
|
|
186
|
+
"--dk-component-menu-*": "The floating surface, named for the shape rather than for this component so a Dropdown can share it.",
|
|
187
|
+
"React Select": "import { Select } from '@karmo-co/drift-kit/react'. Uncontrolled: defaultValue seeds it, onChange reports it.",
|
|
188
|
+
"--dk-component-menu-max-width / --dk-component-menu-viewport-gap": "The menu sizes to its content now, floored by the trigger's width and capped by these two. It used to take the trigger's width outright, so a narrow select opened a narrow menu that truncated every option in it.",
|
|
189
|
+
"dk.position({ align })": "'start' | 'center' | 'end'. Alignment used to be inferred from matchWidth, which only ever agreed with the intent because the menu was exactly as wide as its trigger. Tooltip keeps the centred default.",
|
|
190
|
+
"data-raised": "The trigger lifted onto an elevation, the same attribute Input carries. The shadow is the whole of it: padding and height still come from the size rung. It stacks with the focus ring rather than replacing it.",
|
|
191
|
+
"data-sheet": "auto | always | never. On a phone the menu is a bottom sheet by default: full width, off the bottom edge, with a scrim, a heading, 44px rows and the page locked from scrolling. 'never' opts out for a select inside something that is already a sheet; 'always' forces it at any width. Nothing to change in existing markup: the default is the new behaviour.",
|
|
192
|
+
"--dk-surface-backdrop-soft": "40% black, beside upstream's 80% --dk-surface-backdrop-black. A sheet dims the page without owning it the way a modal does."
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
## 17.0.0 - 2026-09-24
|
|
2
|
+
|
|
3
|
+
### Migration
|
|
4
|
+
|
|
5
|
+
Two renames, both on Modal, and both mechanical:
|
|
6
|
+
|
|
7
|
+
- **`data-size` on `.dk-component-modal__box`: `sm` / `md` / `lg` become
|
|
8
|
+
`small` / `medium` / `large`**, and `xlarge` joins them at 800px. Every other
|
|
9
|
+
component in the kit says small / medium / large; the modal was the one that
|
|
10
|
+
did not. `medium` is still the unmarked default, so a box with no `data-size`
|
|
11
|
+
is unaffected.
|
|
12
|
+
- **`.is-closing` on the overlay becomes `[data-closing]`.** It was the last
|
|
13
|
+
state-carrying class in a kit that spells every other state as an attribute.
|
|
14
|
+
Anything that ADDS the class to play the exit, or reads it to know a dialog is
|
|
15
|
+
leaving, has to move; `dk.component.modal.open()` and `.close()` already do.
|
|
16
|
+
The keyframe names are deliberately unchanged, because an animation's name is
|
|
17
|
+
an identifier consumers observe (`animationName === "dk-component-modal-slide-out"`),
|
|
18
|
+
and renaming it would break that silently.
|
|
19
|
+
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
## 18.0.0 - 2026-10-01
|
|
2
|
+
|
|
3
|
+
### Migration
|
|
4
|
+
|
|
5
|
+
- **Table's loading row uses the Loading spinner.** `.dk-component-table__spinner`
|
|
6
|
+
and its tokens (`spinner-size`, `spinner-track`, `spinner-head`,
|
|
7
|
+
`spinner-duration`) are gone; put a
|
|
8
|
+
`<div class="dk-component-loading-spinner" data-variant="ring" data-size="small" data-animation="enter">`
|
|
9
|
+
with a `__message` in the loading cell. React `loadingLabel` is now a string.
|
|
10
|
+
- **Table's `sort-icon-color-active` token now means pressed**, not sorted; the
|
|
11
|
+
sorted colour is `sort-icon-color-sorted`.
|
|
12
|
+
- **Dropzone's root is a container now.** The `<label>` that was the root
|
|
13
|
+
becomes `label.dk-component-dropzone__area` inside a
|
|
14
|
+
`div.dk-component-dropzone`, which can also hold a field label, a file list,
|
|
15
|
+
a caption and an error. Wrap the old markup in the div and rename the
|
|
16
|
+
label's class. `root.__dcDropzone.reset()` and `refresh()` keep working.
|
|
17
|
+
- **Tag's `data-pill` (React `pill`) is removed.** A tag's corner now always
|
|
18
|
+
comes from the theme's `data-border-radius` (sharp, normal, bubbly), like
|
|
19
|
+
every other component's. Delete the attribute or prop; nothing replaces it.
|
|
20
|
+
Under bubbly a tag is already nearly round, and a filter chip that must look
|
|
21
|
+
different will be its own clickable component. This is breaking, so the next
|
|
22
|
+
release is a major.
|
|
23
|
+
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
## 19.0.0 - 2026-10-06
|
|
2
|
+
|
|
3
|
+
### Migration
|
|
4
|
+
|
|
5
|
+
- **Stepper's `data-labels="beside"` is removed** (React `labels` is now
|
|
6
|
+
`'visible' | 'hidden'`). Each variant puts its labels where they read best:
|
|
7
|
+
beside the number for `data-variant="inline"`, below the indicator for the
|
|
8
|
+
rest. For the inline look, use `data-variant="inline"`; `data-labels="hidden"`
|
|
9
|
+
is unchanged.
|
|
10
|
+
- **Stepper's narrow summary line is removed** (`.dk-component-stepper__summary`
|
|
11
|
+
and `messages.summary`). On a narrow stepper the current step now keeps its
|
|
12
|
+
own words in place instead; see Changes.
|
|
13
|
+
- **Stepper's `--dk-component-stepper-connector-gap` token is removed**:
|
|
14
|
+
connectors now touch the indicators in both orientations.
|
|
15
|
+
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
## 2.0.0 — 2026-09-09
|
|
2
|
+
|
|
3
|
+
- The `dk` vocabulary: every token is `--dk-…`, tier words `primitive` /
|
|
4
|
+
`responsive` / `dynamic` / `component`, classes `.dk-component-{name}`, JS
|
|
5
|
+
namespace `dk.component.{name}.{fn}`. Component-library / Figma name parity
|
|
6
|
+
dropped by decision; `refresh:tokens`, `check:upstream` and the upstream
|
|
7
|
+
parity test removed. Map in `migrations/2.0.0-dk-vocabulary.json`.
|
|
8
|
+
- Components added since 1.0.0: body, heading, hero, eyebrow, icon (with the
|
|
9
|
+
Karmo glyph runtime); surface levels and tone scopes (`data-surface`,
|
|
10
|
+
`data-tone`, `data-tone-fill`); fixed Karmo alias rungs for body, overline and
|
|
11
|
+
input type.
|
|
12
|
+
|
|
13
|
+
### Rename map: `migrations/2.0.0-dk-vocabulary.json`
|
|
14
|
+
|
|
15
|
+
Token names before → after the dk vocabulary. Classes: dc-{x} → dk-component-{x}. JS: window.dc{X} → dk.component.{x}.{fn}.
|
|
16
|
+
|
|
17
|
+
The full map is `migrations/2.0.0-dk-vocabulary.json` in the drift-kit repo.
|
|
18
|
+
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
## 20.0.0 - 2026-10-12
|
|
2
|
+
|
|
3
|
+
### Migration
|
|
4
|
+
|
|
5
|
+
- **Tabs is now the underline tabs; the old Tabs is Segmented control.** The
|
|
6
|
+
sliding pill in a raised frame (`.dk-component-tabs` > `.dk-component-tab`
|
|
7
|
+
+ `.dk-component-tab-indicator`) is `.dk-component-segmented` > `__option`
|
|
8
|
+
+ `__indicator`, with radio semantics (`role="radiogroup"`, `role="radio"`,
|
|
9
|
+
`aria-checked`, written by the runtime); `.active` is no longer read. Its
|
|
10
|
+
tokens are `--dk-component-segmented-*`, its runtime
|
|
11
|
+
`dk.component.segmented`, its event `dk-component-segmented:change`
|
|
12
|
+
`{ value, values, option }`. The full map is
|
|
13
|
+
`migrations/20.0.0-tabs-to-segmented.json`. React `Tabs` keeps its name and
|
|
14
|
+
props but renders the new tabs; use `SegmentedControl` for the old look.
|
|
15
|
+
|
|
16
|
+
### Rename map: `migrations/20.0.0-tabs-to-segmented.json`
|
|
17
|
+
|
|
18
|
+
```json
|
|
19
|
+
{
|
|
20
|
+
"version": "20.0.0",
|
|
21
|
+
"summary": "Tabs is now the underline tabs Forge's design system pages use (with a pill variant); the old Tabs, a sliding pill in a raised frame, is now Segmented control, with radio semantics, and gains multiple selection, icon-only options and full width.",
|
|
22
|
+
"rename": {
|
|
23
|
+
".dk-component-tabs (with .dk-component-tab children)": ".dk-component-segmented",
|
|
24
|
+
".dk-component-tab": ".dk-component-segmented__option",
|
|
25
|
+
".dk-component-tab-indicator": ".dk-component-segmented__indicator",
|
|
26
|
+
".dk-component-tab-ico": ".dk-component-segmented__icon",
|
|
27
|
+
"--dk-component-tabs-*": "--dk-component-segmented-*",
|
|
28
|
+
"dk.component.tabs (the slider)": "dk.component.segmented",
|
|
29
|
+
"dk-component-tabs:change { index, tab }": "dk-component-segmented:change { value, values, option }"
|
|
30
|
+
},
|
|
31
|
+
"semantics": {
|
|
32
|
+
"role=\"tablist\" + role=\"tab\" + aria-selected": "role=\"radiogroup\" + role=\"radio\" + aria-checked (the runtime writes them). A strip that switched panels keeps doing so: aria-controls on an option shows its panel.",
|
|
33
|
+
".active": "no longer read or written; aria-checked is the state"
|
|
34
|
+
},
|
|
35
|
+
"added": {
|
|
36
|
+
".dk-component-tabs": "New: a tablist of .dk-component-tabs__tab (with __icon, __label, __count), an underline by default and data-variant=\"pill\", data-size, data-full-width, data-activation=\"manual\"; the runtime adds the __indicator.",
|
|
37
|
+
".dk-component-segmented[data-multiple]": "Several chosen: a group of toggle buttons with aria-pressed, each pressed option on its own pill.",
|
|
38
|
+
"React": "Tabs keeps its name and props and renders the new tabs (adds variant, count, fullWidth, activation); SegmentedControl is new (options, multiple, value/defaultValue/onChange, size, fullWidth)."
|
|
39
|
+
},
|
|
40
|
+
"migrate": "Replace the classes as in rename. Leave role, aria-selected and tabindex to the runtime, or write role=\"radio\" with aria-checked yourself."
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
## 21.0.0 - 2026-10-14
|
|
2
|
+
|
|
3
|
+
### Migration
|
|
4
|
+
|
|
5
|
+
- **Segmented control is now Glider.** `.dk-component-segmented` and its
|
|
6
|
+
parts are `.dk-component-glider`, `__option`, `__indicator`, `__icon` and
|
|
7
|
+
`__label`; its tokens are `--dk-component-glider-*`, its runtime
|
|
8
|
+
`dk.component.glider`, its event `dk-component-glider:change`, and React's
|
|
9
|
+
`SegmentedControl` is `Glider` (types `GliderProps`, `GliderOption` and so
|
|
10
|
+
on). Nothing else changes. The map is
|
|
11
|
+
`migrations/21.0.0-segmented-to-glider.json`.
|
|
12
|
+
- **Tabs has no pill variant.** Tabs is the underline alone:
|
|
13
|
+
`data-variant="pill"` and React's `variant` are gone, with the pill's
|
|
14
|
+
tokens (`--dk-component-tabs-pill-*`, `-count-on-pill-surface`); the
|
|
15
|
+
count's corner is `--dk-component-tabs-count-radius`. A pill look that
|
|
16
|
+
picks a value, one or several, is the Glider's. The map is
|
|
17
|
+
`migrations/21.0.0-tabs-pill-removed.json`.
|
|
18
|
+
- **Motion is a scale and patterns.** Seven durations became four (quick 120,
|
|
19
|
+
brisk 200, moderate 320, slow 480) and five easings four (standard,
|
|
20
|
+
emphasized, exit, spring). Components read patterns: state, press, travel,
|
|
21
|
+
enter, enter-large, exit, exit-large, expand, feedback, loop and cascade.
|
|
22
|
+
The old names are removed; the map is `migrations/21.0.0-motion-scale.json`.
|
|
23
|
+
|
|
24
|
+
### Rename map: `migrations/21.0.0-motion-scale.json`
|
|
25
|
+
|
|
26
|
+
```json
|
|
27
|
+
{
|
|
28
|
+
"version": "21.0.0",
|
|
29
|
+
"summary": "Motion is four durations, four easings and the patterns components read (13 Oct: snappy and fluid). Read a pattern by its job; the scale names below map to the nearest step, but a component should move to a pattern, not to the step.",
|
|
30
|
+
"rename": {
|
|
31
|
+
"--dk-motion-duration-snap": "--dk-motion-duration-quick (or --dk-motion-state-duration)",
|
|
32
|
+
"--dk-motion-duration-fast": "--dk-motion-duration-quick (or --dk-motion-state-duration)",
|
|
33
|
+
"--dk-motion-duration-default": "--dk-motion-duration-brisk (or --dk-motion-press-duration)",
|
|
34
|
+
"--dk-motion-duration-slower": "--dk-motion-duration-slow (or --dk-motion-enter-large-duration)",
|
|
35
|
+
"--dk-motion-duration-slowest": "--dk-motion-duration-slow (or --dk-motion-enter-large-duration)",
|
|
36
|
+
"--dk-motion-easing-in-out": "--dk-motion-easing-standard (or --dk-motion-state-easing)",
|
|
37
|
+
"--dk-motion-easing-out": "--dk-motion-easing-standard (or --dk-motion-state-easing)",
|
|
38
|
+
"--dk-motion-easing-ease": "--dk-motion-easing-standard (or --dk-motion-state-easing)",
|
|
39
|
+
"--dk-primitive-motion-duration-125": "--dk-primitive-motion-duration-120",
|
|
40
|
+
"--dk-primitive-motion-duration-150": "--dk-primitive-motion-duration-120",
|
|
41
|
+
"--dk-primitive-motion-duration-250": "--dk-primitive-motion-duration-200",
|
|
42
|
+
"--dk-primitive-motion-duration-300": "--dk-primitive-motion-duration-320",
|
|
43
|
+
"--dk-primitive-motion-duration-400": "--dk-primitive-motion-duration-480",
|
|
44
|
+
"--dk-primitive-motion-duration-500": "--dk-primitive-motion-duration-480",
|
|
45
|
+
"--dk-primitive-motion-easing-in-out": "--dk-primitive-motion-easing-standard",
|
|
46
|
+
"--dk-primitive-motion-easing-out": "--dk-primitive-motion-easing-standard",
|
|
47
|
+
"--dk-primitive-motion-easing-ease": "--dk-primitive-motion-easing-standard"
|
|
48
|
+
},
|
|
49
|
+
"changed": {
|
|
50
|
+
"--dk-motion-duration-moderate": "250ms -> 320ms",
|
|
51
|
+
"--dk-motion-duration-slow": "300ms -> 480ms",
|
|
52
|
+
"reduced motion": "fades at 120 to 160ms instead of cutting to 0.01ms"
|
|
53
|
+
},
|
|
54
|
+
"migrate": "Inside drift-kit every component already reads patterns."
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Rename map: `migrations/21.0.0-segmented-to-glider.json`
|
|
59
|
+
|
|
60
|
+
```json
|
|
61
|
+
{
|
|
62
|
+
"version": "21.0.0",
|
|
63
|
+
"summary": "Segmented control is renamed Glider (13 Oct). Same markup, tokens, behaviour and semantics under the new name.",
|
|
64
|
+
"rename": {
|
|
65
|
+
".dk-component-segmented": ".dk-component-glider",
|
|
66
|
+
".dk-component-segmented__option": ".dk-component-glider__option",
|
|
67
|
+
".dk-component-segmented__indicator": ".dk-component-glider__indicator",
|
|
68
|
+
".dk-component-segmented__icon": ".dk-component-glider__icon",
|
|
69
|
+
".dk-component-segmented__label": ".dk-component-glider__label",
|
|
70
|
+
"--dk-component-segmented-*": "--dk-component-glider-*",
|
|
71
|
+
"dk.component.segmented": "dk.component.glider",
|
|
72
|
+
"dk-component-segmented:change": "dk-component-glider:change",
|
|
73
|
+
"React SegmentedControl": "React Glider",
|
|
74
|
+
"React SegmentedControlProps, SegmentedSingleProps, SegmentedMultipleProps, SegmentedOption, SegmentedSize": "GliderProps, GliderSingleProps, GliderMultipleProps, GliderOption, GliderSize"
|
|
75
|
+
},
|
|
76
|
+
"migrate": "Replace the names as in rename; nothing else changes."
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Rename map: `migrations/21.0.0-tabs-pill-removed.json`
|
|
81
|
+
|
|
82
|
+
```json
|
|
83
|
+
{
|
|
84
|
+
"version": "21.0.0",
|
|
85
|
+
"summary": "Tabs loses its pill variant (13 Oct): tabs are the underline alone, and a pill look that picks a value is the Glider's.",
|
|
86
|
+
"removed": {
|
|
87
|
+
"[data-variant=\"pill\"] on .dk-component-tabs": "Gone. Tabs that switch panels drop the attribute and become the underline; a pill strip that picks a value becomes a .dk-component-glider (data-multiple for several).",
|
|
88
|
+
"React Tabs variant, TabsVariant": "Gone; drop the prop.",
|
|
89
|
+
"--dk-component-tabs-pill-gap, -pill-height, -pill-small-height, -pill-large-height, -pill-padding-inline, -pill-small-padding-inline, -pill-large-padding-inline, -pill-surface, -pill-selected-color, -pill-surface-hover, -count-on-pill-surface": "Gone with the variant."
|
|
90
|
+
},
|
|
91
|
+
"rename": {
|
|
92
|
+
"--dk-component-tabs-pill-radius": "--dk-component-tabs-count-radius"
|
|
93
|
+
},
|
|
94
|
+
"migrate": ""
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
## 22.0.0 - 2026-10-15
|
|
2
|
+
|
|
3
|
+
### Migration
|
|
4
|
+
|
|
5
|
+
- **Breadcrumb's separator is a slash, drawn as text.**
|
|
6
|
+
`--dk-component-breadcrumb-separator-icon` is gone; the separator's colour
|
|
7
|
+
is still `--dk-component-breadcrumb-separator-color`. Past pages are now
|
|
8
|
+
subtle neutral pills rather than primary links, and an item with a menu
|
|
9
|
+
wraps its link (or current page) and its `__more` in `span.__pill`. The
|
|
10
|
+
old markup still renders; it only lacks the joined pair.
|
|
11
|
+
- **Edged buttons are 2px smaller.** Plain, raised and outline Buttons and
|
|
12
|
+
Icon buttons now take their border out of their padding, so they are the
|
|
13
|
+
size borderless ones are (44px at medium, was 46). A layout that measured
|
|
14
|
+
around the old size moves by that much.
|
|
15
|
+
- **App shell sits on white.** `--dk-component-app-shell-surface` is the page
|
|
16
|
+
surface, not the grey canvas.
|
|
17
|
+
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
## 23.0.0 - 2026-10-17
|
|
2
|
+
|
|
3
|
+
### Breaking
|
|
4
|
+
|
|
5
|
+
- **Box has five sizes** (16 Oct): `xsmall` (12), `small` (18), `medium`
|
|
6
|
+
(24, the default), `large` (32) and `xlarge` (40). Past medium the corner
|
|
7
|
+
and the header grow too (radius 300 and 400, an h5 and an h4 title over a
|
|
8
|
+
body-md description). The tokens moved with the names:
|
|
9
|
+
`--dk-component-box-padding` is now 24, `-small-padding` 18 and
|
|
10
|
+
`-large-padding` 32; `-xsmall-*`, `-xlarge-*`, `-large-radius`,
|
|
11
|
+
`-xlarge-radius` and the large and xlarge title and description tokens are
|
|
12
|
+
new.
|
|
13
|
+
|
|
14
|
+
### Migration
|
|
15
|
+
|
|
16
|
+
| Was | Now, for the same box |
|
|
17
|
+
|---|---|
|
|
18
|
+
| `data-size="small"` (`size="small"`) | `data-size="xsmall"` |
|
|
19
|
+
| no `data-size` (`size="medium"`) | `data-size="small"` |
|
|
20
|
+
| `data-size="large"` (`size="large"`) | no `data-size` (`size="medium"`) |
|
|
21
|
+
|
|
22
|
+
An override of `--dk-component-box-padding`, `-small-padding` or
|
|
23
|
+
`-large-padding` moves the same way.
|
|
24
|
+
|
|
25
|
+
### Behaviour changes
|
|
26
|
+
|
|
27
|
+
- **Glider labels a rung smaller, with more air** (17 Oct): small, medium and
|
|
28
|
+
large labels are 10, 12 and 14px (were 12, 14, 16), the padding beside them
|
|
29
|
+
12, 16 and 20px (were 8, 12, 16), and the icons 12, 14 and 16px (were 14,
|
|
30
|
+
16, 20). Heights are unchanged (36, 44, 52). An option is wider than it was.
|
|
31
|
+
|
|
32
|
+
- **Card is rebuilt** (16 Oct). Same markup (`__media`, `__body`, `__title`,
|
|
33
|
+
`__subtitle` still render), new look and reach:
|
|
34
|
+
- `data-variant` raised (default), outline, flat; `data-size` small,
|
|
35
|
+
medium, large; `data-orientation="horizontal"`; `data-media="inset"`;
|
|
36
|
+
`__media` takes `data-ratio` (16/9 default, 1/1, 4/3, 3/2, 21/9) and
|
|
37
|
+
`data-fit="contain"`; new `__eyebrow`, `__meta`, one full-width `__cta`,
|
|
38
|
+
and `__corner[data-corner="start|end"]` for buttons over the top corners
|
|
39
|
+
(a heart toggle that fills when pressed, a Dropdown menu); with either,
|
|
40
|
+
a `__link` in the title stretches over the card. `data-corners="hover"`
|
|
41
|
+
fades the corners in under the pointer or the focus (a pressed toggle and
|
|
42
|
+
an open menu stay shown; a screen that cannot hover always shows them);
|
|
43
|
+
shown always on a card with no media, they take a row above the words,
|
|
44
|
+
and beside the body both sit on the media.
|
|
45
|
+
- It paints the kit surface, so `data-tone`, `data-tone-fill` and
|
|
46
|
+
`data-surface` drive it, and hover and press climb that surface's
|
|
47
|
+
control rungs. It is no longer a tone boundary: on a solid or tinted
|
|
48
|
+
panel it takes the panel's colours, as Box does. `data-surface="0"`
|
|
49
|
+
gives it the page back.
|
|
50
|
+
- Surface tier: grows on hover, presses on the surface rung. Only a link,
|
|
51
|
+
a button, a tab stop or a stretched card moves; a static card is still.
|
|
52
|
+
- The corner is radius 300 (was a literal 14px), the media is 16/9 (was
|
|
53
|
+
132px), the title body-md (was 15px), and the subtitle wraps to two
|
|
54
|
+
lines (was one) through `--dk-component-card-subtitle-lines`.
|
|
55
|
+
- Selected (`aria-pressed`, `aria-current`) wears the primary edge;
|
|
56
|
+
`aria-disabled` stills it and dims it; the focus ring is an outline.
|
|
57
|
+
- React `Card` gains variant, size, orientation, tone, toneFill, surface,
|
|
58
|
+
eyebrow, meta, cta, cornerStart, cornerEnd, mediaRatio, mediaFit,
|
|
59
|
+
mediaInset, selected and disabled.
|
|
60
|
+
- **Two control press rungs** (16 Oct): text-width controls (Button, Glider,
|
|
61
|
+
Tabs, Breadcrumb and Top bar links, the phone sidebar tabs) press to 0.97;
|
|
62
|
+
small squares take the new compact rung, 0.93 (Icon button, Pagination
|
|
63
|
+
pages, Calendar days, Day of month cells), with `--dk-interactive-press-compact`
|
|
64
|
+
and `data-interactive="compact"`. Accordion headers press on the panel rung
|
|
65
|
+
(0.99): full width, they lurched at the item rung.
|
|
66
|
+
- **The quiet ramp follows the tone.** `--dk-interactive-surface-hover` and
|
|
67
|
+
`-active` and the nested fill read the control fills: the same greys on a
|
|
68
|
+
plain surface, the tone's and the level's next rung on a toned one.
|
|
69
|
+
- **Words step a rung per state** on Breadcrumb, Sidebar, Pagination, Glider,
|
|
70
|
+
Tabs and Top bar links (they rest a step lighter); Dropdown and Select menu
|
|
71
|
+
words stay full strength. On a solid tone the quiet word ramp steps to the
|
|
72
|
+
tone's white.
|
|
73
|
+
- **Presses:** Dropdown and Select options, Sidebar items and the Action bar
|
|
74
|
+
summary press on the item rung; Breadcrumb and Top bar links on the control
|
|
75
|
+
rung, Pagination, Calendar and Day of month cells on the compact rung (see
|
|
76
|
+
above); Choice card grows on hover.
|
|
77
|
+
- **Selected and highlighted:** the Sidebar's current item, Pagination's
|
|
78
|
+
current page and a picked day read the foundation's selected; a checked
|
|
79
|
+
Table row its highlighted tint.
|
|
80
|
+
- **Sidebar** items press a rung past hover and hover with the fill alone.
|
|
81
|
+
- **Accordion** sits on the surface it is on, and **Dropzone** on the control
|
|
82
|
+
fill, so both follow the tone, the level and the fill; Dropzone's hover,
|
|
83
|
+
press, chosen-file and focus states follow a status tone too.
|
|
84
|
+
- **Inset Glider** words go full strength inside a status tone, as inset
|
|
85
|
+
Breadcrumb's do; an inset Select chevron keeps its tone colour on hover.
|
|
86
|
+
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
## 3.0.0 — 2026-09-09
|
|
2
|
+
|
|
3
|
+
### Breaking
|
|
4
|
+
|
|
5
|
+
- `data-theme` values are lowercase: `data-theme="light"` / `data-theme="dark"`,
|
|
6
|
+
matching every other Drift attribute (`data-surface`, `data-tone`,
|
|
7
|
+
`data-density`, `data-border-radius`, `data-border-width`). `Light` / `Dark`
|
|
8
|
+
no longer match any selector. Kit selectors are normalised to the quoted form
|
|
9
|
+
`[data-theme="light"]` (238 selectors; the 3 unquoted `[data-alignment=…]`
|
|
10
|
+
selectors are quoted too). Token values are unchanged — 1,542 tokens across
|
|
11
|
+
six theme/surface/tone scopes compare equal between 2.0.0 with `Light`/`Dark`
|
|
12
|
+
and 3.0.0 with `light`/`dark`.
|
|
13
|
+
- Consumers: set `<body data-theme="light|dark">`, lowercase any stored theme
|
|
14
|
+
preference on read, and update your own `[data-theme=Light|Dark]` selectors
|
|
15
|
+
and `=== 'Light'` / `'Dark'` comparisons. Map in
|
|
16
|
+
`migrations/3.0.0-data-theme.json`.
|
|
17
|
+
|
|
18
|
+
### Rename map: `migrations/3.0.0-data-theme.json`
|
|
19
|
+
|
|
20
|
+
```json
|
|
21
|
+
{
|
|
22
|
+
"version": "3.0.0",
|
|
23
|
+
"date": "2026-09-09",
|
|
24
|
+
"summary": "data-theme values are lowercase like every other Drift attribute. Selectors in the kit are quoted [data-theme=\"light\"] / [data-theme=\"dark\"].",
|
|
25
|
+
"attributes": {
|
|
26
|
+
"data-theme": {
|
|
27
|
+
"Light": "light",
|
|
28
|
+
"Dark": "dark"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"consumers": "Set <body data-theme=\"light|dark\">; lowercase any stored preference on read; update your own [data-theme=Light|Dark] selectors and === 'Light'/'Dark' comparisons. Token values are unchanged (1,542 tokens x 6 scopes compared equal)."
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|