Examples
Basic usage
What's included in brio's core CSS?
Tokens, composition primitives, and components that a typical web-app UI needs on day one: alert, badge, button, card, code, details, dialog, dismiss, divider, drawer, icon, input, list, nav-link, navbar, sidebar, sidebar-nav, spinner, table.
<details class="details">
<summary>What's included in brio's core CSS?</summary>
<p>
Tokens, composition primitives, and components that a typical web-app UI needs on
day one: alert, badge, button, card, code, details, dialog, dismiss, divider,
drawer, icon, input, list, nav-link, navbar, sidebar, sidebar-nav, spinner, table.
</p>
</details>
.is-flush
How do I override a component's tokens?
Set the component's scoped custom property on the element, a parent, or a theme-level selector. Every variant and state remap flows from these tokens, so override once and every derived state follows.
Where do I find the full token list?
The Tokens reference page catalogs every semantic token and its default value. Component-scoped tokens live in each component's reference page under Token overrides.
Can I use brio with a JS framework?
The CSS works anywhere. The small progressive-enhancement JS layer targets authored HTML via queries and attributes; it doesn't conflict with frameworks that render HTML server-side or client-side.
<div>
<details class="details is-flush">
<summary>How do I override a component's tokens?</summary>
<p>
Set the component's scoped custom property on the element, a parent, or a
theme-level selector. Every variant and state remap flows from these tokens, so
override once and every derived state follows.
</p>
</details>
<details class="details is-flush">
<summary>Where do I find the full token list?</summary>
<p>
The <a href="{{.Site.BasePath}}/tokens">Tokens</a> reference page catalogs every
semantic token and its default value. Component-scoped tokens live in each
component's reference page under Token overrides.
</p>
</details>
<details class="details is-flush">
<summary>Can I use brio with a JS framework?</summary>
<p>
The CSS works anywhere. The small progressive-enhancement JS layer targets
authored HTML via queries and attributes; it doesn't conflict with frameworks
that render HTML server-side or client-side.
</p>
</details>
</div>
.is-filled
Why does brio use cascade layers?
Layers give us a reliable order (reset < tokens < animations < base < composition < blocks <
utilities < overrides) that doesn't depend on import order or source position. A component in
blocks always beats a composition rule with the same specificity;
utilities always win over blocks; overrides always win over everything. Specificity
becomes a per-layer concern rather than a global conflict.
<details class="details is-filled">
<summary>Why does brio use cascade layers?</summary>
<p>
Layers give us a reliable order (<code
>reset < tokens < animations < base < composition < blocks <
utilities < overrides</code
>) that doesn't depend on import order or source position. A component in
<code>blocks</code> always beats a composition rule with the same specificity;
utilities always win over blocks; overrides always win over everything. Specificity
becomes a per-layer concern rather than a global conflict.
</p>
</details>
.no-border
Disclosure without a border
Use it inside a container that already carries its own frame: a card, a drawer body, a sidebar. The border was the only thing marking the widget's bounds, so give the disclosure some other context to sit in.
<details class="details no-border">
<summary>Disclosure without a border</summary>
<p>
Use it inside a container that already carries its own frame: a card, a drawer body,
a sidebar. The border was the only thing marking the widget's bounds, so give the
disclosure some other context to sit in.
</p>
</details>
Callout colors
Info: using brio with an existing design system
Expose brio's semantic tokens in your design system as the source of truth, and your existing components can consume them without competing with brio's cascade.
Ready: the .details animation works natively
No JavaScript is required for the expand/collapse animation -- it's built on
::details-content and
interpolate-size: allow-keywords.
Caution: don't hide critical information
Details is for secondary information the reader might want. Do not fold away error messages, accessibility notices, or content the reader must see to complete a task.
Destructive: uninstalling brio
Removing brio.css from your bundle returns every component to unstyled HTML. Double-check that no consumer code relies on brio's token values before removing.
<div class="stack is-snug">
<details class="details is-info">
<summary>Info: using brio with an existing design system</summary>
<p>
Expose brio's semantic tokens in your design system as the source of truth, and
your existing components can consume them without competing with brio's cascade.
</p>
</details>
<details class="details is-success">
<summary>Ready: the .details animation works natively</summary>
<p>
No JavaScript is required for the expand/collapse animation -- it's built on
<code>::details-content</code> and
<code>interpolate-size: allow-keywords</code>.
</p>
</details>
<details class="details is-warning">
<summary>Caution: don't hide critical information</summary>
<p>
Details is for secondary information the reader might want. Do not fold away
error messages, accessibility notices, or content the reader must see to
complete a task.
</p>
</details>
<details class="details is-danger">
<summary>Destructive: uninstalling brio</summary>
<p>
Removing brio.css from your bundle returns every component to unstyled HTML.
Double-check that no consumer code relies on brio's token values before
removing.
</p>
</details>
</div>
Source comments
Details -- styled native <details> with animated expand/collapse
Animation: chevron rotates on open; content height animates from 0 to
auto via the ::details-content pseudo-element + interpolate-size.
Browsers without ::details-content support still open and close the
widget; they just don't get the height animation.
No JS required.
Reference
Source: src/css/details.css
Applies to
.details
Classes
| Class | Kind | Description |
|---|---|---|
.no-border | opt-out | Removes the border entirely |
.is-flush | variant | No border with bottom-only rule, for use inside containers |
.is-filled | variant | Subtle fill when closed, raised surface when open |
.is-info | variant | Info callout color variant |
.is-warning | variant | Warning callout color variant |
.is-danger | variant | Danger callout color variant |
.is-success | variant | Success callout color variant |
Slots
| Slot | Description |
|---|---|
summary | The disclosure trigger; carries the rotating chevron and the hover / focus treatment every other trigger atom has |
Tokens
| Token | Default | Description |
|---|---|---|
--details-border-color | var(--color-border) | Border color for the details container |
--details-border-width | var(--border-width-default) | Border width for the details container |
--details-padding-block | var(--ui-pad-block-snug) | Block-axis inner padding for the details container |
--details-padding-inline | var(--ui-pad-inline-snug) | Inline-axis inner padding for the details container |
--details-chevron-size | 0.5em | Size of the chevron indicator |
--details-chevron-color | var(--color-text-muted) | Color of the chevron indicator |
--details-chevron-color-hover | var(--color-text) | Color of the chevron indicator on hover |
--details-summary-padding-block | var(--ui-pad-block-compact) | Block-axis padding for the summary trigger row |
--details-summary-padding-inline | var(--ui-pad-inline-tight) | Inline-axis padding for the summary trigger row |
--details-summary-bg-hover | var(--color-fill) | Background color for the summary row on hover |