@fusion.dev/helix-headless 0.4.2 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/custom-elements.json +39 -39
  2. package/dist/app-header/root/app-header-root.d.ts +8 -28
  3. package/dist/avatar/group/avatar-group.d.ts +5 -30
  4. package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +9 -28
  5. package/dist/chat/root/chat-root.d.ts +9 -28
  6. package/dist/color-picker/root/color-picker-root.d.ts +10 -28
  7. package/dist/combobox/root/combobox-root.d.ts +18 -60
  8. package/dist/copy-button/root/copy-button-root.d.ts +6 -21
  9. package/dist/data-table/root/data-table-root.d.ts +9 -32
  10. package/dist/date-input/root/date-input-root.d.ts +5 -21
  11. package/dist/date-picker/root/date-picker-root.d.ts +10 -35
  12. package/dist/dialog/root/dialog-root.d.ts +7 -29
  13. package/dist/donut-chart/root/donut-chart-root.d.ts +5 -25
  14. package/dist/file-input/root/file-input-root.d.ts +5 -22
  15. package/dist/heatmap/root/heatmap-root.d.ts +13 -36
  16. package/dist/image/root/image-root.d.ts +5 -22
  17. package/dist/input/root/input-root.d.ts +11 -29
  18. package/dist/kbd/root/kbd-root.d.ts +8 -26
  19. package/dist/legend/root/legend-root.d.ts +6 -24
  20. package/dist/listbox/root/listbox-root.d.ts +4 -26
  21. package/dist/megamenu/root/megamenu-root.d.ts +7 -39
  22. package/dist/meter/root/meter-root.d.ts +6 -24
  23. package/dist/number-input/root/number-input-root.d.ts +11 -20
  24. package/dist/outline/root/outline-root.d.ts +8 -27
  25. package/dist/pagination/root/pagination-root.d.ts +21 -54
  26. package/dist/pagination/summary/pagination-summary.d.ts +10 -31
  27. package/dist/progress/root/progress-root.d.ts +4 -21
  28. package/dist/rating/root/rating-root.d.ts +8 -31
  29. package/dist/slider/root/slider-root.d.ts +9 -32
  30. package/dist/splitter/root/splitter-root.d.ts +17 -85
  31. package/dist/steps/root/steps-root.d.ts +8 -26
  32. package/dist/steps/step/step.d.ts +11 -41
  33. package/dist/toast/item/toast-item.d.ts +6 -30
  34. package/dist/toggle/group/toggle-group.d.ts +7 -25
  35. package/dist/tooltip/root/tooltip-root.d.ts +14 -36
  36. package/dist/tour/root/tour-root.d.ts +6 -29
  37. package/dist/tree/item/tree-item.d.ts +13 -35
  38. package/dist/video/root/video-root.d.ts +12 -40
  39. package/dist/video-playlist/menu/video-playlist-menu.d.ts +3 -25
  40. package/dist/virtual-list/root/virtual-list-root.d.ts +5 -44
  41. package/package.json +17 -18
  42. package/{jsx.d.ts → types/jsx.d.ts} +724 -2537
  43. package/{vue.d.ts → types/vue.d.ts} +39 -39
  44. /package/{svelte.d.ts → types/svelte.d.ts} +0 -0
@@ -10,29 +10,12 @@ declare const HelixHeadlessFileInput_base: (new (...args: any[]) => import("@fus
10
10
  * replaced.** It is the only way to open a file picker: the dialog is a
11
11
  * privileged action a script cannot start on its own, and it opens for a click
12
12
  * on the input or on a `<label>` pointing at it. A component that draws its own
13
- * button ends up calling `.click()` on a hidden input anyway, and then has two
14
- * things a screen reader can find where there is one control.
13
+ * button calls `.click()` on a hidden input anyway, and then has two things a
14
+ * screen reader can find where there is one control.
15
15
  *
16
- * So the input stays, holds the label, the name, the validity and the focus,
17
- * and the visible trigger is a `<label for>` rather than a button. A label is
18
- * not focusable and does not need to be: the input is, and Enter and Space on it
19
- * open the picker because that is what the platform does with a file input.
20
- *
21
- * **Dropping is why this is a component rather than an attribute.** A bare file
22
- * input accepts a drop only on the input itself, which is invisible here, so the
23
- * field is the drop target and it says when something is over it.
24
- *
25
- * **Files are not a value a consumer can hand back.** Every other control here
26
- * takes the controlled and uncontrolled pair, and this one does not: a `FileList`
27
- * cannot be constructed from markup, and browsers refuse a consumer writing
28
- * `value` on a file input outright, because a page that could name a file could
29
- * read it. So this element owns what was chosen, announces it, and offers
30
- * `clear()` for the one change a consumer can legitimately ask for.
31
- *
32
- * **The list under it is the control's own, not decoration.** A file input with
33
- * files chosen reports "3 files" and nothing else, which is unusable for
34
- * checking that the right ones were picked. The names are rendered, and each
35
- * carries the way to take it back out.
16
+ * So the input stays, holding the label, the name, the validity and the focus,
17
+ * and the visible trigger is a `<label for>`. A label is not focusable and does
18
+ * not need to be: the input is, and Enter and Space on it open the picker.
36
19
  *
37
20
  * @since 0.1.0
38
21
  * @status stable
@@ -6,49 +6,26 @@ declare const HelixHeadlessHeatmap_base: typeof HelixElement & (new (...args: an
6
6
  * @summary A strip of cells colored by how each one went: uptime, contributions, activity.
7
7
  *
8
8
  * @description
9
- * **One family covers both readings, and the split people expect does not
10
- * exist.** The argument for two is that a contribution graph needs a continuous
11
- * ramp where uptime needs a category. GitHub's contribution graph is five
12
- * discrete levels rather than a gradient, so a cell that takes a tone covers
13
- * both: `rows="1"` is a status strip, `rows="7"` is a contribution graph, and
14
- * heatmap is the name of that.
9
+ * **One family covers both readings.** `rows="1"` is a status strip, `rows="7"`
10
+ * is a contribution graph, and a cell that takes a tone covers both: GitHub's
11
+ * contribution graph is five discrete levels rather than a gradient.
15
12
  *
16
13
  * **What it cannot be is a bar chart with flat values**, and the data model is
17
- * what closes that rather than taste: chart color is per *series* everywhere
18
- * else here, `--chart-series-1` through `-15`, so ninety differently colored
19
- * cells would be ninety series.
14
+ * what closes that: chart color is per *series* everywhere else here, so ninety
15
+ * differently colored cells would be ninety series.
20
16
  *
21
- * The data is a real `<table>` named by `for`, the same convention every chart
22
- * in this library uses, which also answers the accessibility question without
23
- * inventing anything: **the table is the accessible representation**, it is
24
- * already in the markup, and the drawing is a picture of it. Hide it with
25
- * `data-hidden-visually` and the reading is still there for anyone who needs it.
17
+ * **The data is a real `<table>` named by `for`**, the convention every chart
18
+ * in this library uses, and it is the accessible representation. Hide it with
19
+ * `data-hidden-visually` and the reading is still there.
26
20
  *
27
21
  * A cell takes its tone from its own `data-tone`, or from `thresholds` and
28
- * `tones` bucketing its value. The first is what a status page wants and the
29
- * second is what a contribution graph wants; the cell's own wins, so a page that
30
- * already knows a day was degraded does not have to encode that as a number for
31
- * this component to decode again.
22
+ * `tones` bucketing its value. The cell's own wins, so a page that already
23
+ * knows a day was degraded does not encode that as a number to be decoded.
32
24
  *
33
25
  * **A tone is a name this tier never resolves.** `tone="success"` writes
34
- * `--heatmap-tone: var(--heatmap-tone-success)` onto the cell and stops there,
35
- * so what a name is worth is the answering tier's to say: `helix/styled` points
36
- * the semantic names at the theme's status colors and the numbered ones at a
37
- * ramp, and a consumer on `headless` declares the same names as whatever they
38
- * like. Reaching for `var(--helix-color-${tone})` here is the dependency the
39
- * tiers exist to prevent, and it fails loudly rather than quietly: off-tier that
40
- * property is not undefined, it is *invalid*, so
41
- * `background-color: var(--heatmap-tone, …)` declines its own fallback and every
42
- * cell comes out transparent, leaving a blank page under a row of footer text.
43
- * `shared/chart/series.css` records the same fault in Bar Chart.
44
- *
45
- * **On a narrow container it draws fewer cells rather than thinner ones.**
46
- * Ninety cells in 320 pixels is three and a half pixels each, which is a smear
47
- * and not a strip. The cells it drops are the oldest, and it reflects `visible`
48
- * and fires `helix-resize` when the number changes, because **a summary saying
49
- * "99.2% over 90 days" above a strip drawing 45 of them is telling the reader
50
- * something false about the picture in front of them** and nothing about the
51
- * picture says so.
26
+ * `--heatmap-tone: var(--heatmap-tone-success)` and stops, so what a name is
27
+ * worth is the answering tier's to say. Reaching for `var(--helix-color-*)`
28
+ * here is the dependency the tiers exist to prevent.
52
29
  *
53
30
  * @since 0.1.0
54
31
  * @status experimental
@@ -5,31 +5,14 @@ import { type PropertyValues, type TemplateResult } from 'lit';
5
5
  *
6
6
  * @description
7
7
  * **An element rather than a recipe, because of the part CSS cannot do.**
8
- * Reserving the box is `aspect-ratio` and needs nothing from anybody; `Link` and
9
- * the megamenu row are recipes in `preview.css` for exactly that reason. What
10
- * cannot be written as a rule is *when the picture arrived*: a stylesheet has no
11
- * way to know, and no way to hear that it never will. So this owns three states
12
- * and the two events that move between them, and leaves the box to the
8
+ * Reserving the box is `aspect-ratio` and needs nothing from anybody. What
9
+ * cannot be written as a rule is *when the picture arrived*: a stylesheet has
10
+ * no way to know, and no way to hear that it never will. So this owns three
11
+ * states and the two events that move between them, and leaves the box to the
13
12
  * stylesheet.
14
13
  *
15
14
  * **The placeholder is a slot with nothing in it by default.** A default of
16
- * `Skeleton` would be right and is not written here: this tier renders no other
17
- * component, and reaching for one would make the headless package depend on the
18
- * styled one. The styled tier draws the resting placeholder instead, and a
19
- * consumer who wants a `Skeleton`, a blurred thumbnail or a solid color slots
20
- * it.
21
- *
22
- * **No source is a loading state, not a fourth one.** A picture whose `src` has
23
- * not arrived yet is the common case for anything drawn from data, and the
24
- * useful answer there is the same as for one still on the wire: stand something
25
- * in its place and keep the ground reserved. So an element with no `src` shows
26
- * its placeholder and waits, rather than resolving to an empty box that has to
27
- * be laid out twice.
28
- *
29
- * **`alt` is a property and not an afterthought.** An empty string is a real
30
- * answer, meaning the picture is decoration and a screen reader should skip it,
31
- * which is why the attribute is always written even when it is empty: omitting
32
- * it entirely makes a reader announce the file name instead.
15
+ * `Skeleton` would make this tier depend on a component it does not ship.
33
16
  *
34
17
  * @since 0.1.0
35
18
  * @status experimental
@@ -8,39 +8,21 @@ declare const HelixHeadlessInput_base: (new (...args: any[]) => import("@fusion.
8
8
  *
9
9
  * @description
10
10
  * Renders a real `<input>`, so the caret, selection, autofill, spellcheck, the
11
- * password manager, and the on-screen keyboard a phone chooses all come from the
12
- * platform. `delegatesFocus` puts focus on it, and it serializes into the
13
- * server's markup, so the field is usable before any script loads.
11
+ * password manager and the on-screen keyboard a phone chooses are all the
12
+ * platform's. It serializes into the server's markup, so the field is usable
13
+ * before any script loads.
14
14
  *
15
- * **The input inside a shadow root submits nothing**, because the form is on the
15
+ * **The input inside a shadow root submits nothing**, the form being on the
16
16
  * other side of the boundary. The host is form-associated instead and reports
17
- * its own value through `ElementInternals`, which is also what carries validity
18
- * and what answers a form reset.
17
+ * its own value through `ElementInternals`, which also carries validity and
18
+ * answers a form reset.
19
19
  *
20
- * `value` and `defaultValue` are separate for the reason `checked` and
21
- * `defaultChecked` are: `value` is what is in the field now and
22
- * `defaultValue` is what the markup asked for, which is where a reset returns
23
- * to and is gone the moment anyone types.
20
+ * `value` and `defaultValue` are separate the way `checked` and
21
+ * `defaultChecked` are: `value` is what is in the field now, `defaultValue` is
22
+ * what the markup asked for and where a reset returns to.
24
23
  *
25
- * **Validity is the platform's.** The `type` and `required` are set on the real
26
- * input and its `validity` is copied onto the host, so an email field rejects
27
- * what the browser says is not an email rather than what a regular expression
28
- * here guesses. What this adds is the anchor, so `reportValidity` points the
29
- * bubble at the input rather than at the host.
30
- *
31
- * `pattern`, `minlength` and `maxlength` are forwarded with them.
32
- *
33
- * **`clearable` and `reveal` are the field's, not the consumer's.** Neither is
34
- * a slot's worth of variation: one empties the value, the other flips the type,
35
- * and both want the same position, the same size and the same disabled
36
- * behavior. The `end` slot stays for what actually varies, a unit or a prefix.
37
- *
38
- * Both opt in rather than following from `type`, and are booleans that default
39
- * to false. A Lit boolean property that defaults to *true* cannot be turned off
40
- * from HTML at all, only from script, which is the wrong way round for
41
- * revealing a password: a consumer should have to ask for it. Drawer's `modal`
42
- * defaults true for the opposite reason, that losing a focus trap by forgetting
43
- * a word is worse than having to say so.
24
+ * **Validity is the platform's.** `type` and `required` are set on the real
25
+ * input and its `validity` is copied onto the host.
44
26
  *
45
27
  * @since 0.1.0
46
28
  * @status experimental
@@ -5,38 +5,20 @@ import { type TemplateResult } from 'lit';
5
5
  *
6
6
  * @description
7
7
  * Renders a real `<kbd>`, so the passage still means "press this" to anything
8
- * reading the markup rather than only to a reader looking at it.
8
+ * reading the markup.
9
9
  *
10
- * **One key per element, with `keys` as the stated exception.** A *sequence* is
10
+ * **One key per element, with `keys` as the stated exception.** A sequence is
11
11
  * several of these with the separator written between them as ordinary text,
12
- * because that separator is prose: it is "then" in a sequence and "plus" in a
13
- * simultaneous press, and a component that inserted one would have to guess
14
- * which. `keys` cannot be guessing, since a chord written `mod+k` has said
15
- * which it is.
12
+ * because that separator is prose: "then" in a sequence, "plus" in a chord, and
13
+ * a component inserting one would have to guess which.
16
14
  *
17
15
  * **`keys` exists because the glyphs were being typed by hand and were wrong.**
18
16
  * A literal `⌘K` in a template tells a reader on Windows to press a key their
19
- * keyboard does not have, and nothing catches it, a string being a string.
20
- * Given `mod` this renders `⌘K` on Apple and `Ctrl+K` everywhere else.
17
+ * keyboard does not have, and nothing catches it. Given `mod` this renders
18
+ * `⌘K` on Apple and `Ctrl+K` everywhere else.
21
19
  *
22
- * **`within` is what binds it, and it is absent by default.** Without it this
23
- * draws a key and listens for nothing, which is what every existing use of this
24
- * component wants. The two values are the two honest lifetimes:
25
- *
26
- * - `control` listens within the element's own parent, which is the case this
27
- * is mostly for: a hint sitting inside the control it belongs to, where the
28
- * shortcut should work while that control has focus and stop existing when
29
- * the control does.
30
- * - `document` listens on the document, for a chord that works from anywhere.
31
- *
32
- * **A hidden `kbd` is a dead shortcut, and that is the point rather than a
33
- * trap.** Binding here ties the key to its own advertisement, so a hint that is
34
- * not rendered is a shortcut that is not offered. A chord that must outlive its
35
- * label is a chord whose owner is not a label: give it to whatever owns the
36
- * action and let this element only describe it.
37
- *
38
- * Nothing binds until this upgrades, so a shortcut is not available in the
39
- * moments before hydration.
20
+ * **`within` is what binds it, and it is absent by default**, so this draws a
21
+ * key and listens for nothing unless asked.
40
22
  *
41
23
  * @since 0.1.0
42
24
  * @status stable
@@ -6,32 +6,14 @@ import { type PropertyValues, type TemplateResult } from 'lit';
6
6
  * @description
7
7
  * A legend is a list, so it is a list: not part of a chart, and shared by
8
8
  * whichever of them needs one. It takes its entries from the same table the
9
- * chart reads, through `for`, so the two cannot disagree about how many series
10
- * there are or what they are called.
9
+ * chart reads, through `for`, so the two cannot disagree.
11
10
  *
12
- * `entries` picks which way to read that table, and the two ways are the two
13
- * shapes a chart comes in. `series` is one entry per data column, named by the
14
- * header row, which is a line or a bar chart. `categories` is one entry per
15
- * body row, named by its row header, which is a donut: its slices are the rows,
16
- * and it has no axis to write their names along.
11
+ * `entries` picks which way to read that table. `series` is one entry per data
12
+ * column, named by the header row, which is a line or a bar chart.
13
+ * `categories` is one entry per body row, which is a donut: its slices are the
14
+ * rows and it has no axis to write their names along.
17
15
  *
18
- * **The table is resolved on the client and there is no server pass.** `for` is
19
- * an id and resolving it wants a document, which the server render does not
20
- * have, so a legend ships empty and fills in on hydration. The table itself is
21
- * ordinary markup and is served whole, so nothing is missing from the page
22
- * before then, only from this list.
23
- *
24
- * The table is watched, because a legend that goes stale when its data changes
25
- * is a legend that has to be told, and nothing else in the page would know to
26
- * tell it.
27
- *
28
- * **Not hidden from a screen reader, though the mapping it draws is a color
29
- * one.** The argument for hiding it is real: the colors mean nothing to a
30
- * reader who cannot see them, and the names are already in the table. It stays
31
- * because the cost of being wrong runs one way. A duplicated list of three
32
- * words is noise; a consumer who took the table out of the accessibility tree
33
- * and then found the legend hidden too has a chart nobody can read, and neither
34
- * decision announces itself.
16
+ * **The table is resolved on the client and there is no server pass.**
35
17
  *
36
18
  * @since 0.1.0
37
19
  * @status experimental
@@ -9,37 +9,15 @@ declare const HelixHeadlessListbox_base: (new (...args: any[]) => import("@fusio
9
9
  * **The listbox is the control and the options are its vocabulary.** A Select
10
10
  * is this with a trigger in front of it, a Combobox is this with a text input,
11
11
  * and a command palette is this again: they differ in what opens the list and
12
- * what filters it, not in what the list is. Building it as its own family is
13
- * what stops each of those reimplementing the keyboard and the filtering.
12
+ * what filters it, not in what the list is.
14
13
  *
15
14
  * **`value` is comma separated when `multiple` is set**, the way
16
15
  * `Checkbox.Group`'s is. It stays a string because that is what a form submits
17
- * and what `MixinControllerValue` holds; an array property would be a second
18
- * shape for the same fact, and the two would disagree the first time one was
19
- * set without the other.
16
+ * and what `MixinControllerValue` holds.
20
17
  *
21
18
  * **Chosen and active are two different things.** Chosen is the answer; active
22
- * is where the keyboard is. In a single-select list they move together, which
23
- * is the radio group pattern and is what a reader expects from arrow keys. With
24
- * `multiple` they come apart: the arrows move without choosing and Space
25
- * chooses, because a reader passing over four options on the way to the fifth
26
- * did not mean to pick all five.
27
- *
28
- * **This does not use `KeyboardNavigationController`, and the reason is where
29
- * focus lives rather than whether moving chooses.** The controller already
30
- * separates those, taking `origin: 'focus' | 'selection'`. What it cannot do is
31
- * leave focus alone: `NavigableItem.focusItem()` moves DOM focus to the item,
32
- * its model being a roving tab stop. A filterable list cannot have that, focus
33
- * having to stay in the field or typing stops, so what moves is a virtual
34
- * cursor and `aria-activedescendant` is what says where it is. No flag
35
- * reconciles the two.
36
- *
37
- * **Filtering hides rather than removes.** An option the filter has hidden is
38
- * still the consumer's element in the consumer's order, so a filter that clears
39
- * puts the list back exactly as it was with nothing re-rendered. What counts as
40
- * a match is a plain case-insensitive substring, and a consumer wanting more
41
- * hides options itself: `filter` is a convenience over the same `hidden` the
42
- * component uses.
19
+ * is where the keyboard is. Only the second moves with the arrows, and
20
+ * `aria-activedescendant` names it while focus stays on the list.
43
21
  *
44
22
  * @since 0.1.0
45
23
  * @status experimental
@@ -8,49 +8,17 @@ declare const HelixHeadlessMegamenu_base: (new (...args: any[]) => import("@fusi
8
8
  * **A disclosure, not a menu, and that is the whole design.** `role="menu"` and
9
9
  * `menuitem` are for an application's commands: they take over the arrow keys,
10
10
  * announce "menu" to a reader who asked for navigation, and put every link
11
- * inside a role that is not a link. What this is, is a button that shows and
12
- * hides a region of the consumer's own anchors, which is the pattern the APG
13
- * gives for site navigation and the one that leaves a middle click, a modified
11
+ * inside a role that is not a link. This is a button that shows and hides a
12
+ * region of the consumer's own anchors, which leaves a middle click, a modified
14
13
  * click and the status bar working. `Menu` remains right for a row of commands.
15
14
  *
16
- * **It renders no links**, for the reason `Navigation` gives: a router renders
17
- * its own anchor and cannot render one inside this shadow root. The panel is a
18
- * slot, and what goes in it is lists and headings the consumer wrote.
15
+ * **It renders no links.** A router renders its own anchor and cannot render
16
+ * one inside this shadow root, so the panel is a slot and what goes in it is
17
+ * the lists and headings the consumer wrote.
19
18
  *
20
19
  * **It opens on hover, and a press is always the fallback.** `open-on` says
21
- * which, and hover is the resting value because a nav bar is the one place a
22
- * reader expects to sweep along a row and see each section: making them click,
23
- * read, click again to compare two is the interaction hover exists to save.
24
- *
25
- * Hover is not the only way in, and the code is mostly about that. There is no
26
- * hover on a touch screen, so a tap opens the panel there whatever `open-on`
27
- * says, which is why the pointer type is checked rather than assumed. Focus
28
- * still opens nothing, for the same reason a tab through a nav bar should not
29
- * unfold it, but Enter on the trigger does, and a keyboard press arrives as a
30
- * click with no pointer behind it.
31
- *
32
- * A mouse click on a trigger the pointer is already sitting on is the case
33
- * worth naming: toggling there would close the panel and the pointer, still on
34
- * the trigger, would open it again, so the trigger would flicker and never
35
- * close. In hover mode the mouse's own clicks are left alone and hover governs
36
- * on its own. Escape and a click away still close it.
37
- *
38
- * The delays are intent, not decoration. Opening waits, so a pointer crossing
39
- * the bar on its way somewhere else opens nothing; closing waits longer, so the
40
- * diagonal from the trigger down into the panel does not pass through a gap and
41
- * shut it. Moving along a bar where a panel is already down opens the next one
42
- * at once, because at that point the reader is reading the bar, not crossing it.
43
- *
44
- * **Focus is not trapped and not moved.** A disclosure leaves the reader on the
45
- * trigger and the panel follows in the tab order, which is what the markup
46
- * already says, so Tab walks into the links and out the far side without
47
- * anything managing it. Escape closes and puts focus back, because Escape is
48
- * the one key that has to return the reader to where they were.
49
- *
50
- * The custom property below is written by this component and read by whoever
51
- * draws it. A consumer never sets it: it carries a number this element computed
52
- * and CSS cannot, which is what lets a stylesheet on this tier draw the thing
53
- * without measuring anything itself.
20
+ * which, and hover is the resting value because a nav bar is where a reader
21
+ * expects to sweep along a row and see each section.
54
22
  *
55
23
  * @since 0.1.0
56
24
  * @status experimental
@@ -9,31 +9,13 @@ export type MeterBand = 'fair' | 'good' | 'poor';
9
9
  * **Not `Progress`, and the difference is the whole component.** A progress bar
10
10
  * reports how far along a task is: it starts empty, ends full, and full is
11
11
  * always the good end. A meter reports a level in a range that is not going
12
- * anywhere, and which end is good is a fact about the thing being measured.
13
- * Disk usage, a battery, a score, a rate limit. So there is no `indeterminate`
14
- * here: a measurement whose value is unknown is not a measurement.
12
+ * anywhere, and which end is good is a fact about the thing measured. So there
13
+ * is no `indeterminate`: a measurement whose value is unknown is not one.
15
14
  *
16
- * **`low`, `high` and `optimum` are the reason it exists**, and they follow the
17
- * HTML specification's own rule. The three thresholds cut the range into a low
18
- * band, a middle and a high band, and `optimum` says which of the three is the
19
- * one to be in. The band the value falls in is then the good one, the one next
20
- * to it is fair, and the one two away is poor.
21
- *
22
- * **Without thresholds there are no bands, and this is where it parts company
23
- * with `<meter>`.** The platform defaults `optimum` to the midpoint and both
24
- * thresholds to the ends, so a bare `<meter value="20" max="100">` computes as
25
- * optimum and every browser paints it green. A gauge with nothing said about it
26
- * is not good news; it is a reading. Declared thresholds turn the color on.
27
- *
28
- * **And the bands are drawn as well as colored.** A tick sits at `low` and at
29
- * `high`, so the reading can be placed against them without seeing color at
30
- * all, which is the same call the carousel's current dot makes by lengthening
31
- * rather than only changing color.
32
- *
33
- * The properties below are written by this component and read by whoever draws
34
- * it. A consumer sets none of them: they carry a number this element computed
35
- * and CSS cannot, which is what lets a stylesheet on this tier draw the thing
36
- * without measuring anything itself.
15
+ * **`low`, `high` and `optimum` are the reason it exists**, following the HTML
16
+ * specification's own rule. The three cut the range into a low band, a middle
17
+ * and a high band, and `optimum` says which is the one to be in; the band next
18
+ * to it is fair and the one two away is poor.
37
19
  *
38
20
  * @since 0.1.0
39
21
  * @status stable
@@ -5,17 +5,13 @@ declare const HelixHeadlessNumberInput_base: (new (...args: any[]) => import("@f
5
5
  * @summary A number a reader types or steps to.
6
6
  *
7
7
  * @description
8
- * **A family rather than `Input`'s `type="number"`, and rather than a part of
9
- * it.** `Input` already takes that type and will keep it: a bare numeric field
10
- * in a form is a text field with a different keyboard, and that is all it needs
11
- * to be. This is a different control. It renders two buttons, binds four keys
12
- * that `Input` does not, and owns clamping and stepping, so it is an alternative
13
- * and not a variant. Namespacing it under `Input` would say it is contained by
14
- * one, which is what `IconButton` is top-level for.
8
+ * **A family rather than `Input`'s `type="number"`.** It renders two buttons,
9
+ * binds four keys `Input` does not, and owns clamping and stepping, so it is an
10
+ * alternative and not a variant.
15
11
  *
16
12
  * **The field is `type="text"` with `inputmode="decimal"`, and that is a
17
- * measurement rather than a preference.** Asked of a real browser, `type=number`
18
- * fails three ways this control cannot live with:
13
+ * measurement.** Asked of a real browser, `type=number` fails three ways this
14
+ * control cannot live with:
19
15
  *
20
16
  * ```
21
17
  * typed "12e" into type=number → .value === ""
@@ -24,21 +20,16 @@ declare const HelixHeadlessNumberInput_base: (new (...args: any[]) => import("@f
24
20
  * same keystrokes into type=text → .value === "12e"
25
21
  * ```
26
22
  *
27
- * The first is fatal on its own: the element cannot see what the reader typed,
28
- * so a controlled value is destroyed by a keystroke and there is nothing left to
29
- * correct or to report. The second changes a value because a page scrolled past.
30
- * The third means the caret cannot be managed at all, which any correcting or
31
- * formatting field needs. `inputmode` is what keeps the phone keypad, which is
23
+ * The first is fatal alone: the element cannot see what the reader typed. The
24
+ * second changes a value because a page scrolled past. The third means the
25
+ * caret cannot be managed at all. `inputmode` keeps the phone keypad, which is
32
26
  * the one thing `type=number` was giving.
33
27
  *
34
28
  * Validity is still the platform's, through `min`, `max` and `step` mirrored
35
- * onto the host's own `setValidity`. What is not mirrored is the field's
36
- * `type`, because a text field has no opinion about numbers.
29
+ * onto the host's own `setValidity`.
37
30
  *
38
- * **Stepping is separate from typing.** A reader mid-way through "1" of "12"
39
- * has not asked for anything to be clamped, so the value is only pulled into
40
- * range when they step, blur, or the form asks. Clamping on every keystroke is
41
- * what makes a field that cannot be typed backwards into.
31
+ * **Stepping is separate from typing**, so a reader mid-way through a number is
32
+ * not corrected under their hands.
42
33
  *
43
34
  * @since 0.1.0
44
35
  * @status experimental
@@ -5,35 +5,16 @@ import { type PropertyValues, type TemplateResult } from 'lit';
5
5
  *
6
6
  * @description
7
7
  * A table of contents, named for the job. It sits beside `Navigation`, which is
8
- * the same job at the other scale: one navigates a site, the other navigates a
9
- * document. `Contents` and `TableOfContents` are both taken by something else,
10
- * `display: contents` and the `content` part in one case and the `Table` family
11
- * in the other.
8
+ * the same job at the other scale: one navigates a site, the other a document.
12
9
  *
13
- * **The links are the consumer's own anchors**, the same as `Navigation`. A router
14
- * renders its own `<a>` and cannot render one inside this shadow root, and the
15
- * hrefs are fragments the page already knows.
10
+ * **The links are the consumer's own anchors.** A router renders its own `<a>`
11
+ * and cannot render one inside this shadow root, and the hrefs are fragments
12
+ * the page already knows.
16
13
  *
17
- * **What is current is decided by position, not by intersection.** The obvious
18
- * build is an `IntersectionObserver` with a thin band near the top of the
19
- * window, and it fails in both directions: with headings far apart nothing is
20
- * in the band and the marker goes out entirely, and with headings close
21
- * together several are and it flickers between them. So the observer is only
22
- * the *trigger*, and the answer is computed from where the headings actually
23
- * are: the current section is the last one whose top has passed the reading
24
- * line, or the first heading before any of them has.
25
- *
26
- * That rule is what a reader means. Two headings on screen at once are not
27
- * ambiguous to a person: they are reading under the upper one.
28
- *
29
- * **`aria-current="location"`, and it is written onto the consumer's anchor.**
30
- * This library does not normally write to light-DOM children, because an
31
- * attribute added after upgrade is one a server's markup never had. It is right
32
- * here for the reason `Carousel` names its slides: what is current is derived
33
- * from scroll position, so no server could have rendered it and there is
34
- * nothing for hydration to disagree with. `location` rather than `page`, which
35
- * is what `Navigation` uses: `page` means this is the page, and every one of these
36
- * is the same page.
14
+ * **What is current is decided by position, not by intersection.** An
15
+ * `IntersectionObserver` with a band near the top fails in both directions:
16
+ * with headings far apart nothing is in the band and the marker goes out, and
17
+ * with headings close together several are in it at once.
37
18
  *
38
19
  * @since 0.1.0
39
20
  * @status experimental
@@ -4,67 +4,34 @@ import { type PropertyValues, type TemplateResult } from 'lit';
4
4
  * @summary Moves between pages of a longer result.
5
5
  *
6
6
  * @description
7
- * The component is the truncation. Given a page and a total it works out which
8
- * numbers to show and where the runs break, which is the whole of what a
9
- * consumer would otherwise write by hand and get subtly wrong; everything else
10
- * here is markup around that. So this is data-driven rather than composed:
11
- * `<helix-pagination page="4" pages="12">` rather than twelve slotted children
12
- * a consumer has to keep in step with their own query.
7
+ * **The component is the truncation.** Given a page and a total it works out
8
+ * which numbers to show and where the runs break, so this is data-driven rather
9
+ * than composed: `<helix-pagination page="4" pages="12">` rather than twelve
10
+ * slotted children a consumer keeps in step with their own query.
13
11
  *
14
12
  * **The width does not change as you page.** The run around the current page is
15
- * `siblings` either side normally, and grows to absorb a gap slot when it meets
16
- * a boundary, so the control holds the same number of slots from the first page
17
- * to the last. Every arrangement that lets the count vary makes the buttons move
18
- * under the pointer between one click and the next.
13
+ * `siblings` either side, and grows to absorb a gap slot at a boundary, so the
14
+ * control holds the same number of slots from the first page to the last.
15
+ * Anything that lets the count vary moves the buttons under the pointer between
16
+ * one click and the next.
19
17
  *
20
- * **A gap is only drawn where it hides more than one page.** An ellipsis
21
- * standing in for a single number costs the same space as the number and tells
22
- * the reader less.
23
- *
24
- * **And a gap is a control.** It goes to the middle of the run it stands over,
25
- * which is the binary-search step: any page in a set of any size is a handful of
26
- * presses, where a jump of a fixed size would need forty of them on a set of a
27
- * hundred and twenty-eight. A menu listing the hidden pages was the other idea
28
- * and the arithmetic refuses it, for the reasons on `renderGap`.
18
+ * **A gap is only drawn where it hides more than one page, and a gap is a
19
+ * control.** It goes to the middle of the run it stands over, which is the
20
+ * binary-search step: any page in a set of any size is a handful of presses.
29
21
  *
30
22
  * **`href` is what makes these links.** Set it and every page renders an `<a>`
31
- * with `{page}` filled in, so the server's output is crawlable, a middle click
32
- * opens a tab, and the address bar is the state. Left unset they are buttons,
33
- * which is right for a table paging in place. Either way the click announces
34
- * `helix-navigate` first, and `preventDefault()` on it stops the navigation as
35
- * well as the change: that is how a router takes over without the page reloading
36
- * underneath it.
37
- *
38
- * **`helix-navigate` and not `helix-change`**, for the reason the calendar pages
39
- * with it too. Moving to page four chooses nothing; it moves the view over a
40
- * result that has not changed. A consumer driving the view has to be able to
41
- * hear that without being told an answer changed.
42
- *
43
- * **The count and the page-size control are their own elements.** A slot can
44
- * only offer one place: at one end of one row, in one order, on one axis.
45
- * `Pagination.Summary` and `Pagination.Size` sit beside this one instead, so
46
- * the direction and the order of a footer are the consumer's, which is what
47
- * they were always going to want. `total` and `page-size` stay here for the
48
- * arithmetic below and nothing else.
49
- *
50
- * **A single page renders nothing.** A control offering one page offers no
51
- * navigation, and the row it sits in should close over it rather than hold a
52
- * space for a thing that cannot be used. Reported through
53
- * `ElementInternals.states` rather than an attribute on the host, because an
54
- * attribute written at upgrade is markup a server's output never had and a
55
- * framework reports the difference; a custom state carries the same fact to a
56
- * stylesheet and none of that to the DOM.
23
+ * with `{page}` filled in, so the output is crawlable, a middle click opens a
24
+ * tab, and the address bar is the state. Left unset they are buttons, which is
25
+ * right for a table paging in place. Either way the click announces
26
+ * `helix-navigate` first, and `preventDefault()` stops the navigation as well
27
+ * as the change, which is how a router takes over.
57
28
  *
58
- * **And a page change is announced.** Moving the page moves `aria-current` onto
59
- * a different element, which a screen reader does not announce, so a reader who
60
- * pressed 5 hears the word "5" from the button they were on and nothing at all
61
- * about having arrived. A polite live region says where they now are. It ships
62
- * empty and is filled only on a change, because a region populated as it enters
63
- * the document announces itself on arrival.
29
+ * **`helix-navigate` and not `helix-change`.** Moving to page four chooses
30
+ * nothing; it moves the view over a result that has not changed.
64
31
  *
65
- * `page` is what the element holds and `default-page` seeds it. A consumer
66
- * paging through a router refuses `helix-navigate` and assigns their own, which
67
- * is how they take the decision.
32
+ * **The count and the page-size control are their own elements.**
33
+ * `Pagination.Summary` and `Pagination.Size` sit beside this one, because a
34
+ * slot can only offer one place: at one end of one row, in one order.
68
35
  *
69
36
  * @since 0.1.0
70
37
  * @status experimental