Panel
A titled content region: a header with a configurable heading and actions, a body, an optional footer, and an optional collapsible or scrollable form.
Live demo
Panel frames a labelled section of content. Its header carries a title (rendered at a configurable heading level for a correct document outline), an actions slot pushed to the trailing edge by a spacer, and the body it labels via aria-labelledby.
The default variant is a static <section>; the collapsible variant is a native <details>/<summary> disclosure that needs no JavaScript in markup. The custom element drives it with aria-expanded. A footer slot adds a quiet trailing row, and the scroll flag turns the body into a focusable, keyboard-scrollable region capped at a fixed height.
When to use
How this component composes with the rest of the set.
Props
5 props, straight from the manifest.
| Prop | Type | Default | Bindings | Description |
|---|---|---|---|---|
Appearance
Variants
default
A static titled section.
collapsible
A native disclosure; the header toggles the body open and closed.
States
open
The collapsible panel is expanded; the marker rotates to point down.
collapsed
The collapsible panel is closed; the body is hidden, the marker points right.
hover
Pointer over a collapsible header; the overlay paints the hover tint.
active
Collapsible header pressed; the overlay paints the press tint.
focus-visible
Keyboard focus on the toggle or the scroll body: an inset token-colored ring.
Anatomy
The named parts that make up the component, with their selectors.
panel
The root section (or details) carrying the variant and modifier classes.
header
The title row; for the collapsible variant it's the clickable summary/toggle.
title
The heading, rendered at the configured level and referenced by the section's aria-labelledby.
spacer
A flexible gap that pushes the actions slot to the trailing edge of the header.
body
The content region the panel labels; becomes a scroll container under the scroll flag.
footer
A quiet trailing row for metadata or secondary actions.
Tokens & coverage
What the component consumes, checked live against what the algorithm produces.
Live coverage check against the xoji-default register
(derive(xojiDefault, { anchors }) →
coverComponent(manifest, register)). Every token this component
consumes must be a key the algorithm produces.
--bg-0
--bg-1
--border-normal
--border-thick
--border-thin
--duration-fast
--ease-emphasized
--ease-standard
--fg-1
--fg-2
--font-display
--line
--line-2
--radius-lg
--ring
--space-2
--space-3
--space-4
--space-8
--state-hover
--state-press
--text-sm
--weight-semibold
Slots
The panel body content.
Controls aligned to the trailing edge of the header.
A quiet trailing row below the body.
Accessibility
Code
Header, collapsible, and scroll
A titled panel with actions and a footer, a collapsible disclosure, and a scrollable body.
<xoji-panel title="Connections" level="2">
<button slot="actions" class="xoji-button xoji-button--ghost xoji-button--neutral xoji-button--sm">Refresh</button>
<p>Two services are connected.</p>
<p slot="footer">Last synced a moment ago.</p>
</xoji-panel>
<xoji-panel title="Advanced options" variant="collapsible">
<label><input type="checkbox" /> Verbose logging</label>
<label><input type="checkbox" /> Telemetry</label>
</xoji-panel>
<xoji-panel title="Activity log" scroll>
<p>A long stream of entries that scrolls inside the panel body…</p>
</xoji-panel>
<script lang="ts">
import { Panel } from "@xoji/svelte";
</script>
<Panel title="Connections" level={2}>
{#snippet actions()}
<button class="xoji-button xoji-button--ghost xoji-button--neutral xoji-button--sm">Refresh</button>
{/snippet}
<p>Two services are connected.</p>
{#snippet footer()}
<span>Last synced a moment ago.</span>
{/snippet}
</Panel>
<Panel title="Advanced options" variant="collapsible">
<label><input type="checkbox" /> Verbose logging</label>
<label><input type="checkbox" /> Telemetry</label>
</Panel>
<Panel title="Activity log" scroll>
<p>A long stream of entries that scrolls inside the panel body…</p>
</Panel>
---
import { Panel } from "@xoji/astro";
---
<Panel title="Connections" level={2}>
<button slot="actions" class="xoji-button xoji-button--ghost xoji-button--neutral xoji-button--sm">Refresh</button>
<p>Two services are connected.</p>
<p slot="footer">Last synced a moment ago.</p>
</Panel>
<Panel title="Advanced options" variant="collapsible">
<label><input type="checkbox" /> Verbose logging</label>
<label><input type="checkbox" /> Telemetry</label>
</Panel>
<Panel title="Activity log" scroll>
<p>A long stream of entries that scrolls inside the panel body…</p>
</Panel>