Skip to content

feat(Splitter): new component - #441

Merged
IgorShevchik merged 1 commit into
mainfrom
sync/nuxt-ae2bd5e
Aug 19, 2026
Merged

feat(Splitter): new component#441
IgorShevchik merged 1 commit into
mainfrom
sync/nuxt-ae2bd5e

Conversation

@IgorShevchik

Copy link
Copy Markdown
Collaborator

Port of nuxt/ui@ae2bd5eb (nuxt/ui#6670). Third of six in the queue, after #439.

Resizable panels separated by draggable handles, wrapping reka-ui's SplitterGroup / SplitterPanel / SplitterResizeHandle.

Adopted by decision, not by default

A whole new component is not a mechanical port, so it went to the maintainer with the alternatives — adopt / skip / defer to an issue — rather than being decided in the sync. Answer: adopt and adapt.

It duplicates nothing here. We already have DashboardPanel and DashboardResizeHandle, but those build on reka-ui's Primitive, not on the splitter primitives — checked before proposing, because "we already have something like that" would have been the cheap wrong answer.

What is upstream's, and what is not

The component is upstream's, with §1's mechanical rewrites: uib24ui throughout (prop, slot props, item key, appConfig.b24ui?.splitter) and #build/b24ui/splitter. The reka-ui imports keep their own names, so the template is unchanged.

The theme is adapted, not copied. Upstream's handle focus ring is focus-visible:outline-2 focus-visible:outline-primary. Ours follows the convention already in button.ts and checkbox.ts:

outline-transparent focus-visible:outline-2 focus-visible:outline-offset-2
focus-visible:outline-(--ui-color-accent-soft-element-blue)

— the same token table.ts uses for focusable rows.

Docs, the four examples and the playground page were translated. bg-elevated/50bg-(--ui-color-bg-content-secondary), border-defaultborder-(--ui-color-divider-default), text-mutedtext-description. color="neutral" variant="subtle"color="air-secondary", since this fork's Button has no variant. And the collapsible example's i-lucide-panel-left-{open,close} become the b24icons the dictionary maps panelOpen/panelClose to — outline/OpenChatIcon and outline/CloseChatIcon, not main/…, which is where a first guess put them.

Registered in the three places a component needs: src/theme/index.ts, src/runtime/types/index.ts, and the outer block of src/runtime/types/theme.ts — that file has two, and the prose one closes at line 159, so the placement is worth stating rather than assuming.

Not ported

docs/public/components/{light,dark}/splitter.png. They feed a preview grid this fork does not have: docs/public/components/ does not exist here and nothing references it.

Tests

Upstream's spec, with uib24ui and its bg-primary probe class replaced by a token. 24 snapshots generated against our theme, plus the accessibility pass.

Checked in the output rather than the source that the theme adaptation actually landed:

data-slot="handle" class="group relative shrink-0 outline-transparent
focus-visible:outline-2 focus-visible:outline-offset-2
focus-visible:outline-(--ui-color-accent-soft-element-blue) … w-2 cursor-col-resize"

Verify (CI=true)

dev:prepare (all four apps) · lint · typecheck · test · build · docs:generate — all green. Tests 6734 passed | 6 skipped across 296 files.

docs:generate prerenders 1248 routes, up from 1240 — the component page and its four examples build and render, which is the check that the docs half actually works rather than merely existing.

Ledger

6f82268b and 3d2129d2 reconciled with #439 and squash 167cd6f3. cursorae2bd5eb, entry and log added, .sync/dep-parity.json refreshed to the new cursor.

Remaining: 2beb2345 + a630c943 (showcase, no-ops) and 14ac2438 (ProgressGroup, also adopted, plus the --percent fix for the existing Progress).


Generated by Claude Code

Port of nuxt/ui@ae2bd5eb (#6670). Resizable panels separated by draggable
handles, wrapping reka-ui's `SplitterGroup` / `SplitterPanel` /
`SplitterResizeHandle`.

Adopting a whole component is not a mechanical port, so it was put to the
maintainer with the alternatives rather than decided here. It duplicates
nothing: `DashboardPanel` and `DashboardResizeHandle` build on reka-ui's
`Primitive`, not on the splitter primitives — checked before proposing,
because "we already have something like that" would have been the cheap
wrong answer.

The component itself is upstream's, with §1's mechanical rewrites: `ui` ->
`b24ui` throughout, `#build/b24ui/splitter`, `appConfig.b24ui?.splitter`.
The reka-ui imports keep their own names, so the template is unchanged.

The theme is adapted rather than copied. Upstream's handle focus ring is
`focus-visible:outline-primary`; ours follows `button.ts` and `checkbox.ts`
— `outline-transparent focus-visible:outline-2 focus-visible:outline-offset-2`
with `--ui-color-accent-soft-element-blue`, the token `table.ts` already
uses for focusable rows.

Docs, the four examples and the playground page were translated, not
copied. `bg-elevated/50`, `border-default` and `text-muted` become the
matching b24ui tokens; `color="neutral" variant="subtle"` becomes
`color="air-secondary"`, since this Button has no `variant`; and the
collapsible example's `i-lucide-panel-left-{open,close}` become the
b24icons the dictionary maps `panelOpen`/`panelClose` to —
`outline/OpenChatIcon` and `outline/CloseChatIcon`, not `main/…`, which is
where a first guess put them.

Registered in the three places a component needs: `src/theme/index.ts`,
`src/runtime/types/index.ts`, and the outer block of
`src/runtime/types/theme.ts` — that file has two, and the `prose` one
closes at line 159.

Not ported: `docs/public/components/{light,dark}/splitter.png`. They feed a
preview grid this fork does not have — `docs/public/components/` does not
exist here and nothing references it.

Tests are upstream's spec with `ui` -> `b24ui` and its `bg-primary` probe
replaced by a token; 24 snapshots generated against our theme, plus the axe
pass. Checked in the output rather than the source that the adaptation
landed: the rendered handle carries our focus ring.

Verify (CI=true): dev:prepare across all four apps · lint · typecheck ·
test (6734 passed, 6 skipped, 296 files) · build · docs:generate — all
green. docs:generate prerenders 1248 routes against 1240 before, so the
component page and its four examples build and render.
@IgorShevchik
IgorShevchik merged commit 62a2acc into main Aug 19, 2026
1 check passed
@IgorShevchik
IgorShevchik deleted the sync/nuxt-ae2bd5e branch August 19, 2026 14:37
IgorShevchik added a commit that referenced this pull request Aug 20, 2026
…stry (#447)

Splitter (#441) and ProgressGroup (#443) each shipped with `src/`, tests, a docs
page and the nuxt playground, and each was missing four further registries. None
of them can fail a build, which is why both passed the full gate twice.

- `docs/nuxt.config.ts` → `pages`: neither route was listed. The page still
  prerenders, because the crawler follows the sidebar link, so nothing looked
  wrong — but that array is the declared list the `/raw/<page>.md` routes are
  generated from, and `skills/b24-ui-nuxt/references/components.md` links to
  exactly those URLs. `empty` and `page-card-group` had been missing longer;
  both are registered here too.
- `playgrounds/demo`: no page for either component, so the Demo link the docs
  page is supposed to carry had nothing to point at. Both pages are written the
  way the other 79 are — `<PlaygroundPage>` with `#controls`, the theme's
  variants driven through `<Matrix>` — rather than copied from the nuxt
  playground, which is a scratch page with a different job.
- `useNavigation.ts` in the demo playground: both names added in place.
- The Demo link itself, on both docs pages.

Also on those pages: `description:` was Nuxt UI's sentence verbatim on Splitter
and near-verbatim on ProgressGroup, both rewritten; `keywords:` added; and
Splitter's Reka link used `iconName: RekaIcon`, a name that has never existed in
`src/runtime/dictionary/icons.ts`. `resolveIcon()` returns `undefined` for it and
the template renders the link with no icon, silently — the convention on the
other 34 pages is an avatar, `/b24ui/avatar/rekaui.svg`, and that is what it uses
now.

`test/utils/docs-component-registries.spec.ts` holds the part of this that is
mechanically checkable: page ↔ `pages` entry in both directions, every
`iconName:` resolvable in the dictionary, every Demo link and every demo nav
entry resolving to a file that exists. It cannot demand a demo page per
component (33 of 120 pages carry no Demo link by choice) or tell a rewritten
description from a copied one; those stay in the checklist that `.sync/PORTING.md`
§6 now spells out for the next component.

Verified: four mutations against the new spec — dropping the `splitter` route,
adding a route with no page behind it, restoring `iconName: RekaIcon`, renaming
the demo page away — each red, and the tree green. Both demo pages loaded in a
browser off `demo:generate`'s output: the splitter renders two panel groups, the
progress group six segments, no console errors.

Co-authored-by: Shevchik Igor <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants