Accordion
Collapsible content sections with expand/collapse animation. Supports single or multiple open items, three visual variants, keyboard navigation, and full ARIA accordion pattern.
Playground
Design tokens are named values — colors, spacing, radii — that form the single source of truth for your design system.
Semantic tokens map to foundation tokens. Swap the foundation layer and the entire UI updates automatically.
Yes — semantic tokens handle dark mode via the CSS light-dark() function. No manual dark: classes needed.
<Accordion>
<AccordionItem value="item-1" title="Section One">
Content for the first section.
</AccordionItem>
<AccordionItem value="item-2" title="Section Two">
Content for the second section.
</AccordionItem>
<AccordionItem value="item-3" title="Section Three">
Content for the third section.
</AccordionItem>
</Accordion>01 Examples
FAQ Section
Frequently Asked Questions
Everything you need to know to get started.
Basic Svelte knowledge helps, but our components are designed with simple, declarative APIs. If you can write HTML and pass props, you're good to go. Check the Getting Started guide for a gentle introduction.
We use a 3-layer token system: foundation, semantic, and interaction tokens. Dark
mode is handled automatically via light-dark() — no manual dark: classes
needed.
Absolutely. Every component is tree-shakeable. Import only what you need and your bundle stays lean.
Yes. All components follow WAI-ARIA patterns, support keyboard navigation, and
include proper focus management. We test with screen readers and respect prefers-reduced-motion.
<div
class="bg-surface-elevated border-border-subtle w-full overflow-hidden rounded-2xl border"
>
<div class="border-border-subtle border-b px-6 py-4">
<h3 class="text-text-primary font-semibold">Frequently Asked Questions</h3>
<p class="text-text-tertiary mt-0.5 text-sm">
Everything you need to know to get started.
</p>
</div>
<div class="px-6 py-2">
<Accordion defaultValue="faq-1">
<AccordionItem value="faq-1" title="Do I need to know Svelte to use this?">
<p class="text-text-secondary text-sm leading-relaxed">
Basic Svelte knowledge helps, but our components are designed with simple,
declarative APIs. If you can write HTML and pass props, you're good to go. Check the <a
href={resolve('/getting-started')}
class="text-primary underline">Getting Started guide</a
> for a gentle introduction.
</p>
</AccordionItem>
<AccordionItem value="faq-2" title="How does dark mode work?">
<p class="text-text-secondary text-sm leading-relaxed">
We use a 3-layer token system: foundation, semantic, and interaction tokens. Dark
mode is handled automatically via <code
class="bg-surface-base rounded px-1.5 py-0.5 text-xs">light-dark()</code
>
— no manual <code class="bg-surface-base rounded px-1.5 py-0.5 text-xs">dark:</code> classes
needed.
</p>
</AccordionItem>
<AccordionItem value="faq-3" title="Can I use only some components?">
<p class="text-text-secondary text-sm leading-relaxed">
Absolutely. Every component is tree-shakeable. Import only what you need and your
bundle stays lean.
</p>
</AccordionItem>
<AccordionItem value="faq-4" title="Is it accessible?">
<p class="text-text-secondary text-sm leading-relaxed">
Yes. All components follow WAI-ARIA patterns, support keyboard navigation, and
include proper focus management. We test with screen readers and respect <code
class="bg-surface-base rounded px-1.5 py-0.5 text-xs">prefers-reduced-motion</code
>.
</p>
</AccordionItem>
</Accordion>
</div>
</div>Controlled State
<div class="flex w-full flex-col gap-4">
<div class="flex items-center gap-2">
<span class="text-text-tertiary text-xs font-medium">Open:</span>
<Badge size="xs" intent="primary" variant="soft">{faqValue || '(none)'}</Badge>
</div>
<Accordion variant="card" bind:value={faqValue}>
<AccordionItem value="what-is" title="What is Urbicon UI?">
A Svelte 5 component library with built-in i18n, semantic design tokens, and zero-config
dark mode.
</AccordionItem>
<AccordionItem value="pricing" title="Is it free?">
Yes — Urbicon UI is free and open-source under the MIT license.
</AccordionItem>
<AccordionItem value="support" title="Where do I get help?">
Check the documentation, open a GitHub issue, or join the community Discord.
</AccordionItem>
</Accordion>
</div>Custom Trigger
Includes community support, basic components, and public repos only.
Priority support, private repos, advanced theming, and early access to new components.
<Accordion variant="card" defaultValue="pro">
<AccordionItem value="free">
{#snippet trigger({ open: _open })}
<div class="flex w-full items-center gap-3">
<div
class="bg-surface-hover rounded-modify flex size-8 items-center justify-center text-sm"
>
🆓
</div>
<div class="flex-1 text-left">
<p class="text-text-primary text-sm font-semibold">Free Plan</p>
<p class="text-text-tertiary text-xs">Up to 3 projects</p>
</div>
<Badge size="xs" intent="neutral" variant="soft">$0/mo</Badge>
</div>
{/snippet}
<p class="text-text-secondary text-sm">
Includes community support, basic components, and public repos only.
</p>
</AccordionItem>
<AccordionItem value="pro">
{#snippet trigger({ open: _open })}
<div class="flex w-full items-center gap-3">
<div
class="bg-primary/10 text-primary rounded-modify flex size-8 items-center justify-center"
>
<ZapIcon size={16} />
</div>
<div class="flex-1 text-left">
<p class="text-text-primary text-sm font-semibold">Pro Plan</p>
<p class="text-text-tertiary text-xs">Unlimited projects</p>
</div>
<Badge size="xs" intent="primary" variant="filled">$29/mo</Badge>
</div>
{/snippet}
<p class="text-text-secondary text-sm">
Priority support, private repos, advanced theming, and early access to new components.
</p>
</AccordionItem>
</Accordion>02 Built on Collapsible
Accordion vs Collapsible
Single panel → Collapsible
Use Collapsible when you
need a single panel that manages its own state. No context, no coordination — just expand
and collapse.
Multi-panel → Accordion
<div class="flex w-full max-w-lg flex-col gap-2.5">
<p class="text-text-tertiary text-xs font-medium tracking-wider uppercase">
Single panel → Collapsible
</p>
<Collapsible variant="card" title="Standalone expand/collapse" defaultOpen>
<p class="text-text-secondary text-sm">
Use <code class="bg-surface-base rounded px-1.5 py-0.5 text-xs">Collapsible</code> when you
need a single panel that manages its own state. No context, no coordination — just expand
and collapse.
</p>
</Collapsible>
</div>
<div class="flex w-full max-w-lg flex-col gap-2.5">
<p class="text-text-tertiary text-xs font-medium tracking-wider uppercase">
Multi-panel → Accordion
</p>
<Accordion variant="card" defaultValue="a1">
<AccordionItem value="a1" title="Coordinated panel A">
Opening this closes panel B. Accordion coordinates multiple Collapsible panels via a
shared context.
</AccordionItem>
<AccordionItem value="a2" title="Coordinated panel B">
Single-mode, multiple-mode, non-collapsible — all managed by the Accordion wrapper.
</AccordionItem>
</Accordion>
</div>Architecture note: Each AccordionItem uses Collapsible internally
for its expand/collapse animation and ARIA attributes. The Accordion component adds
multi-item coordination (single/multiple mode, non-collapsible, disabled) on top. See the Collapsible docs for the standalone API.
03 Customization
slotClasses Override
<Accordion
variant="card"
defaultValue="styled"
slotClasses={{
item: 'border-2 border-primary/40 rounded-2xl bg-primary/5',
trigger: 'text-primary font-bold',
chevron: 'text-primary',
contentInner: 'text-text-primary border-l-2 border-primary/40 pl-4'
}}
>
<AccordionItem value="styled" title="Restyled via slotClasses">
Every slot is overridable: the item gets a two-pixel primary border + tinted background,
the trigger inherits the primary intent in bold, the chevron matches, and the content
inner pane adds a primary left rail for emphasis.
</AccordionItem>
<AccordionItem value="another" title="Consistent overrides">
slotClasses are applied to every item in the accordion, keeping the visual language
consistent across sections.
</AccordionItem>
</Accordion>Glassmorphism (unstyled)
unstyled gives full creative
freedom while keeping structure, animation, and accessibility intact.<Accordion unstyled defaultValue="glass-1" class="flex w-full flex-col gap-3">
<AccordionItem
unstyled
value="glass-1"
title="Frosted Glass Header"
class="overflow-hidden rounded-xl border border-white/20 bg-white/10 backdrop-blur-md transition-colors hover:bg-white/15"
slotClasses={{
trigger:
'flex w-full items-center justify-between px-5 py-3.5 text-sm font-medium text-white',
chevron: 'size-4 text-white/60 transition-transform duration-200',
contentInner: 'px-5 pb-4 pt-1 text-sm text-white/80'
}}
>
The accordion strips all defaults in unstyled mode. Every visual detail — background,
blur, text color, spacing — is hand-crafted through class props.
</AccordionItem>
<AccordionItem
unstyled
value="glass-2"
title="Layered Transparency"
class="overflow-hidden rounded-xl border border-white/20 bg-white/10 backdrop-blur-md transition-colors hover:bg-white/15"
slotClasses={{
trigger:
'flex w-full items-center justify-between px-5 py-3.5 text-sm font-medium text-white',
chevron: 'size-4 text-white/60 transition-transform duration-200',
contentInner: 'px-5 pb-4 pt-1 text-sm text-white/80'
}}
>
A single backdrop-blur layer on the item wraps both trigger and content — no seams, no
double-blur where the two panes meet.
</AccordionItem>
<AccordionItem
unstyled
value="glass-3"
title="Creative Freedom"
class="overflow-hidden rounded-xl border border-white/20 bg-white/10 backdrop-blur-md transition-colors hover:bg-white/15"
slotClasses={{
trigger:
'flex w-full items-center justify-between px-5 py-3.5 text-sm font-medium text-white',
chevron: 'size-4 text-white/60 transition-transform duration-200',
contentInner: 'px-5 pb-4 pt-1 text-sm text-white/80'
}}
>
Proof that <code class="rounded bg-white/15 px-1.5 py-0.5">unstyled</code> gives full creative
freedom while keeping structure, animation, and accessibility intact.
</AccordionItem>
</Accordion>Terminal (unstyled)
NODE_ENV=production
PORT=3000
LOG_LEVEL=info
Process exited with code 0
● app.service - Application Server
Active: active (running)
Memory: 128.4M
[12:34:01] GET /api/health 200 2ms
[12:34:03] POST /api/users 201 45ms
[12:34:05] Listening on :3000
<Accordion
unstyled
defaultValue="term-1"
class="w-full divide-y divide-emerald-500/20 font-mono"
>
<AccordionItem
unstyled
value="term-1"
title="$ cat /etc/config"
slotClasses={{
trigger:
'flex w-full items-center justify-between py-3 text-xs text-emerald-300 transition-colors hover:text-emerald-400',
chevron: 'size-3.5 text-emerald-400 transition-transform duration-200',
contentInner: 'pb-3 text-xs leading-relaxed text-emerald-200'
}}
>
<p>NODE_ENV=production</p>
<p>PORT=3000</p>
<p>LOG_LEVEL=info</p>
<p class="mt-2 text-emerald-300">Process exited with code 0</p>
</AccordionItem>
<AccordionItem
unstyled
value="term-2"
title="$ systemctl status app"
slotClasses={{
trigger:
'flex w-full items-center justify-between py-3 text-xs text-emerald-300 transition-colors hover:text-emerald-400',
chevron: 'size-3.5 text-emerald-400 transition-transform duration-200',
contentInner: 'pb-3 text-xs leading-relaxed text-emerald-200'
}}
>
<p class="text-emerald-400">● app.service - Application Server</p>
<p class="text-emerald-400">
Active: <span class="text-emerald-300">active (running)</span>
</p>
<p class="text-emerald-400"> Memory: 128.4M</p>
</AccordionItem>
<AccordionItem
unstyled
value="term-3"
title="$ tail -f /var/log/app.log"
slotClasses={{
trigger:
'flex w-full items-center justify-between py-3 text-xs text-emerald-300 transition-colors hover:text-emerald-400',
chevron: 'size-3.5 text-emerald-400 transition-transform duration-200',
contentInner: 'pb-3 text-xs leading-relaxed text-emerald-200'
}}
>
<p class="text-emerald-400">[12:34:01] GET /api/health 200 2ms</p>
<p class="text-emerald-400">[12:34:03] POST /api/users 201 45ms</p>
<p class="text-emerald-300">[12:34:05] Listening on :3000</p>
</AccordionItem>
</Accordion>The frosted-glass and terminal treatments above are preset material: register the slotClasses combination once under presets.Accordion / presets.AccordionItem on BlocksProvider and opt in per instance via preset — see Customization.
04 Accessibility
Built-in ARIA
Each trigger uses aria-expanded and aria-controls to link to its content panel. Content
panels have role="region" with aria-labelledby pointing back to the trigger. The data-state attribute exposes open / closed for CSS-only styling.
Keyboard Navigation
Tab moves focus between triggers. Enter / Space toggle the focused item. Focus rings use focus-visible: so they only appear on keyboard navigation,
never on mouse clicks.
Disabled State
Disabled triggers get the native disabled attribute, removing them from the tab order
and preventing activation. Visual feedback via opacity-50 and cursor-not-allowed.
Reduced Motion
The expand/collapse animation uses CSS grid-template-rows transitions. When prefers-reduced-motion is enabled, transition durations
are reduced via the design token system.
05 API Reference
Prop | Type | Default | Description | |
|---|---|---|---|---|
children required | Snippet | — | Content to render inside the Accordion component | |
class | string | — | Additional CSS classes to apply to the Accordion component | |
collapsible | boolean | true | Whether items can be fully collapsed | |
defaultValue | string | string[] | — | Default open item(s) | |
disabled | boolean | false | Whether the Accordion is disabled and non-interactive | |
onValueChange | (value: string | string[]) => void | — | Callback when open items change | |
preset | string | — | Apply a named preset registered via <BlocksProvider presets={{ Accordion: {...} }}>.
Prefer this over class overrides when the requested look falls outside the
semantic intent palette — presets keep hover/active/dark-mode logic coherent
and make the custom look reusable across the project. | |
size | smmdlg | 'md' | Size variant that controls dimensions and spacing of the Accordion | |
slotClasses | Partial<Record<AccordionSlots, string>> | — | Per-slot class overrides. Slots: base | item | trigger | chevron | content | contentInner | |
transitionDuration | number | — | Override every item's expand/collapse animation duration in milliseconds. Defaults to the
--blocks-collapse-duration token (the normal 250ms). Respects prefers-reduced-motion. | |
transitionEasing | string | — | Override every item's expand/collapse easing as a CSS <easing-function> — e.g.
'ease-in-out', 'cubic-bezier(0.4,0,0.2,1)', or a token like 'var(--blocks-ease-springy)'.
Defaults to the --blocks-collapse-easing token. (A CSS string, not the (t) => number
easing function the overlay components take — Accordion animates via CSS.) | |
type | singlemultiple | 'single' | Allow single or multiple items open at once | |
unstyled | boolean | — | Remove default styles | |
value | string | string[] | — | Controlled open item(s) – string for single, string[] for multiple | |
variant | defaultcardghost | 'default' | Visual style variant for the Accordion component | |
...AccordionVariants variant | VariantProps | — | Styling variants from AccordionVariants | |
...HTMLAttributes<HTMLDivElement> inherited | HTMLAttributes | — | HTML attributes (excluding: 'children') |
06 Types
Local type definitions used by this component.
Name | Kind | Category | Used by | Description | |
|---|---|---|---|---|---|
AccordionContext | interface | helper | 0 | Context for Accordion ↔ AccordionItem communication | |
AccordionProps | interface | props | 0 | Props interface for Accordion component | |
AccordionItemProps | interface | props | 0 | Props interface for AccordionItem component | |
AccordionVariants | type | variant | 1 | — | |
AccordionSlots | type | variant | 0 | Slot names derived from the tv() config — single source of truth for slotClasses. |
07 Installation
Import
import { Accordion, AccordionItem } from '@urbicon-ui/blocks';