@ntmats/hc-ui 0.1.1 → 0.3.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 +41 -3
- package/dist/index.d.ts +588 -8
- package/dist/index.js +1366 -42
- package/dist/styles.css +1228 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ Imported as `@hc/ui`: `npm install --save-exact @hc/ui@npm:@ntmats/hc-ui@<versio
|
|
|
5
5
|
React presentation primitives, with React and React DOM supplied by each consumer. The initial contract has no router, data, persistence, icon-library or product-specific dependencies.
|
|
6
6
|
|
|
7
7
|
```ts
|
|
8
|
-
import { Button, Chip, IconButton, SegmentedControl, Skeleton, Surface } from '@hc/ui';
|
|
8
|
+
import { Button, Checkbox, Chip, CommandPalette, Dialog, Field, FilterBar, IconButton, ListRow, Menu, MenuItem, MenuItemRadio, MenuRadioGroup, Meter, PageHeader, PanelHeader, Popover, SegmentedControl, Sheet, SheetChips, SheetOptions, Skeleton, SortMenu, Surface, Table } from '@hc/ui';
|
|
9
9
|
import '@hc/ui/styles.css';
|
|
10
10
|
import '@hc/tokens/css';
|
|
11
11
|
```
|
|
@@ -14,13 +14,51 @@ Place `data-hc-ds` and the active `data-theme="light|dark"` on an ancestor. Cont
|
|
|
14
14
|
|
|
15
15
|
`SegmentedControl` is a controlled, single-selection radiogroup. Tab enters at the selected option, every arrow key moves and selects (radio group pattern), Home/End move to the first/last enabled option, and disabled options are skipped. Its density can be comfortable (38px, HCUI view toggles) or compact (32px). `hideLabels` renders icon-only options at 40px wide; labels remain the accessible names and tooltips. Typography uses the variable-font tokens (`font-weight-control`, `font-stretch-condensed`) and the shared `rond` roundness; load Google Sans Flex with the `wdth` and `ROND` axes. A caller owns persistence and any URL state.
|
|
16
16
|
|
|
17
|
-
`
|
|
17
|
+
`SegmentedControl` also takes `indicator="slide"` (experimental): instead of raising the chosen option in place, one knob slides under the options by its own width (260ms, `ease-pop`; none under `prefers-reduced-motion`), as HCUI's point cost toggle. Options sit at equal width with no gap, 34px tall (a 40px track; 32px with `density="compact"`), with 12px/600 labels that are muted until chosen, and the whole track shrinks slightly on press. `equalWidth` makes every option as wide as the widest (with either indicator), and `optionWidth` fixes that width in px: HCUI's toggle is `optionWidth={58}` (a 122×40 track). Long labels are cut with an ellipsis. Semantics and keyboard do not change: a radio group, one tab stop at the chosen option, arrows choose; the focus ring is on the focused option. The knob is decorative (`aria-hidden`) and absent when `value` matches no option.
|
|
18
|
+
|
|
19
|
+
`Surface` only supplies a semantic presentational role (`page`, `main`, `content`, `elevated`, `overlay`, or `card`). It does not add positioning, z-index, backdrop blur or modal behavior. `card` (experimental) is HCUI's `surface` mixin: the surface fill in both themes (`elevated` turns lighter in dark; `card` does not), the card ring and soft shadow (`shadow-card`, no border) and 20px corners. `radius` (`md` 16px, `lg` 20px, `xl` 24px) sets the corners of the rounded tones (`content`, `elevated`, `overlay`, `card`); HCUI's `card-surface` is `tone="card" radius="xl"`.
|
|
18
20
|
|
|
19
21
|
Styles are not in a cascade layer: unlayered consumer resets (HCUI's `button` rule, Tailwind preflight) would override layered declarations. Customize through props and tokens, not overrides of internal classes.
|
|
20
22
|
|
|
21
23
|
HCUI uses `SegmentedControl` through its `ViewToggle` wrapper. During the bootstrap it compiles the source through Vite/TypeScript aliases; switch to the pinned package once published.
|
|
22
24
|
|
|
23
|
-
`Button` is a pill with `secondary` (default), `primary`, `outline` and `danger` variants, `md`/`lg` sizes,
|
|
25
|
+
`Button` is a pill with `secondary` (default), `primary`, `outline` and `danger` variants, `sm`/`md`/`lg` sizes (36/44/52px), optional `icon` and `iconEnd`, `fullWidth`, and `loading`, which keeps focus and label, shows a spinner and ignores clicks. Long labels are cut with an ellipsis. `IconButton` requires `label` (accessible name and tooltip) and comes in 36/40/44px; the 36px size keeps a 44px touch target. Pass `selected` to make it a toggle (it fills with the accent). `variant` sets the fill: `muted` (default), `surface` (the surface color with the card ring and shadow, as HCUI's theme button on phones), `outline` (the surface color with a 1px border ring, as HCUI's sort button on phones) or `ghost` (no fill; `bg-muted` and primary text on hover, as HCUI's desktop utilities, with `tone="secondary"` at rest). `shape="rounded"` gives 12px corners instead of a circle. `iconKey` keys the icon: when it changes (and on first render) the new icon turns and fades in (360ms, `ease-pop`; none under reduced motion), as HCUI's sun and moon. A toggle that keeps its look, such as a theme switch, passes `role="switch"` and `aria-checked` instead of `selected`; the theme state stays with the app. HCUI's theme button: `variant="surface"` on phones and `variant="ghost" shape="rounded" size="sm" tone="secondary"` on desktop. `Chip` is a filter/option pill; pass `selected` to make it a toggle (`aria-pressed`), omit it for a plain action. All three read the shared `rond` roundness, default to `type="button"`, forward refs, scale slightly on press, show the shared focus ring and drop motion under `prefers-reduced-motion`.
|
|
26
|
+
|
|
27
|
+
`Field` is a filled pill text input. `label` is required (`hideLabel` keeps it for screen readers), with `icon`, an `end` slot, `hint`, `error` and comfortable/compact density (44/40px). It forwards its ref and input props to the `input`; value and validation stay with the caller.
|
|
28
|
+
|
|
29
|
+
`Checkbox` (experimental) keeps a native `input type="checkbox"` transparently over a drawn box, so keyboard, forms, `aria` and `<label>` behave as usual. Wrap it in a `<label>` with the visible text (the whole row becomes the target and its hover reaches the box) or pass `aria-label`. Sizes are `md` (20px, touch lists and sheets) and `sm` (16px, dense desktop lists). `indeterminate` only changes the look and the DOM property, which assistive technology reads as mixed; the value still comes from `checked`. It forwards its ref to the input; `className` goes to the wrapper and every other prop to the input. The check and dash are inline SVGs (no icon library). The 16/20px box is not a touch target on its own: give it a label row of at least 44px.
|
|
30
|
+
|
|
31
|
+
`Dialog` (experimental) is a centered modal on the native `<dialog>` and `showModal()`: the browser puts it in the top layer, makes the rest of the page inert (Tab stays inside, clicks and scripts cannot focus what is behind) and sends Esc to the topmost dialog only, so stacked dialogs work. It is controlled: `open` shows it, and Esc, a backdrop click (both only when `dismissible`, the default) and the close button call `onClose`; the dialog stays open until the caller sets `open` to false, so `onClose` can ask first (a confirmation on top) or refuse. A press that starts inside and ends on the backdrop (selecting text) does not close it. `title` is required and names the dialog (`aria-labelledby`); `description` describes it (`aria-describedby`); `children` go in the body and `actions` in the footer. `closeLabel` adds a close `IconButton` with that accessible name (none by default; `closeIcon` replaces the inline X). Focus goes to the first `autoFocus` element, else the first focusable one, and returns to the element focused before opening; page scroll is locked while any dialog is open. Sizes: `sm` (400px, HCUI's desktop confirmation: compact title, actions on the right, `Button size="sm"` at 38px) and `md` (default, 440px, HCUI's Sheet in its desktop form: header, scrolling body, footer whose actions share the width, `shadow-modal`, blurred backdrop, entry and exit animation, none under `prefers-reduced-motion`). It renders where it is declared, so it inherits `data-hc-ds` and `data-theme` without a portal (the `::backdrop` inherits them too in current browsers). Texts come from the caller. `Dialog` stays centered at every width; the bottom sheet of phones is `Sheet`.
|
|
32
|
+
|
|
33
|
+
`Menu` (experimental) is a menu button. `trigger` is the button that opens it: pass an element (it receives `aria-haspopup="menu"`, `aria-expanded`, `aria-controls`, a ref and the click and key handlers, composed with its own) or a function `(props, { open }) => …` that spreads the props. `label` names the menu. Children: `MenuItem` (an action; optional `icon` at the end of the row, `hint` as a second line, `danger`, `disabled`; `onSelect` runs it and closes the menu unless it calls `event.preventDefault()`, as a confirmation step does), `MenuRadioGroup` with `MenuItemRadio` (one choice such as a sort order: the chosen item is bold with a check, and choosing closes the menu), `MenuItemCheckbox` (toggles in place; `closeOnSelect` to close), `MenuSeparator` and `MenuLabel` (plain text). Keyboard: Enter, Space or ArrowDown on the trigger open it at the checked item (else the first), ArrowUp at the last; arrows wrap and skip disabled items; Home/End; typing letters jumps to the next item starting with them; Enter/Space choose; Esc closes and returns focus to the trigger; Tab closes and continues from the trigger. A press outside closes without moving focus. After an action, focus goes back to the trigger unless the action moved it. If the items change while open and focus is lost, it moves to the first item. Visual: HCUI's `.desktop-sort__menu` (200px wide, `shadow-soft`, 36px rows, `text-secondary` items, `bg-muted-2` hover, a focus ring 3px out as on HCUI's desktop; `--hc-focus-ring-offset` still overrides it). Width and height limits are the caller's (`style`, `className`). It forwards its ref to the `role="menu"` element.
|
|
34
|
+
|
|
35
|
+
`Popover` (experimental) is a non-modal dialog anchored to its `trigger` (`aria-haspopup="dialog"`), named by `label`, with free content; a function child receives `close`. Focus stays on the trigger (pass `autoFocus` to move it to the first focusable element inside) and Tab is not trapped; Esc closes and returns focus to the trigger; a press outside closes. Visual: HCUI's `.team-rules__popover` (300px, at most `min(460px, 70vh)` with its own scroll, `radius-lg`, `shadow-float`, 12px text). Styles of the content (headings, lists) are the caller's. It forwards its ref to the `role="dialog"` element.
|
|
36
|
+
|
|
37
|
+
Both open below the trigger, aligned by its end, 4px away (`side="bottom|top"`, `align="start|end"`), and flip to the other side or edge when there is no room in the viewport and the trigger's clipping ancestors; the numbers come from `getBoundingClientRect`, with no positioning dependency (`placePopup` is the pure rule). Without `portal` the popup is absolutely positioned inside a wrapper around the trigger (`.hc-ui-popup-anchor`, `position: relative` at zero specificity, so `anchorClassName` can place it), inherits the theme and is cut by an `overflow` ancestor. With `portal` it renders, with fixed coordinates that follow scrolling and resizing, into the trigger's open `<dialog>` (the top layer) or `document.body`, or into the element passed; the portal container carries `data-hc-ds` and the trigger's `data-theme` (or `theme`). Esc and outside presses go to the newest popup only, so a menu opened from a popover closes alone, and Esc never reaches an enclosing `Dialog`. The cost: while a popup is open, Esc is taken on `document` in the capture phase and stopped there, so it never reaches the popup's own content either: the `onKeyDown` passed to `Menu` does not see it, and a field inside a `Popover` cannot use Esc to clear itself (give it a clear button instead). Open state can be controlled (`open`, `onOpenChange`) or not (`defaultOpen`). Neither animates, so there is no motion to reduce.
|
|
38
|
+
|
|
39
|
+
`Meter` (experimental) is a bar of a `value` against a `max` (the minimum is 0), such as a team's points against its limit. By default it is a `role="meter"` named by `label`: `aria-valuenow` stays within 0..`max` (as ARIA requires) and `aria-valuetext` carries the real amount (`valueText`, default `"<value> / <max>"`; pass a localized sentence such as "375 of 300 points, 75 over"). The visible numbers are the caller's. Pass `decorative` instead of `label` when the numbers are already next to it: the bar is then `aria-hidden` and has no role. `tone` is `auto` (danger when `value > max`), or forced with `over` or `default`. Shape: `thickness` 3, 4 (default) or 5px; `track` `muted` (default), `border` or `none`; `cap` `round` (default) or `square`; `overflow` `clamp` (default: a value over the limit fills the bar) or `mark` (the bar scales to the larger of value and limit, and a 2px danger line marks the limit). HCUI's bars: the mobile team bar is `thickness={3} track="none" animated` placed at the bottom edge, the desktop sidebar draft uses the defaults, and the team rules bar is `thickness={5} track="border" cap="square" overflow="mark"`. `animated` transitions the width (none under `prefers-reduced-motion`). It renders only spans, so it fits inside a button; its `display` and `position` have zero specificity, so a class can place it (absolute at an edge, a grid row). It forwards its ref to the root span.
|
|
40
|
+
|
|
41
|
+
`Sheet` (experimental) is a `Dialog` (`size="md"`) that can rise from the bottom of the screen: the same native `<dialog>`, focus, Tab trap, Esc, focus return, scroll lock, stacking, `title`, `children` and controlled `open`/`onClose` (Esc, the backdrop, the close button and the drag only ask to close). `closeLabel` is required, since every sheet shows the close button; `footer` holds the actions (they share the width); `width` is the centered panel's maximum in px (440 by default; HCUI's filters use 520). `presentation` picks the form, and the library does not guess it: `bottom` (full width at the bottom edge, top corners `radius-sheet`, a 36×5px grabber, the bottom safe area, at most the viewport height minus 32px and the top safe area, rising entry and falling exit), `center` (the `md` panel), or `auto` (default), which is `bottom` below 960px and `center` from 960px through a CSS media query (HCUI's cut). For another cut, choose `bottom` or `center` yourself. At the bottom the sheet drags on touch, as HCUI's `useSheetDrag`: down past 20% of its height, or a flick faster than 0.5px/ms over 20px, closes it and fades the backdrop with it; less springs back. Inside content that has scrolled down, a downward drag scrolls first, and only at the top moves the sheet. Gestures more sideways than vertical (a row of chips), fields and anything inside `[data-hc-sheet-nodrag]` (a map, a canvas) never drag. Up from the grabber or the header (the top 64px, or `[data-hc-sheet-handle]`) it expands to full height (`data-expanded`), and down from there returns. If `onClose` keeps it open (an action in progress), the sheet comes back after the drag. The script only reads `--hc-sheet-drag`, which the CSS sets to `on` at the bottom. Under `prefers-reduced-motion` nothing animates or glides; the drag still follows the finger. It forwards its ref to the `<dialog>`.
|
|
42
|
+
|
|
43
|
+
`SheetOptions` is a single-choice list for inside a sheet: rows of at least 52px on a `bg-muted-2` card, with the chosen row's label in semibold and a check at the end; each option may have a `hint` (second line), an `icon` (before the text, sized by the caller) and `disabled`. `SheetChips` is the same choice as wrapping `Chip`s, the chosen one filled. Both are `radiogroup`s named by `label`, controlled by `value` and `onChange` (a `value` that matches no option leaves none chosen). Keyboard: one tab stop, at the chosen option (else the first enabled one); arrows and Home/End move the focus, skipping disabled options, without choosing; Space or Enter chooses. Choosing does not move with the arrows because a choice often closes the sheet. Section labels and spacing around them are the caller's.
|
|
44
|
+
|
|
45
|
+
`Chip` also works as a filter (experimental). `size` is `md` (default, HCUI `chip`), `sm` (36px, 12px text: HCUI's filter pills and sort trigger) or `xs` (26px, 11px, muted fill without ring: HCUI's compact quick filters and chosen-value tags; selected turns to the contrast fill). `variant="quiet"` is the lighter rule-filter pill (border ring, secondary text; primary and bolder once `applied`). `count` puts a muted number after the label (results a quick filter keeps) and `badge` a filled badge (values a filter has applied); both show what they are given, a numeric 0 included (HCUI's quick filters show "0"), and hide only when `undefined`, `null`, `false` or `''` (so a badge that disappears with nothing applied takes `badge={n || undefined}`). A chip that opens a popup has a down chevron: it appears whenever `aria-haspopup` is set, so a `Chip` given as the `trigger` of `Menu` or `Popover` gets it with no extra prop (`chevron` forces it on or off, as the rule pills that swap the chevron for the badge). `aria-expanded="true"` and `applied` draw a 1.5px ring in the primary text color; `selected` (the accent fill) wins over both. `onClear` adds a separate remove button (×) named by `clearLabel`, which the type requires whenever `onClear` is given, so it is a second tab stop rather than a button inside a button: the chip renders as a `span` holding the main button (which takes the ref, `onClick` and `aria-pressed`; without `onClick` it is plain text, a chosen-value tag) and the remove button; `className` and `style` go on the `span`. Texts and numbers are the caller's, already formatted.
|
|
46
|
+
|
|
47
|
+
`FilterBar` (experimental) lays out a row of filters: a `role="group"` named by `label`, with optional `start` (a search field, an "advanced filters" chip) and `end` (a `SortMenu`, a view toggle) on the same line and a `footer` under it (chosen-value tags, "clear all"). `layout="wrap"` wraps the filters (desktop); `layout="scroll"` keeps them on one line that scrolls sideways with a hidden scrollbar, on their own line under the slots, and bleeds into the page gutter by `--hc-filter-bar-bleed` (HCUI's phones: 16px); 4px of padding above and below, taken back by a negative margin, keep focus rings visible without changing the layout. `layout="auto"` (default) is `scroll` below 960px and `wrap` from 960px, by a CSS media query (HCUI's cut). `density="compact"` spaces filters by 4px (HCUI's desktop quick filters). `sticky` is HCUI's desktop band: sticky at `--hc-filter-bar-top`, the surface fill and 8px above and below; bleeding to the panel edges and the offset are the caller's. Which filters exist, their values, the search and persistence stay with the app. It forwards its ref to the root.
|
|
48
|
+
|
|
49
|
+
`SortMenu` (experimental) is HCUI's desktop sort control: a `Chip` (`size="sm"` by default, `triggerClassName` for a class) showing the current option, named "`label`: option", that opens a `Menu` with a `MenuRadioGroup` of `options` (`value`, `label`, optional `hint` and `disabled`), with the `Menu` keyboard and focus. `applied` marks a choice in effect with the ring; other `Menu` props (`align`, `side`, `portal`, `open`, `onOpenChange`) pass through. On phones, compose instead: `IconButton variant="outline" size="lg"` with `aria-haspopup="dialog"` opens a `Sheet` with `SheetOptions variant="list"` (plain 48px rows divided by a line, 560/720 weights, a 20px check in the text color: HCUI's mobile sort sheet). HCUI draws the desktop focus ring 3px out and the mobile filter pills' 1px out: set `--hc-focus-ring-offset` on the bar.
|
|
50
|
+
|
|
51
|
+
`Table` (experimental) is a dense data table on a semantic `<table>`: HCUI's desktop Meta table. `columns` describe it (`key`, `header`, `cell(row, index)` with free content, `align` `start`/`center`/`end`, `numeric` for end-aligned tabular figures, `width` in px or any CSS length, `tone` `muted` or `secondary` for the header and cells, `className` for the header and every cell, such as a class that hides an optional column in a narrow container, and `rowHeader` for the cell that names the row, rendered as `<th scope="row">`); `rows` and `rowKey` give the data, already filtered, formatted and in order. A `sortable` column puts its header text in a button and carries `aria-sort` (`none` until sorted); `sort` (`{ key, direction }`) marks the sorted column, which shows a 12px arrow, and `onSort(key)` reports a press: the new order (toggle, default direction) and sorting the rows are the caller's. `stickyHeader` sticks the header row to the top of the scrolling ancestor at `--hc-table-sticky-top` (HCUI: under its filter band). `onRowClick` makes the rows react to a press and puts the row header's content in a button, so the keyboard reaches each row once (its press does not also reach the row); give a `rowHeader` column, or rows can only be pressed with a pointer. `selectedKey` fills the row open elsewhere (a side panel) with `bg-muted` and sets `aria-current` on its button. Rows are 4px 8px cells with a line under each and the `bg-muted-2` hover; the header is 11px muted text on the surface fill with a line under it. Focus rings are 3px out (HCUI's desktop) unless `--hc-focus-ring-offset` is set. Empty states, pagination ("show more") and the cell content styles are the caller's. It forwards its ref to the `<table>`.
|
|
52
|
+
|
|
53
|
+
`PageHeader` (experimental) is HCUI's desktop page heading: a row of at least 40px with the `title` (22px semibold on a 27.5px line, pulled up 4px to drop the font's inner space; an `h1` by default, `level` 2 or 3 change only the tag), an optional `count` in muted 13px text inside the heading after a space (hidden at 0) and `actions` at the end, 8px apart. The row wraps when it does not fit. `visuallyHidden` keeps the heading only for assistive technology, for a page whose content already shows its title. Spacing around it is the caller's. As in HCUI, the title's font is set with the `font` shorthand, which drops the `ROND` axis: this reproduces HCUI's look. It forwards its ref to the `<header>`.
|
|
54
|
+
|
|
55
|
+
`PanelHeader` (experimental) is the header of a panel or a section. `variant="bar"` (default) is HCUI's desktop context panel header: 52px, 8px 12px 8px 16px, a line under it, 13px text. It shows either a `trail` (steps of a breadcrumb in a `nav` named by `trailLabel`: earlier steps with `onSelect` are buttons that go back, steps without it are plain text, the last step is the heading, with `aria-current="location"`, all cut with an ellipsis, 14px chevrons between them) or a `title` with an optional `eyebrow` (HCUI's `eyebrow`: 11px uppercase, wide and muted) above and `subtitle` (muted) under it. `actions` go at the end, then, with `onClose`, a 32px round close button named by `closeLabel` (`closeTitle` for a tooltip that says more, such as the Esc shortcut; `closeIcon` replaces the inline X). `variant="section"` is HCUI's section heading (`section-title`: 17px, 680 weight, 90% width), with an `icon` in a 36px muted circle before it, the `subtitle` under it or, with `subtitleInline`, on the same baseline and cut with an ellipsis, and `actions` at the end; its 12px gap has zero specificity, so a class sets the row's layout (HCUI's desktop home: 8px and at least 44px tall). The title is an `h2` by default (`level`). Texts come from the caller. It forwards its ref to the `<header>`.
|
|
56
|
+
|
|
57
|
+
`ListRow` (experimental) is HCUI's `list-row`: a full-width button row of at least 56px with 8px 16px of padding, a 12px gap between its children, primary text, a translucent veil of the text color on hover (4%, only with a mouse) and press (7%), and the focus ring drawn inside (`--hc-focus-ring-offset`, -2px by default) with 20px corners. The base rule has zero specificity, so each list sets its height, padding, corners, font and dividers with its own class, as HCUI's lists do. Content and semantics (`role="option"` with `aria-selected`, `aria-pressed`) are the caller's. It forwards its ref to the button.
|
|
58
|
+
|
|
59
|
+
`CommandPalette` (experimental) is HCUI's desktop ⌘K palette: a search field over grouped results, in a modal `<dialog>` opened with `showModal()` (focus in the field, Esc and a backdrop click call `onClose`, focus returns to the trigger, page scroll locked). It is presentation and keyboard only: `query` and `onQueryChange` drive the field, and `groups` are the results the caller already searched, filtered and capped (`heading`, the uppercase title that names a group, and `items` with `key`, `label`, `detail`, an `icon` slot in a 32px box, `onSelect`, an optional `onAltSelect` and `altHint`). The field is a combobox (ARIA 1.2) over a listbox; focus never leaves it: arrows (wrapping), Home and End move the active item through `aria-activedescendant` and scroll it into view, Enter calls `onSelect`, ⌘/Ctrl+Enter calls `onAltSelect` (else `onSelect`), the pointer activates the item under it and a click selects without taking focus. A new query starts at the first item. Choosing does not close it; close it in `onSelect`. Opening on ⌘K is the caller's shortcut. Texts come from props: `label` (the dialog and the list; `inputLabel` for the field), `placeholder`, `selectHint` and `altHint` (keys on the active item), `hints` (the footer: `{ keys, label }`), `loading` (text at the end of the field; the list is `aria-busy`) and `empty` (a message at the top of the list). It looks like HCUI's: 640px wide, at most 560px tall, 12vh from the top, a 1px border, `shadow-soft`, no blur, no animation. The backdrop is `bg-overlay` unless `--hc-command-palette-backdrop` is set (HCUI: `.palette::backdrop { --hc-command-palette-backdrop: rgb(0 0 0 / 28%); }`), and the list's overflow and padding have zero specificity, so `listClassName` can reserve the scrollbar gutter. The search field keeps the browser's placeholder color, as in HCUI. It forwards its ref to the `<dialog>`.
|
|
60
|
+
|
|
61
|
+
Focus rings read `--hc-focus-ring-offset` (default 2px). Set it to `-2px` on a control inside an `overflow: hidden` group so the ring is not cut.
|
|
24
62
|
|
|
25
63
|
`Skeleton` is a static shape that pulses, hidden from assistive technology; the enclosing region announces loading once. Its base rule has zero specificity (`:where`), so the consumer sizes each shape with `width`/`height` or its own class.
|
|
26
64
|
|