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 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 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 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”.