Using CSS :checked for Pure-CSS Accordions and Toggles
The CSS :checked pseudo-class enables developers to
create interactive, state-driven user interface components—such as
toggle switches, collapsible panels, and multi-item accordions—entirely
without JavaScript. By pairing native HTML
<input type="checkbox"> or
<input type="radio"> elements with their
corresponding <label> tags, developers can exploit
browser-level state management to show, hide, or transform adjacent
content using combinator selectors. This article breaks down the
underlying mechanics, structural requirements, and styling techniques
needed to implement robust, pure-CSS interactive components.
The Core Mechanism: The "Checkbox Hack"
Traditional pure-CSS styling is inherently stateless; styles respond
dynamically only to transient user events, such as hovering
(:hover) or focusing (:focus). The
:checked pseudo-class bypasses this limitation by
leveraging the built-in binary state of HTML form inputs.
This pattern relies on three interdependent elements:
- A State Container (
<input>): An<input type="checkbox">or<input type="radio">tracks whether the component is active (open/on) or inactive (closed/off). - A State Controller (
<label>): Using theforattribute referencing the input'sid, a<label>can trigger the input state from anywhere in the document when clicked. This allows the input itself to be visually hidden while preserving clickability. - A Sibling Combinator (
+or~): CSS cannot traverse upwards or target parent elements based on child state without modern selectors like:has(). Sibling combinators—adjacent sibling (+) or general sibling (~)—allow changes in the input to alter subsequent elements directly.
<input type="checkbox" id="toggle" class="visually-hidden">
<label for="toggle" class="toggle-button">Toggle Content</label>
<div class="collapsible-content">
<p>This content is controlled by the input state.</p>
</div>/* Visually hide the input while keeping it accessible */
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
/* Default state: content hidden */
.collapsible-content {
display: none;
}
/* Checked state: content revealed */
.visually-hidden:checked ~ .collapsible-content {
display: block;
}Creating a Pure-CSS Toggle Switch
A toggle switch requires transforming a standard checkbox into a
sliding interface. By hiding the raw checkbox and styling its label as
the switch track and knob, the :checked pseudo-class drives
the transition.
HTML Structure
<input type="checkbox" id="switch" class="switch-input">
<label for="switch" class="switch-label">
<span class="switch-button"></span>
</label>CSS Styling
.switch-input {
position: absolute;
opacity: 0;
}
.switch-label {
display: inline-block;
width: 50px;
height: 26px;
background-color: #ccc;
border-radius: 13px;
cursor: pointer;
position: relative;
transition: background-color 0.2s ease-in-out;
}
.switch-button {
position: absolute;
top: 3px;
left: 3px;
width: 20px;
height: 20px;
border-radius: 50%;
background-color: #fff;
transition: transform 0.2s ease-in-out;
}
/* Active Switch States */
.switch-input:checked + .switch-label {
background-color: #0070f3;
}
.switch-input:checked + .switch-label .switch-button {
transform: translateX(24px);
}When the label is activated, the checkbox toggles to
:checked, immediately updating the label's background color
and translating the inner .switch-button span along the
X-axis.
Creating Accordions: Checkboxes vs. Radios
Accordions follow the same operational principle as toggles, but the choice of input type determines whether multiple panels can remain open simultaneously.
Multi-Panel Accordions (Checkboxes)
Using <input type="checkbox"> allows every panel
to operate independently. Users can expand several sections
simultaneously because each checkbox manages its own state isolated from
other checkboxes.
Mutually Exclusive Accordions (Radio Buttons)
Using <input type="radio"> with a shared
name attribute forces mutual exclusivity. When a user
clicks a panel header, any previously opened panel within the same group
collapses automatically because standard HTML only permits a single
radio button per name group to be :checked at a time.
<div class="accordion-item">
<input type="radio" name="faq" id="panel-1" class="accordion-input">
<label for="panel-1" class="accordion-header">Section 1</label>
<div class="accordion-body">
<p>Content for the first section.</p>
</div>
</div>
<div class="accordion-item">
<input type="radio" name="faq" id="panel-2" class="accordion-input">
<label for="panel-2" class="accordion-header">Section 2</label>
<div class="accordion-body">
<p>Content for the second section.</p>
</div>
</div>Animating Collapsible Content
A common limitation of the display: none to
display: block pattern is that display cannot
be animated with CSS transitions. To create smooth expansion effects,
modern CSS offers two primary methods:
Method 1: CSS Grid
(grid-template-rows)
Animating height from 0 to an unknown, dynamic height
can be achieved smoothly by animating CSS Grid row templates:
.accordion-body {
display: grid;
grid-template-rows: 0fr;
transition: grid-template-rows 0.3s ease-out;
}
.accordion-body > div {
overflow: hidden;
}
.accordion-input:checked ~ .accordion-body {
grid-template-rows: 1fr;
}Method 2: Maximum Height Transitions
A classic alternative utilizes max-height and
overflow: hidden:
.accordion-body {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease-in-out;
}
.accordion-input:checked ~ .accordion-body {
max-height: 500px; /* Must exceed expected inner content height */
}Accessibility and Structural Considerations
While :checked-based components eliminate runtime
dependencies on JavaScript, they introduce distinct accessibility and
structural concerns:
- DOM Order Dependence: Sibling combinators
(
+,~) require the<input>to precede the target content in the DOM tree. This can limit complex layout decoupling unless modern:has()parent selectors (e.g.,.card:has(.toggle:checked)) are adopted. - Screen Reader Semantics: Pure-CSS accordions convey
state via standard input controls (
checkboxorradio), not standard accordion roles (aria-expandedandaria-controls). Users of assistive technologies will hear form control interactions rather than disclosure widgets. - Native HTML Alternative: For accordions
specifically, the native HTML
<details>and<summary>elements provide built-in collapsible behavior with appropriate accessibility semantics out of the box, reducing the necessity of using the:checkedhack for non-form disclosure patterns.