Production-Ready Components
Eight modern UI components — built with the platform, no framework required. Every interaction uses HTML and CSS. Copy the code, adapt the styles.
Popover Menu
Native popover API — no JavaScript positioning needed. Auto-dismisses on outside click, supports keyboard navigation, and uses @starting-style for smooth entry.
<button popovertarget="menu">
Options
</button>
<div id="menu"
popover
role="menu">
<button class="menu-item"
role="menuitem">
Open
</button>
<button class="menu-item"
role="menuitem">
Download
</button>
</div>
[popover].menu {
padding: 0.25rem;
background: oklch(14% 0.02 265);
border-radius: 0.75rem;
opacity: 0;
translate: 0 -0.5rem;
transition: opacity 250ms, translate 250ms,
display 250ms allow-discrete;
@starting-style {
opacity: 0;
translate: 0 -0.5rem;
}
}
[popover].menu:popover-open {
opacity: 1;
translate: 0 0;
}
Toast Notification
Dismissible notification using the Popover API. Enters with @starting-style, exits with a CSS transition. Fixed positioning keeps it anchored to the viewport corner.
<button
popovertarget="toast"
popovertargetaction="show">
Show Toast
</button>
<div id="toast"
class="toast"
popover>
<strong>Changes saved</strong>
<span>Your work is saved.</span>
</div>
.toast {
position: fixed;
bottom: 1.5rem;
right: 1.5rem;
margin: 0;
opacity: 0;
translate: 0 1rem;
transition:
opacity 250ms,
translate 250ms,
display 250ms allow-discrete;
@starting-style {
opacity: 0;
translate: 0 1rem;
}
}
.toast:popover-open {
opacity: 1;
translate: 0 0;
}
Modal Dialog
Native <dialog> element with closedby="any" for click-outside dismissal. Animated backdrop uses @starting-style. Fully accessible with focus trap built in.
<button
onclick="dialog.showModal()">
Open Modal
</button>
<dialog
id="dialog"
closedby="any">
<h2>Title</h2>
<p>Dialog content.</p>
<button
onclick="dialog.close()">
Close
</button>
</dialog>
dialog {
opacity: 0;
scale: 0.96;
transition:
opacity 250ms,
scale 250ms,
display 250ms allow-discrete,
overlay 250ms allow-discrete;
@starting-style {
opacity: 0;
scale: 0.96;
}
}
dialog[open] {
opacity: 1;
scale: 1;
}
dialog::backdrop {
background: oklch(5% 0 0 / 0.8);
backdrop-filter: blur(4px);
}
Reshapes automatically as its container width changes — no media queries needed.
@containerResponsive Card
Uses @container to switch between stacked and side-by-side layouts at 24rem. Resize the demo above to see it respond to its own container, not the viewport.
/* Wrap in a container */
<div class="card-container">
<div class="card">
<div class="card__image">
<!-- image -->
</div>
<div class="card__body">
<h3>Title</h3>
<p>Description</p>
</div>
</div>
</div>
.card-container {
container-type: inline-size;
container-name: card;
}
.card {
display: grid;
grid-template-rows: auto 1fr;
}
@container card (min-width: 24rem) {
.card {
grid-template-rows: none;
grid-template-columns: 8rem 1fr;
}
}
What is the Popover API?
popover attribute and popovertarget to wire things up declaratively.
How does @starting-style work?
display: none. The browser captures the start values and transitions into the final state.
Do these need JavaScript?
<details> is a native HTML element with built-in toggle behaviour. The chevron rotation and body animation are all CSS, triggered by the [open] attribute.
Animated Accordion
Built on the native <details> + <summary> elements. Content animates in via @starting-style. Chevron rotates via CSS on the [open] attribute — zero JavaScript.
<details class="accordion-item">
<summary>
Question?
<svg class="chevron">
<!-- chevron path -->
</svg>
</summary>
<div class="accordion-body">
Answer text here.
</div>
</details>
.chevron {
transition: rotate 250ms ease;
}
[open] .chevron {
rotate: 180deg;
}
.accordion-body {
animation: slide-in 250ms ease both;
@starting-style {
opacity: 0;
translate: 0 -0.5rem;
}
}
Toggle Switch
An iOS-style toggle built from a visually-hidden checkbox. The track and thumb are styled entirely with CSS using :has(input:checked) — no JavaScript, fully keyboard accessible.
<label class="toggle">
<input
type="checkbox"
class="visually-hidden">
<span class="track"
aria-hidden="true">
</span>
Notifications
</label>
.track {
width: 2.75rem;
height: 1.5rem;
border-radius: 9999px;
background: oklch(25% 0.03 265);
transition: background 250ms;
}
.track::after {
content: "";
/* ...thumb styles */
transition: translate 250ms;
}
.toggle:has(input:checked) .track {
background: var(--color-primary);
}
.toggle:has(input:checked) .track::after {
translate: 1.25rem 0;
}
Tab Bar
Tabs driven entirely by CSS. Radio inputs paired with :has() on the wrapper control which panel is visible. Active underline is a bottom border that transitions on the label.
<div class="tabs">
<input type="radio"
name="t"
id="tab-a"
checked>
<input type="radio"
name="t"
id="tab-b">
<label for="tab-a">Alpha</label>
<label for="tab-b">Beta</label>
<div class="panel a">...</div>
<div class="panel b">...</div>
</div>
.panel { display: none; }
.tabs:has(#tab-a:checked)
.panel.a {
display: block;
}
.tabs:has(#tab-b:checked)
.panel.b {
display: block;
}
.tabs:has(#tab-a:checked)
label[for="tab-a"] {
border-bottom-color:
var(--color-primary);
}
Tooltip
Two approaches: a Popover-based tooltip that renders in the top layer (always above other content), and a CSS-only hover tooltip using opacity + translate transitions.
<!-- CSS-only hover approach -->
<div class="tip-wrapper">
<button>Hover me</button>
<div class="tip"
role="tooltip">
Tooltip text
</div>
</div>
<!-- Popover approach -->
<button
popovertarget="tip">
Click me
</button>
<div id="tip"
popover
role="tooltip">
Tooltip text
</div>
.tip {
opacity: 0;
translate: -50% 0.5rem;
transition:
opacity 150ms,
translate 150ms;
}
.tip-wrapper:hover .tip,
.tip-wrapper:focus-within .tip {
opacity: 1;
translate: -50% 0;
}
/* Arrow via pseudo-element */
.tip::after {
content: "";
border: 5px solid transparent;
border-top-color: currentColor;
}