Installation
npm install @teasim/[email protected] @teacss/[email protected]yarn add @teasim/[email protected] @teacss/[email protected]pnpm add @teasim/[email protected] @teacss/[email protected]bun add @teasim/[email protected] @teacss/[email protected]Configure the CSS entry, source scan, and Astro SSR settings from the
Astra setup. This component uses the
application’s TeaCSS 0.7.1 build.
The open state is <details> and <summary>, so a row opens before any
JavaScript runs and keeps working if none ever does. A delegated runtime binds
on top to add what the element has no answer for — arrow-key roving focus,
multiple, defaultValue, and an accordion:change event. There is no client
directive to write; it binds itself and survives a view transition.
Preview
Does this need JavaScript?
name.Where do the styles come from?
Can I restyle it?
class, and caller classes merge last.---
import { Accordion, AccordionContent, AccordionItem, AccordionSummary } from "@teasim/astra";
---
<Accordion>
<AccordionItem name="faq" open>
<AccordionSummary><h3>Does this need JavaScript?</h3></AccordionSummary>
<AccordionContent>No. The disclosure is the browser's.</AccordionContent>
</AccordionItem>
<AccordionItem name="faq">
<AccordionSummary><h3>Where do the styles come from?</h3></AccordionSummary>
<AccordionContent>Your application's TeaCSS build.</AccordionContent>
</AccordionItem>
</Accordion>Examples
One at a time
Give sibling Items the same non-empty name and the browser closes the open
one when another opens. The group name is document-wide, so a page with two
independent accordions needs two names.
Green
Black
Open at most one member of a named group initially — two Items sharing a name
and both carrying open is a state the browser has to resolve, and it closes
one of them for you.
Several at once
Dropping name is not enough. multiple defaults to false, and on bind the
runtime keeps the first open Item and closes the rest — so two Items that both
carry open render as one open row. Pass multiple on the root to keep
them all.
Application-generated CSS
Native disclosure first
<Accordion multiple>
<AccordionItem open>…</AccordionItem>
<AccordionItem open>…</AccordionItem>
</Accordion>The two mechanisms are independent and both apply. name is the browser’s,
and it works with scripting off; multiple is the runtime’s, and it wins on
bind. Setting multiple while siblings share a name gets you the browser’s
exclusivity anyway — use one or the other, not both.
API reference
Four components, each rendering one native element.
| Component | Renders | Own props |
|---|---|---|
Accordion |
<div> |
multiple, defaultValue, collapsible, orientation, loopFocus |
AccordionItem |
<details> |
value — plus native open and name |
AccordionSummary |
<summary> |
none |
AccordionContent |
<div> |
none |
Accordion
| Prop | Type | Default | Notes |
|---|---|---|---|
multiple |
boolean |
false |
Several Items open at once |
defaultValue |
string | string[] |
— | Which value starts open; an array joins with commas |
collapsible |
boolean |
true |
The last open Item may close |
orientation |
"horizontal" | "vertical" |
"vertical" |
Which arrow keys move focus |
loopFocus |
boolean |
true |
Focus wraps at either end |
AccordionItem
| Prop | Type | Default | Notes |
|---|---|---|---|
value |
string |
— | Identifies the Item to defaultValue |
open |
boolean |
false |
Native <details> attribute |
name |
string |
— | Native group name; siblings sharing one are mutually exclusive |
AccordionSummary must be the first direct element child of its Item —
that is a <details> requirement, not this component’s. AccordionContent
renders a plain <div> and does not force a region landmark.
AccordionItem and AccordionSummary reserve aria-controls, aria-disabled,
aria-expanded, and role; Summary reserves tabindex as well. Passing those
attributes is ignored rather than merged. Root and Content otherwise forward
their native <div> attributes. Every part reserves data-slot and class
composition hooks, so the component retains its own identity.
How the props reach the runtime
Accordion serializes the root options into its owned data-* attributes:
data-multiple, data-default-value, data-collapsible, data-orientation,
and data-loop-focus. Some default values are omitted. The runtime reads
the resulting DOM on bind; applications should pass the public props instead
of writing those private attributes themselves.
Family installers are not public API. Import all four components from
@teasim/astra; the component’s own <script> loads its behavior. There is
no public @teasim/astra/accordion entry. Drive a bound Accordion through
events instead:
const root = document.querySelector('[data-slot="accordion/root"]');
// Listen: fires on the root whenever the open set changes.
root.addEventListener("accordion:change", (event) => {
event.detail.value; // string[] — the open Items' values
});
// Drive: dispatch on the root, not on an Item.
root.dispatchEvent(new CustomEvent("accordion:set", { detail: { value: ["shipping"] } }));Both carry { value } rather than a bare array, and accordion:set is ignored
unless its target is the root itself. An Item needs a value for either
direction to name it.
accordionRecipe
accordionRecipe exposes root, item, summary, and content. It lives on
@teasim/astra/recipes, never on the family subpath, so rendering an Accordion
never pulls a recipe in behind it.
import { accordionRecipe } from "@teasim/astra/recipes";
const classes = accordionRecipe();
classes.item({ className: "border-color:neutral-600" });