<!-- GENERATED by scripts/build-llms.mjs from llms/layout.md — do not edit this file. -->

# `lr-split-panel`

- **Import** `import '@aceshooting/lyra-ui/components/lr-split-panel.js';` (stable tag alias; registers the tag)
- **Class** `LyraSplitPanel`, also available unregistered from `@aceshooting/lyra-ui/components/layout/split-panel/split-panel.class.js`
- **Family** `components/layout/` — see `llms/index.md` for its siblings
- **Status** `stable` since `8.0.0` — see the maturity and deprecation policy in `llms/shared.md`
- **Release history** [CHANGELOG.md](../../CHANGELOG.md); family-wide breaking-change summaries: [llms-full.txt](../../llms-full.txt)
- **Deprecations** none
- **Optional peers** none
- **Themeable via** 6 parts, 10 custom properties — see this component's own `@csspart`/`@cssprop` list below
- **Library-wide behavior** (events, form association, `locale`/`strings`, tokens, TS types): `llms/shared.md`

---

## `lr-split-panel`

Accessible two-pane resizing with the public `wa-split-panel` / `sl-split-panel` contract. Use this
component when migrated markup has named `start` and `end` panes. The separate `<lr-multi-split>` is
Lyra's multi-panel layout: its direct default-slot children, responsive collapse modes, and
multi-divider events are intentionally a different API.

Both `lr-reposition-request` detail fields `position` and `positionInPixels` measure from the
selected primary edge and agree with accepted public property readback, including `primary="end"`.
Canceling preserves both prior values; direct property writes remain silent.

**Properties:**

- `position: number = 50` (reflected) — divider position from the selected `primary` pane's edge,
  as a percentage from 0–100. With no `primary`, the logical `start` pane is the reference.
- `positionInPixels: number` (attribute `position-in-pixels`) — the same position in pixels.
  Assigning either position updates the other after the component has a layout box; both remain
  synchronized after pointer/keyboard changes and host resizes.
- `orientation: 'horizontal'|'vertical' = 'horizontal'` (reflected) — side-by-side panes or stacked
  panes. `vertical: boolean = false` (reflected) is the synchronized Shoelace spelling: setting
  either API updates the other. If both attributes occur in initial markup, the canonical
  `orientation` attribute wins.
- `disabled: boolean = false` (reflected) — makes the divider pointer/keyboard-inert and removes it
  from the tab order. Host resizing can still update synchronized position values.
- `primary?: 'start'|'end'` — when unset, the panes resize proportionally and `position` stays fixed
  as the host changes size. When set, that pane keeps its pixel size and the other pane absorbs the
  resize. Position values are always measured from the selected primary edge.
- `snap: string | SnapFunction = ''` — pointer-drag snap behavior. A string accepts space-separated
  pixels, percentages, and repeat expressions (`'160px 50% repeat(100px)'`) and reflects to the
  `snap` attribute. It parses and caches the numeric value/unit projection from at most the first
  16,384 UTF-16 code units and 256 finite valid tokens; later source text cannot affect snapping. A property-bound
  `SnapFunction` receives `{ pos, size, snapThreshold }` in pixels and returns the desired pixel
  position; callback code decides how to use the supplied threshold. The setter also accepts
  `undefined` for mapped source compatibility, clearing the configuration to the canonical `''`
  read value. Function and empty values remove the serializable attribute.
- `snapThreshold: number = 12` (attribute `snap-threshold`) — maximum pixel distance at which a
  string snap point takes effect. Non-finite values fall back safely and negative values clamp to
  zero.

**Events:** `lr-reposition-request` (cancelable; `detail: SplitPanelRepositionDetail`, where
`{ position, positionInPixels }` is the final snapped and constrained proposed position measured
from the selected `primary` pane's edge. Call `preventDefault()` to leave both position properties
unchanged. It is not emitted when a consumer assigns `position` or `positionInPixels` directly),
`lr-reposition` (non-cancelable, no detail) — bubbling and composed, emitted after an accepted
pointer or keyboard interaction commits the divider position; direct property assignments stay
silent.

**Slots:** `start` (logical start pane), `end` (logical end pane), `divider` (optional decorative
custom-handle content inside the separator; its assigned subtree is inert, so the separator remains
the sole pointer/keyboard resize control). Under RTL, logical start/end and horizontal pointer/arrow
behavior mirror together; vertical behavior does not invert.

**CSS parts:** `base split-panel` (both tokens are on the same outer wrapper), `start panel` and
`end panel` (each pane exposes its individual token plus the shared `panel` token), `divider`
(focusable `role="separator"`, with value/min/max and disabled ARIA state).

**Themeable custom properties:** `--divider-width` (default `4px`), `--divider-hit-area` (requested
default `12px`, with Lyra's `--lr-icon-button-size` minimum target remaining the floor), `--min`
(default `0`) and `--max` (default `100%`) for the primary pane, or the start pane when no primary is
selected. Lyra-prefixed aliases are `--lr-split-panel-divider-width`,
`--lr-split-panel-divider-hit-area`, `--lr-split-panel-min`, and `--lr-split-panel-max`; when both
spellings are set, the Lyra-prefixed value wins. Constraint values may be lengths, percentages, or
`calc()` expressions and are re-applied when their computed sizes change.

`--lr-split-panel-divider-hover-color` (default `var(--lr-color-brand)`) is the divider's background
on hover/keyboard focus. `--lr-split-panel-divider-active-color` (default
`var(--lr-color-border-strong)`) is its background while being dragged, or focused and pressed via
the keyboard. Both are independent, component-scoped hooks rather than the bare shared token, so
retinting this divider does not also retint any other component that happens to default to the same
color.

Keyboard: focus the divider, then use Left/Right for a horizontal split or Up/Down for a vertical
split. Each arrow moves one percent of the current allocation; horizontal arrows mirror under RTL.
`Home` and `End` move to the current `--min` and `--max` bounds. Pointer dragging uses capture and
cleans up on pointer up, cancellation, capture loss, disconnect, and orientation changes.

**Optional peer deps:** none.

```js
import "@aceshooting/lyra-ui/components/layout/split-panel/split-panel.js";
```

```html
<lr-split-panel
  primary="start"
  position-in-pixels="240"
  snap="25% 50% 75%"
  aria-label="Resize editor panes"
  style="block-size: 20rem; --min: 10rem; --max: 30rem"
>
  <nav slot="start" aria-label="Files">…</nav>
  <main slot="end">…</main>
  <span slot="divider" aria-hidden="true">⋮</span>
</lr-split-panel>
```

**Known gotchas:** a vertical split needs a definite block size so percentages have an axis to
resolve against. `snap` callbacks are JavaScript functions and must be assigned as properties, not
serialized into an HTML attribute. The visible divider can remain narrow because its transparent
hit region expands independently; use `--divider-width` for the painted line and
`--divider-hit-area` for the requested interaction region.

---
