@fusion.dev/helix 0.50.0 → 0.52.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.
@@ -13281,7 +13281,7 @@
13281
13281
  }
13282
13282
  },
13283
13283
  {
13284
- "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`.",
13284
+ "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`.",
13285
13285
  "name": "--carousel-per-view",
13286
13286
  "inheritedFrom": {
13287
13287
  "name": "HelixHeadlessCarousel",
@@ -13295,6 +13295,14 @@
13295
13295
  "name": "HelixHeadlessCarousel",
13296
13296
  "module": "src/carousel/root/carousel-root.ts"
13297
13297
  }
13298
+ },
13299
+ {
13300
+ "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.",
13301
+ "name": "--carousel-slide-min",
13302
+ "inheritedFrom": {
13303
+ "name": "HelixHeadlessCarousel",
13304
+ "module": "src/carousel/root/carousel-root.ts"
13305
+ }
13298
13306
  }
13299
13307
  ],
13300
13308
  "members": [
@@ -13521,9 +13529,9 @@
13521
13529
  "kind": "field",
13522
13530
  "name": "perView",
13523
13531
  "type": {
13524
- "text": "number | undefined"
13532
+ "text": "number | 'auto' | undefined"
13525
13533
  },
13526
- "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.",
13534
+ "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.",
13527
13535
  "attribute": "per-view",
13528
13536
  "reflects": true,
13529
13537
  "inheritedFrom": {
@@ -13533,12 +13541,40 @@
13533
13541
  },
13534
13542
  {
13535
13543
  "kind": "field",
13536
- "name": "across",
13544
+ "name": "clamped",
13537
13545
  "type": {
13538
13546
  "text": "number"
13539
13547
  },
13540
13548
  "privacy": "private",
13541
- "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.",
13549
+ "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.",
13550
+ "readonly": true,
13551
+ "inheritedFrom": {
13552
+ "name": "HelixHeadlessCarousel",
13553
+ "module": "src/carousel/root/carousel-root.ts"
13554
+ }
13555
+ },
13556
+ {
13557
+ "kind": "field",
13558
+ "name": "fits",
13559
+ "type": {
13560
+ "text": "boolean"
13561
+ },
13562
+ "privacy": "private",
13563
+ "description": "Whether the track is fitting slides to its own box rather than to a count.",
13564
+ "readonly": true,
13565
+ "inheritedFrom": {
13566
+ "name": "HelixHeadlessCarousel",
13567
+ "module": "src/carousel/root/carousel-root.ts"
13568
+ }
13569
+ },
13570
+ {
13571
+ "kind": "field",
13572
+ "name": "across",
13573
+ "type": {
13574
+ "text": "number | undefined"
13575
+ },
13576
+ "privacy": "private",
13577
+ "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.",
13542
13578
  "readonly": true,
13543
13579
  "inheritedFrom": {
13544
13580
  "name": "HelixHeadlessCarousel",
@@ -13551,13 +13587,108 @@
13551
13587
  "type": {
13552
13588
  "text": "number"
13553
13589
  },
13554
- "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.",
13590
+ "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`.",
13555
13591
  "readonly": true,
13556
13592
  "inheritedFrom": {
13557
13593
  "name": "HelixHeadlessCarousel",
13558
13594
  "module": "src/carousel/root/carousel-root.ts"
13559
13595
  }
13560
13596
  },
13597
+ {
13598
+ "kind": "method",
13599
+ "name": "measure",
13600
+ "privacy": "private",
13601
+ "return": {
13602
+ "type": {
13603
+ "text": "number | undefined"
13604
+ }
13605
+ },
13606
+ "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.",
13607
+ "inheritedFrom": {
13608
+ "name": "HelixHeadlessCarousel",
13609
+ "module": "src/carousel/root/carousel-root.ts"
13610
+ }
13611
+ },
13612
+ {
13613
+ "kind": "field",
13614
+ "name": "readFit",
13615
+ "privacy": "private",
13616
+ "readonly": true,
13617
+ "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.",
13618
+ "inheritedFrom": {
13619
+ "name": "HelixHeadlessCarousel",
13620
+ "module": "src/carousel/root/carousel-root.ts"
13621
+ }
13622
+ },
13623
+ {
13624
+ "kind": "method",
13625
+ "name": "handed",
13626
+ "privacy": "private",
13627
+ "return": {
13628
+ "type": {
13629
+ "text": "string | typeof nothing"
13630
+ }
13631
+ },
13632
+ "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.",
13633
+ "inheritedFrom": {
13634
+ "name": "HelixHeadlessCarousel",
13635
+ "module": "src/carousel/root/carousel-root.ts"
13636
+ }
13637
+ },
13638
+ {
13639
+ "kind": "field",
13640
+ "name": "fitted",
13641
+ "type": {
13642
+ "text": "number | undefined"
13643
+ },
13644
+ "privacy": "private",
13645
+ "description": "What the box resolved to, and `undefined` until it has been laid out.",
13646
+ "inheritedFrom": {
13647
+ "name": "HelixHeadlessCarousel",
13648
+ "module": "src/carousel/root/carousel-root.ts"
13649
+ }
13650
+ },
13651
+ {
13652
+ "kind": "field",
13653
+ "name": "sizes",
13654
+ "type": {
13655
+ "text": "ResizeObserver | undefined"
13656
+ },
13657
+ "privacy": "private",
13658
+ "description": "Watches the pane a fitted track laid out, while `auto` is what was asked.",
13659
+ "inheritedFrom": {
13660
+ "name": "HelixHeadlessCarousel",
13661
+ "module": "src/carousel/root/carousel-root.ts"
13662
+ }
13663
+ },
13664
+ {
13665
+ "kind": "field",
13666
+ "name": "watched",
13667
+ "type": {
13668
+ "text": "HTMLElement | undefined"
13669
+ },
13670
+ "privacy": "private",
13671
+ "description": "The pane being watched, so a re-rendered run moves the watch with it.",
13672
+ "inheritedFrom": {
13673
+ "name": "HelixHeadlessCarousel",
13674
+ "module": "src/carousel/root/carousel-root.ts"
13675
+ }
13676
+ },
13677
+ {
13678
+ "kind": "method",
13679
+ "name": "watchPane",
13680
+ "privacy": "private",
13681
+ "return": {
13682
+ "type": {
13683
+ "text": "void"
13684
+ }
13685
+ },
13686
+ "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.",
13687
+ "inheritedFrom": {
13688
+ "name": "HelixHeadlessCarousel",
13689
+ "module": "src/carousel/root/carousel-root.ts"
13690
+ }
13691
+ },
13561
13692
  {
13562
13693
  "kind": "field",
13563
13694
  "name": "panes",
@@ -13638,7 +13769,7 @@
13638
13769
  }
13639
13770
  }
13640
13771
  ],
13641
- "description": "The leading padding the track keeps for a slide to draw its own edge in.\n\nA styled track carries one so a slide's shadow and focus ring have\nsomewhere to go: a scroll container clips at its padding box, and a slide\nflush against that edge loses whatever it paints outside itself. The\nheadless tier has no stylesheet and so no padding, and this reads zero\nthere, which is why both tiers can share the arithmetic below.\n\nPhysical sides rather than logical, to match the physical `offsetLeft` and\n`offsetTop` it is subtracted from. The bleed is one length on all four\nsides, so the two agree whatever the writing mode; if that stops being\ntrue, this is the method that has to know.",
13772
+ "description": "The leading padding the track keeps for a slide to draw its own edge in.\n\nA styled track carries one so a slide's shadow and focus ring have\nsomewhere to go: a scroll container clips at its padding box, and a slide\nflush against that edge loses whatever it paints outside itself. The\nheadless tier has no stylesheet and so no padding, and this reads zero\nthere, which is why both tiers can share the arithmetic below.\n\nPhysical sides rather than logical, to match the physical `offsetLeft` and\n`offsetTop` it is subtracted from.\n\n**The side read is the side the track scrolls on, and not one side standing\nfor four.** They are one length on a track laid out at the defaults and\nstop being one the moment a consumer raises `--carousel-bleed`, which the\nhook exists to invite: a slide on `--helix-shadow-lg` reaches 44px and is\ntold to ask for it. So a horizontal track's room is its inline padding and\na vertical one's is its block padding, and each sheet puts the bleed where\nthe scroller clips. Reading one side for both would have a vertical track\nmeasuring a length nothing on its axis was laid out with.",
13642
13773
  "inheritedFrom": {
13643
13774
  "name": "HelixHeadlessCarousel",
13644
13775
  "module": "src/carousel/root/carousel-root.ts"
@@ -14356,9 +14487,9 @@
14356
14487
  {
14357
14488
  "name": "per-view",
14358
14489
  "type": {
14359
- "text": "number | undefined"
14490
+ "text": "number | 'auto' | undefined"
14360
14491
  },
14361
- "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.",
14492
+ "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.",
14362
14493
  "fieldName": "perView",
14363
14494
  "inheritedFrom": {
14364
14495
  "name": "HelixHeadlessCarousel",
@@ -14374,7 +14505,7 @@
14374
14505
  "helix": {
14375
14506
  "category": "layout",
14376
14507
  "since": "0.1.0",
14377
- "status": "experimental",
14508
+ "status": "stable",
14378
14509
  "documentation": "https://fusion.dev/helix/components/carousel",
14379
14510
  "aliases": [
14380
14511
  "Slider",
@@ -14389,6 +14520,10 @@
14389
14520
  "description": "Slides with controls and indicators, one at a time.",
14390
14521
  "slug": "preview"
14391
14522
  },
14523
+ {
14524
+ "description": "As many slides as the track holds, from a minimum width and no count.",
14525
+ "slug": "auto"
14526
+ },
14392
14527
  {
14393
14528
  "description": "A carousel the page owns, so the slide it shows can be driven from elsewhere.",
14394
14529
  "slug": "controlled"
@@ -93290,7 +93425,7 @@
93290
93425
  "summary": "Renders sandbox source in a frame of its own.",
93291
93426
  "helix": {
93292
93427
  "since": "0.1.0",
93293
- "status": "experimental",
93428
+ "status": "stable",
93294
93429
  "documentation": "https://fusion.dev/helix/components/sandbox",
93295
93430
  "aliases": [
93296
93431
  "Result",
@@ -93482,7 +93617,7 @@
93482
93617
  "text": "number"
93483
93618
  },
93484
93619
  "default": "0",
93485
- "description": "What the panel's height becomes when it is closed.\n\nZero closes it outright. A panel whose contents include the control that\nreopens it, a row of tabs being the usual one, wants that row left showing,\nand only whoever slotted it knows how tall that is.",
93620
+ "description": "What the panel's height becomes when it is closed.\n\nZero closes it outright. A panel whose contents include the control that\nreopens it, a row of tabs being the usual one, wants that row left showing,\nand only whoever slotted it knows how tall that is.\n\n**A floor rather than a second state**, which is what keeps `panel-size` the\nwhole truth about the panel: a divider driven to the end lands here and\n`panel-size` reads this number, so \"closed\" is something a consumer already\nstores rather than a property they have to learn. The splitter inside has a\n`collapsed` of its own and it is refused, for the reason `handleCollapse`\ngives.",
93486
93621
  "attribute": "panel-collapsed-size"
93487
93622
  },
93488
93623
  {
@@ -93592,6 +93727,16 @@
93592
93727
  "description": "`panel-collapsed-size` in the units the splitter works in.",
93593
93728
  "readonly": true
93594
93729
  },
93730
+ {
93731
+ "kind": "field",
93732
+ "name": "panelFloor",
93733
+ "type": {
93734
+ "text": "number"
93735
+ },
93736
+ "privacy": "private",
93737
+ "description": "The least the panel goes to, in the pixels this element holds it in.\n\nThe same number as `panelSliver` and not the same arithmetic, which is the\npoint of having both: bounding the splitter at `100 - panelSliver` instead\nwould ask a percentage to come back through a string and compare equal to\nthe one the splitter derives, and the collapse it is meant to land on is an\n`>=` on those two floats. Held here, in whole pixels a consumer stated, it\nis exact.",
93738
+ "readonly": true
93739
+ },
93595
93740
  {
93596
93741
  "kind": "method",
93597
93742
  "name": "fromSource",
@@ -93739,6 +93884,13 @@
93739
93884
  "privacy": "private",
93740
93885
  "readonly": true
93741
93886
  },
93887
+ {
93888
+ "kind": "field",
93889
+ "name": "handleCollapse",
93890
+ "privacy": "private",
93891
+ "readonly": true,
93892
+ "description": "Refuses a pane of either splitter declaring itself shut.\n\n**The collapse is a second state, and this element has nowhere to put\none.** A splitter's `collapsed` overrides its `position` and remembers what\nit was open at, and it answers to neither `split` nor `panel-size`. Allowed\nin here, it would leave the arrangement in a state nothing outside this\nelement can see or set: assigning `panel-size` back moves nothing while\n`collapsed` stands, and a consumer who stored the two numbers this element\npublishes and restored a sandbox from them would get it open. That is one\nelement holding the arrangement and the other showing it, which is the state\nthe two requests above were rewritten to end.\n\n**So \"shut\" is spelled in pixels here.** `panel-collapsed-size` is the floor\n`handlePanelChange` holds the panel to, `panel-size` reads it when the\ndivider is at the end, and `helix-before-panel-resize` is where a consumer\nhears about it, in the units they already store. That is the same trade the\nother two make: the sandbox is the single decision point because it refuses\nwhat the splitter asks and re-announces the move in its own vocabulary.\n\n**Stopped as well as refused**, because `start` and `end` name the panes of\na splitter a consumer of this element is not supposed to know about, and the\ntwo events above already say which divider moved. A collapse from something\nslotted into a pane is somebody else's and passes through untouched, which\nis what `target` against `currentTarget` is asking.\n\nReached at all only since Splitter stopped returning on a refused\n`helix-before-resize`. While it did, every collapse in here was unreachable\nand `panel-collapsed-size` was inert with it."
93893
+ },
93742
93894
  {
93743
93895
  "kind": "method",
93744
93896
  "name": "answer",
@@ -94120,7 +94272,7 @@
94120
94272
  "type": {
94121
94273
  "text": "HelixBeforePanelResizeEvent"
94122
94274
  },
94123
- "description": "Fired before the panel's divider is let go, with its height in pixels. Cancelable: refusing it takes the decision and nothing moves.",
94275
+ "description": "Fired before the panel's divider is let go, with its height in pixels, and a panel driven shut arrives as `panel-collapsed-size`, which is the least it goes to. Cancelable: refusing it takes the decision and nothing moves.",
94124
94276
  "name": "helix-before-panel-resize",
94125
94277
  "reactProp": "onhelix-before-panel-resize"
94126
94278
  },
@@ -94202,7 +94354,7 @@
94202
94354
  "text": "number"
94203
94355
  },
94204
94356
  "default": "0",
94205
- "description": "What the panel's height becomes when it is closed.\n\nZero closes it outright. A panel whose contents include the control that\nreopens it, a row of tabs being the usual one, wants that row left showing,\nand only whoever slotted it knows how tall that is.",
94357
+ "description": "What the panel's height becomes when it is closed.\n\nZero closes it outright. A panel whose contents include the control that\nreopens it, a row of tabs being the usual one, wants that row left showing,\nand only whoever slotted it knows how tall that is.\n\n**A floor rather than a second state**, which is what keeps `panel-size` the\nwhole truth about the panel: a divider driven to the end lands here and\n`panel-size` reads this number, so \"closed\" is something a consumer already\nstores rather than a property they have to learn. The splitter inside has a\n`collapsed` of its own and it is refused, for the reason `handleCollapse`\ngives.",
94206
94358
  "fieldName": "panelCollapsedSize"
94207
94359
  },
94208
94360
  {
@@ -94244,7 +94396,7 @@
94244
94396
  "helix": {
94245
94397
  "category": "tooling",
94246
94398
  "since": "0.1.0",
94247
- "status": "experimental",
94399
+ "status": "stable",
94248
94400
  "documentation": "https://fusion.dev/helix/components/sandbox",
94249
94401
  "aliases": [
94250
94402
  "Playground",
@@ -94277,7 +94429,7 @@
94277
94429
  "slug": "sized-panes"
94278
94430
  },
94279
94431
  {
94280
- "description": "A third pane down the side, sized and collapsible, with every separator named.",
94432
+ "description": "A third pane under both, sized and collapsible, with every separator named.",
94281
94433
  "slug": "with-a-panel"
94282
94434
  }
94283
94435
  ],
@@ -94846,7 +94998,7 @@
94846
94998
  "summary": "Editable source for a sandbox preview.",
94847
94999
  "helix": {
94848
95000
  "since": "0.1.0",
94849
- "status": "experimental",
95001
+ "status": "stable",
94850
95002
  "documentation": "https://fusion.dev/helix/components/sandbox",
94851
95003
  "aliases": [
94852
95004
  "Editor",
@@ -95341,7 +95493,7 @@
95341
95493
  "summary": "Reports where the cursor is and what is being edited.",
95342
95494
  "helix": {
95343
95495
  "since": "0.1.0",
95344
- "status": "experimental",
95496
+ "status": "stable",
95345
95497
  "documentation": "https://fusion.dev/helix/components/sandbox",
95346
95498
  "aliases": [
95347
95499
  "Status line"
@@ -95880,7 +96032,7 @@
95880
96032
  "summary": "Shows what the previewed example wrote to the console.",
95881
96033
  "helix": {
95882
96034
  "since": "0.1.0",
95883
- "status": "experimental",
96035
+ "status": "stable",
95884
96036
  "documentation": "https://fusion.dev/helix/components/sandbox",
95885
96037
  "aliases": [
95886
96038
  "Console",
@@ -96306,7 +96458,7 @@
96306
96458
  "summary": "The bar across the top of a sandbox.",
96307
96459
  "helix": {
96308
96460
  "since": "0.1.0",
96309
- "status": "experimental",
96461
+ "status": "stable",
96310
96462
  "documentation": "https://fusion.dev/helix/components/sandbox",
96311
96463
  "aliases": [
96312
96464
  "Action bar"
@@ -105746,7 +105898,7 @@
105746
105898
  },
105747
105899
  "privacy": "private",
105748
105900
  "default": "undefined",
105749
- "description": "Where the divider was before the gesture that may be about to shut it.\n\nA drag writes a position on every pointer move, so by the time it reaches\nthe edge the number it started from is gone. Taken once, on the press, and\nwritten back if the gesture ends shut, so the pane opens to where it was\nrather than to the edge it was dragged against.",
105901
+ "description": "Where the divider was before the gesture that may be about to shut it.\n\nA drag writes a position on every pointer move, so by the time it reaches\nthe edge the number it started from is gone. Taken once, on the press, and\nwritten back if the gesture ends shut, so the pane opens to where it was\nrather than to the edge it was dragged against.\n\n**A pointer gesture only, because it is the only one that loses it**, and\n`settle` answers for the rest from `began`: a key and a double click apply\nnothing before they settle, so where the divider is at the top of that\nmethod is where it was. Read once and cleared there, exactly as `began` is,\nand for the second reason as well as the first. Left standing it measured\nthe next gesture against the last one's press, so a collapse pressed from\n20 reopened at the 60 a drag had started from an hour earlier.",
105750
105902
  "inheritedFrom": {
105751
105903
  "name": "HelixHeadlessSplitter",
105752
105904
  "module": "src/splitter/root/splitter-root.ts"
@@ -106076,7 +106228,7 @@
106076
106228
  }
106077
106229
  }
106078
106230
  ],
106079
- "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.",
106231
+ "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 it asks for. So the collapse is reached either way and is refused, or\nnot, 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**The open position is taken here for every door but the pointer's, which\nis the same split `began` makes and for the same reason.** A drag has\noverwritten the position on the way to the edge, so it reads it on the\npress; a key, a double click and `moveTo` apply nothing until the request\nbelow, so where the divider is at the top of this method is where it was.\nCaptured on the press alone, the class docstring's promise that a shut pane\nknows where to open back to held for a drag and for nothing else: measured\n17 Sep 2026, `Home` on a splitter resting at 60 shut the start pane and\nreopened it at 0, the resize above having written the edge and the collapse\nhaving nothing to put back. Stale, it was worse than absent, `opened` never\nbeing cleared: a press at 60, then a key collapse from 20, reopened at 60.\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.",
106080
106232
  "inheritedFrom": {
106081
106233
  "name": "HelixHeadlessSplitter",
106082
106234
  "module": "src/splitter/root/splitter-root.ts"
@@ -106800,7 +106952,7 @@
106800
106952
  "helix": {
106801
106953
  "category": "layout",
106802
106954
  "since": "0.1.0",
106803
- "status": "experimental",
106955
+ "status": "stable",
106804
106956
  "documentation": "https://fusion.dev/helix/components/splitter",
106805
106957
  "aliases": [
106806
106958
  "SplitPane",
@@ -106879,7 +107031,7 @@
106879
107031
  "type": {
106880
107032
  "text": "HelixBeforeCollapseEvent"
106881
107033
  },
106882
- "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.",
107034
+ "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.",
106883
107035
  "name": "helix-before-collapse",
106884
107036
  "reactProp": "onhelix-before-collapse",
106885
107037
  "inheritedFrom": {
@@ -106891,7 +107043,7 @@
106891
107043
  "type": {
106892
107044
  "text": "HelixBeforeResizeEvent"
106893
107045
  },
106894
- "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.",
107046
+ "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.",
106895
107047
  "name": "helix-before-resize",
106896
107048
  "reactProp": "onhelix-before-resize",
106897
107049
  "inheritedFrom": {
@@ -194,11 +194,28 @@ scrollbar-width: none;
194
194
  display: none;
195
195
  }
196
196
 
197
+ :host([per-view="auto"]:not([orientation="vertical"])) [part="track"] {
198
+ --carousel-per-view: max(
199
+ 1,
200
+ round(
201
+ down,
202
+ calc(
203
+ (100cqi + var(--_carousel-gap)) /
204
+ (var(--carousel-slide-min, 100cqi) + var(--_carousel-gap))
205
+ ),
206
+ 1
207
+ )
208
+ );
209
+ container-type: inline-size;
210
+ }
211
+
197
212
  :host([orientation="vertical"]) [part="track"] {
198
213
  block-size: var(--carousel-block-size, 20rem);
199
214
  flex-direction: column;
200
215
  overflow-x: hidden;
201
216
  overflow-y: auto;
217
+ padding-block: var(--_carousel-bleed);
218
+ scroll-padding-block: var(--_carousel-bleed);
202
219
  scroll-snap-type: y mandatory;
203
220
  }
204
221
 
@@ -209,6 +226,10 @@ margin-inline-end: var(--carousel-run-end, 0px);
209
226
  scroll-snap-align: start;
210
227
  }
211
228
 
229
+ :host([per-view="auto"]:not([orientation="vertical"])) [part="slide"] {
230
+ flex-grow: 1;
231
+ }
232
+
212
233
  [part="slide"] ::slotted(*) {
213
234
  flex: 1 1 0;
214
235
  min-block-size: 0;
@@ -16,7 +16,7 @@ import type { CSSResultGroup } from 'lit';
16
16
  * draws one across.
17
17
  *
18
18
  * @since 0.1.0
19
- * @status experimental
19
+ * @status stable
20
20
  * @category layout
21
21
  * @aliases Slider, Gallery, Slideshow
22
22
  * @documentation https://fusion.dev/helix/components/carousel
@@ -59,7 +59,7 @@ export type PreviewMessage = PreviewConsole | PreviewPress | PreviewReady | Prev
59
59
  * reach up and remove the attribute.
60
60
  *
61
61
  * @since 0.1.0
62
- * @status experimental
62
+ * @status stable
63
63
  * @aliases Result, Output
64
64
  * @documentation https://fusion.dev/helix/components/sandbox
65
65
  *
@@ -21,7 +21,7 @@ import type { CSSResultGroup, PropertyValues, TemplateResult } from 'lit';
21
21
  * outer one, which is the order that makes the panel span the full width.
22
22
  *
23
23
  * @since 0.1.0
24
- * @status experimental
24
+ * @status stable
25
25
  * @category tooling
26
26
  * @aliases Playground, REPL, Editor
27
27
  * @documentation https://fusion.dev/helix/components/sandbox
@@ -33,7 +33,7 @@ import type { CSSResultGroup, PropertyValues, TemplateResult } from 'lit';
33
33
  * @slot toolbar - A bar across the top, spanning both panes.
34
34
  *
35
35
  * @event {HelixBeforeEditEvent} helix-before-edit - Fired before the source is edited, by the source pane and heard here. Cancelable: refusing it takes the decision and nothing moves.
36
- * @event {HelixBeforePanelResizeEvent} helix-before-panel-resize - Fired before the panel's divider is let go, with its height in pixels. Cancelable: refusing it takes the decision and nothing moves.
36
+ * @event {HelixBeforePanelResizeEvent} helix-before-panel-resize - Fired before the panel's divider is let go, with its height in pixels, and a panel driven shut arrives as `panel-collapsed-size`, which is the least it goes to. Cancelable: refusing it takes the decision and nothing moves.
37
37
  * @event {HelixBeforeSplitEvent} helix-before-split - Fired before the panes' divider is let go, with the lead pane's share. Cancelable: refusing it takes the decision and nothing moves.
38
38
  * @event {HelixCaretEvent} helix-caret - Fired when the cursor moves in the source, by the source pane and heard here.
39
39
  * @event {HelixConsoleEvent} helix-console - Relayed from the frame by the preview pane, one per console call.
@@ -108,6 +108,13 @@ export declare class HelixSandbox extends HelixElement {
108
108
  * Zero closes it outright. A panel whose contents include the control that
109
109
  * reopens it, a row of tabs being the usual one, wants that row left showing,
110
110
  * and only whoever slotted it knows how tall that is.
111
+ *
112
+ * **A floor rather than a second state**, which is what keeps `panel-size` the
113
+ * whole truth about the panel: a divider driven to the end lands here and
114
+ * `panel-size` reads this number, so "closed" is something a consumer already
115
+ * stores rather than a property they have to learn. The splitter inside has a
116
+ * `collapsed` of its own and it is refused, for the reason `handleCollapse`
117
+ * gives.
111
118
  */
112
119
  panelCollapsedSize: number;
113
120
  /**
@@ -176,6 +183,17 @@ export declare class HelixSandbox extends HelixElement {
176
183
  private get homePanelPosition();
177
184
  /** `panel-collapsed-size` in the units the splitter works in. */
178
185
  private get panelSliver();
186
+ /**
187
+ * The least the panel goes to, in the pixels this element holds it in.
188
+ *
189
+ * The same number as `panelSliver` and not the same arithmetic, which is the
190
+ * point of having both: bounding the splitter at `100 - panelSliver` instead
191
+ * would ask a percentage to come back through a string and compare equal to
192
+ * the one the splitter derives, and the collapse it is meant to land on is an
193
+ * `>=` on those two floats. Held here, in whole pixels a consumer stated, it
194
+ * is exact.
195
+ */
196
+ private get panelFloor();
179
197
  /**
180
198
  * Whether a change came from inside this sandbox's source region.
181
199
  *
@@ -296,6 +314,37 @@ export declare class HelixSandbox extends HelixElement {
296
314
  */
297
315
  private readonly handlePanesChange;
298
316
  private readonly handlePanelChange;
317
+ /**
318
+ * Refuses a pane of either splitter declaring itself shut.
319
+ *
320
+ * **The collapse is a second state, and this element has nowhere to put
321
+ * one.** A splitter's `collapsed` overrides its `position` and remembers what
322
+ * it was open at, and it answers to neither `split` nor `panel-size`. Allowed
323
+ * in here, it would leave the arrangement in a state nothing outside this
324
+ * element can see or set: assigning `panel-size` back moves nothing while
325
+ * `collapsed` stands, and a consumer who stored the two numbers this element
326
+ * publishes and restored a sandbox from them would get it open. That is one
327
+ * element holding the arrangement and the other showing it, which is the state
328
+ * the two requests above were rewritten to end.
329
+ *
330
+ * **So "shut" is spelled in pixels here.** `panel-collapsed-size` is the floor
331
+ * `handlePanelChange` holds the panel to, `panel-size` reads it when the
332
+ * divider is at the end, and `helix-before-panel-resize` is where a consumer
333
+ * hears about it, in the units they already store. That is the same trade the
334
+ * other two make: the sandbox is the single decision point because it refuses
335
+ * what the splitter asks and re-announces the move in its own vocabulary.
336
+ *
337
+ * **Stopped as well as refused**, because `start` and `end` name the panes of
338
+ * a splitter a consumer of this element is not supposed to know about, and the
339
+ * two events above already say which divider moved. A collapse from something
340
+ * slotted into a pane is somebody else's and passes through untouched, which
341
+ * is what `target` against `currentTarget` is asking.
342
+ *
343
+ * Reached at all only since Splitter stopped returning on a refused
344
+ * `helix-before-resize`. While it did, every collapse in here was unreachable
345
+ * and `panel-collapsed-size` was inert with it.
346
+ */
347
+ private readonly handleCollapse;
299
348
  /**
300
349
  * Puts a divider where this element says it is.
301
350
  *