CSS :indeterminate Pseudo Class
The :indeterminate CSS pseudo-class selects the elements that are in an indeterminate state. Read about the pseudo-class & practice with examples.
The CSS :indeterminate pseudo-class matches form controls that are in an indeterminate state — a third state that is neither "on" nor "off". This chapter explains which elements can enter that state, how to trigger it, and how to style it, with runnable examples for checkboxes, radio groups, and progress bars.
What "indeterminate" means
Some form controls normally have two states (checked / unchecked, or a known progress value). The indeterminate state represents a third "undecided" condition. The :indeterminate pseudo-class targets three kinds of elements:
- Checkboxes (
<input type="checkbox">) whoseindeterminateDOM property is set totrue. This is the classic "some, but not all, child items are selected" tri-state checkbox. - Radio buttons (
<input type="radio">) — every button in a same-named group is matched while no option in that group is selected. - Progress bars (
<progress>) that have novalueattribute, meaning the task length is unknown so the bar animates without a fixed fill.
The indeterminate state is set in JavaScript, not CSS — you cannot enter it with a CSS rule alone. For checkboxes you set element.indeterminate = true via JavaScript. Setting that property does not change checked; the two are independent. The visual indeterminate look (usually a dash instead of a tick) and a :checked match are separate things. Resetting the form with form.reset() clears a checkbox’s indeterminate flag back to false.
The :checked pseudo-class styles the checked state, while :indeterminate styles the undecided state. A control can be one, the other, or neither, but never both at the same time. You can combine :indeterminate with other pseudo-classes such as :hover to style an indeterminate element only while the pointer is over it, or with :focus to highlight it when it receives keyboard focus.
Syntax
:indeterminate {
/* css declarations */
}Scope the selector to a specific element type to avoid unintended matches:
/* Only checkboxes */
input[type="checkbox"]:indeterminate { outline: 2px dashed orange; }
/* Only radio buttons */
input[type="radio"]:indeterminate { opacity: 0.7; }
/* Only progress bars */
progress:indeterminate { opacity: 0.5; }You can also invert the match with :not to target controls that have left the indeterminate state:
/* Style a progress bar once it has a known value */
progress:not(:indeterminate) { border: 2px solid green; }Example: styling an indeterminate checkbox
A checkbox only becomes indeterminate when JavaScript sets the indeterminate property to true. The CSS rule fires the moment that property is set, and clears the moment it is set back to false. This example highlights the control with a coloured box-shadow.
<!DOCTYPE html>
<html>
<head>
<title>Title of the document</title>
<style>
input:indeterminate {
box-shadow: 0 0 2px 2px #666;
}
</style>
</head>
<body>
<h2>Indeterminate selector example</h2>
<form>
<input type="checkbox" id="box" /> Checkbox
<script>
var checkbox = document.getElementById("box");
checkbox.indeterminate = true;
</script>
</form>
</body>
</html>Common use case: a "select all" tri-state checkbox
The most practical application of :indeterminate is a master checkbox that summarizes a list of options: checked when all children are selected, unchecked when none are, and indeterminate when some are. This pattern is common in file managers, email clients, and data tables.
The JavaScript logic keeps the master checkbox’s indeterminate and checked properties in sync with the child checkboxes. CSS handles the rest.
<!DOCTYPE html>
<html>
<head>
<title>Tri-state checkbox</title>
<style>
/* Highlight master while in partial-selection state */
#all:indeterminate {
outline: 2px solid #8ebf42;
}
</style>
</head>
<body>
<label><input type="checkbox" id="all" /> Select all</label>
<ul>
<li><label><input type="checkbox" class="child" /> Apples</label></li>
<li><label><input type="checkbox" class="child" /> Bananas</label></li>
<li><label><input type="checkbox" class="child" /> Cherries</label></li>
</ul>
<script>
var all = document.getElementById("all");
var children = document.querySelectorAll(".child");
function syncParent() {
var checked = [...children].filter(function (c) { return c.checked; }).length;
all.checked = checked === children.length;
all.indeterminate = checked > 0 && checked < children.length;
}
all.addEventListener("change", function () {
children.forEach(function (c) { c.checked = all.checked; });
});
children.forEach(function (c) {
c.addEventListener("change", syncParent);
});
syncParent();
</script>
</body>
</html>Check one of the child boxes: the "Select all" checkbox becomes indeterminate and the green outline appears. Check all three: the outline disappears and it switches to the fully-checked state instead.
Example: an indeterminate radio group
A radio group is in the indeterminate state while none of its options is selected. The :indeterminate pseudo-class matches every <input type="radio"> in the group until the user picks an answer. Once any option is chosen, the whole group leaves the indeterminate state and the styling is removed.
This is useful for prompting users to make a choice — for example, you can use it in form validation alongside :invalid and :valid to visually guide users to unanswered required questions.
<!DOCTYPE html>
<html>
<head>
<title>Title of the document</title>
<style>
label {
margin-right: .5em;
position: relative;
top: 1px;
}
input[type="radio"]:indeterminate + label {
color: #8ebf42;
}
</style>
</head>
<body>
<h2>:indeterminate selector example</h2>
<form>
<input type="radio" name="option" value="yes" id="yes" />
<label for="yes">Yes</label>
<input type="radio" name="option" value="no" id="no" />
<label for="no">No</label>
<input type="radio" name="option" value="dont-know" id="dont-know" />
<label for="dont-know">Don’t know</label>
</form>
</body>
</html>Example: an indeterminate progress bar
A <progress> element without a value attribute has no known completion percentage, so it matches :indeterminate. Browsers render an animated "working" bar by default. Adding a value attribute (and optionally max) moves the element into the determinate state and the CSS rule no longer applies.
<!DOCTYPE html>
<html>
<head>
<title>Indeterminate progress</title>
<style>
progress:indeterminate {
opacity: 0.6;
width: 200px;
}
/* Once a value is set, show a green accent */
progress:not(:indeterminate) {
accent-color: #2a9d8f;
width: 200px;
}
</style>
</head>
<body>
<p>Indeterminate (no value): <progress></progress></p>
<p>Determinate (value set): <progress value="60" max="100"></progress></p>
</body>
</html>The accent-color property controls the fill colour of <progress> in browsers that support it (Chrome 93+, Firefox 92+, Safari 15.4+). For older browser support you may need vendor-prefixed pseudo-elements such as ::-webkit-progress-bar and ::-moz-progress-bar, but these are not part of the standard CSS specification.
Combining with other pseudo-classes
:indeterminate can be chained with other state pseudo-classes to build precise selectors:
/* Indeterminate checkbox that is also focused */
input[type="checkbox"]:indeterminate:focus {
outline: 3px solid royalblue;
}
/* Indeterminate and disabled checkbox — show a muted style */
input[type="checkbox"]:indeterminate:disabled {
opacity: 0.4;
cursor: not-allowed;
}See :focus, :disabled, and :enabled for more details on those pseudo-classes.
Accessibility
The indeterminate flag is purely visual at the CSS level; it does not change the value submitted with the form. A tri-state checkbox still submits as either checked or unchecked, depending on the current checked state.
For assistive technology, you must expose the third state explicitly using the aria-checked="mixed" attribute on the master control. Without it, screen readers announce the control as simply checked or unchecked and users relying on AT will not know the partial-selection meaning.
<!-- Accessible tri-state master checkbox -->
<input
type="checkbox"
id="all"
aria-checked="mixed"
aria-label="Select all items"
/>Keep aria-checked in sync with the indeterminate property in your JavaScript:
function syncParent() {
var checked = [...children].filter(function (c) { return c.checked; }).length;
if (checked === 0) {
all.indeterminate = false;
all.checked = false;
all.setAttribute("aria-checked", "false");
} else if (checked === children.length) {
all.indeterminate = false;
all.checked = true;
all.setAttribute("aria-checked", "true");
} else {
all.indeterminate = true;
all.checked = false;
all.setAttribute("aria-checked", "mixed");
}
}For :required radio groups where no option has been chosen, consider also using aria-required="true" on each input so assistive technology can identify the unanswered group.
Browser support
The :indeterminate pseudo-class is supported in all modern browsers (Chrome, Edge, Firefox, Safari). All three element types — checkboxes, radio groups, and progress bars — are matched in current browser versions.
| Element | Notes |
|---|---|
input[type="checkbox"]:indeterminate | Supported in all modern browsers. Requires JS to set the indeterminate property. |
input[type="radio"]:indeterminate | Supported in all modern browsers. Automatically applies until the user selects an option. |
progress:indeterminate | Supported in all modern browsers. Applies automatically when the value attribute is absent. |