CSS :indeterminate Pseudo Clase
La pseudo-clase CSS :indeterminate selecciona elementos en estado indeterminado. Lee sobre ella y practica con ejemplos.
La pseudo-clase CSS :indeterminate coincide con controles de formulario que están en un estado indeterminado — un tercer estado que no es ni "activado" ni "desactivado". Este capítulo explica qué elementos pueden entrar en ese estado, cómo activarlo y cómo darle estilo, con ejemplos ejecutables para checkboxes, grupos de radio y barras de progreso.
Qué significa "indeterminate"
Algunos controles de formulario normalmente tienen dos estados (marcado / desmarcado, o un valor de progreso conocido). El estado indeterminado representa una tercera condición "indefinida". La pseudo-clase :indeterminate apunta a tres tipos de elementos:
- Checkboxes (
<input type="checkbox">) cuya propiedad DOMindeterminateestá establecida entrue. Este es el clásico checkbox tri-estado "algunos, pero no todos, los elementos hijos están seleccionados". - Botones de radio (
<input type="radio">) — cada botón en un grupo con el mismo nombre coincide mientras ninguna opción de ese grupo esté seleccionada. - Barras de progreso (
<progress>) que no tienen atributovalue, lo que significa que la duración de la tarea es desconocida y la barra se anima sin un relleno fijo.
El estado indeterminado se establece en JavaScript, no en CSS — no puedes entrar en él solo con una regla CSS. Para los checkboxes se establece element.indeterminate = true mediante JavaScript. Establecer esa propiedad no cambia checked; ambas son independientes. La apariencia visual indeterminada (normalmente un guion en lugar de una marca) y la coincidencia con :checked son cosas separadas. Reiniciar el formulario con form.reset() restablece el indicador indeterminate de un checkbox a false.
La pseudo-clase :checked da estilo al estado marcado, mientras que :indeterminate da estilo al estado indefinido. Un control puede estar en uno, en el otro, o en ninguno, pero nunca en ambos al mismo tiempo. Puedes combinar :indeterminate con otras pseudo-clases como :hover para dar estilo a un elemento indeterminado solo mientras el puntero está sobre él, o con :focus para resaltarlo cuando recibe el foco del teclado.
Sintaxis
:indeterminate {
/* css declarations */
}Delimita el selector a un tipo de elemento específico para evitar coincidencias no deseadas:
/* 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; }También puedes invertir la coincidencia con :not para apuntar a controles que han salido del estado indeterminado:
/* Style a progress bar once it has a known value */
progress:not(:indeterminate) { border: 2px solid green; }Ejemplo: dar estilo a un checkbox indeterminado
Un checkbox solo se vuelve indeterminado cuando JavaScript establece la propiedad indeterminate en true. La regla CSS se activa en el momento en que se establece esa propiedad, y se borra en el momento en que se devuelve a false. Este ejemplo resalta el control con un box-shadow de color.
<!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>Caso de uso común: un checkbox tri-estado "seleccionar todo"
La aplicación más práctica de :indeterminate es un checkbox maestro que resume una lista de opciones: marcado cuando todos los hijos están seleccionados, desmarcado cuando ninguno lo está, e indeterminado cuando algunos lo están. Este patrón es común en gestores de archivos, clientes de correo electrónico y tablas de datos.
La lógica JavaScript mantiene las propiedades indeterminate y checked del checkbox maestro sincronizadas con los checkboxes hijos. CSS se encarga del resto.
<!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>Marca uno de los checkboxes hijos: el checkbox "Select all" se vuelve indeterminado y aparece el contorno verde. Marca los tres: el contorno desaparece y pasa al estado completamente marcado.
Ejemplo: un grupo de radio indeterminado
Un grupo de radio está en estado indeterminado mientras ninguna de sus opciones está seleccionada. La pseudo-clase :indeterminate coincide con cada <input type="radio"> del grupo hasta que el usuario elige una respuesta. Una vez que se elige cualquier opción, todo el grupo sale del estado indeterminado y el estilo se elimina.
Esto es útil para indicar a los usuarios que deben hacer una elección — por ejemplo, puedes usarlo en la validación de formularios junto con :invalid y :valid para guiar visualmente a los usuarios hacia las preguntas obligatorias sin responder.
<!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>Ejemplo: una barra de progreso indeterminada
Un elemento <progress> sin atributo value no tiene porcentaje de finalización conocido, por lo que coincide con :indeterminate. Los navegadores renderizan una barra animada de "trabajando" por defecto. Añadir un atributo value (y opcionalmente max) mueve el elemento al estado determinado y la regla CSS deja de aplicarse.
<!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>La propiedad accent-color controla el color de relleno de <progress> en los navegadores que la admiten (Chrome 93+, Firefox 92+, Safari 15.4+). Para compatibilidad con navegadores más antiguos puede que necesites pseudo-elementos con prefijo de proveedor como ::-webkit-progress-bar y ::-moz-progress-bar, pero estos no forman parte de la especificación CSS estándar.
Combinación con otras pseudo-clases
:indeterminate se puede encadenar con otras pseudo-clases de estado para crear selectores precisos:
/* 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;
}Consulta :focus, :disabled y :enabled para más detalles sobre esas pseudo-clases.
Accesibilidad
El indicador indeterminado es puramente visual en el nivel CSS; no cambia el valor enviado con el formulario. Un checkbox tri-estado se sigue enviando como marcado o desmarcado, dependiendo del estado actual de checked.
Para la tecnología de asistencia, debes exponer el tercer estado explícitamente usando el atributo aria-checked="mixed" en el control maestro. Sin él, los lectores de pantalla anuncian el control simplemente como marcado o desmarcado y los usuarios que dependen de AT no sabrán el significado de la selección parcial.
<!-- Accessible tri-state master checkbox -->
<input
type="checkbox"
id="all"
aria-checked="mixed"
aria-label="Select all items"
/>Mantén aria-checked sincronizado con la propiedad indeterminate en tu 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");
}
}Para grupos de radio :required donde no se ha elegido ninguna opción, considera también usar aria-required="true" en cada input para que la tecnología de asistencia pueda identificar el grupo sin responder.
Compatibilidad con navegadores
La pseudo-clase :indeterminate es compatible con todos los navegadores modernos (Chrome, Edge, Firefox, Safari). Los tres tipos de elementos — checkboxes, grupos de radio y barras de progreso — coinciden en las versiones actuales de los navegadores.
| Elemento | Notas |
|---|---|
input[type="checkbox"]:indeterminate | Compatible con todos los navegadores modernos. Requiere JS para establecer la propiedad indeterminate. |
input[type="radio"]:indeterminate | Compatible con todos los navegadores modernos. Se aplica automáticamente hasta que el usuario selecciona una opción. |
progress:indeterminate | Compatible con todos los navegadores modernos. Se aplica automáticamente cuando el atributo value está ausente. |