No script tags
Menus, modals, tabs and tooltips — in CSS.
Everything on this page is state held by the browser itself: a checkbox, an open attribute, the top layer, or the URL fragment. The server rendered it once and then stopped being involved.
Slide-out sidebar
body:has(#sidebar-toggle:checked)A visually hidden checkbox holds the open state, and :has() lifts that state up to the body so a panel elsewhere in the document can slide in — the parent selector replaces the script that used to toggle a class.
translate + transitionThe panel is always in the DOM at translate: -100%; checking the box moves it to 0 and the transition does the rest.
Accordions
details[open] .chevron<details> and <summary> are a disclosure widget with built-in keyboard support; the chevron rotation is just a rule matching the open attribute.
Is there really no JavaScript?
What happens in older browsers?
Where does the state live?
summary::-webkit-details-markerThe native triangle is removed with list-style: none so the custom chevron can take its place.
Dropdown menu
popover + :popover-openThe menu is a popover element opened by its invoker's popovertarget attribute; the browser promotes it to the top layer, closes it on Esc or an outside click, and returns focus — all behaviours that used to need a library.
position-anchor + position-areaAnchor positioning pins the menu under its button without measuring anything in JavaScript, and position-try-fallbacks flips it above when there is no room below.
Modal dialog
::backdropThe same popover primitive, styled as a centred panel with a dimmed ::backdrop pseudo-element; light dismiss and Esc-to-close come free with popover="auto".
Top layer
Still zero JavaScript
This dialog is inert markup until the browser's popover machinery shows it. Press Escape, click outside, or use the button below.
@starting-styleThe fade-and-scale entrance uses @starting-style with transition-behavior: allow-discrete, so an element going from display: none animates in properly.
Tabs from the URL
:targetEach panel is hidden until the URL fragment matches its id, so tab state lives in the address bar and is shareable, bookmarkable, and survives a reload.
Each tab is an ordinary anchor pointing at a fragment, and each panel is a section with a matching id. Following the link changes the fragment, which changes what :target matches.
Panels are display: none by default and display: block when targeted. The active tab is highlighted with :has() — the tab strip checks whether the panel it points at is currently the target.
Fragment navigation adds a history entry and scrolls the panel into view, which is why the panels carry a scroll margin. In exchange, tab state is in the URL and survives a reload.
:not(:has(:target))The first panel is shown by default with a rule that only applies while no panel is targeted — the CSS equivalent of an initial state.
Tooltips
anchor-name / position-anchorThe tooltip is positioned against a named anchor instead of a wrapper with position: relative, so it can escape overflow: hidden and never needs measuring.
position-areaOne property picks the cell of the 3×3 grid around the anchor to sit in; swapping block-start for inline-end moves the tooltip with no other change.
Form controls and validation
:user-invalid:user-invalid only matches after the field has been interacted with and blurred, so the error appears when the user is done typing rather than the moment the page loads.
appearance: noneThe checkbox and slider are native controls stripped back with appearance: none and rebuilt with ::-webkit-slider-thumb and :checked — they keep their keyboard behaviour.