Skip to main content

Panel

Info:Layout html svelte astro Success: coverage 23/23

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

live · @xoji/astro

Panel

Project overview

A panel is a titled region with a header, body, and optional footer. The heading level is configurable so it slots cleanly into the document outline, and the actions slot parks controls on the right of the header.

Info:section level 3

Same disclosure, started expanded with open. Actions live in the header alongside the marker.

Build log

Success:live

12:04:01 build resolving 50 component manifests

12:04:01 build coverage check — 50/50 green

12:04:02 emit wrote tokens.css (276 declarations)

12:04:02 emit wrote tokens.json

12:04:03 gauntlet 200 runs · 0 invariant breaks

12:04:03 gauntlet contrast floor held at 4.51:1

12:04:04 site 37 pages rendered

12:04:04 done build complete in 3.1s

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.

Use as the building block of a Dock column; .xoji-dock .xoji-panel drops the border and radius so panels stack flush.
Put a Button cluster in the actions slot for per-panel controls; the spacer keeps them right-aligned.
Set level so nested panels keep a valid heading hierarchy: a top-level panel at 2, sub-panels at 3, and so on.

Props

5 props, straight from the manifest.

PropTypeDefaultBindingsDescription
title string
html svelte astro
The panel heading text. When set (or an actions slot is filled) the header renders.
level 1 | 2 | 3 | 4 | 5 | 6
1 2 3 4 5 6
2
html svelte astro
The heading level for the title, so the panel slots into the document outline correctly.
variant PanelVariant
default collapsible
default
html svelte astro
`default` is a static section; `collapsible` is a disclosure with a clickable header.
open boolean false
html svelte astro
For the collapsible variant: whether the body starts expanded.
scroll boolean false
html svelte astro
Caps the body height and makes it a focusable, keyboard-scrollable region.

Appearance

Variants

default

.xoji-panel

A static titled section.

collapsible

.xoji-panel--collapsible

A native disclosure; the header toggles the body open and closed.

States

open

.xoji-panel--collapsible[open] > summary .xoji-panel__marker

The collapsible panel is expanded; the marker rotates to point down.

collapsed

.xoji-panel__marker

The collapsible panel is closed; the body is hidden, the marker points right.

hover

.xoji-panel--collapsible > summary:hover::after

Pointer over a collapsible header; the overlay paints the hover tint.

active

.xoji-panel--collapsible > summary:active::after

Collapsible header pressed; the overlay paints the press tint.

focus-visible

.xoji-panel__toggle: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

.xoji-panel

The root section (or details) carrying the variant and modifier classes.

--bg-0 --line --border-thin --radius-lg

header

.xoji-panel__header

The title row; for the collapsible variant it's the clickable summary/toggle.

--bg-1 --line --border-thin --space-3 --space-4 --space-2

title

.xoji-panel__title

The heading, rendered at the configured level and referenced by the section's aria-labelledby.

--font-display --weight-semibold --text-sm --fg-1

spacer

.xoji-panel__spacer

A flexible gap that pushes the actions slot to the trailing edge of the header.

body

.xoji-panel__body

The content region the panel labels; becomes a scroll container under the scroll flag.

--space-4 --space-3

footer

.xoji-panel__footer

A quiet trailing row for metadata or secondary actions.

--bg-1 --line --border-thin --fg-2 --text-sm --space-3 --space-4 --space-2

Tokens & coverage

What the component consumes, checked live against what the algorithm produces.

Success:fully covered 23/23 consumed tokens produced default register: 276 tokens

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

default
html svelte astro

The panel body content.

actions
html svelte astro

Controls aligned to the trailing edge of the header.

footer
html svelte astro

A quiet trailing row below the body.

Accessibility

The default variant is a native <section> labelled by its title via aria-labelledby, so it is announced as a named region.
The title renders at a configurable heading level so the panel participates correctly in the document outline; the binding warns when neither a title nor an actions slot supplies a heading.
The collapsible variant uses native <details>/<summary> (or a <button> with aria-expanded in the custom element), giving keyboard toggling and screen-reader disclosure semantics for free.
The disclosure marker is decorative (aria-hidden); expanded state lives in [open] / aria-expanded, not the rotation.
Under scroll the body is a focusable region (tabindex="0", role="region") so keyboard users can scroll it, with the standard inset focus ring.

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>