@fusion.dev/helix-headless 0.49.1 → 0.51.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9612,12 +9612,16 @@
9612
9612
  "name": "--carousel-pane"
9613
9613
  },
9614
9614
  {
9615
- "description": "How many slides go across, written onto the track by the component so a sheet divides the track by the same number the positions are counted from. Read it rather than selecting on `per-view`, which cannot be enumerated without a ceiling. See `perView`.",
9615
+ "description": "How many slides go across, written onto the track by the component so a sheet divides the track by the same number the positions are counted from. Read it rather than selecting on `per-view`, which cannot be enumerated without a ceiling. Under `per-view=\"auto\"` the component writes nothing and the sheet declares it instead, so the same name answers in both modes. See `perView`.",
9616
9616
  "name": "--carousel-per-view"
9617
9617
  },
9618
9618
  {
9619
9619
  "description": "How far short of a whole pixel the run ends, on the last slide alone, for a sheet to take up as `margin-inline-end`. See `runEnd`.",
9620
9620
  "name": "--carousel-run-end"
9621
+ },
9622
+ {
9623
+ "description": "The narrowest a slide may be laid out, which is what `per-view=\"auto\"` fits the track with. A length, since the sheet divides by it. Unset is the whole track, which is one across.",
9624
+ "name": "--carousel-slide-min"
9621
9625
  }
9622
9626
  ],
9623
9627
  "cssParts": [
@@ -9808,20 +9812,40 @@
9808
9812
  "kind": "field",
9809
9813
  "name": "perView",
9810
9814
  "type": {
9811
- "text": "number | undefined"
9815
+ "text": "number | 'auto' | undefined"
9812
9816
  },
9813
- "description": "How many slides are shown across the track at once. Absent shows one.\n\n**Here rather than on the styled tier, because it decides how many places\nthe track can stop.** From the stylesheet it looks like a width, one rule\nper value setting a custom property that the slides divide the track by.\nBut a carousel showing four of six slides has three positions, not six, and\neverything that counts positions is in this file. Declared up there, `goTo`\nclamps to the last *slide*, so three of the six indicators point at an\noffset the track has already reached: pressing next walks the dots to the\nend while the slides stop moving a third of the way through.\n\n**The number is published to the styles rather than enumerated by them**,\nas `--carousel-per-view` on the track, which is what keeps a width and a\ncount from disagreeing. A sheet can only name a value it wrote a rule for,\nso the styled tier enumerated `per-view=\"2\"` through `5` and called five a\njudgment; above it the pane fell back to one across while this file went on\ncounting positions. Measured in Chromium over eight slides, six across gave\nthree dots, a pane taking the whole track and five slides reachable only by\ndragging. Written from here there is no ceiling to exceed, and a consumer's\nown sheet reads the same declaration the styled one does. `runEnd` publishes\nits correction the same way and for the same reason.\n\n**On the track and not on the host.** A custom property on the host is a\nreflected attribute by another name: it would arrive on upgrade, and a\nconsumer's server markup never had it. Inside the component's own shadow\nroot it serializes with the rest of the template on the server, and the\ntrack's own declaration beats anything a consumer inherits in, so the\nattribute is the one answer to how wide a pane is.\n\nA value the track cannot honor is taken to the nearest one it can, by\n`across`, and both tiers read that rather than this.\n\nNo default, because it reflects. A reflected property with a value writes\nits attribute on upgrade, and the host then carries something a consumer's\nserver markup never had, which React reports as a mismatch. The one-across\ncase is the absent attribute, and the styles read it that way.",
9817
+ "description": "How many slides are shown across the track at once. Absent shows one.\n\n**Here rather than on the styled tier, because it decides how many places\nthe track can stop.** From the stylesheet it looks like a width, one rule\nper value setting a custom property that the slides divide the track by.\nBut a carousel showing four of six slides has three positions, not six, and\neverything that counts positions is in this file. Declared up there, `goTo`\nclamps to the last *slide*, so three of the six indicators point at an\noffset the track has already reached: pressing next walks the dots to the\nend while the slides stop moving a third of the way through.\n\n**The number is published to the styles rather than enumerated by them**,\nas `--carousel-per-view` on the track, which is what keeps a width and a\ncount from disagreeing. A sheet can only name a value it wrote a rule for,\nso the styled tier enumerated `per-view=\"2\"` through `5` and called five a\njudgment; above it the pane fell back to one across while this file went on\ncounting positions. Measured in Chromium over eight slides, six across gave\nthree dots, a pane taking the whole track and five slides reachable only by\ndragging. Written from here there is no ceiling to exceed, and a consumer's\nown sheet reads the same declaration the styled one does. `runEnd` publishes\nits correction the same way and for the same reason.\n\n**On the track and not on the host.** A custom property on the host is a\nreflected attribute by another name: it would arrive on upgrade, and a\nconsumer's server markup never had it. Inside the component's own shadow\nroot it serializes with the rest of the template on the server, and the\ntrack's own declaration beats anything a consumer inherits in, so the\nattribute is the one answer to how wide a pane is.\n\nA value the track cannot honor is taken to the nearest one it can, by\n`across`, and both tiers read that rather than this.\n\n**`auto` fits as many as the track can hold**, at `--carousel-slide-min`\napiece, and nothing is written onto the track: the count is the sheet's,\nand the sheet's alone, because it is the one number here CSS can work out\nfor itself. Everything it takes is a length the sheet already has, so the\npanes are laid out at their final width by the first rule that runs, on a\nserver's markup as on the client's, and no script is waited on. That is the\nwhole reason a declared count was ever the only option: a carousel that\nasks how wide its container is can only ask on the client, so the panes go\ndown at one width and move to another when the answer arrives. Written\ninline the count would outrank the sheet that computes it, which is why an\n`auto` track is handed nothing. A count stays for the design that calls for\nexactly three.\n\nThe styled sheet says how it divides, and the headless `auto` example\ncarries the same three lines for a consumer writing their own.\n\n**A value rather than the resting behavior**, which is what the\nresponsiveness policy asks of every axis that a box decides: a splitter\nturns on `orientation=\"auto\"` and a plain one does not turn at all. Fitting\nchanges what the carousel *does*, since how many are across is how many\nplaces the track can stop, so a carousel that had not asked for it would\nfind its position count moving with the window.\n\nNo default, because it reflects. A reflected property with a value writes\nits attribute on upgrade, and the host then carries something a consumer's\nserver markup never had, which React reports as a mismatch. The one-across\ncase is the absent attribute, and the styles read it that way.",
9814
9818
  "attribute": "per-view",
9815
9819
  "reflects": true
9816
9820
  },
9817
9821
  {
9818
9822
  "kind": "field",
9819
- "name": "across",
9823
+ "name": "clamped",
9820
9824
  "type": {
9821
9825
  "text": "number"
9822
9826
  },
9823
9827
  "privacy": "private",
9824
- "description": "`perView` as both tiers use it: a whole number of slides, one at least.\n\n**The clamp is here because the arithmetic is here**, and the two readers\nof this number have to be given the same one. A fraction is floored, since\na position is a whole slide along: one and a half across leaves a last\nposition the track runs out before reaching, which is the fault `goTo`\nalready carries a comment about. Zero and a negative divide the track into\nnothing and into a pane facing the wrong way, and a non-numeric attribute\nparses to `NaN`, which `Math.max` propagates: it counted `NaN` positions,\ndrew no indicators at all and left `index` at `NaN`. All of them read as\none across, which is what a carousel with nothing sayable asked for.\n\nNot clamped to the number of slides. Six across four slides is four panes\nat a sixth of the track each and the one place to stand, which is what was\nasked for as nearly as it can be had, and the slide count is a fact the\nserver does not have: clamping to it would draw one width there and another\non upgrade.",
9828
+ "description": "A declared `perView` as both tiers use it: a whole number, one at least.\n\n**The clamp is here because the arithmetic is here**, and the two readers\nof this number have to be given the same one. A fraction is floored, since\na position is a whole slide along: one and a half across leaves a last\nposition the track runs out before reaching, which is the fault `goTo`\nalready carries a comment about. Zero and a negative divide the track into\nnothing and into a pane facing the wrong way, and a non-numeric attribute\nparses to `NaN`, which `Math.max` propagates: it counted `NaN` positions,\ndrew no indicators at all and left `index` at `NaN`. All of them read as\none across, which is what a carousel with nothing sayable asked for.\n\n**A vertical `auto` is one of them.** Fitting divides the room the page\ngave the track, and a vertical track's room is a height the consumer chose\nwith `--carousel-block-size`: there is nothing there to fit against that\nthey did not already decide, so the count is theirs to state. It also\ncannot be had cheaply, the block axis wanting `container-type: size`, which\nlays a track out as empty wherever its height is not definite.\n\nNot clamped to the number of slides. Six across four slides is four panes\nat a sixth of the track each and the one place to stand, which is what was\nasked for as nearly as it can be had, and the slide count is a fact the\nserver does not have: clamping to it would draw one width there and another\non upgrade.",
9829
+ "readonly": true
9830
+ },
9831
+ {
9832
+ "kind": "field",
9833
+ "name": "fits",
9834
+ "type": {
9835
+ "text": "boolean"
9836
+ },
9837
+ "privacy": "private",
9838
+ "description": "Whether the track is fitting slides to its own box rather than to a count.",
9839
+ "readonly": true
9840
+ },
9841
+ {
9842
+ "kind": "field",
9843
+ "name": "across",
9844
+ "type": {
9845
+ "text": "number | undefined"
9846
+ },
9847
+ "privacy": "private",
9848
+ "description": "How many slides go across, and `undefined` while a fitted track is unlaid.\n\n**Undefined is the honest answer and the indicators read it as one**, which\nis the whole of how a fitted carousel keeps from drawing a run of dots at\none length and correcting it at another. The count a fitted track goes\nacross is whatever its box resolved to, so nothing knows it until there is\na box: not the server, which has no layout, and not the render that draws\nthe panes, layout being what follows a render rather than what precedes it.\nThe dots wait for it, which costs them nothing, because a server serves no\nindicators for any carousel, `slides` being empty there.\n\nMeasured 16 Sep 2026 in Chromium 151.0.7922.34 and WebKit 26.5, recording\nevery run of dots a carousel has from the upgrade on: a fitted one goes\nnone, then five, and never another number; a declared one goes straight to\nsix. What a reader can see is the run missing, never the run wrong.",
9825
9849
  "readonly": true
9826
9850
  },
9827
9851
  {
@@ -9830,9 +9854,76 @@
9830
9854
  "type": {
9831
9855
  "text": "number"
9832
9856
  },
9833
- "description": "How many distinct views the track holds, which is what the indicators count.\n\nA window of `perView` slides sliding one at a time over `n` of them stops\n`n - perView + 1` times, so six slides four across is three: 0 to 3, 1 to 4,\n2 to 5. One at minimum, since a carousel showing everything it has still\nhas the one place to be.",
9857
+ "description": "How many distinct views the track holds, which is what the indicators count.\n\nA window of `perView` slides sliding one at a time over `n` of them stops\n`n - perView + 1` times, so six slides four across is three: 0 to 3, 1 to 4,\n2 to 5. One at minimum, since a carousel showing everything it has still\nhas the one place to be.\n\nOne across until a fitted track has been laid out, which is the same answer\na carousel that has never been told anything gives, and the dots are\nwithheld rather than counted from it. See `across`.",
9834
9858
  "readonly": true
9835
9859
  },
9860
+ {
9861
+ "kind": "method",
9862
+ "name": "measure",
9863
+ "privacy": "private",
9864
+ "return": {
9865
+ "type": {
9866
+ "text": "number | undefined"
9867
+ }
9868
+ },
9869
+ "description": "How many slides the box turned out to hold, from the pane it laid out.\n\n**The measurement asks what happened rather than what was asked for**, and\nthat is why it is the pane's width and not the count in the declaration.\nThe sheet divides the track by `--carousel-per-view`, which under `auto` is\narithmetic rather than a number, and a consumer's own sheet may divide it\nsome other way entirely or grow the panes to fill a track holding\neverything it has. Reading the box back gets the same answer as the\ndeclaration wherever the two agree and the true one wherever they do not,\nso a width and a count cannot disagree in either direction.\n\nThe sheet's own arithmetic run backward, on the lengths that were actually\nused: a gap added to the room, divided by a pane and a gap. Rounded,\nbecause the pane a sheet lays out is a sixty-fourth off its exact share by\ndesign and the quotient lands a ten-thousandth off a whole number.\n\nUndefined where there is nothing to measure, which is a harness rather than\na browser: happy-dom lays nothing out and reports zero for every box, and a\ncount taken from zeros would be a number with no layout behind it."
9870
+ },
9871
+ {
9872
+ "kind": "field",
9873
+ "name": "readFit",
9874
+ "privacy": "private",
9875
+ "readonly": true,
9876
+ "description": "Takes the reading, and puts the index back inside what it leaves.\n\n**A fitted carousel's last position moves with the window**, which a\ndeclared one's never does: eight slides at one across has eight places to\nstand and four across has five, so a track widened while the reader sits on\nthe last of them is a track resting somewhere it can no longer be. Through\n`goTo`, so the correction goes out as the same cancelable announcement\nevery other index change does and a consumer holding `index` hears it\nrather than being overwritten.\n\nOnly on a change, so the observer's own re-render cannot feed itself."
9877
+ },
9878
+ {
9879
+ "kind": "method",
9880
+ "name": "handed",
9881
+ "privacy": "private",
9882
+ "return": {
9883
+ "type": {
9884
+ "text": "string | typeof nothing"
9885
+ }
9886
+ },
9887
+ "description": "The count handed to the styles, and nothing at all where they work it out.\n\nA declared count is this file's: it decides how many places the track can\nstop, so the number the panes are divided by has to be the number the\npositions were counted from, and writing it on the track inside the\ncomponent's own shadow root is what keeps a sheet from having to enumerate\nthe values it will answer for.\n\nA fitted one is the sheet's, and handing it anything would take it back: an\ninline declaration outranks every rule short of `!important`, so a track\ntold `1` here could never be told three by the rule that fits it. The\nabsence is not a gap either, because the rule that computes the count\ndeclares it on this same element, which beats anything a consumer inherits\nin exactly as the inline one did."
9888
+ },
9889
+ {
9890
+ "kind": "field",
9891
+ "name": "fitted",
9892
+ "type": {
9893
+ "text": "number | undefined"
9894
+ },
9895
+ "privacy": "private",
9896
+ "description": "What the box resolved to, and `undefined` until it has been laid out."
9897
+ },
9898
+ {
9899
+ "kind": "field",
9900
+ "name": "sizes",
9901
+ "type": {
9902
+ "text": "ResizeObserver | undefined"
9903
+ },
9904
+ "privacy": "private",
9905
+ "description": "Watches the pane a fitted track laid out, while `auto` is what was asked."
9906
+ },
9907
+ {
9908
+ "kind": "field",
9909
+ "name": "watched",
9910
+ "type": {
9911
+ "text": "HTMLElement | undefined"
9912
+ },
9913
+ "privacy": "private",
9914
+ "description": "The pane being watched, so a re-rendered run moves the watch with it."
9915
+ },
9916
+ {
9917
+ "kind": "method",
9918
+ "name": "watchPane",
9919
+ "privacy": "private",
9920
+ "return": {
9921
+ "type": {
9922
+ "text": "void"
9923
+ }
9924
+ },
9925
+ "description": "Starts or stops watching, according to what the count was asked to be.\n\n**The pane and not the track, because both questions move it.** A narrower\ntrack divides into a narrower pane, and so does a slide arriving or leaving\nwhere the panes grow to fill a track holding everything it has. A watch on\nthe track answers the first and sleeps through the second.\n\n**An observation is delivered after layout and inside the same rendering\nstep**, and the update it asks for is a microtask on the end of it, so the\ndots are added to a frame that has not been painted. Taking the reading\nfrom `updated` instead would be the same arithmetic one Lit warning\nearlier, and the warning is about the right thing: a render that has to\nread layout is a render that has to run twice.\n\nCalled from `connectedCallback`, `firstUpdated` and `updated`, so a\nconsumer switching to `auto` after mount is watched, one switching away\nstops being, and a carousel moved in the DOM is watched again. Observing is\nwhat asks for the first reading, so the watch is rebuilt rather than kept\nacross a mode change."
9926
+ },
9836
9927
  {
9837
9928
  "kind": "field",
9838
9929
  "name": "panes",
@@ -10489,9 +10580,9 @@
10489
10580
  {
10490
10581
  "name": "per-view",
10491
10582
  "type": {
10492
- "text": "number | undefined"
10583
+ "text": "number | 'auto' | undefined"
10493
10584
  },
10494
- "description": "How many slides are shown across the track at once. Absent shows one.\n\n**Here rather than on the styled tier, because it decides how many places\nthe track can stop.** From the stylesheet it looks like a width, one rule\nper value setting a custom property that the slides divide the track by.\nBut a carousel showing four of six slides has three positions, not six, and\neverything that counts positions is in this file. Declared up there, `goTo`\nclamps to the last *slide*, so three of the six indicators point at an\noffset the track has already reached: pressing next walks the dots to the\nend while the slides stop moving a third of the way through.\n\n**The number is published to the styles rather than enumerated by them**,\nas `--carousel-per-view` on the track, which is what keeps a width and a\ncount from disagreeing. A sheet can only name a value it wrote a rule for,\nso the styled tier enumerated `per-view=\"2\"` through `5` and called five a\njudgment; above it the pane fell back to one across while this file went on\ncounting positions. Measured in Chromium over eight slides, six across gave\nthree dots, a pane taking the whole track and five slides reachable only by\ndragging. Written from here there is no ceiling to exceed, and a consumer's\nown sheet reads the same declaration the styled one does. `runEnd` publishes\nits correction the same way and for the same reason.\n\n**On the track and not on the host.** A custom property on the host is a\nreflected attribute by another name: it would arrive on upgrade, and a\nconsumer's server markup never had it. Inside the component's own shadow\nroot it serializes with the rest of the template on the server, and the\ntrack's own declaration beats anything a consumer inherits in, so the\nattribute is the one answer to how wide a pane is.\n\nA value the track cannot honor is taken to the nearest one it can, by\n`across`, and both tiers read that rather than this.\n\nNo default, because it reflects. A reflected property with a value writes\nits attribute on upgrade, and the host then carries something a consumer's\nserver markup never had, which React reports as a mismatch. The one-across\ncase is the absent attribute, and the styles read it that way.",
10585
+ "description": "How many slides are shown across the track at once. Absent shows one.\n\n**Here rather than on the styled tier, because it decides how many places\nthe track can stop.** From the stylesheet it looks like a width, one rule\nper value setting a custom property that the slides divide the track by.\nBut a carousel showing four of six slides has three positions, not six, and\neverything that counts positions is in this file. Declared up there, `goTo`\nclamps to the last *slide*, so three of the six indicators point at an\noffset the track has already reached: pressing next walks the dots to the\nend while the slides stop moving a third of the way through.\n\n**The number is published to the styles rather than enumerated by them**,\nas `--carousel-per-view` on the track, which is what keeps a width and a\ncount from disagreeing. A sheet can only name a value it wrote a rule for,\nso the styled tier enumerated `per-view=\"2\"` through `5` and called five a\njudgment; above it the pane fell back to one across while this file went on\ncounting positions. Measured in Chromium over eight slides, six across gave\nthree dots, a pane taking the whole track and five slides reachable only by\ndragging. Written from here there is no ceiling to exceed, and a consumer's\nown sheet reads the same declaration the styled one does. `runEnd` publishes\nits correction the same way and for the same reason.\n\n**On the track and not on the host.** A custom property on the host is a\nreflected attribute by another name: it would arrive on upgrade, and a\nconsumer's server markup never had it. Inside the component's own shadow\nroot it serializes with the rest of the template on the server, and the\ntrack's own declaration beats anything a consumer inherits in, so the\nattribute is the one answer to how wide a pane is.\n\nA value the track cannot honor is taken to the nearest one it can, by\n`across`, and both tiers read that rather than this.\n\n**`auto` fits as many as the track can hold**, at `--carousel-slide-min`\napiece, and nothing is written onto the track: the count is the sheet's,\nand the sheet's alone, because it is the one number here CSS can work out\nfor itself. Everything it takes is a length the sheet already has, so the\npanes are laid out at their final width by the first rule that runs, on a\nserver's markup as on the client's, and no script is waited on. That is the\nwhole reason a declared count was ever the only option: a carousel that\nasks how wide its container is can only ask on the client, so the panes go\ndown at one width and move to another when the answer arrives. Written\ninline the count would outrank the sheet that computes it, which is why an\n`auto` track is handed nothing. A count stays for the design that calls for\nexactly three.\n\nThe styled sheet says how it divides, and the headless `auto` example\ncarries the same three lines for a consumer writing their own.\n\n**A value rather than the resting behavior**, which is what the\nresponsiveness policy asks of every axis that a box decides: a splitter\nturns on `orientation=\"auto\"` and a plain one does not turn at all. Fitting\nchanges what the carousel *does*, since how many are across is how many\nplaces the track can stop, so a carousel that had not asked for it would\nfind its position count moving with the window.\n\nNo default, because it reflects. A reflected property with a value writes\nits attribute on upgrade, and the host then carries something a consumer's\nserver markup never had, which React reports as a mismatch. The one-across\ncase is the absent attribute, and the styles read it that way.",
10495
10586
  "fieldName": "perView"
10496
10587
  }
10497
10588
  ],
@@ -10518,6 +10609,10 @@
10518
10609
  "description": "The same carousel on the headless tier, with every rule written by hand.",
10519
10610
  "slug": "preview"
10520
10611
  },
10612
+ {
10613
+ "description": "As many slides as the track holds, from a minimum width and no count.",
10614
+ "slug": "auto"
10615
+ },
10521
10616
  {
10522
10617
  "description": "Three slides across, with the pane, the gap and the run's end written by hand.",
10523
10618
  "slug": "several-across"
@@ -19150,7 +19245,7 @@
19150
19245
  "text": "boolean"
19151
19246
  },
19152
19247
  "default": "false",
19153
- "description": "Whether the control refuses changes while still submitting its value.\n\n**Enforced by refusing every request in one place**, which is the seam this\ncomponent already has: `requestValue` is what every path here comes through,\nand gating it once is what stops the next call site from being written\nwithout the check.\n\nNot `disabled`, which is the confusion worth naming: a disabled control\nsubmits nothing and leaves the tab order, and a read-only one submits its\nvalue and stays reachable, which is what a form showing a color somebody\nelse set needs.\n\n**One gate is not enough here, and the second is `move`.** This picker\nholds a model beside its value, which Date Picker and Time Picker do not:\n`moved` is the HSB the square is drawn from, and it is written before the\nvalue is requested. Gating only the request would leave the square under\nthe reader's pointer and the value where it was, which is a control that\nrefuses and looks like it accepted.",
19248
+ "description": "Whether the control refuses changes while still submitting its value.\n\n**Enforced by refusing every request in one place**, which is the seam this\ncomponent already has: `requestValue` is what every path here comes through,\nand gating it once is what stops the next call site from being written\nwithout the check.\n\nNot `disabled`, which is the confusion worth naming: a disabled control\nsubmits nothing and leaves the tab order, and a read-only one submits its\nvalue and stays reachable, which is what a form showing a color somebody\nelse set needs.\n\n**One gate is not enough here, and the second is `move`.** This picker\nholds a model beside its value, which Date Picker and Time Picker do not:\n`moved` is the HSB the square is drawn from, and it is written before the\nvalue is requested. Gating only the request would leave the square under\nthe reader's pointer and the value where it was, which is a control that\nrefuses and looks like it accepted.\n\n**Two controls are refused whole rather than at either seam**, because\nboth do something before they ask. The syntax toggle rewrites the value, so\nletting it move would show `rgb` over a hex it is no longer allowed to\nrestate. The eyedropper clears the screen and runs a system sampler, so\nletting it open would take a color from the reader and drop it.",
19154
19249
  "attribute": "readonly",
19155
19250
  "reflects": true
19156
19251
  },
@@ -20198,7 +20293,7 @@
20198
20293
  "text": "boolean"
20199
20294
  },
20200
20295
  "default": "false",
20201
- "description": "Whether the control refuses changes while still submitting its value.\n\n**Enforced by refusing every request in one place**, which is the seam this\ncomponent already has: `requestValue` is what every path here comes through,\nand gating it once is what stops the next call site from being written\nwithout the check.\n\nNot `disabled`, which is the confusion worth naming: a disabled control\nsubmits nothing and leaves the tab order, and a read-only one submits its\nvalue and stays reachable, which is what a form showing a color somebody\nelse set needs.\n\n**One gate is not enough here, and the second is `move`.** This picker\nholds a model beside its value, which Date Picker and Time Picker do not:\n`moved` is the HSB the square is drawn from, and it is written before the\nvalue is requested. Gating only the request would leave the square under\nthe reader's pointer and the value where it was, which is a control that\nrefuses and looks like it accepted.",
20296
+ "description": "Whether the control refuses changes while still submitting its value.\n\n**Enforced by refusing every request in one place**, which is the seam this\ncomponent already has: `requestValue` is what every path here comes through,\nand gating it once is what stops the next call site from being written\nwithout the check.\n\nNot `disabled`, which is the confusion worth naming: a disabled control\nsubmits nothing and leaves the tab order, and a read-only one submits its\nvalue and stays reachable, which is what a form showing a color somebody\nelse set needs.\n\n**One gate is not enough here, and the second is `move`.** This picker\nholds a model beside its value, which Date Picker and Time Picker do not:\n`moved` is the HSB the square is drawn from, and it is written before the\nvalue is requested. Gating only the request would leave the square under\nthe reader's pointer and the value where it was, which is a control that\nrefuses and looks like it accepted.\n\n**Two controls are refused whole rather than at either seam**, because\nboth do something before they ask. The syntax toggle rewrites the value, so\nletting it move would show `rgb` over a hex it is no longer allowed to\nrestate. The eyedropper clears the screen and runs a system sampler, so\nletting it open would take a color from the reader and drop it.",
20202
20297
  "fieldName": "readOnly"
20203
20298
  },
20204
20299
  {
@@ -20285,7 +20380,7 @@
20285
20380
  "helix": {
20286
20381
  "category": "forms",
20287
20382
  "since": "0.1.0",
20288
- "status": "experimental",
20383
+ "status": "stable",
20289
20384
  "documentation": "https://fusion.dev/helix/components/color-picker",
20290
20385
  "aliases": [
20291
20386
  "Color",
@@ -73586,7 +73681,7 @@
73586
73681
  "helix": {
73587
73682
  "category": "forms",
73588
73683
  "since": "0.1.0",
73589
- "status": "experimental",
73684
+ "status": "stable",
73590
73685
  "documentation": "https://fusion.dev/helix/components/rating",
73591
73686
  "aliases": [
73592
73687
  "Stars",
@@ -81304,7 +81399,7 @@
81304
81399
  }
81305
81400
  }
81306
81401
  ],
81307
- "description": "Says the arrangement has settled, so a consumer can keep it.\n\nThrough the audited path, so a consumer who owns `position` is told what was\nasked for and decides, and one who does not has already had it applied.\n\n**The second announcement is conditional on the first, because it is derived\nfrom it.** `shutAt` reads the position the gesture ended on, so a refused\n`helix-before-resize` leaves a collapse computed from a number that was\nrefused. Announced anyway, the refusal produced the outcome it was refusing:\nmeasured on a divider at 40 with a listener calling `preventDefault`, Home\nleft `position` reporting 0 and `collapsed` reporting `start`, because the\ncollapse applied and `position` reads the sliver once a pane is shut. The\nreturn of the first request is what says so, and it was being dropped: a\ncancelable event dispatched as a bare statement with nothing reading the\nanswer, which is the shape the navigation cohort named on `Outline` and left.\n\nRefusing the collapse alone still leaves the resize, which is the other way\nround and is coherent: the divider goes where the drag put it and the pane\nis not declared shut. What a consumer cannot have is a pane that collapses\nout from under a position they said no to.\n\n**A gesture that moved nothing announces nothing, and this is the one place\nthat can say so.** A change carrying the position the divider already had\nrewrites a consumer's stored value from a gesture that moved nothing:\nharmless where the value round-trips exactly, and not where it does not,\nwhich is any consumer storing pixels and dividing by a box. The separator's\n`pointerup` held that as an early return of its own and was the only door\nwith one. There are five: the separator's pointer and arrows, the corner's\npointer and arrows, and a double click through `reset`, and `moveTo` is a\nsixth a consumer can open. Measured in Chromium 17 Sep 2026 on the built\nbundle, every door but the guarded one announced a move of zero: a corner\npress with nothing between announced on both splitters, `Home` on a divider\nalready at its minimum announced, and a double click on one already at its\ndefault announced.\n\n**Measured against where the gesture began, and not against `position`.**\n`move` applies as it goes, so by the time a drag settles the position is\nalready the number about to be announced, and comparing those two would\nsilence every drag there is. `began` is the press; a discrete path takes\nnone and compares against the position, which for it is the same thing.\n\n**Per divider rather than per gesture, which is the corner's question.** The\ncorner drives two at once, so \"moved nothing\" cannot mean \"neither moved\":\na drag along one axis moves one of them, and the other would be announcing\na number nobody asked to change. Each settles on its own and guards on its\nown, so the one that moved still announces while the one that did not stays\nquiet. That is also why the guard cannot live in `handleCornerUp`, which\nonly ever sees the pair.\n\n**The collapse counts as movement, because a refusal can leave the two\ndisagreeing.** A consumer who refuses `helix-before-collapse` has a pane at\nthe edge that is not declared shut, and a guard reading the position alone\nwould then never offer the collapse again: `Home` is already at the\nminimum, so every later press is a move of zero."
81402
+ "description": "Says the arrangement has settled, so a consumer can keep it.\n\nThrough the audited path, so a consumer who owns `position` is told what was\nasked for and decides, and one who does not has already had it applied.\n\n**A refused resize ends the resize and not the gesture.** `shutAt` reads the\nposition the gesture ended on, so the collapse is computed from a number the\nconsumer may have declined, and the reading this replaces was that the two\nare therefore one decision: refuse the first and the second cannot stand.\nThat is a reasonable thing to believe about a splitter nobody else owns, and\nit takes \"no\" to mean the gesture is over. What it misses is that a consumer\nwho owns the position refuses in order to *take* the decision, not to end\nit. The sandbox two folders away refuses every `helix-before-resize` from\nthe splitter inside it, re-announces the same move in its own vocabulary and\nwrites the answer back through `position`, which is what makes it the single\ndecision point; returning on its refusal left `panel-collapsed-size` inert\nthere, the shipped `with-a-panel` example measuring a 0px panel against the\n36 its summary promises. So the collapse is reached either way and is\nrefused, or not, on its own.\n\n**Which is honest because the collapse carries its own position.** A shut\npane sits at `collapsed-size`, which is where `position` reads it from once\n`collapsed` is set, and never at the number the resize named: refuse a drag\nto 100 on a splitter with `collapsed-size=\"10\"`, allow the collapse, and the\ndivider is at 90. Nothing writes the refused number either, `requestChange`\napplying nothing when it is refused and the collapse below writing the open\nposition back over whatever a drag had already drawn, so a consumer who says\nno to the position and yes to the collapse gets the pane shut and keeps the\nnumber they had to reopen to. The two coincide only where `collapsed-size`\nis zero, and the states differ even there: a shut pane knows where to open\nback to and a divider parked at the edge does not.\n\nRefusing the collapse alone still leaves the resize, which is the other way\nround and is coherent: the divider goes where the drag put it and the pane\nis not declared shut.\n\n**A gesture that moved nothing announces nothing, and this is the one place\nthat can say so.** A change carrying the position the divider already had\nrewrites a consumer's stored value from a gesture that moved nothing:\nharmless where the value round-trips exactly, and not where it does not,\nwhich is any consumer storing pixels and dividing by a box. The separator's\n`pointerup` held that as an early return of its own and was the only door\nwith one. There are five: the separator's pointer and arrows, the corner's\npointer and arrows, and a double click through `reset`, and `moveTo` is a\nsixth a consumer can open. Measured in Chromium 17 Sep 2026 on the built\nbundle, every door but the guarded one announced a move of zero: a corner\npress with nothing between announced on both splitters, `Home` on a divider\nalready at its minimum announced, and a double click on one already at its\ndefault announced.\n\n**Measured against where the gesture began, and not against `position`.**\n`move` applies as it goes, so by the time a drag settles the position is\nalready the number about to be announced, and comparing those two would\nsilence every drag there is. `began` is the press; a discrete path takes\nnone and compares against the position, which for it is the same thing.\n\n**Per divider rather than per gesture, which is the corner's question.** The\ncorner drives two at once, so \"moved nothing\" cannot mean \"neither moved\":\na drag along one axis moves one of them, and the other would be announcing\na number nobody asked to change. Each settles on its own and guards on its\nown, so the one that moved still announces while the one that did not stays\nquiet. That is also why the guard cannot live in `handleCornerUp`, which\nonly ever sees the pair.\n\n**The collapse counts as movement, because a refusal can leave the two\ndisagreeing.** A consumer who refuses `helix-before-collapse` has a pane at\nthe edge that is not declared shut, and a guard reading the position alone\nwould then never offer the collapse again: `Home` is already at the\nminimum, so every later press is a move of zero."
81308
81403
  },
81309
81404
  {
81310
81405
  "kind": "field",
@@ -81808,7 +81903,7 @@
81808
81903
  "type": {
81809
81904
  "text": "HelixBeforeCollapseEvent"
81810
81905
  },
81811
- "description": "Fired before a pane shuts or reopens, and only where the position it is derived from stood. Cancelable: refusing it leaves the pane as it was and the divider where the gesture put it.",
81906
+ "description": "Fired before a pane shuts or reopens, whether or not the resize that reached it was allowed, and carrying its own position: a shut pane sits at `collapsed-size` rather than at the number the resize named. Cancelable: refusing it leaves the pane as it was and the divider where the gesture put it.",
81812
81907
  "name": "helix-before-collapse",
81813
81908
  "reactProp": "onhelix-before-collapse"
81814
81909
  },
@@ -81816,7 +81911,7 @@
81816
81911
  "type": {
81817
81912
  "text": "HelixBeforeResizeEvent"
81818
81913
  },
81819
- "description": "Fired before the divider settles, and only where the gesture moved it: a press that moved nothing, a drag that came back, an arrow with nowhere left to go and a corner that moved the other divider are all silent on this one. Cancelable: refusing it takes the whole of where the gesture ended, so no collapse follows from it either. A drag has already drawn itself by then and holds where it was dropped until the consumer assigns their own.",
81914
+ "description": "Fired before the divider settles, and only where the gesture moved it: a press that moved nothing, a drag that came back, an arrow with nowhere left to go and a corner that moved the other divider are all silent on this one. Cancelable: refusing it takes the position and leaves the rest of the gesture, so a collapse the gesture reached is still announced and still yours to refuse. A drag has already drawn itself by then and holds where it was dropped until the consumer assigns their own.",
81820
81915
  "name": "helix-before-resize",
81821
81916
  "reactProp": "onhelix-before-resize"
81822
81917
  }
@@ -95151,7 +95246,7 @@
95151
95246
  "helix": {
95152
95247
  "category": "forms",
95153
95248
  "since": "0.1.0",
95154
- "status": "experimental",
95249
+ "status": "stable",
95155
95250
  "documentation": "https://fusion.dev/helix/components/time-picker",
95156
95251
  "aliases": [
95157
95252
  "Time",
@@ -44,8 +44,9 @@ import { type PropertyValues, type TemplateResult } from 'lit';
44
44
  *
45
45
  * @cssproperty --carousel-gap - Space between slides. A sheet sets it and the component measures the run with it.
46
46
  * @cssproperty --carousel-pane - The size one pane is laid out at. A sheet sets it and the component measures the run with it.
47
- * @cssproperty --carousel-per-view - How many slides go across, written onto the track by the component so a sheet divides the track by the same number the positions are counted from. Read it rather than selecting on `per-view`, which cannot be enumerated without a ceiling. See `perView`.
47
+ * @cssproperty --carousel-per-view - How many slides go across, written onto the track by the component so a sheet divides the track by the same number the positions are counted from. Read it rather than selecting on `per-view`, which cannot be enumerated without a ceiling. Under `per-view="auto"` the component writes nothing and the sheet declares it instead, so the same name answers in both modes. See `perView`.
48
48
  * @cssproperty --carousel-run-end - How far short of a whole pixel the run ends, on the last slide alone, for a sheet to take up as `margin-inline-end`. See `runEnd`.
49
+ * @cssproperty --carousel-slide-min - The narrowest a slide may be laid out, which is what `per-view="auto"` fits the track with. A length, since the sheet divides by it. Unset is the whole track, which is one across.
49
50
  */
50
51
  export declare class HelixHeadlessCarousel extends HelixElement {
51
52
  /**
@@ -183,14 +184,37 @@ export declare class HelixHeadlessCarousel extends HelixElement {
183
184
  * A value the track cannot honor is taken to the nearest one it can, by
184
185
  * `across`, and both tiers read that rather than this.
185
186
  *
187
+ * **`auto` fits as many as the track can hold**, at `--carousel-slide-min`
188
+ * apiece, and nothing is written onto the track: the count is the sheet's,
189
+ * and the sheet's alone, because it is the one number here CSS can work out
190
+ * for itself. Everything it takes is a length the sheet already has, so the
191
+ * panes are laid out at their final width by the first rule that runs, on a
192
+ * server's markup as on the client's, and no script is waited on. That is the
193
+ * whole reason a declared count was ever the only option: a carousel that
194
+ * asks how wide its container is can only ask on the client, so the panes go
195
+ * down at one width and move to another when the answer arrives. Written
196
+ * inline the count would outrank the sheet that computes it, which is why an
197
+ * `auto` track is handed nothing. A count stays for the design that calls for
198
+ * exactly three.
199
+ *
200
+ * The styled sheet says how it divides, and the headless `auto` example
201
+ * carries the same three lines for a consumer writing their own.
202
+ *
203
+ * **A value rather than the resting behavior**, which is what the
204
+ * responsiveness policy asks of every axis that a box decides: a splitter
205
+ * turns on `orientation="auto"` and a plain one does not turn at all. Fitting
206
+ * changes what the carousel *does*, since how many are across is how many
207
+ * places the track can stop, so a carousel that had not asked for it would
208
+ * find its position count moving with the window.
209
+ *
186
210
  * No default, because it reflects. A reflected property with a value writes
187
211
  * its attribute on upgrade, and the host then carries something a consumer's
188
212
  * server markup never had, which React reports as a mismatch. The one-across
189
213
  * case is the absent attribute, and the styles read it that way.
190
214
  */
191
- perView?: number;
215
+ perView?: number | 'auto';
192
216
  /**
193
- * `perView` as both tiers use it: a whole number of slides, one at least.
217
+ * A declared `perView` as both tiers use it: a whole number, one at least.
194
218
  *
195
219
  * **The clamp is here because the arithmetic is here**, and the two readers
196
220
  * of this number have to be given the same one. A fraction is floored, since
@@ -202,12 +226,39 @@ export declare class HelixHeadlessCarousel extends HelixElement {
202
226
  * drew no indicators at all and left `index` at `NaN`. All of them read as
203
227
  * one across, which is what a carousel with nothing sayable asked for.
204
228
  *
229
+ * **A vertical `auto` is one of them.** Fitting divides the room the page
230
+ * gave the track, and a vertical track's room is a height the consumer chose
231
+ * with `--carousel-block-size`: there is nothing there to fit against that
232
+ * they did not already decide, so the count is theirs to state. It also
233
+ * cannot be had cheaply, the block axis wanting `container-type: size`, which
234
+ * lays a track out as empty wherever its height is not definite.
235
+ *
205
236
  * Not clamped to the number of slides. Six across four slides is four panes
206
237
  * at a sixth of the track each and the one place to stand, which is what was
207
238
  * asked for as nearly as it can be had, and the slide count is a fact the
208
239
  * server does not have: clamping to it would draw one width there and another
209
240
  * on upgrade.
210
241
  */
242
+ private get clamped();
243
+ /** Whether the track is fitting slides to its own box rather than to a count. */
244
+ private get fits();
245
+ /**
246
+ * How many slides go across, and `undefined` while a fitted track is unlaid.
247
+ *
248
+ * **Undefined is the honest answer and the indicators read it as one**, which
249
+ * is the whole of how a fitted carousel keeps from drawing a run of dots at
250
+ * one length and correcting it at another. The count a fitted track goes
251
+ * across is whatever its box resolved to, so nothing knows it until there is
252
+ * a box: not the server, which has no layout, and not the render that draws
253
+ * the panes, layout being what follows a render rather than what precedes it.
254
+ * The dots wait for it, which costs them nothing, because a server serves no
255
+ * indicators for any carousel, `slides` being empty there.
256
+ *
257
+ * Measured 16 Sep 2026 in Chromium 151.0.7922.34 and WebKit 26.5, recording
258
+ * every run of dots a carousel has from the upgrade on: a fitted one goes
259
+ * none, then five, and never another number; a declared one goes straight to
260
+ * six. What a reader can see is the run missing, never the run wrong.
261
+ */
211
262
  private get across();
212
263
  /**
213
264
  * How many distinct views the track holds, which is what the indicators count.
@@ -216,8 +267,93 @@ export declare class HelixHeadlessCarousel extends HelixElement {
216
267
  * `n - perView + 1` times, so six slides four across is three: 0 to 3, 1 to 4,
217
268
  * 2 to 5. One at minimum, since a carousel showing everything it has still
218
269
  * has the one place to be.
270
+ *
271
+ * One across until a fitted track has been laid out, which is the same answer
272
+ * a carousel that has never been told anything gives, and the dots are
273
+ * withheld rather than counted from it. See `across`.
219
274
  */
220
275
  get positions(): number;
276
+ /**
277
+ * How many slides the box turned out to hold, from the pane it laid out.
278
+ *
279
+ * **The measurement asks what happened rather than what was asked for**, and
280
+ * that is why it is the pane's width and not the count in the declaration.
281
+ * The sheet divides the track by `--carousel-per-view`, which under `auto` is
282
+ * arithmetic rather than a number, and a consumer's own sheet may divide it
283
+ * some other way entirely or grow the panes to fill a track holding
284
+ * everything it has. Reading the box back gets the same answer as the
285
+ * declaration wherever the two agree and the true one wherever they do not,
286
+ * so a width and a count cannot disagree in either direction.
287
+ *
288
+ * The sheet's own arithmetic run backward, on the lengths that were actually
289
+ * used: a gap added to the room, divided by a pane and a gap. Rounded,
290
+ * because the pane a sheet lays out is a sixty-fourth off its exact share by
291
+ * design and the quotient lands a ten-thousandth off a whole number.
292
+ *
293
+ * Undefined where there is nothing to measure, which is a harness rather than
294
+ * a browser: happy-dom lays nothing out and reports zero for every box, and a
295
+ * count taken from zeros would be a number with no layout behind it.
296
+ */
297
+ private measure;
298
+ /**
299
+ * Takes the reading, and puts the index back inside what it leaves.
300
+ *
301
+ * **A fitted carousel's last position moves with the window**, which a
302
+ * declared one's never does: eight slides at one across has eight places to
303
+ * stand and four across has five, so a track widened while the reader sits on
304
+ * the last of them is a track resting somewhere it can no longer be. Through
305
+ * `goTo`, so the correction goes out as the same cancelable announcement
306
+ * every other index change does and a consumer holding `index` hears it
307
+ * rather than being overwritten.
308
+ *
309
+ * Only on a change, so the observer's own re-render cannot feed itself.
310
+ */
311
+ private readonly readFit;
312
+ /**
313
+ * The count handed to the styles, and nothing at all where they work it out.
314
+ *
315
+ * A declared count is this file's: it decides how many places the track can
316
+ * stop, so the number the panes are divided by has to be the number the
317
+ * positions were counted from, and writing it on the track inside the
318
+ * component's own shadow root is what keeps a sheet from having to enumerate
319
+ * the values it will answer for.
320
+ *
321
+ * A fitted one is the sheet's, and handing it anything would take it back: an
322
+ * inline declaration outranks every rule short of `!important`, so a track
323
+ * told `1` here could never be told three by the rule that fits it. The
324
+ * absence is not a gap either, because the rule that computes the count
325
+ * declares it on this same element, which beats anything a consumer inherits
326
+ * in exactly as the inline one did.
327
+ */
328
+ private handed;
329
+ /** What the box resolved to, and `undefined` until it has been laid out. */
330
+ private fitted;
331
+ /** Watches the pane a fitted track laid out, while `auto` is what was asked. */
332
+ private sizes;
333
+ /** The pane being watched, so a re-rendered run moves the watch with it. */
334
+ private watched;
335
+ /**
336
+ * Starts or stops watching, according to what the count was asked to be.
337
+ *
338
+ * **The pane and not the track, because both questions move it.** A narrower
339
+ * track divides into a narrower pane, and so does a slide arriving or leaving
340
+ * where the panes grow to fill a track holding everything it has. A watch on
341
+ * the track answers the first and sleeps through the second.
342
+ *
343
+ * **An observation is delivered after layout and inside the same rendering
344
+ * step**, and the update it asks for is a microtask on the end of it, so the
345
+ * dots are added to a frame that has not been painted. Taking the reading
346
+ * from `updated` instead would be the same arithmetic one Lit warning
347
+ * earlier, and the warning is about the right thing: a render that has to
348
+ * read layout is a render that has to run twice.
349
+ *
350
+ * Called from `connectedCallback`, `firstUpdated` and `updated`, so a
351
+ * consumer switching to `auto` after mount is watched, one switching away
352
+ * stops being, and a carousel moved in the DOM is watched again. Observing is
353
+ * what asks for the first reading, so the watch is rebuilt rather than kept
354
+ * across a mode change.
355
+ */
356
+ private watchPane;
221
357
  /**
222
358
  * The panes rendered for the slides, in order. Empty until the first render.
223
359
  *
@@ -9,6 +9,15 @@ import { caretLeft, caretRight, play, stop } from '@fusion.dev/helix-icons';
9
9
  import { html, nothing } from 'lit';
10
10
  import { property, state } from 'lit/decorators.js';
11
11
  import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
12
+ const COUNT_OR_AUTO = {
13
+ fromAttribute: (value) => {
14
+ if (value === null) {
15
+ return undefined;
16
+ }
17
+ return value === 'auto' ? 'auto' : Number(value);
18
+ },
19
+ toAttribute: (value) => value === undefined ? null : String(value),
20
+ };
12
21
  let HelixHeadlessCarousel = class HelixHeadlessCarousel extends HelixElement {
13
22
  constructor() {
14
23
  super(...arguments);
@@ -20,6 +29,16 @@ let HelixHeadlessCarousel = class HelixHeadlessCarousel extends HelixElement {
20
29
  this.label = 'Carousel';
21
30
  this.loop = false;
22
31
  this.rotating = false;
32
+ this.readFit = () => {
33
+ const fitted = this.measure();
34
+ if (fitted === undefined || fitted === this.fitted) {
35
+ return;
36
+ }
37
+ this.fitted = fitted;
38
+ if (this.index > this.positions - 1) {
39
+ this.goTo(this.positions - 1);
40
+ }
41
+ };
23
42
  this.handleIndicatorKeydown = (event) => {
24
43
  const step = inlineStep(this, event.key);
25
44
  if (step === 0) {
@@ -95,12 +114,55 @@ let HelixHeadlessCarousel = class HelixHeadlessCarousel extends HelixElement {
95
114
  this.indexState = next;
96
115
  this.requestUpdate('index', previous);
97
116
  }
98
- get across() {
117
+ get clamped() {
99
118
  const asked = this.perView ?? 1;
100
- return Number.isFinite(asked) ? Math.max(1, Math.floor(asked)) : 1;
119
+ return typeof asked === 'number' && Number.isFinite(asked) ? Math.max(1, Math.floor(asked)) : 1;
120
+ }
121
+ get fits() {
122
+ return this.perView === 'auto' && this.orientation !== 'vertical';
123
+ }
124
+ get across() {
125
+ return this.fits ? this.fitted : this.clamped;
101
126
  }
102
127
  get positions() {
103
- return Math.max(1, this.slides.length - this.across + 1);
128
+ return Math.max(1, this.slides.length - (this.across ?? 1) + 1);
129
+ }
130
+ measure() {
131
+ const track = this.partNamed('track');
132
+ const pane = this.panes[0]?.getBoundingClientRect().width;
133
+ if (!track || pane === undefined) {
134
+ return undefined;
135
+ }
136
+ const asked = Number.parseFloat(getComputedStyle(track).columnGap);
137
+ const gap = Number.isNaN(asked) ? 0 : asked;
138
+ const room = track.clientWidth - 2 * this.bleed(track);
139
+ if (pane <= 0 || room <= 0) {
140
+ return undefined;
141
+ }
142
+ return Math.max(1, Math.round((room + gap) / (pane + gap)));
143
+ }
144
+ handed() {
145
+ return this.fits ? nothing : `--carousel-per-view: ${this.clamped}`;
146
+ }
147
+ watchPane() {
148
+ if (!this.fits || typeof ResizeObserver === 'undefined') {
149
+ this.sizes?.disconnect();
150
+ this.sizes = undefined;
151
+ this.watched = undefined;
152
+ return;
153
+ }
154
+ this.sizes ??= new ResizeObserver(this.readFit);
155
+ const pane = this.panes[0];
156
+ if (pane === this.watched) {
157
+ return;
158
+ }
159
+ if (this.watched) {
160
+ this.sizes.unobserve(this.watched);
161
+ }
162
+ this.watched = pane;
163
+ if (pane) {
164
+ this.sizes.observe(pane);
165
+ }
104
166
  }
105
167
  get panes() {
106
168
  return [...(this.renderRoot?.querySelectorAll?.('[part="slide"]') ?? [])];
@@ -196,6 +258,7 @@ let HelixHeadlessCarousel = class HelixHeadlessCarousel extends HelixElement {
196
258
  this.addEventListener('pointerenter', this.pause);
197
259
  this.watcher = new MutationObserver(() => this.requestUpdate());
198
260
  this.watcher.observe(this, { childList: true });
261
+ this.watchPane();
199
262
  }
200
263
  disconnectedCallback() {
201
264
  super.disconnectedCallback();
@@ -207,6 +270,9 @@ let HelixHeadlessCarousel = class HelixHeadlessCarousel extends HelixElement {
207
270
  this.removeEventListener('pointerenter', this.pause);
208
271
  this.watcher?.disconnect();
209
272
  this.watcher = undefined;
273
+ this.sizes?.disconnect();
274
+ this.sizes = undefined;
275
+ this.watched = undefined;
210
276
  clearInterval(this.timer);
211
277
  this.timer = undefined;
212
278
  }
@@ -218,11 +284,15 @@ let HelixHeadlessCarousel = class HelixHeadlessCarousel extends HelixElement {
218
284
  if (changed.has('index')) {
219
285
  this.showing = undefined;
220
286
  }
287
+ if (changed.has('perView')) {
288
+ this.fitted = undefined;
289
+ }
221
290
  hostAria(this.internals, { label: this.label, role: 'group' });
222
291
  this.internals.ariaRoleDescription = 'carousel';
223
292
  }
224
293
  firstUpdated(changed) {
225
294
  super.firstUpdated(changed);
295
+ this.watchPane();
226
296
  this.finishHydration();
227
297
  }
228
298
  updated(changed) {
@@ -237,6 +307,7 @@ let HelixHeadlessCarousel = class HelixHeadlessCarousel extends HelixElement {
237
307
  if (count > 0 && this.index > count - 1) {
238
308
  this.goTo(count - 1);
239
309
  }
310
+ this.watchPane();
240
311
  this.assign();
241
312
  }
242
313
  assign() {
@@ -308,7 +379,7 @@ let HelixHeadlessCarousel = class HelixHeadlessCarousel extends HelixElement {
308
379
  <div
309
380
  data-scroller
310
381
  part="track"
311
- style=${`--carousel-per-view: ${this.across}`}
382
+ style=${this.handed()}
312
383
  @scroll=${this.handleTrackScroll}
313
384
  @scrollend=${this.handleScroll}
314
385
  @pointerdown=${this.handleTake}
@@ -328,7 +399,7 @@ let HelixHeadlessCarousel = class HelixHeadlessCarousel extends HelixElement {
328
399
  ></slot>
329
400
  </div>`)}
330
401
  </div>
331
- ${this.slides.length > 1
402
+ ${this.slides.length > 1 && this.across !== undefined
332
403
  ? html `<div part="indicators" role="group" aria-label=${`${this.label} slides`}>
333
404
  ${''}
334
405
  ${Array.from({ length: this.positions }).map((_place, at) => {
@@ -376,8 +447,11 @@ __decorate([
376
447
  state()
377
448
  ], HelixHeadlessCarousel.prototype, "rotating", void 0);
378
449
  __decorate([
379
- property({ attribute: 'per-view', reflect: true, type: Number })
450
+ property({ attribute: 'per-view', converter: COUNT_OR_AUTO, reflect: true })
380
451
  ], HelixHeadlessCarousel.prototype, "perView", void 0);
452
+ __decorate([
453
+ state()
454
+ ], HelixHeadlessCarousel.prototype, "fitted", void 0);
381
455
  __decorate([
382
456
  state()
383
457
  ], HelixHeadlessCarousel.prototype, "showing", void 0);