@blakesteve/roster 5.9.0 → 5.11.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/README.md +206 -25
- package/dist/components/atoms/Button/button-variants.js +1 -1
- package/dist/components/atoms/Eyebrow/eyebrow-variants.d.ts +2 -2
- package/dist/components/atoms/Input/input-variants.js +1 -1
- package/dist/components/atoms/PasswordInput/PasswordInput.cjs +1 -1
- package/dist/components/atoms/PasswordInput/PasswordInput.js +5 -5
- package/dist/components/atoms/Select/select-variants.cjs +1 -1
- package/dist/components/atoms/Select/select-variants.js +11 -2
- package/dist/components/atoms/Skeleton/Skeleton.cjs +1 -1
- package/dist/components/atoms/Skeleton/Skeleton.d.ts +19 -3
- package/dist/components/atoms/Skeleton/Skeleton.js +38 -33
- package/dist/components/atoms/ThemeToggle/ThemeToggle.d.ts +1 -1
- package/dist/components/molecules/CheckboxGroup/CheckboxGroup.d.ts +1 -1
- package/dist/components/molecules/CheckboxGroup/checkbox-group-variants.d.ts +1 -1
- package/dist/components/molecules/Menu/Menu.cjs +1 -1
- package/dist/components/molecules/Menu/Menu.js +60 -53
- package/dist/components/molecules/RangeSlider/RangeSlider.cjs +1 -0
- package/dist/components/molecules/RangeSlider/RangeSlider.d.ts +74 -0
- package/dist/components/molecules/RangeSlider/RangeSlider.js +164 -0
- package/dist/components/molecules/SkeletonPresets/SkeletonPresets.cjs +1 -1
- package/dist/components/molecules/SkeletonPresets/SkeletonPresets.d.ts +13 -4
- package/dist/components/molecules/SkeletonPresets/SkeletonPresets.js +35 -35
- package/dist/components/molecules/Slider/Slider.cjs +1 -0
- package/dist/components/molecules/Slider/Slider.d.ts +87 -0
- package/dist/components/molecules/Slider/Slider.js +128 -0
- package/dist/components/molecules/Slider/track-parts.cjs +1 -0
- package/dist/components/molecules/Slider/track-parts.d.ts +64 -0
- package/dist/components/molecules/Slider/track-parts.js +121 -0
- package/dist/components/molecules/Slider/track.cjs +1 -0
- package/dist/components/molecules/Slider/track.d.ts +115 -0
- package/dist/components/molecules/Slider/track.js +152 -0
- package/dist/components/molecules/Toast/Toaster.d.ts +1 -1
- package/dist/components/organisms/Navbar/Navbar.cjs +1 -1
- package/dist/components/organisms/Navbar/Navbar.js +37 -37
- package/dist/index.d.ts +2 -0
- package/dist/lib/utils.cjs +1 -1
- package/dist/lib/utils.js +4 -1
- package/dist/meta.json +3 -3
- package/dist/roster.cjs +1 -1
- package/dist/roster.css +1 -1
- package/dist/roster.js +67 -63
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -269,7 +269,7 @@ changes nothing.
|
|
|
269
269
|
| ----- | ------- | -------- |
|
|
270
270
|
| `--roster-radius-sm` | `0.25rem` | `Button` `xs`, `Checkbox` `sm` and `md`, `InlineCode`, `Link`, `MultiSelect`'s chip dismiss, the dismiss and reveal buttons in `Alert`, `Toast` and `PasswordInput` |
|
|
271
271
|
| `--roster-radius-md` | `0.375rem` | `Button`, `Input`, `Textarea`, `Select`, `MultiSelect`, `Badge`, `Avatar` and its popover, `Checkbox` `lg`, `CheckboxGroup`'s panel, `Disclosure`, `Dialog`'s and `Sheet`'s close buttons, `Navbar`'s links and menu, popup panels, `Table` |
|
|
272
|
-
| `--roster-radius-lg` | `0.5rem` | `Alert`, `Toast`, `Tooltip`, `CallToAction`, `EmptyState`, `ErrorState`, `LiquidTabs`, `MatchupCard`, `Navbar`'s mobile panel |
|
|
272
|
+
| `--roster-radius-lg` | `0.5rem` | `Alert`, `Toast`, `Tooltip`, `CallToAction`, `EmptyState`, `ErrorState`, `LiquidTabs`, `MatchupCard`, `Navbar`'s mobile panel, `Skeleton` blocks (unless `--roster-skeleton-radius` is set) |
|
|
273
273
|
| `--roster-radius-xl` | `0.75rem` | `Card`, `LiquidTabs`' `pill` strip |
|
|
274
274
|
| `--roster-radius-2xl` | `1rem` | `Dialog`, the top of `Sheet` |
|
|
275
275
|
| `--roster-radius-3xl` | `1.5rem` | nothing yet; wired for the first component that uses it |
|
|
@@ -543,6 +543,18 @@ The band matters more than it looks. Without it the ring sits directly on the
|
|
|
543
543
|
fill, and a `primary-500` ring on a `primary-600` button is 1.37:1 — which is
|
|
544
544
|
why the ring and its offset are a pair rather than two independent knobs.
|
|
545
545
|
|
|
546
|
+
**A row in a popup marks focus with the ring too.** The focused row in `Menu`,
|
|
547
|
+
`Select`, `Combobox` and `MultiSelect` keeps its soft primary tint and adds a
|
|
548
|
+
2px inset ring in `--roster-ring`. The tint alone was the indicator until
|
|
549
|
+
5.11.0, and it measured 1.17:1 against the panel in light and 1.03:1 in dark
|
|
550
|
+
on Roster's own defaults: a keyboard user couldn't see where they were. The
|
|
551
|
+
ring follows the token you already set against your own surfaces, so it holds
|
|
552
|
+
wherever the panel is: 6.37:1 and 5.61:1 on the defaults. If you set
|
|
553
|
+
`--roster-popover-bg` to a surface `--roster-ring` doesn't contrast with, set
|
|
554
|
+
the ring with it. In `Menu` the ring shows for keyboard focus only, because the
|
|
555
|
+
pointer moves focus there too. Headless UI's popups mark the keys' row and the
|
|
556
|
+
pointer's alike, so in theirs it shows on hover as well.
|
|
557
|
+
|
|
546
558
|
### Motion
|
|
547
559
|
|
|
548
560
|
Two hand-rolled utilities, both of which stop for `prefers-reduced-motion`.
|
|
@@ -607,10 +619,14 @@ its `label` (default "loading") says so.
|
|
|
607
619
|
The loading system has its own: `--roster-skeleton` and
|
|
608
620
|
`--roster-skeleton-highlight` are the skeleton's fill and the light that
|
|
609
621
|
crosses it, and the empty part of every progress bar (gray-200 and gray-100,
|
|
610
|
-
gray-700 and gray-600 in dark).
|
|
611
|
-
pace: `--roster-skeleton-duration` (1.8s),
|
|
612
|
-
|
|
613
|
-
|
|
622
|
+
gray-700 and gray-600 in dark). Four durations, unset by default, change the
|
|
623
|
+
pace: `--roster-skeleton-duration` (1.8s, the shimmer),
|
|
624
|
+
`--roster-skeleton-pulse-duration` (2s, the pulse),
|
|
625
|
+
`--roster-progress-travel-duration` (1.4s, the indeterminate pill) and
|
|
626
|
+
`--roster-dots-duration` (1.2s). `--roster-skeleton-pulse-opacity` (0.5) is
|
|
627
|
+
how far a pulse fades. Under reduced motion all of them stop on a resting
|
|
628
|
+
frame. `--roster-skeleton-radius` rounds skeleton blocks without touching the
|
|
629
|
+
shared radius scale (see Skeleton below).
|
|
614
630
|
|
|
615
631
|
### Scrollbars
|
|
616
632
|
|
|
@@ -769,6 +785,8 @@ Every field therefore has a second prop, **named for the element it reaches**:
|
|
|
769
785
|
| `Combobox` | `className` | `inputClassName` | `optionsClassName` |
|
|
770
786
|
| `CheckboxGroup`| `className` | — | `optionsClassName` |
|
|
771
787
|
| `RadioGroup` | `className` | `radioClassName` | `optionsClassName`, `optionClassName`, `labelClassName`, `optionLabelClassName`, `descriptionClassName`, `messageClassName` |
|
|
788
|
+
| `Slider` | `className` | `trackClassName` | `thumbClassName`, `chipClassName`, `labelClassName`, `messageClassName` |
|
|
789
|
+
| `RangeSlider` | `className` | `thumbClassName` | `trackClassName`, `chipClassName`, `labelClassName`, `messageClassName` |
|
|
772
790
|
|
|
773
791
|
`CheckboxGroup` and `RadioGroup` have no single "control" to reach, since they
|
|
774
792
|
hold one per option, so `RadioGroup`'s `radioClassName` lands on every radio
|
|
@@ -924,6 +942,10 @@ look like oversights until you know why:
|
|
|
924
942
|
page does not reach it. All three components copy the nearest `.dark` onto
|
|
925
943
|
the panel to fix that. Custom properties are not carried the same way — set
|
|
926
944
|
`--roster-popover-*` at `:root`.
|
|
945
|
+
- **A row is a click target, and focus is a ring.** Every option row shows
|
|
946
|
+
the pointer cursor, as `Menu`'s items do (a disabled one, `not-allowed`).
|
|
947
|
+
A focused row keeps its tint and adds an inset `--roster-ring`, which is
|
|
948
|
+
what reaches 3:1 against the panel ([Focus](#focus)).
|
|
927
949
|
- **`Menu` shares the panel, not the anchoring.** Its panel takes the same
|
|
928
950
|
surface, tokens and dark carry, but `Menu` places itself (see
|
|
929
951
|
[Menu](#menu)), writing its own inline `max-height`. It reads the same
|
|
@@ -1039,7 +1061,7 @@ rather than vanishing from a field that still reports it.
|
|
|
1039
1061
|
|
|
1040
1062
|
#### CheckboxGroup
|
|
1041
1063
|
|
|
1042
|
-
Three things it adds over the version
|
|
1064
|
+
Three things it adds over the version one app's form hand-rolled:
|
|
1043
1065
|
|
|
1044
1066
|
- **Each option is a Headless UI `Field`**, so its label is wired to its own
|
|
1045
1067
|
checkbox and clicking the text toggles it. The pointer cursor sits on the
|
|
@@ -1109,8 +1131,8 @@ silently drops any selected value whose option has not loaded yet.
|
|
|
1109
1131
|
|
|
1110
1132
|
`CheckboxGroup` exists instead of a multi-select `Select`, and the reason is
|
|
1111
1133
|
that neither multi-selection UI in the portfolio was ever a dropdown.
|
|
1112
|
-
|
|
1113
|
-
category headings;
|
|
1134
|
+
One app's form picks sports from a grouped, scrollable checklist with
|
|
1135
|
+
category headings; another's share form picks tags from a flat row of
|
|
1114
1136
|
toggles. Collapsing either into a menu would have hidden the options behind a
|
|
1115
1137
|
click to save vertical space neither screen was short of, so the component
|
|
1116
1138
|
follows the shape that already worked. A multi-select dropdown is still the
|
|
@@ -1205,7 +1227,7 @@ rather than a rewrite.
|
|
|
1205
1227
|
|
|
1206
1228
|
```tsx
|
|
1207
1229
|
<RadioGroup
|
|
1208
|
-
label="Who can see this
|
|
1230
|
+
label="Who can see this group"
|
|
1209
1231
|
helperText="You can change this at any time."
|
|
1210
1232
|
value={visibility}
|
|
1211
1233
|
onChange={setVisibility}
|
|
@@ -1459,7 +1481,21 @@ import { SkeletonRegion, SkeletonCard } from "@blakesteve/roster";
|
|
|
1459
1481
|
- The fill is `--roster-skeleton` (gray-200, gray-700 in dark), with
|
|
1460
1482
|
`--roster-skeleton-highlight` crossing it. Neutral rather than the brand
|
|
1461
1483
|
shimmer Countdown uses, and its own pair, so retinting one never recolors the
|
|
1462
|
-
other.
|
|
1484
|
+
other.
|
|
1485
|
+
- `animation` picks how it moves: `"shimmer"` (the default, a band of light
|
|
1486
|
+
crossing the fill), `"pulse"` (the fill fading and returning, nothing
|
|
1487
|
+
moving: quieter on a dense page) or `"none"`. Under reduced motion both
|
|
1488
|
+
movements stop on the flat fill, and in forced colors every shape keeps its
|
|
1489
|
+
outline (a pulse stops there, so the outline never fades). Pass it to
|
|
1490
|
+
`SkeletonRegion` and every skeleton inside takes it, presets included; a
|
|
1491
|
+
preset or a skeleton that names its own keeps it. A pulse runs
|
|
1492
|
+
`--roster-skeleton-pulse-duration` (2s) and fades to
|
|
1493
|
+
`--roster-skeleton-pulse-opacity` (0.5).
|
|
1494
|
+
- A block's corners are `--roster-skeleton-radius`, the `lg` step unless
|
|
1495
|
+
set. Set it on `:root` or on a wrapper (`14px`, say) and skeleton blocks
|
|
1496
|
+
follow, and nothing else does: the shared scale and every other component
|
|
1497
|
+
stay as they are. A block's `radius` prop still wins, a line keeps its
|
|
1498
|
+
small step, and a circle stays round.
|
|
1463
1499
|
|
|
1464
1500
|
**Progress and ProgressField.** `ProgressField` is the form to reach for: the
|
|
1465
1501
|
label and value on one baseline above the bar, an optional detail line below.
|
|
@@ -1566,8 +1602,10 @@ import { Menu, MenuButton, MenuItems, MenuItem, MenuSeparator } from "@blakestev
|
|
|
1566
1602
|
- **Styling.** `className` on `MenuItems` and on each item merges last, so a
|
|
1567
1603
|
fill such as `bg-gray-100` takes; the scroll shadows are drawn over it. Its
|
|
1568
1604
|
ring and separators follow `--roster-border-width`. Items show the pointer
|
|
1569
|
-
cursor, a disabled one `not-allowed`.
|
|
1570
|
-
|
|
1605
|
+
cursor, a disabled one `not-allowed`. Keyboard focus adds an inset ring in
|
|
1606
|
+
`--roster-ring` to the item's tint, which alone doesn't reach 3:1 against
|
|
1607
|
+
the panel ([Focus](#focus)). In forced colors the focused item gets an
|
|
1608
|
+
outline, since its fill and ring are dropped.
|
|
1571
1609
|
- **Targets.** Every item is at least 44px tall, and `MenuButton` at least 44
|
|
1572
1610
|
by 44 whatever its size. `MenuButton plain` renders a bare `<button>` for a
|
|
1573
1611
|
trigger drawn its own way; keep its target at least 44 by 44 with a hit
|
|
@@ -1581,6 +1619,120 @@ that its rows are 44px tall rather than 36, and that with neither an inbox nor
|
|
|
1581
1619
|
a theme row it no longer draws an empty band and rule at the top. It keeps its
|
|
1582
1620
|
own white fill, whatever `--roster-popover-bg` says.
|
|
1583
1621
|
|
|
1622
|
+
### Slider
|
|
1623
|
+
|
|
1624
|
+
A value picked along a track: tapped, dragged, stepped with the keys, or set
|
|
1625
|
+
from a named snap point.
|
|
1626
|
+
|
|
1627
|
+
```tsx
|
|
1628
|
+
import { Slider } from "@blakesteve/roster";
|
|
1629
|
+
|
|
1630
|
+
<Slider
|
|
1631
|
+
label="How sure are you?"
|
|
1632
|
+
value={sure}
|
|
1633
|
+
onChange={setSure}
|
|
1634
|
+
snapPoints={[{ value: 100, label: "Certain" }, { value: 75, label: "Likely" }, { value: 50, label: "Toss-up" }]}
|
|
1635
|
+
formatValue={(v) => `${v}%`}
|
|
1636
|
+
/>;
|
|
1637
|
+
```
|
|
1638
|
+
|
|
1639
|
+
- **It starts with a thumb**, in the middle of the range by default.
|
|
1640
|
+
`startValue` moves where it starts, and `defaultValue` sets it outright:
|
|
1641
|
+
`defaultValue={0}` starts it at the low end.
|
|
1642
|
+
- **Not set** is opt-in: `defaultValue={null}`, or `value={null}` when
|
|
1643
|
+
controlled, leaves the track empty with no thumb until the first input. A
|
|
1644
|
+
form takes it as a valid answer: under `name` it submits an empty string.
|
|
1645
|
+
Controlled, `value` can go back to `null`, which is how a consumer offers a
|
|
1646
|
+
clear.
|
|
1647
|
+
- **Snap points** are named stops, any values and labels: a selectable
|
|
1648
|
+
`Chip` under the track for each, in the track's order, moves the thumb
|
|
1649
|
+
there, and a bead on the rail shows where. They're shortcuts, so dragging
|
|
1650
|
+
still lands anywhere between them. A chip shows as pressed (solid rather
|
|
1651
|
+
than outline) while the value is exactly its value, and pressing it again
|
|
1652
|
+
leaves it there. A point outside `min` to `max` is left out.
|
|
1653
|
+
- **The number** shows beside the label (`formatValue` writes it). A screen
|
|
1654
|
+
reader hears it with the nearest snap point's name, "72%, Likely", and
|
|
1655
|
+
"Not set" while there's no value, never a number. Every string is in
|
|
1656
|
+
`labels`.
|
|
1657
|
+
- **In range.** A value outside `min` to `max` shows, and is said, at the
|
|
1658
|
+
end it's past. A controlled owner that turns a change down (a clamp, say)
|
|
1659
|
+
leaves the next key stepping from the value it kept.
|
|
1660
|
+
- **Pointer.** A tap on the track sets the value there; a press on the thumb
|
|
1661
|
+
takes it without a jump, and commits only if the drag moved it. On touch
|
|
1662
|
+
the track waits to see which way a finger goes: across is a drag, down the
|
|
1663
|
+
page scrolls it and sets nothing (and takes no focus), and a tap sets on
|
|
1664
|
+
lift. A pointermove with no press, which WebKit sends when content scrolls
|
|
1665
|
+
under a resting pointer, sets nothing. A second finger is left alone.
|
|
1666
|
+
- **Keys.** The arrows step by `step`, PageUp and PageDown by `pageStep` (a
|
|
1667
|
+
tenth of the range by default), Home and End go to the ends. **On an empty
|
|
1668
|
+
slider an arrow or Page key places the thumb at `startValue`, the middle
|
|
1669
|
+
by default**; it doesn't step from anywhere. A key at an end does nothing.
|
|
1670
|
+
From a value between steps, a key goes to the next step that way. Keys
|
|
1671
|
+
always step from the value last set, never from where the thumb is drawn
|
|
1672
|
+
while it eases there, so a burst of presses counts every one. A key the
|
|
1673
|
+
consumer's own `onKeyDown` handled (`preventDefault`) is left to it.
|
|
1674
|
+
- **Focus.** By keyboard, the ring is round the thumb; an empty slider rings
|
|
1675
|
+
the track. A disabled slider takes no focus at all.
|
|
1676
|
+
- **Form.** Under `name` it submits its value, or an empty string while not
|
|
1677
|
+
set; disabled, nothing, as a disabled input submits nothing. A form's
|
|
1678
|
+
reset puts an uncontrolled slider back where it started.
|
|
1679
|
+
- **Targets.** The track is 44px tall and its rail stops 22px short of each
|
|
1680
|
+
end, so the thumb's 44 by 44 target stays on it at every value. Each chip
|
|
1681
|
+
has Chip's 44px-tall target and is at least 44 wide, and the chip row sits
|
|
1682
|
+
8px under the track so its targets never take the thumb's. Under reduced
|
|
1683
|
+
motion the thumb moves without easing. Under `dir="rtl"` the track runs
|
|
1684
|
+
from the right.
|
|
1685
|
+
- **Look.** The rail is a light surface edged in `--roster-control-border`,
|
|
1686
|
+
which carries a control edge's 3:1 when there's no thumb to show the
|
|
1687
|
+
slider. Snap points are beads set in the rail, the edge's color on the bare
|
|
1688
|
+
rail and the page's on the fill.
|
|
1689
|
+
|
|
1690
|
+
One thumb. Two, for a range, is [RangeSlider](#rangeslider), on the same
|
|
1691
|
+
track, so `value` here stays a number.
|
|
1692
|
+
|
|
1693
|
+
### RangeSlider
|
|
1694
|
+
|
|
1695
|
+
A low and a high along one track: `Slider`'s track, with two thumbs.
|
|
1696
|
+
|
|
1697
|
+
```tsx
|
|
1698
|
+
import { RangeSlider } from "@blakesteve/roster";
|
|
1699
|
+
|
|
1700
|
+
<RangeSlider
|
|
1701
|
+
label="Price"
|
|
1702
|
+
min={0}
|
|
1703
|
+
max={500}
|
|
1704
|
+
step={5}
|
|
1705
|
+
value={price}
|
|
1706
|
+
onChange={setPrice}
|
|
1707
|
+
formatValue={(v) => `$${v}`}
|
|
1708
|
+
/>;
|
|
1709
|
+
```
|
|
1710
|
+
|
|
1711
|
+
- **It starts at the whole range**, `[min, max]`, unless `defaultValue`
|
|
1712
|
+
says otherwise. `value` is `[low, high]`, always low first: a pair given
|
|
1713
|
+
the wrong way round is shown the right way. There's no not-set state; the
|
|
1714
|
+
whole range is what "any" looks like.
|
|
1715
|
+
- **Each thumb is a slider** to the keyboard and a screen reader: its own
|
|
1716
|
+
tab stop, named for the slider and its end ("Price Minimum", "Price
|
|
1717
|
+
Maximum", both in `labels`), whose `aria-valuemin` and `aria-valuemax`
|
|
1718
|
+
run to the other thumb. The thumbs meet and never cross: End on the low
|
|
1719
|
+
thumb takes it to the high one. The whole is a group named by the label.
|
|
1720
|
+
- **Pointer.** A press takes the nearer thumb: a tap on the track moves it
|
|
1721
|
+
there, a drag carries it. Two thumbs on one spot part by the way the drag
|
|
1722
|
+
first moves, up the range taking the high one. Touch, the resting
|
|
1723
|
+
pointer, the second finger and the keys work as `Slider`'s do.
|
|
1724
|
+
- **Snap points** put a bead on the rail and a `Chip` under it, which does
|
|
1725
|
+
what a tap on its mark does: moves the nearer thumb there (halfway, the
|
|
1726
|
+
low one). A chip shows as pressed while either thumb is on it.
|
|
1727
|
+
- **The number** beside the label shows both, "$120 – $340"
|
|
1728
|
+
(`labels.range`). Each thumb says its own value with the nearest snap
|
|
1729
|
+
point's name.
|
|
1730
|
+
- **Form.** Under `name` both values are submitted, low then high, as a
|
|
1731
|
+
multiple select's are: read them with `formData.getAll(name)`.
|
|
1732
|
+
- **Focus, targets, motion, RTL and look** as `Slider`'s: the ring is round
|
|
1733
|
+
the focused thumb, each thumb's 44 by 44 target stays on the track at
|
|
1734
|
+
every value.
|
|
1735
|
+
|
|
1584
1736
|
### Pagination
|
|
1585
1737
|
|
|
1586
1738
|
Moves through pages of a list, built for a long list browsed on a phone.
|
|
@@ -1833,7 +1985,7 @@ function App() {
|
|
|
1833
1985
|
| `CollapsibleSection` | Clamps any content (prose, chips, image grids) to a fixed height with a fade and expand/collapse toggle |
|
|
1834
1986
|
| `Select` | Dropdown selector, on the same size scale as `Button` and `Input` |
|
|
1835
1987
|
| `SegmentBar` | Proportional horizontal bar divided into colored segments with optional legend (a split, not progress) |
|
|
1836
|
-
| `Skeleton` | Line, block or circle placeholder at the content's exact size; `SkeletonRegion` sets `aria-busy`
|
|
1988
|
+
| `Skeleton` | Line, block or circle placeholder at the content's exact size, shimmering, pulsing or still; `SkeletonRegion` sets `aria-busy` |
|
|
1837
1989
|
| `Progress` | Continuous or segmented progress bar: determinate, indeterminate or busy; per-segment state |
|
|
1838
1990
|
| `LoadingDots` | Three dots for a model generating before its first token; replaced by the text |
|
|
1839
1991
|
| `Stat` | A figure with its label and, optionally, its source; a valid `<dl>` group, or `semantics="standalone"` |
|
|
@@ -1868,6 +2020,8 @@ function App() {
|
|
|
1868
2020
|
| `StepProgress` | Named steps: a stepper with markers and connectors, or a compact segmented "Step x of y" |
|
|
1869
2021
|
| `Pullquote` | A line lifted out of prose, as `<figure>` + `<blockquote>` + `<figcaption>` |
|
|
1870
2022
|
| `RadioGroup` | Radio set reporting one `string`; descriptions, error, 44px targets |
|
|
2023
|
+
| `Slider` | A value along a track, starting in the middle or not set on request; named snap-point chips, a visible number, the full keyboard, 44px targets (see [Slider](#slider)) |
|
|
2024
|
+
| `RangeSlider` | A low and a high on one track: two thumbs that meet and never cross, each its own slider to the keyboard (see [RangeSlider](#rangeslider)) |
|
|
1871
2025
|
| `Toast` | Transient floating notice; `Toaster` + the imperative `toast` handle |
|
|
1872
2026
|
|
|
1873
2027
|
### Organisms
|
|
@@ -1979,6 +2133,32 @@ such as `AvatarStrip`'s `ringClass`, stays exactly as they wrote it.
|
|
|
1979
2133
|
|
|
1980
2134
|
`npm run build` fails if any unprefixed class reaches `dist/roster.css`.
|
|
1981
2135
|
|
|
2136
|
+
## Authoring components: cursors
|
|
2137
|
+
|
|
2138
|
+
**If it's clickable, it's the pointer.** Every element that does something
|
|
2139
|
+
when it's clicked shows `cursor: pointer`, and says so in its classes. A
|
|
2140
|
+
browser gives a button the arrow; Tailwind v3's preflight gave it the pointer
|
|
2141
|
+
and v4's doesn't, so a `<button>` with no cursor class shows the arrow. Until 5.11.0 the option rows
|
|
2142
|
+
in `Select`, `Combobox` and `MultiSelect` showed the arrow while `Menu`'s
|
|
2143
|
+
showed the pointer, and `PasswordInput`'s reveal button showed the arrow too.
|
|
2144
|
+
|
|
2145
|
+
- A disabled control shows `not-allowed`.
|
|
2146
|
+
- A busy one may say so: `Button`'s `loading` shows `progress`.
|
|
2147
|
+
- A text field's label keeps the arrow, as a native one does: clicking it puts
|
|
2148
|
+
the caret in the field rather than doing something.
|
|
2149
|
+
|
|
2150
|
+
Three things keep another cursor, because they aren't click targets:
|
|
2151
|
+
|
|
2152
|
+
- **`Breadcrumbs`' current crumb** is the page you're on, not a link:
|
|
2153
|
+
`default`.
|
|
2154
|
+
- **`Tooltip`'s trigger wrapper** isn't necessarily clickable: `default`. A
|
|
2155
|
+
control inside it brings its own pointer.
|
|
2156
|
+
- **A drag surface** says what it does: `Carousel`'s track and `Sheet`'s drag
|
|
2157
|
+
handle show `grab`, then `grabbing`.
|
|
2158
|
+
|
|
2159
|
+
`src/internal/popups.checks.stories.tsx` reads the computed cursor on a row in
|
|
2160
|
+
each popup, with a disabled row as its twin.
|
|
2161
|
+
|
|
1982
2162
|
## Development
|
|
1983
2163
|
|
|
1984
2164
|
Storybook is the component playground. Each component has dedicated stories covering all variants, props, and light/dark mode.
|
|
@@ -2037,14 +2217,15 @@ anything new. It also fails when a listed class starts emitting, so a fix cannot
|
|
|
2037
2217
|
leave a stale entry behind. Run it with `--verbose` to print the list.
|
|
2038
2218
|
|
|
2039
2219
|
Story tests that need the user's own input, a wheel, a mouse drag or a pointer
|
|
2040
|
-
resting on something, get it
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2220
|
+
resting on something, get it from `.storybook/real-input.ts`: Vitest browser
|
|
2221
|
+
commands that drive Playwright's mouse, whose events are trusted. They run in
|
|
2222
|
+
Chromium and in WebKit, where pointer and scroll events differ: WebKit sends a
|
|
2223
|
+
pointermove when content scrolls under a resting pointer, and Chromium doesn't.
|
|
2224
|
+
A synthetic wheel or pointer event never scrolls anything, so "a vertical wheel
|
|
2225
|
+
scrolls the page, not the row" can't be tested without them. The commands fail
|
|
2226
|
+
loudly when the pointer isn't over their target, rather than letting a wheel
|
|
2227
|
+
land on the page and a test pass for the wrong reason. Storybook's own UI has
|
|
2228
|
+
no such input, so those steps are skipped there.
|
|
2048
2229
|
|
|
2049
2230
|
`src/lib/utils.test.ts` pins `cn`. `tailwind-merge` has to be configured with the
|
|
2050
2231
|
prefix or it stops recognising Roster's classes as utilities and silently
|
|
@@ -2073,9 +2254,9 @@ Output in `dist/`:
|
|
|
2073
2254
|
|
|
2074
2255
|
### What `meta.json` is for
|
|
2075
2256
|
|
|
2076
|
-
Consumers read this package to describe it.
|
|
2077
|
-
version from `package.json` and counts components by parsing the exports
|
|
2078
|
-
`dist/*.d.ts`, so both stay correct on their own.
|
|
2257
|
+
Consumers read this package to describe it. A portfolio site's case study takes
|
|
2258
|
+
the version from `package.json` and counts components by parsing the exports
|
|
2259
|
+
out of `dist/*.d.ts`, so both stay correct on their own.
|
|
2079
2260
|
|
|
2080
2261
|
The test count has no such source. `files` is `["dist"]`, so the suite never
|
|
2081
2262
|
leaves this repo, and the number on that page was a hand-typed literal that
|
|
@@ -2109,7 +2290,7 @@ like a file count but counts `describe` blocks — 166 against 92 real files.
|
|
|
2109
2290
|
Pull requests are welcome. Please:
|
|
2110
2291
|
|
|
2111
2292
|
1. Open an issue or discussion first for significant changes
|
|
2112
|
-
2. Follow the existing code style (CVA variants, forwardRef, Storybook stories, unit tests)
|
|
2293
|
+
2. Follow the existing code style (CVA variants, forwardRef, Storybook stories, unit tests) and the authoring rules above: the `rst:` prefix and cursors
|
|
2113
2294
|
3. Fill out the pull request template checklist before requesting review
|
|
2114
2295
|
|
|
2115
2296
|
## License
|
|
@@ -65,7 +65,7 @@ const e = /* @__PURE__ */ r(
|
|
|
65
65
|
// Translucent rather than a solid tint step. A ghost button sits directly
|
|
66
66
|
// on the host's own background, and a solid `bg-*-50` silently disappears
|
|
67
67
|
// wherever that background happens to equal the step it names — which is
|
|
68
|
-
// exactly what happened to
|
|
68
|
+
// exactly what happened to one app, whose paper is the same value it
|
|
69
69
|
// maps `--roster-gray-100` to. An alpha wash darkens or lightens whatever
|
|
70
70
|
// is actually behind it, so it reads on any surface.
|
|
71
71
|
{ variant: "ghost", colorScheme: "primary", className: "rst:text-primary-600 rst:hover:bg-primary-500/10 rst:dark:text-primary-400 rst:dark:hover:bg-primary-400/15" },
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The small uppercase label that sits above a heading, beside a rule, or as a
|
|
3
|
-
* column head. Extracted from
|
|
4
|
-
* class and used 26 times before anyone noticed it was a component.
|
|
3
|
+
* column head. Extracted from a portfolio site, where it was hand-rolled as a
|
|
4
|
+
* CSS class and used 26 times before anyone noticed it was a component.
|
|
5
5
|
*
|
|
6
6
|
* Monospace on purpose: at this size, tracked-out uppercase reads as chrome
|
|
7
7
|
* rather than prose, which is the whole job.
|
|
@@ -49,7 +49,7 @@ const s = /* @__PURE__ */ r(
|
|
|
49
49
|
},
|
|
50
50
|
/* Deliberately the same three heights as Button: h-9 / h-10 / h-11.
|
|
51
51
|
Input was a fixed 42px, which agreed with Button at no size at all, so
|
|
52
|
-
a field beside a submit button never lined up.
|
|
52
|
+
a field beside a submit button never lined up. One app gave up and
|
|
53
53
|
kept a raw <input> pinned to h-11. */
|
|
54
54
|
size: {
|
|
55
55
|
sm: "rst:h-9 rst:px-3",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
1
|
+
"use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),i=require("react"),j=require("../../../node_modules/@fortawesome/react-fontawesome/dist/index.cjs"),c=require("../../../node_modules/@fortawesome/free-solid-svg-icons/index.cjs"),a=require("../../../lib/utils.cjs"),S=require("../Input/Input.cjs"),u=i.forwardRef(({revealable:l=!0,defaultRevealed:d=!1,revealed:r,onRevealChange:p,showLabel:y="Show password",hideLabel:f="Hide password",disabled:s,variant:m,error:w,errorMessage:h,className:v,...x},g)=>{const[I,q]=i.useState(d),o=r!==void 0,t=o?r:I,b=()=>{const n=!t;o||q(n),p?.(n)};return e.jsx(S.Input,{ref:g,type:t?"text":"password",disabled:s,variant:m,error:w,errorMessage:h,className:a.cn("rst:[&_input::-ms-reveal]:hidden",v),endIcon:l?e.jsx("button",{type:"button",onClick:b,disabled:s,"aria-label":t?f:y,"aria-pressed":t,className:a.cn("rst:-m-1 rst:flex rst:items-center rst:rounded-sm rst:p-1 rst:transition-opacity","rst:opacity-70 rst:hover:opacity-100","rst:focus:outline-hidden rst:focus-visible:ring-2 rst:focus-visible:ring-ring",s?"rst:cursor-not-allowed rst:opacity-40 rst:hover:opacity-40":"rst:cursor-pointer"),children:e.jsx(j.FontAwesomeIcon,{icon:t?c.faEyeSlash:c.faEye,className:"rst:h-4 rst:w-4","aria-hidden":"true"})}):void 0,...x})});u.displayName="PasswordInput";exports.PasswordInput=u;
|
|
@@ -10,8 +10,8 @@ const C = i.forwardRef(
|
|
|
10
10
|
revealable: c = !0,
|
|
11
11
|
defaultRevealed: l = !1,
|
|
12
12
|
revealed: s,
|
|
13
|
-
onRevealChange:
|
|
14
|
-
showLabel:
|
|
13
|
+
onRevealChange: p,
|
|
14
|
+
showLabel: d = "Show password",
|
|
15
15
|
hideLabel: m = "Hide password",
|
|
16
16
|
disabled: r,
|
|
17
17
|
variant: u,
|
|
@@ -22,7 +22,7 @@ const C = i.forwardRef(
|
|
|
22
22
|
}, v) => {
|
|
23
23
|
const [g, x] = i.useState(l), e = s !== void 0, t = e ? s : g, I = () => {
|
|
24
24
|
const n = !t;
|
|
25
|
-
e || x(n),
|
|
25
|
+
e || x(n), p?.(n);
|
|
26
26
|
};
|
|
27
27
|
return /* @__PURE__ */ o(
|
|
28
28
|
_,
|
|
@@ -40,13 +40,13 @@ const C = i.forwardRef(
|
|
|
40
40
|
type: "button",
|
|
41
41
|
onClick: I,
|
|
42
42
|
disabled: r,
|
|
43
|
-
"aria-label": t ? m :
|
|
43
|
+
"aria-label": t ? m : d,
|
|
44
44
|
"aria-pressed": t,
|
|
45
45
|
className: a(
|
|
46
46
|
"rst:-m-1 rst:flex rst:items-center rst:rounded-sm rst:p-1 rst:transition-opacity",
|
|
47
47
|
"rst:opacity-70 rst:hover:opacity-100",
|
|
48
48
|
"rst:focus:outline-hidden rst:focus-visible:ring-2 rst:focus-visible:ring-ring",
|
|
49
|
-
r
|
|
49
|
+
r ? "rst:cursor-not-allowed rst:opacity-40 rst:hover:opacity-40" : "rst:cursor-pointer"
|
|
50
50
|
),
|
|
51
51
|
children: /* @__PURE__ */ o(
|
|
52
52
|
N,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("../../../node_modules/class-variance-authority/dist/index.cjs"),t=r.cva("rst:font-ui rst:relative rst:flex rst:w-full rst:cursor-pointer rst:disabled:cursor-not-allowed rst:disabled:opacity-50 rst:items-center rst:rounded-md rst:h-10 rst:pl-4 rst:pr-10 rst:text-left rst:text-sm rst:font-medium rst:elevation-control rst:ring rst:ring-inset rst:transition-all rst:focus:outline-hidden rst:focus-visible:ring-[length:calc(var(--roster-border-width,1px)+1px)] rst:focus-visible:ring-ring",{variants:{variant:{white:"rst:bg-white rst:text-gray-900 rst:ring-gray-300 rst:hover:bg-gray-50 rst:dark:bg-gray-800 rst:dark:text-gray-100 rst:dark:ring-gray-700 rst:dark:hover:bg-gray-700",soft:"rst:bg-gray-100 rst:text-gray-900 rst:ring-[var(--roster-control-border)] rst:hover:bg-gray-200 rst:dark:bg-gray-800 rst:dark:text-gray-100 rst:dark:hover:bg-gray-700",slate:"rst:bg-gray-700 rst:text-gray-100 rst:ring-transparent rst:hover:bg-gray-600 rst:dark:bg-gray-900 rst:dark:text-gray-100 rst:dark:hover:bg-gray-800",outline:"rst:bg-[var(--roster-control-bg)] rst:text-[var(--roster-control-text)] rst:ring-[color:var(--roster-control-border)]",ghost:"rst:bg-transparent rst:text-gray-700 rst:ring-transparent rst:hover:bg-gray-100 rst:hover:text-gray-900 rst:dark:text-gray-300 rst:dark:hover:bg-gray-800 rst:dark:hover:text-gray-100"},size:{sm:"rst:h-9 rst:pl-3 rst:pr-9",default:"rst:h-10 rst:pl-4 rst:pr-10",lg:"rst:h-11 rst:pl-4 rst:pr-10"},error:{true:"rst:ring-error-500 rst:text-error-600 rst:focus-visible:ring-error-500 rst:dark:ring-error-500 rst:dark:text-error-400 rst:dark:focus-visible:ring-error-400",false:""}},defaultVariants:{variant:"outline",size:"default",error:!1}}),s=r.cva("rst:text-sm rst:group rst:relative rst:cursor-
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("../../../node_modules/class-variance-authority/dist/index.cjs"),t=r.cva("rst:font-ui rst:relative rst:flex rst:w-full rst:cursor-pointer rst:disabled:cursor-not-allowed rst:disabled:opacity-50 rst:items-center rst:rounded-md rst:h-10 rst:pl-4 rst:pr-10 rst:text-left rst:text-sm rst:font-medium rst:elevation-control rst:ring rst:ring-inset rst:transition-all rst:focus:outline-hidden rst:focus-visible:ring-[length:calc(var(--roster-border-width,1px)+1px)] rst:focus-visible:ring-ring",{variants:{variant:{white:"rst:bg-white rst:text-gray-900 rst:ring-gray-300 rst:hover:bg-gray-50 rst:dark:bg-gray-800 rst:dark:text-gray-100 rst:dark:ring-gray-700 rst:dark:hover:bg-gray-700",soft:"rst:bg-gray-100 rst:text-gray-900 rst:ring-[var(--roster-control-border)] rst:hover:bg-gray-200 rst:dark:bg-gray-800 rst:dark:text-gray-100 rst:dark:hover:bg-gray-700",slate:"rst:bg-gray-700 rst:text-gray-100 rst:ring-transparent rst:hover:bg-gray-600 rst:dark:bg-gray-900 rst:dark:text-gray-100 rst:dark:hover:bg-gray-800",outline:"rst:bg-[var(--roster-control-bg)] rst:text-[var(--roster-control-text)] rst:ring-[color:var(--roster-control-border)]",ghost:"rst:bg-transparent rst:text-gray-700 rst:ring-transparent rst:hover:bg-gray-100 rst:hover:text-gray-900 rst:dark:text-gray-300 rst:dark:hover:bg-gray-800 rst:dark:hover:text-gray-100"},size:{sm:"rst:h-9 rst:pl-3 rst:pr-9",default:"rst:h-10 rst:pl-4 rst:pr-10",lg:"rst:h-11 rst:pl-4 rst:pr-10"},error:{true:"rst:ring-error-500 rst:text-error-600 rst:focus-visible:ring-error-500 rst:dark:ring-error-500 rst:dark:text-error-400 rst:dark:focus-visible:ring-error-400",false:""}},defaultVariants:{variant:"outline",size:"default",error:!1}}),s=r.cva("rst:data-focus:ring-2 rst:data-focus:ring-inset rst:data-focus:ring-ring rst:forced-colors:data-focus:outline-solid rst:forced-colors:data-focus:outline-2 rst:forced-colors:data-focus:-outline-offset-2 rst:text-sm rst:group rst:relative rst:cursor-pointer rst:select-none rst:py-2.5 rst:pl-4 rst:pr-9 rst:transition-colors rst:text-inherit rst:data-focus:bg-primary-100 rst:data-focus:text-primary-900 rst:dark:data-focus:bg-primary-900/30 rst:dark:data-focus:text-primary-100 rst:data-selected:bg-gray-50 rst:dark:data-selected:bg-gray-700/50 rst:data-disabled:opacity-50 rst:data-disabled:cursor-not-allowed",{variants:{size:{sm:"rst:py-2 rst:pl-3 rst:pr-8",default:"rst:py-2.5 rst:pl-4 rst:pr-9",lg:"rst:py-2.5 rst:pl-4 rst:pr-9"}},defaultVariants:{size:"default"}});exports.selectOptionVariants=s;exports.selectTriggerVariants=t;
|
|
@@ -76,8 +76,17 @@ const s = /* @__PURE__ */ r(
|
|
|
76
76
|
`text-sm` lives here rather than on the popup, and carries no `sm:`. The
|
|
77
77
|
popup used to set `text-base sm:text-sm`, which made a menu row 44px on
|
|
78
78
|
mobile and 40px from `sm` up: the exact breakpoint-dependent geometry this
|
|
79
|
-
change removed from the trigger, still alive one element away.
|
|
80
|
-
|
|
79
|
+
change removed from the trigger, still alive one element away.
|
|
80
|
+
|
|
81
|
+
`cursor-pointer`: a row is a click target, as Menu's are.
|
|
82
|
+
|
|
83
|
+
Focus is the tint and an inset ring in the focus token. The tint alone
|
|
84
|
+
measures under 1.2:1 against the panel in both themes, on the default
|
|
85
|
+
surface and a consumer's; the ring follows the token a consumer tunes
|
|
86
|
+
against their own surfaces, and holds 3:1 on any of them. Headless UI's
|
|
87
|
+
`data-focus` comes from the keys and the pointer alike, so the ring shows
|
|
88
|
+
on hover too. In forced colors, which drop both, focus is an outline. */
|
|
89
|
+
"rst:data-focus:ring-2 rst:data-focus:ring-inset rst:data-focus:ring-ring rst:forced-colors:data-focus:outline-solid rst:forced-colors:data-focus:outline-2 rst:forced-colors:data-focus:-outline-offset-2 rst:text-sm rst:group rst:relative rst:cursor-pointer rst:select-none rst:py-2.5 rst:pl-4 rst:pr-9 rst:transition-colors rst:text-inherit rst:data-focus:bg-primary-100 rst:data-focus:text-primary-900 rst:dark:data-focus:bg-primary-900/30 rst:dark:data-focus:text-primary-100 rst:data-selected:bg-gray-50 rst:dark:data-selected:bg-gray-700/50 rst:data-disabled:opacity-50 rst:data-disabled:cursor-not-allowed",
|
|
81
90
|
{
|
|
82
91
|
variants: {
|
|
83
92
|
size: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("react/jsx-runtime"),
|
|
1
|
+
"use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("react/jsx-runtime"),d=require("react"),i=require("../../../lib/utils.cjs"),b={sm:"rst:text-xs",md:"rst:text-sm",lg:"rst:text-base",inherit:""},j={xs:"rst:h-6 rst:w-6",sm:"rst:h-8 rst:w-8",md:"rst:h-10 rst:w-10",lg:"rst:h-12 rst:w-12",xl:"rst:h-16 rst:w-16"},p={sm:"rst:rounded-sm",md:"rst:rounded-md",lg:"rst:rounded-lg",xl:"rst:rounded-xl",full:"rst:rounded-full"},v="rst:rounded-skeleton",y={shimmer:"rst:animate-skeleton rst:forced-colors:outline-solid rst:forced-colors:outline-1",pulse:"rst:animate-skeleton-pulse rst:forced-colors:outline-solid rst:forced-colors:outline-1 rst:forced-colors:animate-none",none:"rst:skeleton-fill rst:forced-colors:outline-solid rst:forced-colors:outline-1"},S=d.createContext(void 0);function C({shape:e="line",size:s,lines:a=1,width:r,height:h,radius:n,animation:x,className:o}){const f=d.useContext(S),l=y[x??f??"shimmer"];if(e==="circle"){const c=s&&s in j?s:"md";return t.jsx("span",{"aria-hidden":"true",className:i.cn("rst:block rst:shrink-0 rst:rounded-full rst:border rst:border-transparent",j[c],l,o),"data-skeleton":"circle"})}if(e==="block")return t.jsx("span",{"aria-hidden":"true",className:i.cn("rst:block rst:w-full",n?p[n]:v,l,o),style:{height:h??"8rem",width:r},"data-skeleton":"block"});const k=s&&s in b?s:"md",u=Math.max(1,Math.floor(a)),g=c=>{const m=c===u-1;return t.jsxs("span",{className:i.cn("rst:relative rst:block",u>1&&!m&&"rst:w-full"),style:{width:u>1?m?r??"60%":void 0:r},children:[" ",t.jsx("span",{className:i.cn("rst:absolute rst:inset-x-0 rst:top-1/2 rst:h-[0.7em] rst:-translate-y-1/2",p[n??"sm"],l)})]},c)};return t.jsx("span",{"aria-hidden":"true",className:i.cn("rst:block",b[k],o),"data-skeleton":"line",children:Array.from({length:u},(c,m)=>g(m))})}function I({loading:e,skeleton:s,label:a="Loading",announce:r=!0,animation:h,children:n,className:x}){const o=d.useContext(S),[f,l]=d.useState("");return d.useEffect(()=>{const k=setTimeout(()=>l(e&&r?a:""),e?50:0);return()=>clearTimeout(k)},[e,a,r]),t.jsxs(t.Fragment,{children:[t.jsx("span",{role:"status",className:"rst:sr-only","data-skeleton-status":"",children:f}),t.jsx("div",{"aria-busy":e||void 0,className:x,"data-skeleton-region":e?"loading":"ready",children:e?t.jsx(S.Provider,{value:h??o,children:t.jsx("div",{"aria-hidden":"true",children:s})}):n})]})}exports.Skeleton=C;exports.SkeletonRegion=I;
|
|
@@ -24,6 +24,8 @@ declare const RADIUS: {
|
|
|
24
24
|
readonly xl: "rst:rounded-xl";
|
|
25
25
|
readonly full: "rst:rounded-full";
|
|
26
26
|
};
|
|
27
|
+
/** How a skeleton moves while it waits. */
|
|
28
|
+
export type SkeletonAnimation = "shimmer" | "pulse" | "none";
|
|
27
29
|
export interface SkeletonProps {
|
|
28
30
|
/**
|
|
29
31
|
* - `"line"`: a line of text, one line box tall at its `size`.
|
|
@@ -42,8 +44,20 @@ export interface SkeletonProps {
|
|
|
42
44
|
width?: CSSProperties["width"];
|
|
43
45
|
/** A block's height. */
|
|
44
46
|
height?: CSSProperties["height"];
|
|
45
|
-
/**
|
|
47
|
+
/**
|
|
48
|
+
* A block's corners, from the radius scale. Without it a block reads
|
|
49
|
+
* `--roster-skeleton-radius`, which defaults to `lg`. A line's corners are
|
|
50
|
+
* `sm` unless this says otherwise; a circle is always round.
|
|
51
|
+
*/
|
|
46
52
|
radius?: keyof typeof RADIUS;
|
|
53
|
+
/**
|
|
54
|
+
* `"shimmer"` (the default): a highlight sweeps across the fill. `"pulse"`:
|
|
55
|
+
* the fill fades and returns, at `--roster-skeleton-pulse-duration` and down
|
|
56
|
+
* to `--roster-skeleton-pulse-opacity`. `"none"`: the fill holds still.
|
|
57
|
+
* Under reduced motion both movements stop. Inside a `SkeletonRegion`, it
|
|
58
|
+
* defaults to the region's.
|
|
59
|
+
*/
|
|
60
|
+
animation?: SkeletonAnimation;
|
|
47
61
|
className?: string;
|
|
48
62
|
}
|
|
49
63
|
/**
|
|
@@ -53,7 +67,7 @@ export interface SkeletonProps {
|
|
|
53
67
|
* isn't there yet. The region it stands in for says it's busy; use
|
|
54
68
|
* `SkeletonRegion`, which does that, or set `aria-busy` on it yourself.
|
|
55
69
|
*/
|
|
56
|
-
declare function Skeleton({ shape, size, lines, width, height, radius, className }: SkeletonProps): import("react/jsx-runtime").JSX.Element;
|
|
70
|
+
declare function Skeleton({ shape, size, lines, width, height, radius, animation, className }: SkeletonProps): import("react/jsx-runtime").JSX.Element;
|
|
57
71
|
export interface SkeletonRegionProps {
|
|
58
72
|
/** While true, the skeleton shows and the region says it's busy. */
|
|
59
73
|
loading: boolean;
|
|
@@ -66,6 +80,8 @@ export interface SkeletonRegionProps {
|
|
|
66
80
|
* together, a grid of cards say, or wrap them in one region instead.
|
|
67
81
|
*/
|
|
68
82
|
announce?: boolean;
|
|
83
|
+
/** How the skeletons inside move, unless one says otherwise: see `Skeleton`. */
|
|
84
|
+
animation?: SkeletonAnimation;
|
|
69
85
|
children?: ReactNode;
|
|
70
86
|
className?: string;
|
|
71
87
|
}
|
|
@@ -75,5 +91,5 @@ export interface SkeletonRegionProps {
|
|
|
75
91
|
* When `loading` turns false the content replaces the skeleton in the same
|
|
76
92
|
* box, so a skeleton drawn at the content's size means nothing moves.
|
|
77
93
|
*/
|
|
78
|
-
declare function SkeletonRegion({ loading, skeleton, label, announce, children, className }: SkeletonRegionProps): import("react/jsx-runtime").JSX.Element;
|
|
94
|
+
declare function SkeletonRegion({ loading, skeleton, label, announce, animation, children, className }: SkeletonRegionProps): import("react/jsx-runtime").JSX.Element;
|
|
79
95
|
export { Skeleton, SkeletonRegion };
|