@fusion.dev/helix-headless 0.4.2 → 0.4.4

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
@@ -1255,7 +1255,7 @@
1255
1255
  "declarations": [
1256
1256
  {
1257
1257
  "kind": "class",
1258
- "description": "Tracks whether the page has scrolled and carries it as the custom state\n`scrolled`, so the styled tier can lift the bar off the page without a scroll\nhandler of its own. Scroll position is the one piece of state the server\ngenuinely cannot know, so it is read on connect rather than derived: a fresh\nload starts at the top, which is what the server rendered, and a reload\npart-way down corrects on hydration.\n\n**A custom state and never `data-scrolled`.** An attribute the element writes\nonto its own host is one the server did not send, so a hydrating framework\nreports the tree as mismatched, intermittently: only when the reader reloads\nfar enough down the page to pass the threshold. A consumer can answer that\nonly by suppressing hydration warnings on an element they did not write.\n`ElementInternals.states` is deliberately not in the DOM, so there is nothing\nfor a hydration pass to disagree with, and `:state(scrolled)` selects it. The\nbase class made the same move for `data-settled` and its note explains the\nrest.\n\nThe listener is passive and reads `scrollY` only, so it never blocks\nscrolling.\n\nThree regions, each in its own wrapper, because the row is a grid and a grid\nneeds three items to put the middle one in the middle. A bare slot is\n`display: contents`, so its assigned elements become grid items themselves:\ntwo actions would take two tracks and push the navigation off center. These\nwrappers earn their boxes, which is the test; they carry no inset or fill of\ntheir own, so an empty one still paints nothing.\n\nThe host is the banner and spans the viewport; the row inside it is what gets\ncentered. A banner that is itself narrowed leaves its background stopping\nshort of the window edges, which reads as a floating bar rather than as the\ntop of the page. That needs one element and one row, not a wrapper as well:\nthe host carries the material and `content` carries the measure.",
1258
+ "description": "Tracks whether the page has scrolled and carries it as the custom state\n`scrolled`, so the styled tier can lift the bar without a scroll handler of\nits own. It is read on connect rather than derived: a fresh load starts at\nthe top, which is what the server rendered, and a reload part-way down\ncorrects on hydration.\n\n**A custom state and never `data-scrolled`.** An attribute the element writes\nonto its own host is one the server did not send, so a hydrating framework\nreports a mismatch, intermittently: only when the reader reloads far enough\ndown the page. `ElementInternals.states` is not in the DOM, so there is\nnothing for a hydration pass to disagree with, and `:state(scrolled)` selects\nit.",
1259
1259
  "name": "HelixHeadlessAppHeader",
1260
1260
  "cssParts": [
1261
1261
  {
@@ -1576,7 +1576,7 @@
1576
1576
  },
1577
1577
  {
1578
1578
  "kind": "class",
1579
- "description": "A part of `Avatar` rather than a family of its own: it arranges avatars and\nis about nothing else, and its member is a complete `Avatar` the group did\nnot have to explain.\n\n**The overflow count is the group's, not a slot.** Which avatars are hidden\nis a fact the group works out from `max` and how many it was given, so a\nconsumer writing the \"+3\" themselves would be writing a number that has to\nagree with a list they do not control.\n\n**Hidden, not removed.** Past `max`, the extra avatars stay in the DOM and\nstop being shown, so nothing re-renders when `max` changes and the accessible\nname can still be built from all of them. The count is what a sighted reader\ngets; the group's own name is what everyone else gets, and \"Seven people\" is\nmore useful than seven names read in a row.\n\n**And hidden by the stylesheet, not by this class.** Hiding from `updated` is\none update too late and one element too far out: Lit renders on a microtask,\nso an avatar appended past the limit is laid out and painted before anything\nruns to hide it, and marking each one means writing attributes onto markup a\nconsumer owns. The styled tier's\n`:host([max=\"N\"]) ::slotted(:nth-child(n + N+1))` is right at the first style\nresolution and writes nothing.\n\n**The fold is finite, and the count agrees with it.** `An+B` takes a literal,\nso there is one selector per value of `max` and the range stops at twelve.\nPast that the group shows everyone, counts nothing and says so, because\nshowing everyone is a defensible answer and showing everyone beside a \"+5\" is\nnot. `max` above twelve is a list the consumer should be slicing themselves.\n\n**It is a list.** A row of faces is a set of separate people, and a group\nwith no role announces as one run of text. `role=\"list\"` needs its children\nto be `listitem`, which they cannot be told to become without writing an\nattribute onto a consumer's element, so the group carries `role=\"group\"` and\nits name instead: one announcement, correct, and nothing written to anyone\nelse's markup.",
1579
+ "description": "A part of `Avatar` rather than a family of its own: it arranges avatars and\nis about nothing else.\n\n**The overflow count is the group's, not a slot.** Which avatars are hidden\nis a fact the group works out from `max`, so a consumer writing the \"+3\"\nwould be writing a number that has to agree with a list they do not control.\n\n**Hidden, not removed.** Past `max` the extra avatars stay in the DOM and\nstop being shown, so nothing re-renders when `max` changes and the accessible\nname is still built from all of them.",
1580
1580
  "name": "HelixHeadlessAvatarGroup",
1581
1581
  "cssParts": [
1582
1582
  {
@@ -4113,7 +4113,7 @@
4113
4113
  "declarations": [
4114
4114
  {
4115
4115
  "kind": "class",
4116
- "description": "The host is the landmark, so `aria-label` on it names the trail directly and\nthere is no property mirroring one. A page may hold more than one navigation,\nand landmarks are how a screen-reader user chooses between them.\n\n`role=\"list\"` is set explicitly on the `<ol>` even though it is already a\nlist: removing the markers with `list-style: none` strips list semantics in\nSafari, and a breadcrumb trail always removes its markers.\n\nItems are slotted rather than wrapped, so they are `<ol>` children in the flat\ntree and each carries `role=\"listitem\"` itself.\n\n**The trail does not measure itself, and the overflow is markup.** Measuring\nhow many crumbs fit can never reach the server: there is no width without a\nDOM, so the server ships every crumb and the client removes some after\nhydrating, and a trail that collapses paints wrong on every single load. Nor\ndoes anybody specify a breadcrumb in units of fit. Designs say \"keep the root\nand the current page, fold the middle\", which is a fact about depth, or \"fold\nbelow this width\", which is a fact CSS knows.\n\nSo the trail lays out crumbs and separators, and the consumer writes what\nfolds: a `helix-menu` of `helix-menu-item`s in a crumb, where the gap\nbelongs. Server rendered, no observer, no stylesheet, and correct before the\nfirst script runs. A trail that should fold only at some widths is a\ncontainer query in the consumer's own sheet, which is theirs to write and\ntheirs to leave out.\n\n**And the crumbs carry no marker for any of it.** CSS can already see it: the\ncrumb with the menu in it is `:has(helix-menu)`, and the middle is\n`:not(:first-child, [current])`. A marker would be the design rule written\ntwice, once in the HTML and once in the sheet, with nothing keeping the two\nhonest.",
4116
+ "description": "The host is the landmark, so `aria-label` on it names the trail directly. A\npage may hold more than one navigation, and landmarks are how a\nscreen-reader user chooses between them.\n\n`role=\"list\"` is set explicitly on the `<ol>` even though it is one:\n`list-style: none` strips list semantics in Safari, and a breadcrumb trail\nalways removes its markers.\n\nItems are slotted rather than wrapped, so they are `<ol>` children in the\nflat tree and each carries `role=\"listitem\"` itself.\n\n**The trail does not measure itself, and the overflow is markup.**",
4117
4117
  "name": "HelixHeadlessBreadcrumbs",
4118
4118
  "cssParts": [
4119
4119
  {
@@ -7778,7 +7778,7 @@
7778
7778
  "declarations": [
7779
7779
  {
7780
7780
  "kind": "class",
7781
- "description": "**It has no opinion about where it sits.** A chat is asked for as a floating\nbutton, a page, a palette, a sidebar, a modal and an inline block, and none of\nthose is a variant of a conversation: they are containers a conversation is\nput in, and the containers already exist. `Popover` and `SpeedDial` make the\nfloating one, `Drawer` the sidebar, `Dialog` the modal, `Command` the palette,\nand a page is a page. Nothing here is positioned, nothing here has a backdrop,\nand nothing here owns a trigger, because a trigger opens a container and\ncontainers own their own. If a form factor needs a mode on this element, the\nsplit is wrong.\n\n**Scroll ownership is the behavior everything else depends on, and it is the\nmost-got-wrong thing in the category.** A transcript that grows wants to stay\nat the bottom, except when the reader has scrolled up to read, where pinning\nis hostile. The rule: pin while the reader is within `threshold` of the\nbottom, release the moment they leave it, and offer a way back. **Never scroll\na reader who is reading.**\n\nThe element owns it, because it is the only party that knows a message arrived\nand where the viewport was when it did. `pinned` announces before it moves, so\na consumer who wants to own it refuses the request and assigns their own.\n\n**The live region announces on completion, not on arrival.** The transcript is\n`role=\"log\"` with `aria-live=\"polite\"`, and a streaming message carries\n`aria-busy` while it arrives: a screen reader reading a stream token by token\nis unusable, and holding the announcement until the flag clears is the only\narrangement that works. Somebody typing is a separate status region, so it\nnever interleaves with what was said.\n\n**Not a client.** No transport, no protocol, no model. It renders a\nconversation and reports what the reader did.",
7781
+ "description": "**It has no opinion about where it sits.** A chat is asked for as a floating\nbutton, a page, a palette, a sidebar, a modal and an inline block, and none\nof those is a variant of a conversation: they are containers, and the\ncontainers already exist. Nothing here is positioned, nothing has a backdrop,\nand nothing owns a trigger. If a form factor needs a mode on this element,\nthe split is wrong.\n\n**Scroll ownership is the behavior everything else depends on.** A transcript\nthat grows wants to stay at the bottom, except when the reader has scrolled\nup to read, where pinning is hostile. The rule: pin while the reader is\nwithin `threshold` of the bottom, and let go the moment they leave it.",
7782
7782
  "name": "HelixHeadlessChat",
7783
7783
  "cssParts": [
7784
7784
  {
@@ -12631,7 +12631,7 @@
12631
12631
  "declarations": [
12632
12632
  {
12633
12633
  "kind": "class",
12634
- "description": "**The picker itself, with no field around it.** Color Input is this element in\na panel behind a text field, the same way Date Input is `helix-date-picker` in a\npanel: a form wants a field, and a settings pane or a toolbar wants the picker\nsitting flat on the page with nothing to open. Neither should be the other one\nwith parts hidden.\n\n**The square is two real range inputs.** Saturation and brightness are each an\n`<input type=\"range\">`, laid over the square and made invisible, and the\nhandle is drawn from their values. The pointer gets the square; the keyboard\ngets the arrow keys, Home, End and the page keys; a screen reader gets two\nsliders that announce a name and a number. All of it is the platform's, and\nnothing here implements a key handler. There is no `role=\"application\"`, which\nis the usual way this control is built and amounts to asking a screen reader\nto stop helping.\n\n**The working model is HSB, and the value is derived from it.** The reverse\nloses colors: black is `brightness: 0`, whose hue and saturation are both\nundefined, so a reader who drags to the bottom of the square and back would\nget gray where they had red. Holding the model means a color survives a trip\nthrough one that has no hue. A value arriving from outside is read into the\nmodel once, which is the only moment the conversion runs in that direction.\n\n**Every syntax is read; the one `format` names is written.** `#f00`,\n`#ff0000`, `rgb(255 0 0)`, `rgba(255,0,0,.5)` and `hsl(0 100% 50%)` are all\ncolors. Named colors are the exception, and deliberately: resolving\n`rebeccapurple` means shipping a table of a hundred and forty or asking the\nbrowser, and there is no browser during a server render.\n\nThe properties below are written by this component and read by whoever draws\nit. A consumer sets none of them: they carry a number this element computed\nand CSS cannot, which is what lets a stylesheet on this tier draw the thing\nwithout measuring anything itself.",
12634
+ "description": "**The picker itself, with no field around it.** Color Input is this element\nin a panel behind a text field; a settings pane or a toolbar wants the picker\nflat on the page with nothing to open.\n\n**The square is two real range inputs.** Saturation and brightness are each\nan `<input type=\"range\">`, laid over the square and made invisible, and the\nhandle is drawn from their values. The keyboard, the announcements and the\ntouch targets are the platform's, and there is no `role=\"application\"`.\n\n**The working model is HSB, and the value is derived from it.** The reverse\nloses colors: black is `brightness: 0`, whose hue and saturation are both\nundefined, so dragging to the bottom of the square and back would give gray\nwhere there had been red. A value arriving from outside is read into the\nmodel once.",
12635
12635
  "name": "HelixHeadlessColorPicker",
12636
12636
  "cssProperties": [
12637
12637
  {
@@ -13783,7 +13783,7 @@
13783
13783
  "declarations": [
13784
13784
  {
13785
13785
  "kind": "class",
13786
- "description": "**The list is `helix-listbox`, and the filtering is its own.** It already\nowns narrowing, the empty state, the highlight, `aria-activedescendant` and\nscrolling an option into view. Rewriting them here would be a second copy to\nkeep in step; what is left for this element is the field, the panel and the\ntraffic between them.\n\n**The text and the value are two things.** What a reader types is a filter,\nnot an answer: it narrows the list and is thrown away. The value is whichever\noption they then choose, and the field is rewritten to that option's words\nonce they have. A combobox that treated the typing as the value would submit\n\"jav\" to a form, which is the mistake a plain input makes and the reason this\ncomponent exists.\n\nThat is also why the field is emptied of its filter on close rather than left\nholding it: a control reopened still narrowed by the last visit's search is a\ncontrol whose second use starts from the first one's leftovers. Select\nrecords the same.\n\n**Focus stays in the field, which is the whole of what a combobox is.** The\narrows move the list without leaving the text, which is why the listbox has a\npublic `navigate`: the moving, the wrapping, the skipping of disabled options\nand the seeding from the current value all stay in the one place that already\ndoes them.\n\n`role=\"combobox\"` on the input rather than on a wrapper, because the thing\nthe pattern describes is the text field, and `aria-controls` can name the\nlistbox because both are in this shadow root. The active option cannot be\nnamed that way, being the consumer's light DOM, so it is linked by element\nreference for the reason Listbox records at length.\n\n**With `multiple`, the chosen options are tags in the field.** A field that\nsays \"Rust, Ruby, Go\" is a sentence the reader has to parse to find out what\nis in it, and offers nothing to press to take one out: Select does that today\nand it is the weakest part of it. A tag apiece is one object per answer, each\nwith its own remove control, which is what the reader already believes they\nare looking at.\n\nThe panel stays open while more than one can be chosen, because a control\nthat shut after each pick is one the reader has to reopen to keep using. The\nfilter clears on each choice instead, so the next pick starts from the whole\nlist rather than from the last search.\n\n**`creatable` announces, and does not write.** The options are the consumer's\nlight DOM and nothing here puts an element into it: a component that grew the\nconsumer's markup would be a component their framework re-renders over. What\nthis does is offer a row saying what would be added, take the value when the\nreader presses it, and say so with `helix-create`. A consumer who has to\nvalidate first refuses the event and adds nothing.\n\nWhich leaves a value naming no option, and that is what the words fall back\nto: for anything chosen the field shows the option's text if there is one and\nthe value itself otherwise. No copy is stored, so a consumer who then adds a\nreal option with that value has its words take over on the next render.\n\n**`suggest` finishes the word, and only ever forwards.** The field completes\nto the first option the typing begins, with the part the reader did not type\nselected, so the next character replaces it and Delete removes it. Applied on\ninsertion alone: completing after a deletion would put back what the reader\njust took out, and a field that cannot be shortened is a field that cannot be\ncorrected. Measured as the difference between `insertText` and the several\n`delete*` input types, which is what the platform already says about which\nhappened.\n\nThe filter stays what was typed rather than what was completed. Filtering on\nthe completion would narrow the list to the one option it came from, so a\nreader typing \"ru\" would be shown \"Rust\" alone and never learn Ruby was\nthere.",
13786
+ "description": "**The list is `helix-listbox`, and the filtering is its own.** It owns\nnarrowing, the empty state, the highlight, `aria-activedescendant` and\nscrolling an option into view. What is left for this element is the field,\nthe panel and the traffic between them.\n\n**The text and the value are two things.** What a reader types is a filter:\nit narrows the list and is thrown away. The value is whichever option they\nthen choose, and the field is rewritten to that option's words once they\nhave. The filter is cleared on close, so a reopened control does not start\nfrom the last visit's leftovers.\n\n**Focus stays in the field, which is the whole of what a combobox is.** The\narrows move the list without leaving the text, through the listbox's public\n`navigate`. `role=\"combobox\"` is on the input rather than on a wrapper,\nbecause the thing the pattern describes is the text field.\n\n**With `multiple`, the chosen options are tags in the field**, one object per\nanswer, each with its own remove control. The panel stays open while more\nthan one can be chosen and the filter clears on each choice, so the next pick\nstarts from the whole list.\n\n**`creatable` announces, and does not write.** The options are the consumer's\nlight DOM and nothing here puts an element into it: a component that grew the\nconsumer's markup would be one their framework re-renders over. It offers the\nrow and fires the event; adding the option is the consumer's.",
13787
13787
  "name": "HelixHeadlessCombobox",
13788
13788
  "cssParts": [
13789
13789
  {
@@ -16090,7 +16090,7 @@
16090
16090
  "declarations": [
16091
16091
  {
16092
16092
  "kind": "class",
16093
- "description": "**The confirmation is the component.** Writing to the clipboard is three\nlines; what makes this worth an element is that the platform gives a reader\nno feedback at all. A press that copies and looks identical to a press that\nfailed is a control nobody trusts, so they press it again, and the second\npress is indistinguishable from the first.\n\n**Announced after the write, never before.** A clipboard write is a\npermission the browser may refuse, and it is refused for real reasons: an\ninsecure origin, a document that is not focused, a user agent that requires a\ngesture this press did not qualify as. So the confirmation and the event both\nwait for the promise, and a refusal leaves the button exactly as it was.\n`HelixCopyEvent` is not cancelable for the same reason: by the time there is\nanything to say, it has already happened.\n\n**An icon button, extending IconButton.** Both of this button's faces are\nicons and it never carries a word, so it is the square control rather than a\ntext button that happens to hold a glyph: it inherits the square box, and\nwith it the rule that an icon-only control must be named. `label` is not\noptional here for the same reason it is not optional there, and the two\nfaces make it sharper: the accessible name has to describe the press, not\nwhichever picture is showing when a reader arrives.\n\n`data-copied`, not a reflected `copied`. State never reflects here: a\nserialized `copied` attribute would come back from a paste as a button\nclaiming to have copied something it never held.",
16093
+ "description": "**The confirmation is the component.** Writing to the clipboard is three\nlines; what makes this worth an element is that the platform gives a reader\nno feedback at all, so a press that copies looks identical to one that\nfailed.\n\n**Announced after the write, never before.** A clipboard write is a\npermission the browser may refuse, for real reasons: an insecure origin, a\ndocument that is not focused, a gesture that did not qualify. The\nconfirmation and the event both wait for the promise, and a refusal leaves\nthe button exactly as it was.",
16094
16094
  "name": "HelixHeadlessCopyButton",
16095
16095
  "cssParts": [
16096
16096
  {
@@ -17173,7 +17173,7 @@
17173
17173
  "declarations": [
17174
17174
  {
17175
17175
  "kind": "class",
17176
- "description": "**The schema is markup and the data is a property.** Columns are declared as\n`helix-table-column` children, because a schema is small, declarative, and\nsomething a reader should be able to see in the HTML; rows are handed over as\nan array, because data is large and a table that draws ten thousand elements\ncannot window them.\n\n```html\n<helix-data-table .rows=${rows} sort=\"name:asc\" page-size=\"20\">\n <helix-table-column field=\"name\" sortable>Name</helix-table-column>\n <helix-table-column field=\"size\" align=\"end\" sortable .cell=${bytes}>Size</helix-table-column>\n</helix-data-table>\n```\n\n`helix-table` takes rows as markup and is the right component for a table of\ntwelve things a person wrote out.\n\n**Sort and filter apply to the whole set, never the page.** Sorting the drawn\nwindow is correct on the first screen and wrong on every other one, which is\nhow it survives being looked at. The arithmetic lives in `order.ts` where\nnothing can see a window, and its spec asserts the composition rather than the\nthree steps, because each step alone passes either way.\n\n**What it does not do, stated where a consumer will read it: the cell values\ndo not server-render.** The headings do, because a column renders its own, and\nthe row elements do, because their count comes from `rows`. Reading a cell\nneeds the columns, and the columns are markup children that Lit SSR does not\nhand this element: `children` is undefined there. So a crawler and a reader on\na slow connection see a table of the right shape with nothing in it, which is\nstrictly less than `helix-table` serves for a table whose rows are markup.\n\nThat is the cost of \"the schema is markup and the data is a property\". Use\n`helix-table` where the content has to be in the served HTML and the result is\nsmall enough to write out; use this where the result is large, and accept that\nthe first paint is a shell. The obvious workaround, a `columns` property\nbeside the markup columns, is deliberately not offered: it splits one source\nof truth for the columns into two that nothing checks against each other, and\na table rendering one way on the server and another in the browser is that\nfailure with a hydration mismatch on top.\n\n**Every axis is controlled or uncontrolled, and that is what makes a server\nmode fall out rather than be bolted on.** `sort`, `filter`, `page` and\n`page-size` each defer to the consumer when set and seed themselves from a\n`default-*` when not. A consumer who answers all four holds the data flow:\nthey hear the reader's request, fetch, and hand back a new `rows` with a\n`total`. Retrofitting that inverts the flow and is a rewrite, so it is here\nfrom the start.",
17176
+ "description": "**The schema is markup and the data is a property.** Columns are declared as\n`helix-table-column` children, because a schema is small and something a\nreader should see in the HTML; rows are handed over as an array, because a\ntable that draws ten thousand elements cannot window them.\n\n```html\n<helix-data-table .rows=${rows} sort=\"name:asc\" page-size=\"20\">\n <helix-table-column field=\"name\" sortable>Name</helix-table-column>\n <helix-table-column field=\"size\" align=\"end\" sortable .cell=${bytes}>Size</helix-table-column>\n</helix-data-table>\n```\n\n`helix-table` takes rows as markup and is the right component for a table of\ntwelve things a person wrote out.\n\n**Sort and filter apply to the whole set, never the page.** Sorting the drawn\nwindow is correct on the first screen and wrong on every other one.\n\n**The cell values do not server-render.** The headings do, and the row\nelements do, but reading a cell needs the columns, and the columns are markup\nchildren Lit SSR does not hand this element. So a crawler sees a table of the\nright shape with nothing in it. That is the cost of the first paragraph: use\n`helix-table` where the served HTML has to carry the values.",
17177
17177
  "name": "HelixHeadlessDataTable",
17178
17178
  "cssProperties": [
17179
17179
  {
@@ -18601,7 +18601,7 @@
18601
18601
  "declarations": [
18602
18602
  {
18603
18603
  "kind": "class",
18604
- "description": "**The calendar is `helix-date-picker`, not a second one written here.** It\nalready knows about months, ranges, disabled days, the first day of the week\nand every keyboard rule a grid owes a reader, and a picker that reimplemented\na tenth of that would be a worse grid that also had to be maintained. This\nelement is the field, the overlay, and the translation between them.\n\n**Typed as well as picked, and the typing is the reason it is not\n`<input type=\"date\">`.** The native control gives a free picker and a free\nlocale and takes almost all styling with it, which is a fair trade until a\nproduct needs the calendar to match everything around it. What is kept from\nthe native one is the part worth keeping: a real text field, so a reader who\nknows the date types it and never opens anything.\n\n**The value is `YYYY-MM-DD`, always.** That is what a form submits, what\n`min` and `max` compare against, and what the calendar speaks. What the field\n*shows* is the same string: a locale-formatted display would have to be parsed\nback on every keystroke, and half-typed input does not survive a round trip\nthrough a formatter. The same reasoning the sandbox records for its two\nsyntaxes, which is that a document being edited must not be a rendering of\nsomething else.\n\n**Opening does not commit.** The calendar announces a date, this element takes\nit, and the overlay shuts, because a single date has nothing more to say. A\nconsumer who owns `value` answers the announcement the way they answer any\nother control here.",
18604
+ "description": "**The calendar is `helix-date-picker`, not a second one written here.** It\nalready knows about months, ranges, disabled days, the first day of the week\nand every keyboard rule a grid owes a reader. This element is the field, the\noverlay, and the translation between them.\n\n**Typed as well as picked, and the typing is why it is not\n`<input type=\"date\">`.** The native control gives a free picker and a free\nlocale and takes almost all styling with it. What is kept from it is the part\nworth keeping: a real text field.",
18605
18605
  "name": "HelixHeadlessDateInput",
18606
18606
  "cssParts": [
18607
18607
  {
@@ -20324,7 +20324,7 @@
20324
20324
  "declarations": [
20325
20325
  {
20326
20326
  "kind": "class",
20327
- "description": "A grid of days, per the APG date grid: the whole grid is one tab stop, arrows\nmove by day and week, `PageUp` and `PageDown` move by month, and `Home` and\n`End` go to the ends of the week. Only the focused cell is in the tab\nsequence.\n\nRoving `tabindex` is written on the cells, which is free here because they\nare rendered into this shadow root rather than being markup a consumer wrote.\n\nDates are `YYYY-MM-DD` strings and every calculation is in UTC. A local-time\n`Date` shifts across a timezone or a daylight-saving boundary, so the string\nis what the component holds and the arithmetic never leaves UTC.\n\n`month` is what the grid shows, separately from `value`, so paging through\nmonths does not select anything and a consumer can drive the view. Left\nunset, it follows the first selected date, and then the current month.\n\n`mode` decides how many dates are held. Absent takes one. `range` takes two\nand marks everything between them, starting over on the third click rather\nthan replacing an end at random. `multiple` toggles, so clicking a chosen\ndate takes it out. All three read and write the same comma-separated\n`value`.\n\n`locale` is worth setting where the output is compared across environments.\nLeft unset, month and day names come from the runtime's own locale, and a\nserver and a browser need not agree on what that is.\n\nA control whose value changes dispatches `helix-before-change`:\n`Input`, `Select`, `Listbox`, `Radio.Group` and `Toggle.Group` all do, and a\ncalendar submitting a date is the same kind of thing. `helix-select` stays\nwhat it is elsewhere, which is a row or an item being chosen in something\nthat reports no value at all, such as `Menu` or `Tree`.\n\nPaging is `helix-navigate` and not either of those, because it chooses\nnothing: the view moves and the answer does not.\n\nThe properties below are written by this component and read by whoever draws\nit. A consumer sets none of them: they carry a number this element computed\nand CSS cannot, which is what lets a stylesheet on this tier draw the thing\nwithout measuring anything itself.",
20327
+ "description": "A grid of days, per the APG date grid: the whole grid is one tab stop, arrows\nmove by day and week, `PageUp` and `PageDown` by month, `Home` and `End` to\nthe ends of the week. Only the focused cell is in the tab sequence.\n\n**Dates are `YYYY-MM-DD` strings and every calculation is in UTC.** A\nlocal-time `Date` shifts across a timezone or a daylight-saving boundary.\n\n`month` is what the grid shows, separately from `value`, so paging does not\nselect anything and a consumer can drive the view. Left unset it follows the\nfirst selected date, then the current month.\n\n`mode` decides how many dates are held: absent takes one, `range` takes two\nand marks everything between them, `multiple` toggles. All three read and\nwrite the same comma-separated `value`.",
20328
20328
  "name": "HelixHeadlessDatePicker",
20329
20329
  "cssProperties": [
20330
20330
  {
@@ -21630,7 +21630,7 @@
21630
21630
  "declarations": [
21631
21631
  {
21632
21632
  "kind": "class",
21633
- "description": "A real `<dialog>` opened with `showModal()`, not a div with a role and a\nhand-built trap. The platform gives four things that are each a day's work to\nreimplement and easy to get subtly wrong: the top layer, so the dialog is\nabove everything regardless of what `z-index` anyone else wrote; a real\nbackdrop; Escape; and inert-behind, so the page underneath is not reachable\nby pointer, keyboard, or a screen reader's virtual cursor.\n\n**`open` drives `showModal()`, and every way it can close comes back through\n`requestOpen`.** A native dialog can close from three directions: the\nproperty, Escape, and a `<form method=\"dialog\">` submit inside it. Left\nalone, two of those would shut the dialog while the consumer's state still\nsaid open, which is the bug that makes a modal impossible to reopen. Escape\nis intercepted and routed through the request, so `preventDefault()` on the\n`helix-before-close` refuses it; a close that happens anyway is reflected back.\n\n**Nothing here traps focus.** `showModal()` does it, and better: `inert` on\nthe rest of the document is something no JavaScript trap can achieve.\n`FocusTrapController` stays for the surfaces that are not modal.\n\nThe regions are bare slots rather than wrappers, for the reason the card's\nare: an unassigned `<slot>` is `display: contents` and generates no box, so a\ndialog with no footer has no empty band where one would be.\n\nOpened declaratively with the platform's invoker attributes:\n\n```html\n<helix-button commandfor=\"confirm\" command=\"--show\">Delete…</helix-button>\n<helix-dialog id=\"confirm\">…</helix-dialog>\n```\n\n`--show`, `--close` and `--toggle`. Custom commands rather than the built-in\n`show-modal`, because those are defined against native elements and do\nnothing when pointed at a custom one.",
21633
+ "description": "A real `<dialog>` opened with `showModal()`, not a div with a role and a\nhand-built trap. The platform gives four things that are each a day's work to\nreimplement: the top layer, so the dialog is above everything whatever\n`z-index` anyone wrote; a real backdrop; Escape; and inert-behind, so the\npage underneath is unreachable by pointer, keyboard or virtual cursor.\n\n**`open` drives `showModal()`, and every way it can close comes back through\n`requestOpen`.** A native dialog closes from three directions: the property,\nEscape, and a `<form method=\"dialog\">` submit. Left alone, two of them would\nshut the dialog while the consumer's state still said open, which is the bug\nthat makes a modal impossible to reopen.",
21634
21634
  "name": "HelixHeadlessDialog",
21635
21635
  "cssParts": [
21636
21636
  {
@@ -22579,7 +22579,7 @@
22579
22579
  "declarations": [
22580
22580
  {
22581
22581
  "kind": "class",
22582
- "description": "The chart with the narrowest honest use, so the component is narrow too.\n\n**It reads one column and treats the rows as the parts.** Every other chart\nhere takes a column per series; a ring has one whole to divide, and a second\nring inside the first is two charts sharing a picture in the most literal\nway. So the first column of numbers is the series and each row is a slice.\n\n**It refuses negative values rather than drawing them.** A share of a whole\ncannot be less than nothing, and an arc for a negative number either runs\nbackwards over its neighbors or is silently dropped. Those rows are skipped\nand the total is of what is left, which is the only reading that stays true.\n\n**The shape is drawn with a dashed stroke, not with arc commands.** One\n`<circle>` per slice, its dash set to the slice's share of the circumference\nand its offset to where the slice begins. Arc paths need the sweep flag and a\nlarge-arc flag, both of which flip at values a reader never sees coming, and\na slice of exactly one whole becomes a path with no length at all.\n\n**And it keeps its aspect ratio**, alone among the charts here. The others\nset `preserveAspectRatio=\"none\"` so one drawing fills any box and rescue\ntheir strokes with `vector-effect`; that rescues strokes and not geometry, so\na ring drawn the same way is an ellipse in anything but a square.\n\nThe table is the accessible representation and this drawing is `aria-hidden`.\n\nThe properties below are written by this component and read by whoever draws\nit. A consumer sets none of them: they carry a number this element computed\nand CSS cannot, which is what lets a stylesheet on this tier draw the thing\nwithout measuring anything itself.",
22582
+ "description": "The chart with the narrowest honest use, so the component is narrow too.\n\n**It reads one column and treats the rows as the parts.** Every other chart\nhere takes a column per series; a ring has one whole to divide, so the first\ncolumn of numbers is the series and each row is a slice.\n\n**It refuses negative values rather than drawing them.** An arc for a\nnegative number either runs backwards over its neighbors or is silently\ndropped. Those rows are skipped and the total is of what is left.",
22583
22583
  "name": "HelixHeadlessDonutChart",
22584
22584
  "cssProperties": [
22585
22585
  {
@@ -25060,7 +25060,7 @@
25060
25060
  "declarations": [
25061
25061
  {
25062
25062
  "kind": "class",
25063
- "description": "**A real `<input type=\"file\">`, kept and made invisible rather than\nreplaced.** It is the only way to open a file picker: the dialog is a\nprivileged action a script cannot start on its own, and it opens for a click\non the input or on a `<label>` pointing at it. A component that draws its own\nbutton ends up calling `.click()` on a hidden input anyway, and then has two\nthings a screen reader can find where there is one control.\n\nSo the input stays, holds the label, the name, the validity and the focus,\nand the visible trigger is a `<label for>` rather than a button. A label is\nnot focusable and does not need to be: the input is, and Enter and Space on it\nopen the picker because that is what the platform does with a file input.\n\n**Dropping is why this is a component rather than an attribute.** A bare file\ninput accepts a drop only on the input itself, which is invisible here, so the\nfield is the drop target and it says when something is over it.\n\n**Files are not a value a consumer can hand back.** Every other control here\ntakes the controlled and uncontrolled pair, and this one does not: a `FileList`\ncannot be constructed from markup, and browsers refuse a consumer writing\n`value` on a file input outright, because a page that could name a file could\nread it. So this element owns what was chosen, announces it, and offers\n`clear()` for the one change a consumer can legitimately ask for.\n\n**The list under it is the control's own, not decoration.** A file input with\nfiles chosen reports \"3 files\" and nothing else, which is unusable for\nchecking that the right ones were picked. The names are rendered, and each\ncarries the way to take it back out.",
25063
+ "description": "**A real `<input type=\"file\">`, kept and made invisible rather than\nreplaced.** It is the only way to open a file picker: the dialog is a\nprivileged action a script cannot start on its own, and it opens for a click\non the input or on a `<label>` pointing at it. A component that draws its own\nbutton calls `.click()` on a hidden input anyway, and then has two things a\nscreen reader can find where there is one control.\n\nSo the input stays, holding the label, the name, the validity and the focus,\nand the visible trigger is a `<label for>`. A label is not focusable and does\nnot need to be: the input is, and Enter and Space on it open the picker.",
25064
25064
  "name": "HelixHeadlessFileInput",
25065
25065
  "cssParts": [
25066
25066
  {
@@ -26901,7 +26901,7 @@
26901
26901
  "declarations": [
26902
26902
  {
26903
26903
  "kind": "class",
26904
- "description": "**One family covers both readings, and the split people expect does not\nexist.** The argument for two is that a contribution graph needs a continuous\nramp where uptime needs a category. GitHub's contribution graph is five\ndiscrete levels rather than a gradient, so a cell that takes a tone covers\nboth: `rows=\"1\"` is a status strip, `rows=\"7\"` is a contribution graph, and\nheatmap is the name of that.\n\n**What it cannot be is a bar chart with flat values**, and the data model is\nwhat closes that rather than taste: chart color is per *series* everywhere\nelse here, `--chart-series-1` through `-15`, so ninety differently colored\ncells would be ninety series.\n\nThe data is a real `<table>` named by `for`, the same convention every chart\nin this library uses, which also answers the accessibility question without\ninventing anything: **the table is the accessible representation**, it is\nalready in the markup, and the drawing is a picture of it. Hide it with\n`data-hidden-visually` and the reading is still there for anyone who needs it.\n\nA cell takes its tone from its own `data-tone`, or from `thresholds` and\n`tones` bucketing its value. The first is what a status page wants and the\nsecond is what a contribution graph wants; the cell's own wins, so a page that\nalready knows a day was degraded does not have to encode that as a number for\nthis component to decode again.\n\n**A tone is a name this tier never resolves.** `tone=\"success\"` writes\n`--heatmap-tone: var(--heatmap-tone-success)` onto the cell and stops there,\nso what a name is worth is the answering tier's to say: `helix/styled` points\nthe semantic names at the theme's status colors and the numbered ones at a\nramp, and a consumer on `headless` declares the same names as whatever they\nlike. Reaching for `var(--helix-color-${tone})` here is the dependency the\ntiers exist to prevent, and it fails loudly rather than quietly: off-tier that\nproperty is not undefined, it is *invalid*, so\n`background-color: var(--heatmap-tone, …)` declines its own fallback and every\ncell comes out transparent, leaving a blank page under a row of footer text.\n`shared/chart/series.css` records the same fault in Bar Chart.\n\n**On a narrow container it draws fewer cells rather than thinner ones.**\nNinety cells in 320 pixels is three and a half pixels each, which is a smear\nand not a strip. The cells it drops are the oldest, and it reflects `visible`\nand fires `helix-resize` when the number changes, because **a summary saying\n\"99.2% over 90 days\" above a strip drawing 45 of them is telling the reader\nsomething false about the picture in front of them** and nothing about the\npicture says so.",
26904
+ "description": "**One family covers both readings.** `rows=\"1\"` is a status strip, `rows=\"7\"`\nis a contribution graph, and a cell that takes a tone covers both: GitHub's\ncontribution graph is five discrete levels rather than a gradient.\n\n**What it cannot be is a bar chart with flat values**, and the data model is\nwhat closes that: chart color is per *series* everywhere else here, so ninety\ndifferently colored cells would be ninety series.\n\n**The data is a real `<table>` named by `for`**, the convention every chart\nin this library uses, and it is the accessible representation. Hide it with\n`data-hidden-visually` and the reading is still there.\n\nA cell takes its tone from its own `data-tone`, or from `thresholds` and\n`tones` bucketing its value. The cell's own wins, so a page that already\nknows a day was degraded does not encode that as a number to be decoded.\n\n**A tone is a name this tier never resolves.** `tone=\"success\"` writes\n`--heatmap-tone: var(--heatmap-tone-success)` and stops, so what a name is\nworth is the answering tier's to say. Reaching for `var(--helix-color-*)`\nhere is the dependency the tiers exist to prevent.",
26905
26905
  "name": "HelixHeadlessHeatmap",
26906
26906
  "cssProperties": [
26907
26907
  {
@@ -28916,7 +28916,7 @@
28916
28916
  "declarations": [
28917
28917
  {
28918
28918
  "kind": "class",
28919
- "description": "**An element rather than a recipe, because of the part CSS cannot do.**\nReserving the box is `aspect-ratio` and needs nothing from anybody; `Link` and\nthe megamenu row are recipes in `preview.css` for exactly that reason. What\ncannot be written as a rule is *when the picture arrived*: a stylesheet has no\nway to know, and no way to hear that it never will. So this owns three states\nand the two events that move between them, and leaves the box to the\nstylesheet.\n\n**The placeholder is a slot with nothing in it by default.** A default of\n`Skeleton` would be right and is not written here: this tier renders no other\ncomponent, and reaching for one would make the headless package depend on the\nstyled one. The styled tier draws the resting placeholder instead, and a\nconsumer who wants a `Skeleton`, a blurred thumbnail or a solid color slots\nit.\n\n**No source is a loading state, not a fourth one.** A picture whose `src` has\nnot arrived yet is the common case for anything drawn from data, and the\nuseful answer there is the same as for one still on the wire: stand something\nin its place and keep the ground reserved. So an element with no `src` shows\nits placeholder and waits, rather than resolving to an empty box that has to\nbe laid out twice.\n\n**`alt` is a property and not an afterthought.** An empty string is a real\nanswer, meaning the picture is decoration and a screen reader should skip it,\nwhich is why the attribute is always written even when it is empty: omitting\nit entirely makes a reader announce the file name instead.",
28919
+ "description": "**An element rather than a recipe, because of the part CSS cannot do.**\nReserving the box is `aspect-ratio` and needs nothing from anybody. What\ncannot be written as a rule is *when the picture arrived*: a stylesheet has\nno way to know, and no way to hear that it never will. So this owns three\nstates and the two events that move between them, and leaves the box to the\nstylesheet.\n\n**The placeholder is a slot with nothing in it by default.** A default of\n`Skeleton` would make this tier depend on a component it does not ship.",
28920
28920
  "name": "HelixHeadlessImage",
28921
28921
  "cssParts": [
28922
28922
  {
@@ -30212,7 +30212,7 @@
30212
30212
  "declarations": [
30213
30213
  {
30214
30214
  "kind": "class",
30215
- "description": "Renders a real `<input>`, so the caret, selection, autofill, spellcheck, the\npassword manager, and the on-screen keyboard a phone chooses all come from the\nplatform. `delegatesFocus` puts focus on it, and it serializes into the\nserver's markup, so the field is usable before any script loads.\n\n**The input inside a shadow root submits nothing**, because the form is on the\nother side of the boundary. The host is form-associated instead and reports\nits own value through `ElementInternals`, which is also what carries validity\nand what answers a form reset.\n\n`value` and `defaultValue` are separate for the reason `checked` and\n`defaultChecked` are: `value` is what is in the field now and\n`defaultValue` is what the markup asked for, which is where a reset returns\nto and is gone the moment anyone types.\n\n**Validity is the platform's.** The `type` and `required` are set on the real\ninput and its `validity` is copied onto the host, so an email field rejects\nwhat the browser says is not an email rather than what a regular expression\nhere guesses. What this adds is the anchor, so `reportValidity` points the\nbubble at the input rather than at the host.\n\n`pattern`, `minlength` and `maxlength` are forwarded with them.\n\n**`clearable` and `reveal` are the field's, not the consumer's.** Neither is\na slot's worth of variation: one empties the value, the other flips the type,\nand both want the same position, the same size and the same disabled\nbehavior. The `end` slot stays for what actually varies, a unit or a prefix.\n\nBoth opt in rather than following from `type`, and are booleans that default\nto false. A Lit boolean property that defaults to *true* cannot be turned off\nfrom HTML at all, only from script, which is the wrong way round for\nrevealing a password: a consumer should have to ask for it. Drawer's `modal`\ndefaults true for the opposite reason, that losing a focus trap by forgetting\na word is worse than having to say so.",
30215
+ "description": "Renders a real `<input>`, so the caret, selection, autofill, spellcheck, the\npassword manager and the on-screen keyboard a phone chooses are all the\nplatform's. It serializes into the server's markup, so the field is usable\nbefore any script loads.\n\n**The input inside a shadow root submits nothing**, the form being on the\nother side of the boundary. The host is form-associated instead and reports\nits own value through `ElementInternals`, which also carries validity and\nanswers a form reset.\n\n`value` and `defaultValue` are separate the way `checked` and\n`defaultChecked` are: `value` is what is in the field now, `defaultValue` is\nwhat the markup asked for and where a reset returns to.\n\n**Validity is the platform's.** `type` and `required` are set on the real\ninput and its `validity` is copied onto the host.",
30216
30216
  "name": "HelixHeadlessInput",
30217
30217
  "cssParts": [
30218
30218
  {
@@ -31534,7 +31534,7 @@
31534
31534
  "declarations": [
31535
31535
  {
31536
31536
  "kind": "class",
31537
- "description": "Renders a real `<kbd>`, so the passage still means \"press this\" to anything\nreading the markup rather than only to a reader looking at it.\n\n**One key per element, with `keys` as the stated exception.** A *sequence* is\nseveral of these with the separator written between them as ordinary text,\nbecause that separator is prose: it is \"then\" in a sequence and \"plus\" in a\nsimultaneous press, and a component that inserted one would have to guess\nwhich. `keys` cannot be guessing, since a chord written `mod+k` has said\nwhich it is.\n\n**`keys` exists because the glyphs were being typed by hand and were wrong.**\nA literal `⌘K` in a template tells a reader on Windows to press a key their\nkeyboard does not have, and nothing catches it, a string being a string.\nGiven `mod` this renders `⌘K` on Apple and `Ctrl+K` everywhere else.\n\n**`within` is what binds it, and it is absent by default.** Without it this\ndraws a key and listens for nothing, which is what every existing use of this\ncomponent wants. The two values are the two honest lifetimes:\n\n- `control` listens within the element's own parent, which is the case this\n is mostly for: a hint sitting inside the control it belongs to, where the\n shortcut should work while that control has focus and stop existing when\n the control does.\n- `document` listens on the document, for a chord that works from anywhere.\n\n**A hidden `kbd` is a dead shortcut, and that is the point rather than a\ntrap.** Binding here ties the key to its own advertisement, so a hint that is\nnot rendered is a shortcut that is not offered. A chord that must outlive its\nlabel is a chord whose owner is not a label: give it to whatever owns the\naction and let this element only describe it.\n\nNothing binds until this upgrades, so a shortcut is not available in the\nmoments before hydration.",
31537
+ "description": "Renders a real `<kbd>`, so the passage still means \"press this\" to anything\nreading the markup.\n\n**One key per element, with `keys` as the stated exception.** A sequence is\nseveral of these with the separator written between them as ordinary text,\nbecause that separator is prose: \"then\" in a sequence, \"plus\" in a chord, and\na component inserting one would have to guess which.\n\n**`keys` exists because the glyphs were being typed by hand and were wrong.**\nA literal `⌘K` in a template tells a reader on Windows to press a key their\nkeyboard does not have, and nothing catches it. Given `mod` this renders\n`⌘K` on Apple and `Ctrl+K` everywhere else.\n\n**`within` is what binds it, and it is absent by default**, so this draws a\nkey and listens for nothing unless asked.",
31538
31538
  "name": "HelixHeadlessKbd",
31539
31539
  "cssParts": [
31540
31540
  {
@@ -31922,7 +31922,7 @@
31922
31922
  "declarations": [
31923
31923
  {
31924
31924
  "kind": "class",
31925
- "description": "A legend is a list, so it is a list: not part of a chart, and shared by\nwhichever of them needs one. It takes its entries from the same table the\nchart reads, through `for`, so the two cannot disagree about how many series\nthere are or what they are called.\n\n`entries` picks which way to read that table, and the two ways are the two\nshapes a chart comes in. `series` is one entry per data column, named by the\nheader row, which is a line or a bar chart. `categories` is one entry per\nbody row, named by its row header, which is a donut: its slices are the rows,\nand it has no axis to write their names along.\n\n**The table is resolved on the client and there is no server pass.** `for` is\nan id and resolving it wants a document, which the server render does not\nhave, so a legend ships empty and fills in on hydration. The table itself is\nordinary markup and is served whole, so nothing is missing from the page\nbefore then, only from this list.\n\nThe table is watched, because a legend that goes stale when its data changes\nis a legend that has to be told, and nothing else in the page would know to\ntell it.\n\n**Not hidden from a screen reader, though the mapping it draws is a color\none.** The argument for hiding it is real: the colors mean nothing to a\nreader who cannot see them, and the names are already in the table. It stays\nbecause the cost of being wrong runs one way. A duplicated list of three\nwords is noise; a consumer who took the table out of the accessibility tree\nand then found the legend hidden too has a chart nobody can read, and neither\ndecision announces itself.",
31925
+ "description": "A legend is a list, so it is a list: not part of a chart, and shared by\nwhichever of them needs one. It takes its entries from the same table the\nchart reads, through `for`, so the two cannot disagree.\n\n`entries` picks which way to read that table. `series` is one entry per data\ncolumn, named by the header row, which is a line or a bar chart.\n`categories` is one entry per body row, which is a donut: its slices are the\nrows and it has no axis to write their names along.\n\n**The table is resolved on the client and there is no server pass.**",
31926
31926
  "name": "HelixHeadlessLegend",
31927
31927
  "cssParts": [
31928
31928
  {
@@ -33707,7 +33707,7 @@
33707
33707
  "declarations": [
33708
33708
  {
33709
33709
  "kind": "class",
33710
- "description": "**The listbox is the control and the options are its vocabulary.** A Select\nis this with a trigger in front of it, a Combobox is this with a text input,\nand a command palette is this again: they differ in what opens the list and\nwhat filters it, not in what the list is. Building it as its own family is\nwhat stops each of those reimplementing the keyboard and the filtering.\n\n**`value` is comma separated when `multiple` is set**, the way\n`Checkbox.Group`'s is. It stays a string because that is what a form submits\nand what `MixinControllerValue` holds; an array property would be a second\nshape for the same fact, and the two would disagree the first time one was\nset without the other.\n\n**Chosen and active are two different things.** Chosen is the answer; active\nis where the keyboard is. In a single-select list they move together, which\nis the radio group pattern and is what a reader expects from arrow keys. With\n`multiple` they come apart: the arrows move without choosing and Space\nchooses, because a reader passing over four options on the way to the fifth\ndid not mean to pick all five.\n\n**This does not use `KeyboardNavigationController`, and the reason is where\nfocus lives rather than whether moving chooses.** The controller already\nseparates those, taking `origin: 'focus' | 'selection'`. What it cannot do is\nleave focus alone: `NavigableItem.focusItem()` moves DOM focus to the item,\nits model being a roving tab stop. A filterable list cannot have that, focus\nhaving to stay in the field or typing stops, so what moves is a virtual\ncursor and `aria-activedescendant` is what says where it is. No flag\nreconciles the two.\n\n**Filtering hides rather than removes.** An option the filter has hidden is\nstill the consumer's element in the consumer's order, so a filter that clears\nputs the list back exactly as it was with nothing re-rendered. What counts as\na match is a plain case-insensitive substring, and a consumer wanting more\nhides options itself: `filter` is a convenience over the same `hidden` the\ncomponent uses.",
33710
+ "description": "**The listbox is the control and the options are its vocabulary.** A Select\nis this with a trigger in front of it, a Combobox is this with a text input,\nand a command palette is this again: they differ in what opens the list and\nwhat filters it, not in what the list is.\n\n**`value` is comma separated when `multiple` is set**, the way\n`Checkbox.Group`'s is. It stays a string because that is what a form submits\nand what `MixinControllerValue` holds.\n\n**Chosen and active are two different things.** Chosen is the answer; active\nis where the keyboard is. Only the second moves with the arrows, and\n`aria-activedescendant` names it while focus stays on the list.",
33711
33711
  "name": "HelixHeadlessListbox",
33712
33712
  "cssParts": [
33713
33713
  {
@@ -35540,7 +35540,7 @@
35540
35540
  "declarations": [
35541
35541
  {
35542
35542
  "kind": "class",
35543
- "description": "**A disclosure, not a menu, and that is the whole design.** `role=\"menu\"` and\n`menuitem` are for an application's commands: they take over the arrow keys,\nannounce \"menu\" to a reader who asked for navigation, and put every link\ninside a role that is not a link. What this is, is a button that shows and\nhides a region of the consumer's own anchors, which is the pattern the APG\ngives for site navigation and the one that leaves a middle click, a modified\nclick and the status bar working. `Menu` remains right for a row of commands.\n\n**It renders no links**, for the reason `Navigation` gives: a router renders\nits own anchor and cannot render one inside this shadow root. The panel is a\nslot, and what goes in it is lists and headings the consumer wrote.\n\n**It opens on hover, and a press is always the fallback.** `open-on` says\nwhich, and hover is the resting value because a nav bar is the one place a\nreader expects to sweep along a row and see each section: making them click,\nread, click again to compare two is the interaction hover exists to save.\n\nHover is not the only way in, and the code is mostly about that. There is no\nhover on a touch screen, so a tap opens the panel there whatever `open-on`\nsays, which is why the pointer type is checked rather than assumed. Focus\nstill opens nothing, for the same reason a tab through a nav bar should not\nunfold it, but Enter on the trigger does, and a keyboard press arrives as a\nclick with no pointer behind it.\n\nA mouse click on a trigger the pointer is already sitting on is the case\nworth naming: toggling there would close the panel and the pointer, still on\nthe trigger, would open it again, so the trigger would flicker and never\nclose. In hover mode the mouse's own clicks are left alone and hover governs\non its own. Escape and a click away still close it.\n\nThe delays are intent, not decoration. Opening waits, so a pointer crossing\nthe bar on its way somewhere else opens nothing; closing waits longer, so the\ndiagonal from the trigger down into the panel does not pass through a gap and\nshut it. Moving along a bar where a panel is already down opens the next one\nat once, because at that point the reader is reading the bar, not crossing it.\n\n**Focus is not trapped and not moved.** A disclosure leaves the reader on the\ntrigger and the panel follows in the tab order, which is what the markup\nalready says, so Tab walks into the links and out the far side without\nanything managing it. Escape closes and puts focus back, because Escape is\nthe one key that has to return the reader to where they were.\n\nThe custom property below is written by this component and read by whoever\ndraws it. A consumer never sets it: it carries a number this element computed\nand CSS cannot, which is what lets a stylesheet on this tier draw the thing\nwithout measuring anything itself.",
35543
+ "description": "**A disclosure, not a menu, and that is the whole design.** `role=\"menu\"` and\n`menuitem` are for an application's commands: they take over the arrow keys,\nannounce \"menu\" to a reader who asked for navigation, and put every link\ninside a role that is not a link. This is a button that shows and hides a\nregion of the consumer's own anchors, which leaves a middle click, a modified\nclick and the status bar working. `Menu` remains right for a row of commands.\n\n**It renders no links.** A router renders its own anchor and cannot render\none inside this shadow root, so the panel is a slot and what goes in it is\nthe lists and headings the consumer wrote.\n\n**It opens on hover, and a press is always the fallback.** `open-on` says\nwhich, and hover is the resting value because a nav bar is where a reader\nexpects to sweep along a row and see each section.",
35544
35544
  "name": "HelixHeadlessMegamenu",
35545
35545
  "cssProperties": [
35546
35546
  {
@@ -37707,7 +37707,7 @@
37707
37707
  "declarations": [
37708
37708
  {
37709
37709
  "kind": "class",
37710
- "description": "**Not `Progress`, and the difference is the whole component.** A progress bar\nreports how far along a task is: it starts empty, ends full, and full is\nalways the good end. A meter reports a level in a range that is not going\nanywhere, and which end is good is a fact about the thing being measured.\nDisk usage, a battery, a score, a rate limit. So there is no `indeterminate`\nhere: a measurement whose value is unknown is not a measurement.\n\n**`low`, `high` and `optimum` are the reason it exists**, and they follow the\nHTML specification's own rule. The three thresholds cut the range into a low\nband, a middle and a high band, and `optimum` says which of the three is the\none to be in. The band the value falls in is then the good one, the one next\nto it is fair, and the one two away is poor.\n\n**Without thresholds there are no bands, and this is where it parts company\nwith `<meter>`.** The platform defaults `optimum` to the midpoint and both\nthresholds to the ends, so a bare `<meter value=\"20\" max=\"100\">` computes as\noptimum and every browser paints it green. A gauge with nothing said about it\nis not good news; it is a reading. Declared thresholds turn the color on.\n\n**And the bands are drawn as well as colored.** A tick sits at `low` and at\n`high`, so the reading can be placed against them without seeing color at\nall, which is the same call the carousel's current dot makes by lengthening\nrather than only changing color.\n\nThe properties below are written by this component and read by whoever draws\nit. A consumer sets none of them: they carry a number this element computed\nand CSS cannot, which is what lets a stylesheet on this tier draw the thing\nwithout measuring anything itself.",
37710
+ "description": "**Not `Progress`, and the difference is the whole component.** A progress bar\nreports how far along a task is: it starts empty, ends full, and full is\nalways the good end. A meter reports a level in a range that is not going\nanywhere, and which end is good is a fact about the thing measured. So there\nis no `indeterminate`: a measurement whose value is unknown is not one.\n\n**`low`, `high` and `optimum` are the reason it exists**, following the HTML\nspecification's own rule. The three cut the range into a low band, a middle\nand a high band, and `optimum` says which is the one to be in; the band next\nto it is fair and the one two away is poor.",
37711
37711
  "name": "HelixHeadlessMeter",
37712
37712
  "cssProperties": [
37713
37713
  {
@@ -39778,7 +39778,7 @@
39778
39778
  "declarations": [
39779
39779
  {
39780
39780
  "kind": "class",
39781
- "description": "**A family rather than `Input`'s `type=\"number\"`, and rather than a part of\nit.** `Input` already takes that type and will keep it: a bare numeric field\nin a form is a text field with a different keyboard, and that is all it needs\nto be. This is a different control. It renders two buttons, binds four keys\nthat `Input` does not, and owns clamping and stepping, so it is an alternative\nand not a variant. Namespacing it under `Input` would say it is contained by\none, which is what `IconButton` is top-level for.\n\n**The field is `type=\"text\"` with `inputmode=\"decimal\"`, and that is a\nmeasurement rather than a preference.** Asked of a real browser, `type=number`\nfails three ways this control cannot live with:\n\n```\ntyped \"12e\" into type=number → .value === \"\"\nfocused, wheeled over it → 5 became 4\nselectionStart on type=number → null\nsame keystrokes into type=text → .value === \"12e\"\n```\n\nThe first is fatal on its own: the element cannot see what the reader typed,\nso a controlled value is destroyed by a keystroke and there is nothing left to\ncorrect or to report. The second changes a value because a page scrolled past.\nThe third means the caret cannot be managed at all, which any correcting or\nformatting field needs. `inputmode` is what keeps the phone keypad, which is\nthe one thing `type=number` was giving.\n\nValidity is still the platform's, through `min`, `max` and `step` mirrored\nonto the host's own `setValidity`. What is not mirrored is the field's\n`type`, because a text field has no opinion about numbers.\n\n**Stepping is separate from typing.** A reader mid-way through \"1\" of \"12\"\nhas not asked for anything to be clamped, so the value is only pulled into\nrange when they step, blur, or the form asks. Clamping on every keystroke is\nwhat makes a field that cannot be typed backwards into.",
39781
+ "description": "**A family rather than `Input`'s `type=\"number\"`.** It renders two buttons,\nbinds four keys `Input` does not, and owns clamping and stepping, so it is an\nalternative and not a variant.\n\n**The field is `type=\"text\"` with `inputmode=\"decimal\"`, and that is a\nmeasurement.** Asked of a real browser, `type=number` fails three ways this\ncontrol cannot live with:\n\n```\ntyped \"12e\" into type=number → .value === \"\"\nfocused, wheeled over it → 5 became 4\nselectionStart on type=number → null\nsame keystrokes into type=text → .value === \"12e\"\n```\n\nThe first is fatal alone: the element cannot see what the reader typed. The\nsecond changes a value because a page scrolled past. The third means the\ncaret cannot be managed at all. `inputmode` keeps the phone keypad, which is\nthe one thing `type=number` was giving.\n\nValidity is still the platform's, through `min`, `max` and `step` mirrored\nonto the host's own `setValidity`.\n\n**Stepping is separate from typing**, so a reader mid-way through a number is\nnot corrected under their hands.",
39782
39782
  "name": "HelixHeadlessNumberInput",
39783
39783
  "cssParts": [
39784
39784
  {
@@ -41895,7 +41895,7 @@
41895
41895
  "declarations": [
41896
41896
  {
41897
41897
  "kind": "class",
41898
- "description": "A table of contents, named for the job. It sits beside `Navigation`, which is\nthe same job at the other scale: one navigates a site, the other navigates a\ndocument. `Contents` and `TableOfContents` are both taken by something else,\n`display: contents` and the `content` part in one case and the `Table` family\nin the other.\n\n**The links are the consumer's own anchors**, the same as `Navigation`. A router\nrenders its own `<a>` and cannot render one inside this shadow root, and the\nhrefs are fragments the page already knows.\n\n**What is current is decided by position, not by intersection.** The obvious\nbuild is an `IntersectionObserver` with a thin band near the top of the\nwindow, and it fails in both directions: with headings far apart nothing is\nin the band and the marker goes out entirely, and with headings close\ntogether several are and it flickers between them. So the observer is only\nthe *trigger*, and the answer is computed from where the headings actually\nare: the current section is the last one whose top has passed the reading\nline, or the first heading before any of them has.\n\nThat rule is what a reader means. Two headings on screen at once are not\nambiguous to a person: they are reading under the upper one.\n\n**`aria-current=\"location\"`, and it is written onto the consumer's anchor.**\nThis library does not normally write to light-DOM children, because an\nattribute added after upgrade is one a server's markup never had. It is right\nhere for the reason `Carousel` names its slides: what is current is derived\nfrom scroll position, so no server could have rendered it and there is\nnothing for hydration to disagree with. `location` rather than `page`, which\nis what `Navigation` uses: `page` means this is the page, and every one of these\nis the same page.",
41898
+ "description": "A table of contents, named for the job. It sits beside `Navigation`, which is\nthe same job at the other scale: one navigates a site, the other a document.\n\n**The links are the consumer's own anchors.** A router renders its own `<a>`\nand cannot render one inside this shadow root, and the hrefs are fragments\nthe page already knows.\n\n**What is current is decided by position, not by intersection.** An\n`IntersectionObserver` with a band near the top fails in both directions:\nwith headings far apart nothing is in the band and the marker goes out, and\nwith headings close together several are in it at once.",
41899
41899
  "name": "HelixHeadlessOutline",
41900
41900
  "cssParts": [
41901
41901
  {
@@ -42418,7 +42418,7 @@
42418
42418
  "declarations": [
42419
42419
  {
42420
42420
  "kind": "class",
42421
- "description": "The component is the truncation. Given a page and a total it works out which\nnumbers to show and where the runs break, which is the whole of what a\nconsumer would otherwise write by hand and get subtly wrong; everything else\nhere is markup around that. So this is data-driven rather than composed:\n`<helix-pagination page=\"4\" pages=\"12\">` rather than twelve slotted children\na consumer has to keep in step with their own query.\n\n**The width does not change as you page.** The run around the current page is\n`siblings` either side normally, and grows to absorb a gap slot when it meets\na boundary, so the control holds the same number of slots from the first page\nto the last. Every arrangement that lets the count vary makes the buttons move\nunder the pointer between one click and the next.\n\n**A gap is only drawn where it hides more than one page.** An ellipsis\nstanding in for a single number costs the same space as the number and tells\nthe reader less.\n\n**And a gap is a control.** It goes to the middle of the run it stands over,\nwhich is the binary-search step: any page in a set of any size is a handful of\npresses, where a jump of a fixed size would need forty of them on a set of a\nhundred and twenty-eight. A menu listing the hidden pages was the other idea\nand the arithmetic refuses it, for the reasons on `renderGap`.\n\n**`href` is what makes these links.** Set it and every page renders an `<a>`\nwith `{page}` filled in, so the server's output is crawlable, a middle click\nopens a tab, and the address bar is the state. Left unset they are buttons,\nwhich is right for a table paging in place. Either way the click announces\n`helix-navigate` first, and `preventDefault()` on it stops the navigation as\nwell as the change: that is how a router takes over without the page reloading\nunderneath it.\n\n**`helix-navigate` and not `helix-change`**, for the reason the calendar pages\nwith it too. Moving to page four chooses nothing; it moves the view over a\nresult that has not changed. A consumer driving the view has to be able to\nhear that without being told an answer changed.\n\n**The count and the page-size control are their own elements.** A slot can\nonly offer one place: at one end of one row, in one order, on one axis.\n`Pagination.Summary` and `Pagination.Size` sit beside this one instead, so\nthe direction and the order of a footer are the consumer's, which is what\nthey were always going to want. `total` and `page-size` stay here for the\narithmetic below and nothing else.\n\n**A single page renders nothing.** A control offering one page offers no\nnavigation, and the row it sits in should close over it rather than hold a\nspace for a thing that cannot be used. Reported through\n`ElementInternals.states` rather than an attribute on the host, because an\nattribute written at upgrade is markup a server's output never had and a\nframework reports the difference; a custom state carries the same fact to a\nstylesheet and none of that to the DOM.\n\n**And a page change is announced.** Moving the page moves `aria-current` onto\na different element, which a screen reader does not announce, so a reader who\npressed 5 hears the word \"5\" from the button they were on and nothing at all\nabout having arrived. A polite live region says where they now are. It ships\nempty and is filled only on a change, because a region populated as it enters\nthe document announces itself on arrival.\n\n`page` is what the element holds and `default-page` seeds it. A consumer\npaging through a router refuses `helix-navigate` and assigns their own, which\nis how they take the decision.",
42421
+ "description": "**The component is the truncation.** Given a page and a total it works out\nwhich numbers to show and where the runs break, so this is data-driven rather\nthan composed: `<helix-pagination page=\"4\" pages=\"12\">` rather than twelve\nslotted children a consumer keeps in step with their own query.\n\n**The width does not change as you page.** The run around the current page is\n`siblings` either side, and grows to absorb a gap slot at a boundary, so the\ncontrol holds the same number of slots from the first page to the last.\nAnything that lets the count vary moves the buttons under the pointer between\none click and the next.\n\n**A gap is only drawn where it hides more than one page, and a gap is a\ncontrol.** It goes to the middle of the run it stands over, which is the\nbinary-search step: any page in a set of any size is a handful of presses.\n\n**`href` is what makes these links.** Set it and every page renders an `<a>`\nwith `{page}` filled in, so the output is crawlable, a middle click opens a\ntab, and the address bar is the state. Left unset they are buttons, which is\nright for a table paging in place. Either way the click announces\n`helix-navigate` first, and `preventDefault()` stops the navigation as well\nas the change, which is how a router takes over.\n\n**`helix-navigate` and not `helix-change`.** Moving to page four chooses\nnothing; it moves the view over a result that has not changed.\n\n**The count and the page-size control are their own elements.**\n`Pagination.Summary` and `Pagination.Size` sit beside this one, because a\nslot can only offer one place: at one end of one row, in one order.",
42422
42422
  "name": "HelixHeadlessPagination",
42423
42423
  "cssParts": [
42424
42424
  {
@@ -43715,7 +43715,7 @@
43715
43715
  "declarations": [
43716
43716
  {
43717
43717
  "kind": "class",
43718
- "description": "Its own element rather than a part of `Pagination`, so a consumer decides\nwhere it goes. Inside the run it could only ever be at one end of one row;\nbeside it, it goes above, below, in a table's caption, or on the other side of\nthe page, and the direction and order of the whole footer are the consumer's.\n\n**It repeats `page`, `page-size` and `total`, and the cost of that is skew\nrather than verbosity.** Those three come from the consumer's own query, so\nwriting them on two elements is one variable used twice; what it risks is the\ntwo disagreeing, and a count reading `76-100 of 248` beside a run showing page\n7 is the same failure this element's own clamping exists to prevent, arriving\nfrom the outside instead.\n\n**Nothing removes it at this level, and that was measured rather than\nassumed.** A coordinating root can only reach children the consumer laid out\nby writing attributes onto them, which a framework reports as a hydration\nerror, or through context, which is dead on a server: `@lit/context`\nsubscribes from `connectedCallback`, and Lit SSR does not call it, so every\nvalue is `undefined` in exactly the render this element exists to be part of.\nConfirmed against Lit SSR directly. The fork is real: children in the light\nDOM mean the consumer owns the layout and no root can feed them; children in a\nshadow root mean a root feeds them by ordinary binding and owns the layout.\n\n**It resolves one level up, at a composite, and the state is why.** `page`,\n`page-size` and `total` are facts about a *result*, not about a pagination, so\nthe element that should hold them once is the one that holds the result: a\ndata table composing a pagination inside itself, rather than a pagination root\ninvented to hold three pagination elements together. That root would have been\nplumbing; a table's paging state is the thing itself. And because a composite\nrenders its parts into its own shadow root, it lands on the side of the fork\nwhere binding works and the server renders, at no cost to anyone.\n\nRenders nothing without a `total` and a `page-size`. A wrong count is worse\nthan none: it is the one number in a footer a reader cannot check against\nanything else on screen.",
43718
+ "description": "Its own element rather than a part of `Pagination`, so a consumer decides\nwhere it goes: above, below, in a table's caption, or on the other side of\nthe page. Inside the run it could only ever be at one end of one row.\n\n**It repeats `page`, `page-size` and `total`, and the cost of that is skew.**\nThose three come from the consumer's own query, so writing them on two\nelements is one variable used twice; what it risks is the two disagreeing.\n\n**Nothing removes that at this level, and it was measured.** A coordinating\nroot can only reach light-DOM children by writing attributes onto them, which\na framework reports as a hydration error, or through context, which is dead\non a server: `@lit/context` subscribes from `connectedCallback`, and Lit SSR\ndoes not call it.",
43719
43719
  "name": "HelixHeadlessPaginationSummary",
43720
43720
  "members": [
43721
43721
  {
@@ -45902,7 +45902,7 @@
45902
45902
  "declarations": [
45903
45903
  {
45904
45904
  "kind": "class",
45905
- "description": "Determinate by default, and `indeterminate` for a task whose total is not\nknown yet. `Loader` is still the right answer where the wait is short or has\nno place of its own on the page; a bar earns its width where the work is the\nsubject of the region it sits in.\n\nA bar that fills at a made-up rate would be a bar that lies, so the\nindeterminate one does not fill: it runs a segment across the track, which\nsays work is happening and claims no proportion at all. `aria-valuenow` is\nomitted with it, which is the platform's own signal for the same thing.\n\n**The fill is a fraction of the track, written as a custom property.**\n`--progress-fraction` is derived from `value` and `max` rather than measured,\nso it renders on a server with no layout and needs nothing after upgrade. That\nis what keeps it out of the class of geometry this library writes only on the\nclient. A consumer never sets it: it carries a number this element computed\nand CSS cannot, which is what lets a stylesheet draw the thing without\nmeasuring anything itself.\n\n**`value` is clamped, and out-of-range input is not an error.** A count that\nruns past its own total is ordinary in real applications, and a bar that\noverflows its track or reports `aria-valuenow` above `aria-valuemax` is worse\nthan one that reads as finished.\n\nSemantics are on the host through `ElementInternals`, the same as everywhere\nelse: a `role` in the shadow root announces correctly and leaves the host\nanonymous to anything walking the light DOM, and a `role` written onto the\nhost as an attribute is one a server's markup never had.",
45905
+ "description": "Determinate by default, and `indeterminate` for a task whose total is not\nknown yet. `Loader` is still right where the wait is short or has no place of\nits own on the page.\n\nA bar that fills at a made-up rate would be a bar that lies, so the\nindeterminate one does not fill: it runs a segment across the track, which\nsays work is happening and claims no proportion. `aria-valuenow` is omitted\nwith it, which is the platform's own signal for the same thing.\n\n**The fill is a fraction of the track, written as a custom property.**",
45906
45906
  "name": "HelixHeadlessProgress",
45907
45907
  "cssProperties": [
45908
45908
  {
@@ -48346,7 +48346,7 @@
48346
48346
  "declarations": [
48347
48347
  {
48348
48348
  "kind": "class",
48349
- "description": "**Half marks decide the shape.** Supporting them means the value is not an\ninteger and the targets are not the marks: five stars at a half step is ten\nplaces to press, and a mark that is filled to the middle. The pointer handling\nand `step` are both built around that rather than added over it.\n\n**A partial mark is the whole mark, clipped.** Not a second glyph filled to\nthe middle: that needs a matching pair drawn for every mark anybody uses, and\nthe moment a consumer wants a heart or a circle they need to draw one too. The\nfill is the same marks over the same marks, cut off partway, so any mark works\nand any fraction does.\n\n**Read-only is not this control grayed out, it is a different thing.** The\ncommoner use is showing a score nobody is being asked for, a product's\naverage, and that is not a control at all: it takes no focus, answers no keys,\nhas no options to choose between, and reads as one thing rather than as five.\nSo `readonly` is `role=\"img\"` with the score in its name, where the control is\na radio group. A disabled control is still a control and still says so; this\nis not one.\n\n**Moving is choosing here**, which is the radio group's reading and the\nopposite of `Menu`'s: arriving at four stars *is* rating it four, because\nnothing else follows and there is nothing to confirm. That is what\n`KeyboardNavigationController` takes `selects` for, and why this can use it\nwhere a menu cannot.\n\n**The mark is a stylesheet's, not a slot's.** A slot projects its content\nonce, and there are two layers of marks here: slotting a star would put it in\nthe track or the fill and leave the other empty. So the mark is a shape the\nstyled tier masks with, which is also what lets a partial one be a clip of the\nwhole rather than a second drawing of it.\n\nThe properties below are written by this component and read by whoever draws\nit. A consumer sets none of them: they carry a number this element computed\nand CSS cannot, which is what lets a stylesheet on this tier draw the thing\nwithout measuring anything itself.",
48349
+ "description": "**Half marks decide the shape.** Supporting them means the value is not an\ninteger and the targets are not the marks: five stars at a half step is ten\nplaces to press. The pointer handling and `step` are built around that\nrather than added over it.\n\n**A partial mark is the whole mark, clipped**, not a second glyph filled to\nthe middle: the fill is the same marks over the same marks, cut off partway,\nso any mark works and any fraction does.\n\n**Read-only is not this control grayed out.** The commoner use is showing a\nscore nobody is being asked for, which is a reading rather than a disabled\ninput, and it is announced as one.",
48350
48350
  "name": "HelixHeadlessRating",
48351
48351
  "cssProperties": [
48352
48352
  {
@@ -51577,7 +51577,7 @@
51577
51577
  "declarations": [
51578
51578
  {
51579
51579
  "kind": "class",
51580
- "description": "A real `<input type=\"range\">`, so the drag, the arrow keys, Home and End, the\npage keys and the touch target are all the platform's. Every one of those is\na thing a hand-built slider gets wrong, and the last is the one nobody\nnotices: a native range has a hit area larger than its thumb, sized by the\nplatform for a finger.\n\n**The value is a string, like every other form control's.** A range submits\ntext and `MixinControllerValue` holds text, so the number is a reading of the\nvalue rather than the value itself. `valueAsNumber` is there for anything\ndoing arithmetic on it.\n\n**Marks are drawn rather than left to `<datalist>`.** A datalist's ticks\ncannot be styled at all, and Firefox does not render them for a range at all,\nso the platform's version is a mark that looks like someone else's design\nwhere it appears and no mark where it does not. Drawing them costs the\nsemantics nothing, because a mark is decoration: what a reader can actually\nland on is `step`, which the input enforces whether or not anything is drawn.\n\n`markers` is a count rather than a flag tied to `step`. A flag reads well\nuntil a slider from zero to a hundred stepping by one asks for a hundred and\none ticks, and then it is either noise or a silent refusal to draw. A number\nis what the reader sees, so it cannot surprise anyone.\n\n**A range is two of them, not a hand-built one.** There is no native\ntwo-thumb range, so two are stacked on one track and each thumb keeps its own\ndrag, its own arrow keys, its own Home and End and its own finger-sized hit\narea. Rebuilding would throw away every argument this component is made of,\nto gain a second handle.\n\nCrossing is stopped in the handler rather than by the bounds, and that is not\nthe obvious way round. Giving the lower input `max` of the upper reads well\nand breaks the drawing: an input's thumb sits at a fraction of *its own*\nrange, so a lower thumb bounded at the upper one paints against a shorter\ntrack and the two stop lining up. Both inputs span the whole range and the\nvalues are clamped against each other on the way in.\n\nThe properties below are written by this component and read by whoever draws\nit. A consumer sets none of them: they carry a number this element computed\nand CSS cannot, which is what lets a stylesheet on this tier draw the thing\nwithout measuring anything itself.",
51580
+ "description": "A real `<input type=\"range\">`, so the drag, the arrow keys, Home and End, the\npage keys and the touch target are the platform's. The last is the one nobody\nnotices: a native range has a hit area larger than its thumb, sized by the\nplatform for a finger.\n\n**The value is a string, like every other form control's.** A range submits\ntext and `MixinControllerValue` holds text, so the number is a reading of the\nvalue. `valueAsNumber` is there for arithmetic.\n\n**Marks are drawn rather than left to `<datalist>`**, whose ticks cannot be\nstyled and which Firefox does not render for a range at all. A mark is\ndecoration; what a reader can land on is `step`, which the input enforces\nwhether or not anything is drawn. `markers` is a count rather than a flag, so\na slider from zero to a hundred cannot silently ask for a hundred and one.\n\n**A range is two of them.** There is no native two-thumb range, so two are\nstacked on one track and each thumb keeps its own.",
51581
51581
  "name": "HelixHeadlessSlider",
51582
51582
  "cssProperties": [
51583
51583
  {
@@ -54171,7 +54171,7 @@
54171
54171
  "declarations": [
54172
54172
  {
54173
54173
  "kind": "class",
54174
- "description": "**One axis, and nesting is how you get two.** The sandbox this was taken from\nmoves a cross: a divider down the middle, another across the bottom, and a\ncorner that moves both. That is a layout rather than a component, and every\npart of it that is worth sharing is one divider between two panes. A splitter\ninside a splitter gives the cross back, and gives the three-pane and the\nfour-pane arrangements it never had.\n\n**Where two of them cross there is a corner, and it moves both.** A splitter\nslotted into a pane of another, at right angles to it, has one end of its\ndivider sitting exactly on the other's. That end grows a handle: `move` under\nthe pointer, both arrow axes on the keyboard, and a drag that carries the two\npositions at once.\n\n**The outer splitter draws it and the nested one announces itself upward.**\nThe nested one drawing its own is the tempting version, since it already\nknows where its divider begins and needs no measurement of the other, and it\nis clipped: the crossing lies on the boundary between a pane and the divider\nbeside it, a handle centered there reaches back over the boundary, and a pane\nis `overflow: auto`. Exactly half the corner is cut off. The outer separator\nis the one element that already straddles the seam, so the corner belongs in\nit.\n\nThat makes the channel between the two a single call in a single direction:\n`cross(child, at)` says where the child crosses, `moveTo` drives either one,\nand neither reads the other's state. The child is what calls it, because a\nparent cannot poll for a child that has not upgraded yet.\n\nThe one number it carries is a percentage rather than a measurement, which is\nwhat keeps either of them from measuring the other, and it assumes the nested\nsplitter fills its pane. That is what slotting one into a pane does. Give the\nnested splitter its own inset, or wrap it in something smaller than the pane,\nand the corner is drawn where the crossing would be rather than where it is.\n\n**It is a client-side enhancement and says so.** Finding the parent means\ninspecting the tree, which a server cannot do, so the corner appears at\n`firstUpdated` and never in the served markup. That is allowed for exactly\nthis: it is an affordance rather than content, and a splitter with no corner\nyet is a splitter with two working dividers.\n\nWhere two dividers are parallel there is no corner, because there is nothing\nto take hold of. Where they cross without a corner, the inner divider takes\nthe pointer: every separator sits on the same layer, so a tie goes to document\norder and a deeper splitter is always later.\n\n**A divider that is moving says so, whoever is moving it.** `:hover` cannot\ncarry that: a drag holds the pointer through capture and a fast one leaves the\ntarget behind, and a corner moves the nested divider while the reader is on\nthe crossing, nowhere near it. So a drag in progress is a state, and both\ndividers a corner moves are lit for as long as it moves them.\n\n**The divider is a real `role=\"separator\"` with `aria-valuenow`, moved by the\narrow keys as well as by pointer.** A divider only a mouse can move is\nunusable without one, which is the reason the sandbox's carries the same, and\n`Home` and `End` take it to its bounds.\n\n**It goes where it is put, all the way to either end.** `min` and `max` are\nnothing and everything unless a consumer says otherwise, because a divider\nthat stops dead short of the edge gives a reader no way to put a pane away.\nWhere a pane should keep a sliver of itself, only whoever put the tabs on it\nknows how much, so they say so.\n\n**A pane can be shut, and that is not the same as a divider at the edge.**\nThe geometry is identical and the knowledge is not: only a shut pane knows\nwhere to open back to. So `collapsed` is a second state rather than a reading\nof the position, and the position it opens to is simply the one collapsing\nnever overwrote. `collapsed-size` keeps a sliver showing where a pane has a\ntab strip worth leaving, and collapse overrides `min` and `max`, which is\nwhat those are for: they keep a pane from vanishing by accident, and this is\nthe deliberate version.\n\nThe two are kept in step in one direction only: a drag that settles on the\ncollapsed size latches shut, and any move opens it again. Otherwise a reader\ncould drag a pane closed and the consumer's own button would still be\noffering to close it.\n\nA pane shut to nothing is told apart from one shut to a sliver, because the\ntwo want different dividers: a sliver still has a boundary worth drawing, and\nnothing leaves the divider sitting on the container's own edge.\n\n**The box is measured once, when a drag starts.** Measuring per move is a\nforced layout on every pointer event, in the middle of a gesture whose whole\njob is to stay ahead of the pointer, and nothing about the box moves while the\ndivider does.\n\n**`helix-before-resize` fires when it settles, not while it moves.** A drag is a\nstream of positions and only the one it ends at is a preference; announcing\neach would ask a consumer to write to storage a hundred times to record one\ndecision. Nothing is stored here either: where a preference lives, and whether\nit should be kept at all, is the consumer's to decide, and a component writing\nto storage on its own would be deciding for all of them under a key it chose.\n\nThe properties below are written by this component and read by whoever draws\nit. A consumer sets none of them: they carry a number this element computed\nand CSS cannot, which is what lets a stylesheet on this tier draw the thing\nwithout measuring anything itself.",
54174
+ "description": "**One axis, and nesting is how you get two.** A splitter inside a splitter\ngives back the cross, and gives the three-pane and four-pane arrangements a\nsingle divider never had.\n\n**Where two of them cross there is a corner, and it moves both.** A splitter\nslotted into a pane of another, at right angles to it, grows a handle at the\ncrossing: `move` under the pointer, both arrow axes on the keyboard, and a\ndrag that carries the two positions at once. It appears at `firstUpdated`\nrather than in the served markup, being an affordance rather than content,\nand a splitter with no corner yet is a splitter with two working dividers.\n\n**The divider is a real `role=\"separator\"` with `aria-valuenow`**, moved by\nthe arrow keys as well as by pointer, with `Home` and `End` taking it to its\nbounds. A drag in progress is a state rather than `:hover`, so both dividers\na corner moves are lit for as long as it moves them.\n\n**It goes where it is put, all the way to either end.** `min` and `max` are\nnothing and everything unless a consumer says otherwise, because a divider\nthat stops short of the edge gives a reader no way to put a pane away.\n\n**A pane can be shut, and that is not the same as a divider at the edge.**\nThe geometry is identical and the knowledge is not: only a shut pane knows\nwhere to open back to. So `collapsed` is a second state rather than a reading\nof the position, and the position it opens to is the one collapsing never\noverwrote. `collapsed-size` keeps a sliver showing where a pane has a tab\nstrip worth leaving, and collapse overrides `min` and `max`, which is what\nthose are for.",
54175
54175
  "name": "HelixHeadlessSplitter",
54176
54176
  "cssProperties": [
54177
54177
  {
@@ -55701,7 +55701,7 @@
55701
55701
  "declarations": [
55702
55702
  {
55703
55703
  "kind": "class",
55704
- "description": "**`active` names where the reader is. `complete` stays on each step.** They\nare two different facts and only one of them is positional. Deriving both\nfrom a position is the obvious design and it is wrong the first time anybody\nsteps back: return to stage one of three and stages one and two are still\nfinished, while \"everything before the active one\" says only the first is.\nSo this element drives `current` and never touches `complete`.\n\n**`active` is optional, and a sequence without it still renders.** Left\nunset, each step's own `current` stands and nothing here overwrites it, which\nis what lets the whole sequence be right in the server's markup: this element\nwould have to count its slotted children to know which one is third, and the\nserver has no children to count. Set it, and it wins, because it is a\nstatement about the sequence where the attribute on a step is a statement\nabout one step.\n\nThat is also why the numbering is not JavaScript. It is a CSS counter in the\ntier above, which the browser resolves at layout with no script at all, so\nthe third step reads \"3\" in the server's own markup.\n\n**The connector belongs to the step, not to the list.** Each one draws the\nline that arrives at it and hides it when it is first, the same arrangement\nBreadcrumbs uses for its separator and for the same reason: a line drawn\nbetween items needs either an index or a parent that counts, and drawing it\non the item needs neither.\n\n`role=\"list\"` on the host rather than an `<ol>` in the shadow root. The steps\nare the consumer's elements in the light DOM, so an `<ol>` here would be a\nlist whose items are somewhere else.",
55704
+ "description": "**`active` names where the reader is. `complete` stays on each step.** Only\none of them is positional: return to stage one of three and stages one and\ntwo are still finished, where \"everything before the active one\" says only\nthe first is. So this element drives `current` and never touches `complete`.\n\n**`active` is optional, and a sequence without it still renders.** Left\nunset, each step's own `current` stands, which is what lets the whole\nsequence be right in the server's markup: this element would have to count\nits slotted children to know which is third, and the server has none to\ncount.",
55705
55705
  "name": "HelixHeadlessSteps",
55706
55706
  "slots": [
55707
55707
  {
@@ -56192,7 +56192,7 @@
56192
56192
  "declarations": [
56193
56193
  {
56194
56194
  "kind": "class",
56195
- "description": "**The name is a button, and only the name.** A stepper is something a reader\ngoes back through, and the thing they reach for is the name of the stage. The\nmarker is `aria-hidden` decoration, so wrapping it in the button would add\nhit area and no meaning: the accessible name would be the text either way,\nand what is pressable would stop being the same shape as what is readable.\n\nIt is `helix-button`, not a `<button>` this element resets and dresses: the\nhover wash, the focus ring and the disabled treatment are decisions the\nbutton already carries. `ghost` is the quiet shape, transparent until it is\npointed at.\n\nIt says nothing about where a reader may go. Every step is pressable and\nannounces itself with `helix-select`; refusing the ones ahead is `disabled`,\nwhich the consumer sets because only the consumer knows whether a form can be\nskipped. A component that guessed would be wrong on the first wizard that\nlets you jump to the end.\n\n**The marker is a slot whose fallback is the state.** A finished stage shows\na check, the current one a target, and one not yet reached an empty ring:\nthree marks that say what a reader wants to know without a legend. A consumer\nwho wants their own fills the slot instead.\n\nThe step renders exactly one of the three, chosen from its own two booleans.\nRendering all three and hiding two in CSS would work and is worse: the\naccessibility tree would carry marks that are not there, and the markup would\nsay three things about a stage that is one.\n\nNumbering belongs to the tier above. `numeric` hides the mark and shows a CSS\ncounter, which the browser resolves at layout, so the digit costs no script\nand is right in the server's markup however the steps are ordered. Nothing\nhere knows which appearance is in use.\n\n**`complete` and `current` are separate, and both can be false.** A stage\nthat is neither is one the reader has not reached, which is the majority of\nany sequence and therefore the state that needs no attribute. They can also\nboth be true: a reader who steps back into a finished stage is in it and has\nfinished it, and a stepper that forgets the second is one that loses its\nchecks the moment somebody reviews their answers.\n\n**The `content` slot opens on the current step, and only down a column.** A\nrow has no room under a step to put a form in, and stretching one to fit\nwould set every step's height by the tallest. `current` is what opens it, so\nthe state that says where the reader is is also the state that shows them the\nstage: one attribute, no second flag to keep in step with it, and it renders\nopen on the server rather than after hydration.",
56195
+ "description": "**The name is a button, and only the name.** The marker is `aria-hidden`\ndecoration, so wrapping it would add hit area and no meaning. It is\n`helix-button` rather than a `<button>` this element resets: the hover wash,\nthe focus ring and the disabled treatment are decisions the button carries.\n\n**It says nothing about where a reader may go.** Every step is pressable and\nannounces itself with `helix-select`; refusing the ones ahead is `disabled`,\nwhich the consumer sets, because only they know whether a form can be\nskipped.\n\n**The marker is a slot whose fallback is the state.** A finished stage shows\na check, the current one a target, one not yet reached an empty ring. Exactly\none is rendered, chosen from the step's own two booleans, rather than three\nwith two hidden in CSS: the accessibility tree would otherwise carry marks\nthat are not there.",
56196
56196
  "name": "HelixHeadlessStep",
56197
56197
  "cssParts": [
56198
56198
  {
@@ -65607,7 +65607,7 @@
65607
65607
  "declarations": [
65608
65608
  {
65609
65609
  "kind": "class",
65610
- "description": "An element rather than a slot, because it carries a timeout, a tone, a\ndismiss control and a lifetime that pauses: behavior, state and API, which\nis the test for whether a region becomes an element in this library.\n\n**The timer is the item's own.** Held by the region, one message arriving\nwould restart another's, so a stack of three would go from three seconds each\nto nine for the last one. Each message is on its own clock from the moment it\nis rendered.\n\n**A message is the opposite of the page it is over.** Inverted by default,\nbecause a thing arriving in a corner has to be told apart from the page in\nthe moment before it is read, and inverting does that without spending a\ncolor that means something. It is a flip of `color-scheme` rather than a\nnamed inverse surface: every token in the subtree re-resolves, so a `Retry`\na consumer slots in comes out right without knowing where it is. `Tooltip`\ncan name the inverse surface directly because it holds no controls; this\ncannot. Set `data-color-mode` on the region to take the page's mode instead.\n\n**No duration means it stays.** That is the safe default rather than the\ncommon one: anything carrying an action has to still be there when the reader\ndecides to take it, and a component cannot tell from the outside whether the\nmessage it was given is disposable.\n\n**The clock stops while it is being read.** Hover pauses it, and so does\nfocus landing anywhere inside, which is the case that matters more: a\nkeyboard reader tabbing towards the action in a toast is exactly the person a\ntimer must not race. Both resume where they left off rather than starting\nagain, so a message half-read does not get a full second life every time a\npointer crosses it.\n\n**Going away is a request, never an act.** It dispatches a cancelable\n`helix-dismiss` and does nothing else: the consumer owns the list, so the\nitem removing itself would leave the two disagreeing. Auto-dismiss and the\ndismiss control raise the same event, so a consumer handles one path and not\ntwo.",
65610
+ "description": "An element rather than a slot, because it carries a timeout, a tone, a\ndismiss control and a lifetime that pauses: behavior, state and API.\n\n**The timer is the item's own.** Held by the region, one message arriving\nwould restart another's, so a stack of three would go from three seconds each\nto nine for the last.\n\n**A message is the opposite of the page it is over.** Inverted by default, so\na thing arriving in a corner is told apart from the page before it is read,\nand by a flip of `color-scheme` rather than by spending a color that means\nsomething.",
65611
65611
  "name": "HelixHeadlessToastItem",
65612
65612
  "cssProperties": [
65613
65613
  {
@@ -66442,7 +66442,7 @@
66442
66442
  "declarations": [
66443
66443
  {
66444
66444
  "kind": "class",
66445
- "description": "The indicator is one element that travels, not a background that appears on\nwhichever item is current. That difference is the whole point: a fill that\nswaps is two things changing at once and reads as a flicker, while one that\nmoves is a single object the eye follows, and it says where the selection\ncame from as well as where it went.\n\nIts whole box is measured from the selected item and written onto\nthe indicator as custom properties, because labels are different lengths and\nno proportion of the group is the right answer. Measurement is client-side by\nnecessity, so the server renders the indicator hidden and it fades in once it\nhas somewhere to be, which is the harmless direction: it appears rather than\njumping from a wrong place.\n\nA radio group, and only that. Navigation is links marked with `aria-current`\nand styled by the `data-navigation-item` recipe: radio semantics would announce a\nset of destinations as a set of choices. A tab controls a panel.\n\nBeing a radio group, it is a form control, and it says so through\n`HelixFormElement`: it reports its value under a `name`, comes back to the\nmarkup's choice on reset, and restores the one a reader made when the browser\nbrings a form back.\n\nThe semantics were already right: the host has carried `role=\"radiogroup\"`\nand the items `role=\"radio\"` from the start. What none of it did was reach a\nform. A button in a shadow root submits nothing whatever its role, so the\nvalue has to be reported by the host, which is the only thing a form can\nsee.",
66445
+ "description": "The indicator is one element that travels, not a background that appears on\nwhichever item is current. A fill that swaps is two things changing at once\nand reads as a flicker; one that moves is a single object the eye follows,\nand it says where the selection came from as well as where it went.\n\nIts box is measured from the selected item and written onto the indicator as\ncustom properties, labels being different lengths. Measurement is client-side\nby necessity, so the server renders the indicator hidden and it fades in once\nit has somewhere to be, which is the harmless direction.",
66446
66446
  "name": "HelixHeadlessToggleGroup",
66447
66447
  "cssParts": [
66448
66448
  {
@@ -68239,7 +68239,7 @@
68239
68239
  "declarations": [
68240
68240
  {
68241
68241
  "kind": "class",
68242
- "description": "The trigger is slotted and stays the consumer's own element. The bubble is\nrendered here, in the top layer through the popover API, so it clears any\nancestor with `overflow: hidden` without a `z-index` anywhere.\n\nPosition comes from `FloatingController`, which flips to another side when\nthe preferred one does not fit and slides along it to stay in view.\n\n**Two forms, and they are not equivalent for a screen reader.**\n\nWrapping is the convenience form and is right where the bubble decorates a\ncontrol that already says what it is. The trigger stays the consumer's own\nelement, slotted, and nothing here can describe it: an IDREF cannot cross a\nshadow boundary, an element reference is valid only into the same tree or a\nshadow-including *ancestor* (the bubble's tree is neither, it is inward), and\na string on the trigger means writing to markup the consumer server-rendered.\nAll three measured.\n\n`for` is the form that works. The tooltip sits *beside* the trigger in the\nconsumer's own tree and names it by id, so both are in one tree and an IDREF\nbetween them is legal in the ordinary way:\n\n```html\n<helix-button id=\"save\" aria-describedby=\"save-tip\">Save</helix-button>\n<helix-tooltip id=\"save-tip\" for=\"save\" content=\"Save your work\"></helix-tooltip>\n```\n\nNote that the reference points at *this host*, not at the bubble inside its\nshadow root: the description is computed from the flat tree, which descends\ninto shadow content on its own.\n\n**The consumer writes that `aria-describedby`, and nothing here writes it for\nthem.** WebAwesome and Spectrum Web Components both do write it, with\n`setAttribute` on the trigger at runtime, which is the one thing this library\ndeclines everywhere: React rendered that element on the server without the\nattribute and reports a mismatch it will not patch up. Two ids in the markup\nis the whole cost of the version that is correct before any JavaScript runs.\n\nBecause it is the consumer's line to write, it is also theirs to forget, so\n`for` audits itself on first open and says so once, rather than on every\nopen, since a warning that repeats is one people learn to filter out.\n\nIt shows on hover and on focus, and hides on leave, on blur, on Escape, and\nonce the trigger has been activated: the hint is spent when the control has\nbeen used.\nA tooltip that answers only the pointer is one a keyboard reader never sees.\n\nThe pointer waits out `delay` first, or sweeping a row of controls flashes a\ntooltip for each of them. Focus does not wait: a keyboard reader landing on a\ncontrol has already committed to it.\n\nOpen state comes from `MixinControllerOpen`, so it announces `helix-before-open` and\n`helix-before-close` like every other overlay here, and a consumer can drive it with\n`open` where they need to.",
68242
+ "description": "The trigger is slotted and stays the consumer's own element. The bubble is\nrendered here, in the top layer through the popover API, so it clears any\nancestor with `overflow: hidden` without a `z-index` anywhere. Position comes\nfrom `FloatingController`, which flips to another side when the preferred one\ndoes not fit.\n\n**Two forms, and they are not equivalent for a screen reader.** Wrapping is\nthe convenience form and is right where the bubble decorates a control that\nalready says what it is: nothing here can describe a slotted trigger, an\nIDREF being unable to cross a shadow boundary.\n\n`for` is the form that works. The tooltip sits *beside* the trigger in the\nconsumer's own tree and names it by id, so an IDREF between them is legal in\nthe ordinary way:\n\n```html\n<helix-button id=\"save\" aria-describedby=\"save-tip\">Save</helix-button>\n<helix-tooltip id=\"save-tip\" for=\"save\" content=\"Save your work\"></helix-tooltip>\n```\n\nThe reference points at *this host*, not at the bubble in its shadow root:\nthe description is computed from the flat tree, which descends into shadow\ncontent on its own.\n\n**The consumer writes that `aria-describedby`, and nothing here writes it for\nthem.** Setting it at runtime is a hydration mismatch React will not patch\nup. `for` audits itself on first open and warns once where it is missing.\n\nIt shows on hover and on focus, and hides on leave, on blur, on Escape, and\nonce the trigger has been activated: the hint is spent when the control has\nbeen used.",
68243
68243
  "name": "HelixHeadlessTooltip",
68244
68244
  "cssParts": [
68245
68245
  {
@@ -69084,7 +69084,7 @@
69084
69084
  "declarations": [
69085
69085
  {
69086
69086
  "kind": "class",
69087
- "description": "The root owns the order and nothing else. Which stop is current, what Next\nand Back mean, when the run is finished; the stops own where they point, and\nthe consumer owns every word.\n\n**The spotlight is a shadow, not a hole.** The scrim is a box the size of the\ncurrent target with an enormous spread on its `box-shadow`, so the darkness\nis what the shadow paints and the target is simply where the box is. A\n`clip-path` on a full-screen scrim is the other way to do it and is worse\nhere: it needs the viewport in the same coordinate space as the cut, which\nstops being true the moment anything between them is transformed.\n\n**It measures, and this is a case where measuring is the only option.** The\ntarget is an element somewhere else on the page whose position nothing can\nknow statically. That is different from a trail folding by width, where the\ndesign rule was expressible and the measurement was answering a question\nnobody had asked. Here the question is \"where is that button\", and there is\nno other way to ask it.\n\n**Nothing is measured on the server and nothing needs to be.** A tour starts\nclosed and a closed tour draws nothing, so the server renders the stops as\ninert markup and the first frame is correct. This is also what makes it\nacceptable for the root to write `current` onto stops it does not own: there\nis no server state for the write to disagree with.\n\n**It cannot point inside a modal, and nothing can fix that here.**\n`showModal()` makes everything outside the dialog's own subtree inert, so a\npanel anywhere else is unpressable however the top layer is ordered: this is\nwhat modal means rather than a stacking accident. Measured with a modal\n`Drawer` open, `elementsFromPoint` over the tour's own Done button returned\nthe drawer, the press light-dismissed the drawer, and the tour heard nothing\nat all. Anything a tour points into has to stay reachable, which for `Drawer`\nand `Dialog` means not modal while the tour is running.\n\n**Focus is not trapped.** A reader has to be able to look at, and reach, the\nthing being pointed at. `Escape` leaves, which is the one binding a tour\nneeds and the one every reader already tries.",
69087
+ "description": "The root owns the order and nothing else. Which stop is current, what Next\nand Back mean, when the run is finished; the stops own where they point, and\nthe consumer owns every word.\n\n**The spotlight is a shadow, not a hole.** The scrim is a box the size of the\ncurrent target with an enormous spread on its `box-shadow`, so the darkness\nis what the shadow paints. A `clip-path` on a full-screen scrim needs the\nviewport in the same coordinate space as the cut, which stops being true the\nmoment anything between them is transformed.\n\n**It measures, and the target is an element elsewhere on the page whose\nposition nothing can know statically.** Nothing is measured on the server and\nnothing needs to be: a tour starts when a reader asks for it.",
69088
69088
  "name": "HelixHeadlessTour",
69089
69089
  "cssParts": [
69090
69090
  {
@@ -70219,7 +70219,7 @@
70219
70219
  "declarations": [
70220
70220
  {
70221
70221
  "kind": "class",
70222
- "description": "**`open`, on `MixinControllerOpen`, rather than a bare boolean of its own.** A\nbranch showing its children is the same state a disclosure holds, and\n`AccordionItem` is on that mixin too. A hand-written toggle announces nothing,\nso a consumer can neither know a branch opened nor own the state.\n`aria-expanded` is still what the row renders, because that is the attribute a\ntree uses; the property is what differs, not the semantics.\n\nAn element rather than a slot because it owns state: whether it is open,\nwhether it is selected, and whether it is the one holding focus.\n\nA branch is any item with something slotted into `children`, detected in\n`firstUpdated` rather than declared by the consumer. That is a client-side\nenhancement by necessity, since the server cannot inspect children, so the\nserver renders the node as a leaf and the twisty appears on hydration. It\nappears rather than disappearing, which is the harmless direction.\n\n**`href` makes the row itself the link.** A tree used as navigation is a list\nof links, and the row is what a reader presses: with the anchor slotted into\nthe name instead, everything outside the words is dead, Enter does nothing,\nand there is no middle-click, no status bar and no open-in-new-tab. Given an\n`href` the row *is* an `<a>`, so the platform provides all of that and the\nkeyboard needs no handler at all.\n\nIt changes the shape of the ARIA, which is why it is a property and not the\ndefault. An anchor cannot contain the group, since the group holds more\nanchors and nesting them is invalid, so the row carries `treeitem` and the\ngroup becomes its sibling under a presentational wrapper. That is exactly the\nnavigation flavor of the tree pattern, and the arrangement without an `href`\nstays as it was: `treeitem` containing `group`, which is what an `<li>`\nholding a `<ul>` is.\n\nA consumer whose framework owns routing slots its own link instead and leaves\n`href` unset. The row follows that one too, so the whole row works either\nway; what the anchor adds is what only a real anchor can give.\n\nThe properties below are written by this component and read by whoever draws\nit. A consumer sets none of them: they carry a number this element computed\nand CSS cannot, which is what lets a stylesheet on this tier draw the thing\nwithout measuring anything itself.",
70222
+ "description": "**`open`, on `MixinControllerOpen`, rather than a bare boolean of its own.**\nA branch showing its children is the same state a disclosure holds. A\nhand-written toggle announces nothing, so a consumer could neither know a\nbranch opened nor own the state. `aria-expanded` is still what the row\nrenders: the property differs, not the semantics.\n\nAn element rather than a slot, because it owns state: whether it is open,\nselected, and whether it holds focus.\n\nA branch is any item with something slotted into `children`, detected in\n`firstUpdated` because the server cannot inspect children. The server renders\nthe node as a leaf and the twisty appears on hydration, which is the harmless\ndirection.\n\n**`href` makes the row itself the link.** With the anchor slotted into the\nname instead, everything outside the words is dead: no Enter, no middle\nclick, no status bar. Given an `href` the row *is* an `<a>`.",
70223
70223
  "name": "HelixHeadlessTreeItem",
70224
70224
  "cssProperties": [
70225
70225
  {
@@ -72304,7 +72304,7 @@
72304
72304
  "declarations": [
72305
72305
  {
72306
72306
  "kind": "class",
72307
- "description": "The entries again, reachable without leaving the picture. A playlist beside\nthe player is a list a reader scrolls to; the same list in the control bar is\none they open over the video, which is what a full-screen player has and what\na phone has room for.\n\n**It is placed by the consumer, not rendered by the playlist**, and that is\nforced rather than chosen. A shadow root cannot project into a sibling's\nlight DOM, so nothing the playlist renders can land inside the player's\ncontrol bar. What can is an element the consumer writes there, which is what\n`Video`'s `controls` slot is for:\n\n```html\n<helix-video-playlist>\n <helix-video slot=\"player\" controls>\n <helix-video-playlist-menu slot=\"controls\"></helix-video-playlist-menu>\n </helix-video>\n <helix-video-playlist-item value=\"one\" src=\"/one.mp4\">One</helix-video-playlist-item>\n</helix-video-playlist>\n```\n\nSo it finds its playlist upward rather than being handed one, and that chain\ncrosses a shadow boundary in the ordinary arrangement, which is why it is\n`closestInstance` and not `closest`.\n\n**Choosing dispatches `helix-select`, the same event an entry dispatches.**\nIt never writes `active` on the playlist, which looks like the shorter path\nand skips the cancelable request: an assignment applies the change with no\n`helix-before-activate` for anyone to refuse. Announcing is what an entry\ndoes, the playlist already listens for it, and a consumer refusing a\nselection gets `preventDefault()` on one event rather than two paths that\nbehave differently.",
72307
+ "description": "The entries again, reachable without leaving the picture. A playlist beside\nthe player is a list a reader scrolls to; the same list in the control bar is\none they open over the video.\n\n**It is placed by the consumer, not rendered by the playlist**, and that is\nforced rather than chosen: a shadow root cannot project into a sibling's\nlight DOM, so nothing the playlist renders can land inside the player's\ncontrol bar. What can is an element the consumer writes there, which is what\n`Video`'s `controls` slot is for.",
72308
72308
  "name": "HelixHeadlessVideoPlaylistMenu",
72309
72309
  "cssParts": [
72310
72310
  {
@@ -73239,7 +73239,7 @@
73239
73239
  "declarations": [
73240
73240
  {
73241
73241
  "kind": "class",
73242
- "description": "**The picture is a real `<video>`.** Decoding, buffering, captions, Picture\nin Picture, AirPlay and the codecs a device happens to have are all the\nplatform's, and none of them can be had from anything else. What is replaced\nis the control bar, which is the one part of a video element that cannot be\nstyled and looks different in every browser.\n\n**Every control is a real control.** The scrubber and the volume are\n`<input type=\"range\">`, so a reader gets arrow keys, Home and End, the\nplatform's own touch target and its screen reader announcement without this\nelement implementing any of it. Drawing a div and listening for pointer moves\nis how a media player ends up unusable from a keyboard.\n\n**Native controls are the fallback, not the default.** Where a consumer sets\n`controls`, the browser's bar is used and this one is not drawn: two sets of\ncontrols over one video is worse than either alone, and a consumer who asked\nfor the native bar has a reason.\n\nThe keys are the ones every player uses: Space and `k` to play or pause, the\narrows to seek and to change the volume, `m` to mute, `f` for fullscreen.\nThey are bound on this element rather than on the document, because a video\nis not a palette: keys belong to it only while a reader is on it. Where\n`controls` is set they are not bound at all, since two handlers on one press\nis a video that seeks twice, and the focus stop goes with them.\n\n**`<source>` and `<track>` are copied into the video, never slotted into it.**\nA media element runs resource selection over its own child *nodes*, and a slot\nis one node, so slotted sources reach it as nothing: no text tracks, no\ncurrent source, `readyState` zero. A video given sources that way does not\nmerely lack captions, it does not play.\n\nThey are copied rather than moved, because the consumer's light DOM is theirs\nand a framework that re-renders it would find its children gone. The copies\nare rebuilt whenever the slot changes and the video is asked to load again,\nwhich is what makes a `<track>` added later take effect.\n\n**The lock screen and a headset's buttons work, and one player owns them.**\nThere is one `navigator.mediaSession` per page, so the rule is stated rather\nthan left to render order: whichever player was started last owns it, which\nis what a reader means by \"this one\" when they press play on their\nheadphones. The title comes from `label` and the artwork from `poster`.\n\n**`remote` sends the picture somewhere else.** The Remote Playback API is\nwhat Safari implements for AirPlay and Chrome for Cast, so one control covers\nboth and the platform supplies the device list. It is offered only once\nsomething has answered `watchAvailability`: a button that opens an empty list\nis worse than no button, and a consumer who wants none can set\n`disableremoteplayback` on this element.\n\nThe properties below are written by this component and read by whoever draws\nit. A consumer sets none of them: they carry a number this element computed\nand CSS cannot, which is what lets a stylesheet on this tier draw the thing\nwithout measuring anything itself.",
73242
+ "description": "**The picture is a real `<video>`.** Decoding, buffering, captions, Picture\nin Picture, AirPlay and whatever codecs a device has are the platform's. What\nis replaced is the control bar, which is the one part that cannot be styled.\n\n**Every control is a real control.** The scrubber and the volume are\n`<input type=\"range\">`, so arrow keys, Home and End, the platform's touch\ntarget and its screen reader announcement all come for free.\n\n**Native controls are the fallback, not the default.** Where a consumer sets\n`controls` the browser's bar is used and this one is not drawn, and the keys\nbelow are not bound: two handlers on one press is a video that seeks twice.\n\nThe keys are the ones every player uses: Space and `k` to play or pause, the\narrows to seek and to change volume, `m` to mute, `f` for fullscreen. They\nare bound on this element rather than on the document, because a video is not\na palette.\n\n**`<source>` and `<track>` are copied into the video, never slotted into it.**\nA media element runs resource selection over its own child *nodes*, and a slot\nis one node, so slotted sources reach it as nothing: no text tracks, no\ncurrent source, `readyState` zero. A video given sources that way does not\nmerely lack captions, it does not play. They are copied rather than moved,\nbecause the consumer's light DOM is theirs, and rebuilt whenever the slot\nchanges.",
73243
73243
  "name": "HelixHeadlessVideo",
73244
73244
  "cssProperties": [
73245
73245
  {
@@ -74894,7 +74894,7 @@
74894
74894
  "declarations": [
74895
74895
  {
74896
74896
  "kind": "class",
74897
- "description": "**The set arrives as data, which is what makes this a virtual list rather\nthan a box that hides children.** `for` names a\n`<script type=\"application/json\">` holding an array of objects, and a\n`<template>` child says what one row looks like: elements carrying\n`data-field=\"name\"` are filled with that field of the item.\n\nBoth halves are markup, and that is the whole reason for the shape.\n`VirtualizeController` says why the obvious alternatives are shut: an `items`\nproperty cannot survive React hydration, which skips every property it would\nhave to apply imperatively, and light-DOM children exist by the time anything\ncould decide not to create them. Text in the document is neither. A server\nemits it, React re-emits it as a child rather than applying it, and it is one\nnode whatever the row count.\n\n**The accessible version is not a `<table>`**, the way it is for the charts\nthat share this `for=\"<id>\"` shape: ten thousand rows in one is the cost this\nexists to avoid. It is the rendered window plus `aria-setsize` and\n`aria-posinset`, which is what says \"four thousand of ten thousand\" rather\nthan \"four of twenty\".\n\n**Rows are light DOM, deliberately.** They are cloned from the consumer's\ntemplate and appended to the host, so a document rule reaches them, the base\nlayer applies, and a class in the template means what it says. Cloned into\nthe shadow root they would be markup the consumer wrote and could not style,\nwhich is the one thing a row template must not be.\n\n**Reading the source is client-only**, because a server has no document to\nquery. The window renders empty from the server and fills on the first update,\nwhich is the same client-side enhancement rule every tree-reading component\nhere follows.\n\n**On this tier the consumer gives the viewport its box**, and the component\ncannot do it for them: `helix/headless` ships no stylesheet, and a height is a\ndecision rather than a fact this could publish. So a base list wants two rules\nbefore it is a list at all:\n\n```css\nhelix-headless-virtual-list::part(viewport) {\n block-size: 12rem;\n overflow-y: auto;\n}\n```\n\n**Without them it is not a scroller**, and the failure is worse than it\nsounds. `scrollTop` is always zero, so nothing ever windows; and the box has\nno height of its own, so `clientHeight` is a function of the rows just drawn.\nMeasuring then moves what is being measured, and the two need not converge:\nmeasured in Chromium at 500 rows, it settled into a two-cycle between 378 and\n379 rows and stayed there, at two and a half thousand passes in eight seconds.\n`VirtualizeController` gives up after ten passes and says so, so this now\ndraws a bad window rather than stopping the page, but a bad window is still\nthe answer until the rules are written.\n\nThe styled tier writes them for you, with `--virtual-list-block-size`.",
74897
+ "description": "**The set arrives as data, which is what makes this a virtual list rather\nthan a box that hides children.** `for` names a\n`<script type=\"application/json\">` holding an array of objects, and a\n`<template>` child says what one row looks like: elements carrying\n`data-field=\"name\"` are filled with that field of the item.\n\nBoth halves are markup, and that is the reason for the shape. An `items`\nproperty cannot survive React hydration, which skips every property it would\nhave to apply imperatively, and light-DOM children exist by the time anything\ncould decide not to create them. Text in the document is neither, and it is\none node whatever the row count.\n\n**The accessible version is not a `<table>`**, the way it is for the charts\nthat share this `for=\"<id>\"` shape: ten thousand rows in one is the cost this\nexists to avoid. It is the rendered window plus `aria-setsize`.",
74898
74898
  "name": "HelixHeadlessVirtualList",
74899
74899
  "cssParts": [
74900
74900
  {