ContentToggle
Shows one keyed child at a time and slides between them, for pagers and step content.
Last updated
Uses JavaScript
A small island watches data-active on the root: the outgoing child slides
out toward direction, the new one slides in from the other side, and
apogee:content-toggle-change fires with { active }.
Without JS: The child matching the initial active key shows (marked server-side); changing data-active has no effect.
Chapter one: the island sleeps under a thin fog.
Chapter two: the tide uncovers a path to the lighthouse.
Chapter three: the keeper’s log ends mid-sentence.
Show codeHide code
<ContentToggle active="1">
<p data-key="1">Chapter one.</p>
<p data-key="2">Chapter two.</p>
<p data-key="3">Chapter three.</p>
</ContentToggle>
<Button onclick="…dataset.active = '2'">Next</Button>Add
npx @ava-ui/apogee add content-toggle
Depends on utils (island).
Usage
---
import ContentToggle from '@/components/apogee/ContentToggle/ContentToggle.astro'
---
<ContentToggle active={step} direction="end">
<div data-key="details">…</div>
<div data-key="payment">…</div>
</ContentToggle>
Then, from your own script: root.dataset.active = 'payment'.
API
@composable — exposed to Content Editors in AVA. Any other attribute passes
through to the root <div>.
| Prop | Type | Default | Description |
|---|---|---|---|
active |
string |
— | data-key of the visible child. Change data-active on the root to swap. |
direction |
'start' | 'end' |
'end' |
Side the outgoing content leaves toward; the new one enters from the opposite side. Logical, so it mirrors in RTL. |
class |
string |
— | Extra classes, merged last. |
Children are any elements with a data-key.
Direction
Both values:
direction="start": first
direction="start": second
direction="end": first
direction="end": second
Show codeHide code
<ContentToggle active="a" direction="start">…</ContentToggle>
<ContentToggle active="a" direction="end">…</ContentToggle>Events
The root dispatches apogee:content-toggle-change (detail: { active },
bubbling) after every swap.
States
- Active —
display: block, slides in (apogee-ct-in). - Leaving — the previous child keeps rendering for one animation
(
apogee-ct-out), then hides. - Reduced motion — no slide; the swap is instant.
Examples
Log in and sign up behind a SegmentedControl. The control sets data-active on change:
Welcome back. Log in with your work email.
New here? Create an account in under a minute.
Show codeHide code
<div onchange="this.querySelector('.apogee-content-toggle').dataset.active = event.target.value">
<SegmentedControl name="auth" options={[{ label: 'Log in', value: 'login' }, { label: 'Sign up', value: 'signup' }]} value="login" aria-label="Account" />
<ContentToggle active="login" class="mt-4 text-sm">
<p data-key="login">Welcome back. Log in with your work email.</p>
<p data-key="signup">New here? Create an account in under a minute.</p>
</ContentToggle>
</div>Accessibility
- Only the active child is in the accessibility tree (the rest are
display: none). - Focus does not move on swap; move it yourself when the new content starts a task (a form step).
Tokens
| Token | Used for |
|---|---|
--duration-medium / --ease-standard |
Enter and exit slides |
Do / Don’t
- Do keep children the same height where you can; the box is a grid so it takes the tallest one.
- Don’t use it for tab panels; use Tabs, which carries the ARIA pattern.
Content
- Each child should make sense alone; the reader never sees two at once.
- Name the control that switches them for what the user picks (“Log in”, “Sign up”), not “Option 1”.