jixoai-ui
the terminal design language
loading the style sheet…
Skip to content
docs · print medium: screen

registry:ui · Layout

scaffold-float — the portal half

The consumer half of the float provider: portals children into the scaffold's top layer so they ride the immersive slide with the header.

portal half rides the header slide

scaffold-float

The float portal: render anything into the website scaffold's top layer from anywhere in the page. Nodes keep full Svelte ownership at their authoring position; a context provider adopts the live node into .jx-float-slot on mount, and teardown hands it back. Static chrome (toc rails, nav trees) belongs in the scaffold's chrome snippet instead — this is the DYNAMIC path.

portal concept · no LIVE instance

Authored in the page, adopted by the top layer

A float must dock to a website-scaffold provider — and the only one reachable from this page is the site’s own shell. So this card explains the adoption instead; every component page’s ToC rail rides the real float plane every day (it adopts itself).

authoring DOM (full Svelte ownership)     .jx-top-layer (scroll-free plane)
────────────────────────────────────     ────────────────────────────────────
<ScaffoldFloat>                           ├── .jx-scaffold-header
  └─ aside.docs-aside        adopt()      └── .jx-float-slot
       …children…           ──────►          ├─ any dynamic float
(hidden anchor stays in place)              └─ aside.docs-aside ← moved
                                           (rides the immersive slide)
  1. author — the portal renders its children wherever you place it in the page; the nodes are ordinary Svelte-owned DOM, not a serialized snapshot
  2. adopt — on mount it calls api.adopt(node) from the scaffold's jx-top-layer context; the scaffold's effect re-parents the live node into .jx-float-slot in adoption order (moved, never cloned)
  3. teardown — the hidden anchor keeps the authoring position; the release fn returns the node to it so Svelte finds and destroys its own nodes correctly
  4. ride — because the float now lives inside the top layer, the immersive hide/reveal carries it together with the header by construction: no second scroll listener exists anywhere
scaffold-float-usage.svelte svelte
<script lang="ts">
  import ScaffoldFloat from '@ui/scaffold-float.svelte';
  import Toc from '@ui/toc.svelte';
</script>

<!-- author it anywhere in the page; on mount the live node is adopted
     into the scaffold's top layer and rides the immersive slide -->
<ScaffoldFloat>
  <div class="status-strip">● deploy passing — main #142</div>
</ScaffoldFloat>

<!-- (the toc now lives in the scaffold's chrome snippet — this demo
     out for embedded demos) -->
<aside class="docs-aside" aria-label="On this page">
  <Toc {sections} title="on this page" scrollRoot=".jx-shell-body" />
</aside>

law

Move, never clone

The portal moves the live DOM node — appendChild re-parents it into the float slot — instead of serializing and re-rendering a copy. That single decision is what keeps state, event listeners, and Svelte ownership intact on both ends of the trip.

  • a moved node keeps its listeners, its element state (scroll position, focus), and its Svelte hydration anchors — a clone would lose all three
  • the hidden anchor at the authoring position is the return ticket: teardown hands the node back so Svelte destroys exactly what it created
  • the float plane is ordered multi-node — adopt() appends in adoption order, so multiple custom floats coexist inside .jx-float-slot (static chrome lives in its own slot — the two never mix)
usage
<script lang="ts">
  import ScaffoldFloat from '@ui/scaffold-float.svelte';
  import Toc from '@ui/toc.svelte';
</script>

<!-- author it anywhere in the page; on mount the live node is adopted
     into the scaffold's top layer and rides the immersive slide -->
<ScaffoldFloat>
  <div class="status-strip">● deploy passing — main #142</div>
</ScaffoldFloat>

<!-- (the toc now lives in the scaffold's chrome snippet — this demo
     out for embedded demos) -->
<aside class="docs-aside" aria-label="On this page">
  <Toc {sections} title="on this page" scrollRoot=".jx-shell-body" />
</aside>

types

Types

One portal shape; the area role decides which top-layer cell the shell grid resolves for the adopted node.

area="float"

Default — the dynamic float slot inside .jx-top-layer; your custom floats.

area="toc" / "tree"

The static chrome cells — the shell grid resolves each cell; static chrome usually lives in the scaffold's chrome snippet instead.

multi-node plane

adopt() appends in adoption order — multiple custom floats coexist in the slot.

usage

Usage

Author the float anywhere in the page; the provider adopts the live node on mount.

ScaffoldFloat usage
<script lang="ts">
  import ScaffoldFloat from '@ui/scaffold-float.svelte';
  import Toc from '@ui/toc.svelte';
</script>

<!-- author it anywhere in the page; on mount the live node is adopted
     into the scaffold's top layer and rides the immersive slide -->
<ScaffoldFloat>
  <div class="status-strip">● deploy passing — main #142</div>
</ScaffoldFloat>

<!-- (the toc now lives in the scaffold's chrome snippet — this demo
     out for embedded demos) -->
<aside class="docs-aside" aria-label="On this page">
  <Toc {sections} title="on this page" scrollRoot=".jx-shell-body" />
</aside>

a11y

Accessibility

The portal is a DOM move, not a visual layer change: reading order and focus follow the authored semantics.

Keyboard

KeyAction
TabFocus order is unaffected — the moved node keeps its listeners, focus, and scroll state

ARIA

AttributeValueDescription
aria-labelyoursLabel the floated content yourself (e.g. aside aria-label="On this page")
roleinheritedThe portal adds no roles; the adopted subtree keeps its authored semantics

theming

Theming

No density footprint and no tokens of its own — the portal moves your node; the paint is entirely the floated content's.

xs
● deploy passing — main #142
sm
● deploy passing — main #142
default
● deploy passing — main #142
lg
● deploy passing — main #142
TokenDefaultSource
jx-top-layercontext keystructural
.jx-float-slotadoption orderstructural

axes

Universal props

The eight-axis surface (explicit-props): size · shape · radius · density · color · theme · elevation · motion — each axis takes named steps, auto (inherit the ambient context; stamps nothing), an exact number (px · coefficient · dp · hue per axis), or query() for responsive/container-conditional values. THE PORTAL CARRIER: the content div is adopted into the scaffold's top layer (a REAL portal — the DOM moves), so CSS custom-property inheritance from the authoring position does not reach the adopted subtree. The axes resolve HERE, in the consumer's context, and the carriers stamp the portal wrapper — self-carried across the adoption. Svelte context needs no bridge; only the CSS channel did. All no-own: placement chrome forwards opinions, it manufactures none.

ScaffoldFloat · universal props

Composed inside a website-scaffold: everything floated through this portal reads the axis tree continuously — the wrapper re-stamps the resolved carriers on the adopted root.

scaffold-float-universal.svelte svelte
<ScaffoldFloat area="float" size={14} radius="large">…floats…</ScaffoldFloat>

api

API

Props from the ScaffoldFloat Props interface — a children snippet, the semantic role, and the nine-slot position.

Properties

PropertyTypeDefaultDescription
children * Snippet—The floated content; the live DOM node is adopted into the top layer on mount.
area TopLayerArea: 'toc' | 'tree' | 'float''float'Semantic placement role; the shell grid resolves the cell.
pos string — the nine slot names (left-top · center-top · right-top · left-center · center-center · right-center · left-bottom · center-bottom · right-bottom)'right-bottom' (unstated — the float rule itself carries it)The NINE-SLOT position inside the cell (Owner R3, 2026-09-02): physical names stamped as data-float-pos; website-scaffold.css resolves each to a place-self.

Universal props

PropertyTypeDefaultDescription
size'small' | 'medium' | 'large' | 'auto' | number'auto' · ambient scopethe base scale — root font-size; parts size in em
shape'round' | 'scoop' | 'bevel' | 'notch' | 'square' | 'squircle' | 'auto''auto' · ambient scopecorner geometry (CSS corner-shape; §14 degrade table)
radius'small' | 'medium' | 'large' | 'auto' | number'auto' · ambient scopecorner size; auto = the concentric broadcast (§3)
density'small' | 'medium' | 'large' | 'auto' | number'auto' · ambient scopespacing/leading scale over the kernel channels (§4)
color'primary' | 'secondary' | 'error' | 'warn' | 'success' | 'info' | 'auto' | number | string'auto' · ambient scopethe hue axis of the fixed oklch primary system (§5)
theme'light' | 'dark' | 'system' | 'auto''auto' · ambient scopelight/dark profile; system = the JS-mutable global (§6)
elevation'level-1' | 'level0' | 'level1' | 'level2' | 'level3' | 'level4' | 'level5' | 'auto' | number'auto' · ambient scopeofficial M3 levels over the surface ladder (§7)
motion'reduced' | 'subtle' | 'normal' | 'expressive' | 'auto' | number'auto' · ambient scopeintensity across the motion kernels (§8)
jixoai-ui ui.jixoai.com the jixoai design language

Theme

Brand hue

0°

Auto cycle · 24h