---
title: "Grupo de casillas de verificación"
description: "Hace una pregunta que puede tener varias respuestas entre unas pocas opciones que se muestran todas a la vez. Es un conjunto de casillas de verificación bajo una sola etiqueta, con una descripción y un solo error, que guarda la lista de los valores marcados con un solo nombre y puede traer una casilla que las marca todas."
url: "https://tanrei.chnotchy.com/es/components/checkbox-group/"
last_updated: 2026-10-23T21:17:12.000Z
---

# Grupo de casillas de verificación

> Hace una pregunta que puede tener varias respuestas entre unas pocas opciones que se muestran todas a la vez. Es un conjunto de casillas de verificación bajo una sola etiqueta, con una descripción y un solo error, que guarda la lista de los valores marcados con un solo nombre y puede traer una casilla que las marca todas.

## Descripción general

Usa un grupo de casillas de verificación donde una pregunta tiene una lista por respuesta y las opciones son lo bastante pocas para mostrarlas todas a la vez, hasta unas siete: las formas de recibir avisos, los días en que se envía un informe, los tipos por los que se filtra una lista. La respuesta es un solo valor (la lista de los valores marcados), y un formulario la envía con un solo nombre.

**Se compone de** un [grupo de campos](https://tanrei.chnotchy.com/es/components/field-group/) y [casillas de verificación](https://tanrei.chnotchy.com/es/components/checkbox/). El grupo de campos es el marco: la etiqueta, la marca de obligatorio o de opcional, la descripción, el error y la disposición en una columna o en una fila. Las casillas contienen el valor: cada una es una opción con su propia etiqueta, y cuáles de ellas están marcadas es la lista. El grupo de casillas de verificación no dibuja nada propio y no tiene ningún token. Lo que añade a los dos es el estado que, de otro modo, escribe cada producto: la lista y un solo `change` para ella, un solo nombre en un formulario, con el restablecimiento y la validación propia del formulario de que un conjunto obligatorio no está vacío, y los tres estados de una casilla de seleccionar todo.

Es tan ancho como el lugar en el que se pone.

En la web es `<tanrei-checkbox-group>`. Las demás plataformas todavía no lo han implementado.

### Uso

Cada casilla es la opción que nombra su `value`. El `value` del grupo es la lista de los valores marcados, en el orden de las casillas. Un valor que está en la lista y al que no corresponde ninguna casilla se conserva: las opciones que una búsqueda ha dejado fuera de la vista siguen elegidas.

**Web**

```html
<form>
  <tanrei-checkbox-group name="notify" label="Avísame por"
    supporting-text="Elige todas las opciones que correspondan."
    required required-text="Obligatorio"
    error-text="Elige al menos una forma de contacto.">
    <tanrei-checkbox value="email" label="Correo electrónico" checked></tanrei-checkbox>
    <tanrei-checkbox value="text" label="Mensaje de texto"></tanrei-checkbox>
    <tanrei-checkbox value="push" label="Notificación push"></tanrei-checkbox>
  </tanrei-checkbox-group>
</form>

<script type="module">
  import '@tanrei/web/components/checkbox-group';

  const group = document.querySelector('tanrei-checkbox-group');
  group.addEventListener('change', (event) => {
    // event.detail.value es la lista nueva; group.value ya es la misma.
    group.error = event.detail.value.length === 0;
  });
  // El producto define la respuesta como una lista. Es una propiedad: no hay atributo value.
  group.value = ['email', 'push'];
</script>
```

**React**

```tsx
import { Checkbox, CheckboxGroup } from '@tanrei/react';

const [channels, setChannels] = useState(['email']);

<CheckboxGroup name="notify" label="Avísame por" supportingText="Elige todas las opciones que correspondan."
               value={channels} onChange={(event) => setChannels(event.detail.value)}
               error={channels.length === 0} errorText="Elige al menos una forma de contacto.">
  <Checkbox value="email" label="Correo electrónico" />
  <Checkbox value="text" label="Mensaje de texto" />
  <Checkbox value="push" label="Notificación push" />
</CheckboxGroup>
```

En el marcado, el primer valor se escribe donde lo escribe HTML: con `checked` en las casillas. Una vez que el producto ha definido `value`, es `value` el que decide qué casillas están marcadas.

### Una casilla que las marca todas

Da `selectAllLabel`, y el grupo trae una casilla más antes de la primera, que recibe su nombre de ese texto. Está marcada mientras todas las casillas que la persona puede cambiar están marcadas, indeterminada mientras lo están algunas, y sin marcar mientras no lo está ninguna. Si se pulsa mientras está sin marcar o indeterminada, las marca todas; si se pulsa mientras está marcada, las desmarca todas. No es una entrada de la lista y no envía nada.

```html
<tanrei-checkbox-group name="days" label="Enviar el informe los" select-all-label="Todos los días laborables">
  <tanrei-checkbox value="mon" label="Lunes"></tanrei-checkbox>
  <tanrei-checkbox value="tue" label="Martes"></tanrei-checkbox>
  …
</tanrei-checkbox-group>
```

Una casilla que deshabilitaste tú se deja como está, marcada o no, y no se cuenta: la casilla de seleccionar todo solo cuenta las opciones que la persona puede cambiar. Escribe la etiqueta para este conjunto («Todas estas», «Todos los días laborables») con las palabras de la pregunta; nunca se lee de los textos del producto.

### Un nivel más abajo

El mismo conjunto, escrito a mano con sus dos miembros, es la misma imagen y el mismo grupo para la tecnología de apoyo:

```html
<tanrei-field-group label="Avísame por" supporting-text="Elige todas las opciones que correspondan."
                    required required-text="Obligatorio">
  <tanrei-checkbox name="notify" value="email" label="Correo electrónico" checked></tanrei-checkbox>
  <tanrei-checkbox name="notify" value="text" label="Mensaje de texto"></tanrei-checkbox>
  <tanrei-checkbox name="notify" value="push" label="Notificación push"></tanrei-checkbox>
</tanrei-field-group>
```

Lo que el grupo de campos y las casillas escritos a mano no hacen es guardar la respuesta:

| Aspecto | Escrito a mano | El grupo de casillas de verificación |
| --- | --- | --- |
| La lista | El producto la lee de cada casilla | `value`, y un solo `change` |
| Definir la respuesta | `checked` en cada casilla | `value = […]` |
| Restablecer y restaurar | Cada casilla por su cuenta | Un solo control |
| Un conjunto obligatorio que se deja vacío | Lo comprueba el producto | La validación propia del formulario lo rechaza |
| Un valor cuya casilla no se muestra | Se pierde | Se conserva |
| Una casilla que las marca todas | La escribe el producto, con `indeterminate` | `selectAllLabel` |
| Lo que envía un formulario | Las mismas entradas | Las mismas entradas |

Baja a ese nivel cuando el conjunto no es una sola respuesta:

| Necesitas | Usa |
| --- | --- |
| Varias de unas pocas opciones como una sola respuesta con un solo nombre | El grupo de casillas de verificación |
| Casillas que tienen cada una su propio nombre en el formulario, o un conjunto que contiene algo que no es una casilla | Un [grupo de campos](https://tanrei.chnotchy.com/es/components/field-group/) con [casillas de verificación](https://tanrei.chnotchy.com/es/components/checkbox/) dentro |
| Tu propia regla para lo que significa «todo»: todos los elementos de una página, todos los elementos que coinciden | Un [grupo de campos](https://tanrei.chnotchy.com/es/components/field-group/) con una casilla en la que defines `indeterminate` tú |
| Una de unas pocas | Un [grupo de botones de opción](https://tanrei.chnotchy.com/es/components/radio-group/) |
| Una sola cosa activada o desactivada | Una [casilla de verificación](https://tanrei.chnotchy.com/es/components/checkbox/), o un [interruptor](https://tanrei.chnotchy.com/es/components/switch/) donde tiene efecto de inmediato |
| Varias de muchas, o poco sitio | Un [select](https://tanrei.chnotchy.com/es/components/select/) o una lista con selección múltiple |

## Especificaciones

### Qué aporta cada miembro

| Aspecto | Del grupo de campos | De las casillas de verificación | Propio del grupo |
| --- | --- | --- | --- |
| Propiedades | `label`, `labelHidden`, `supportingText`, `errorText`, `error`, `disabled`, `required`, `optional`, `requiredMark`, `requiredText`, `optionalText`, `orientation` | El `value`, el `label` y el `disabled` de cada casilla, en la casilla | `value`, `selectAllLabel` |
| Eventos | — | Ninguno: una casilla dentro de un grupo no emite nada propio | `change` |
| Tokens | `comp.field-group.*` | `comp.checkbox.*` | Ninguno |

- **Obligatorio** significa al menos una. Un número mínimo o máximo («elige hasta tres») es una regla tuya: lee la longitud de la lista en `change`, activa `error` y escribe `errorText` con el número, con tus propias palabras.
- **El error** es del conjunto. `error` dibuja `errorText` debajo de la última casilla y pone cada casilla en su propio estado de error. El grupo nunca lo activa, y un cambio de valor no lo borra. Una sola casilla en error dentro de un grupo que no lo está es el `error` de esa casilla.
- **Deshabilitado.** Un grupo deshabilitado conserva su valor y no envía nada. Una casilla deshabilitada por separado en un grupo habilitado conserva su entrada y se envía con ella: una opción que siempre está activada es una casilla deshabilitada y marcada, sin ningún campo oculto al lado.
- No hay estado de solo lectura, porque una casilla de verificación no tiene ninguno.

Lo que hace el marco (la marca de obligatorio, la descripción, el error y cómo se anuncia, la columna y la fila que salta de línea) está en la [página del grupo de campos](https://tanrei.chnotchy.com/es/components/field-group/). Lo que hace una casilla está en la [página de la casilla de verificación](https://tanrei.chnotchy.com/es/components/checkbox/).

### Diferenciar un solo grupo

El grupo de casillas de verificación no tiene ningún token propio. Su marco lee los tokens del grupo de campos y sus casillas leen los de la casilla de verificación, se definan donde se definan. Para cambiar una sola instancia, define en ella el token del miembro:

```html
<tanrei-checkbox-group label="Avísame por"
  style="--tanrei-comp-field-group-gap-controls: 12px">…</tanrei-checkbox-group>
```

- Category: Selection
- Level: composed
- Listing: default
- Built from: Field group, Checkbox
- Page: https://tanrei.chnotchy.com/components/checkbox-group/
- Spec: https://github.com/chnotchy/tanrei/blob/main/specs/checkbox-group.spec.json

### Availability

| Design system | Has it | Notes |
| --- | --- | --- |
| Tanrei (`tanrei`) | Yes | — |
| Cupertino (Apple HIG-inspired) (`cupertino`) | Yes | — |
| M3 Expressive-like (Material 3 Expressive-inspired) (`m3-expressive-like`) | Yes | — |
| M3-like (Material 3-inspired) (`m3-like`) | Yes | — |

### Platforms

- Web: the element `<tanrei-checkbox-group>`, registered by importing `@tanrei/web` (or only its own module, `@tanrei/web/components/checkbox-group`).
- React: `CheckboxGroup` from `@tanrei/react`.

| Platform | Name | Status |
| --- | --- | --- |
| Web | `tanrei-checkbox-group` | lagging |
| SwiftUI | `TanreiCheckboxGroup` | planned |
| Jetpack Compose | `TanreiCheckboxGroup` | planned |
| Flutter | `TanreiCheckboxGroup` | planned |
| React Native | `CheckboxGroup` | planned |

Names a platform gives in its own idiom:

| Spec | Web | SwiftUI | Jetpack Compose | Flutter | React Native |
| --- | --- | --- | --- | --- | --- |
| `value` (prop) | `value` | `selection` | `values` | `values` | `value` |
| `label` (prop) | `label` | `label` | `label` | `labelText` | `label` |
| `labelHidden` (prop) | `labelHidden` | `labelsHidden` | `labelHidden` | `labelHidden` | `labelHidden` |
| `supportingText` (prop) | `supportingText` | `supportingText` | `supportingText` | `helperText` | `supportingText` |
| `error` (prop) | `error` | `error` | `isError` | `isError` | `error` |
| `disabled` (prop) | `disabled` | `disabled` | `enabled` | `enabled` | `disabled` |
| `required` (prop) | `required` | `isRequired` | `required` | `required` | `required` |
| `optional` (prop) | `optional` | `isOptional` | `optional` | `optional` | `optional` |
| `change` (event) | `change` | `selection` | `onValuesChange` | `onChanged` | `onValueChange` |

Statuses: `planned` — In the inventory. Nothing shippable exists yet. `lagging` — An implementation of an earlier spec exists; it has not caught up with the current one. `alpha` — Implemented with tests. The API may still change. `beta` — Feature complete and at parity. Only fixes are expected. `stable` — Covered by semantic versioning. `deprecated` — Still shipped; scheduled for removal.

### Anatomy

- `label`: The field group's label: names the set — the question — above the checkboxes. The requirement mark follows it.
- `requirement-label` (optional): The field group's requirement mark: a small tag that is not a control, saying an answer is required or optional; or, for required, an asterisk after the label.
- `supporting-text` (optional): The field group's description: a line under the label, before the checkboxes, that says how to answer. It stays while the group is in error.
- `select-all` (optional): One more checkbox before the first, brought by the group where selectAllLabel is given: the Checkbox component, checked, unchecked or indeterminate from the checkboxes of the set, and checking or unchecking them all. It is no entry of the value.
- `checkboxes`: The checkboxes of the set, in the order given, in a column or in a row that wraps. Each is the Checkbox component with its own label, drawn from its own tokens; which of them are checked is the group's value.
- `error-text` (optional): The field group's error text: the reason the answer was refused or is missing, below the last checkbox.

### Props

| Prop | Type | Default | Web attribute | Description |
| --- | --- | --- | --- | --- |
| `value` | `strings` | `` | — | The values of the checked checkboxes: a list of strings, empty while none is checked (the platforms whose idiom it is take a set). The checkbox group sets it when a person switches a checkbox, and emits change; the product may also set it. It is the set's value: what a form submits under the group's one name. |
| `label` | `string` | `""` | `label` | Names the set, visibly and to assistive technology: the question its controls answer ("Notify me by"). Required. (From Field group.) |
| `labelHidden` | `boolean` | `false` | `label-hidden` | Does not draw the label, which stays the accessible name of the group. For a set whose visible heading is elsewhere. (From Field group.) |
| `supportingText` | `string` | `""` | `supporting-text` | The description: how to answer ("Choose all that apply"), drawn under the label and given to assistive technology as the group's description. (From Field group.) |
| `errorText` | `string` | `""` | `error-text` | What is wrong with the set as a whole and how to put it right ("Choose at least one"), drawn below the controls while error is true. (From Field group: On the web it is also what the form's own validation says of a required group left empty (CKG-8).) |
| `error` | `boolean` | `false` | `error` | Puts the set in the error condition: the error text is drawn, and each selection control that has an error condition of its own (a checkbox, a radio button) is drawn in it. What an answer means is the product's to validate; the group never sets this itself. One control of a valid group may still be in error by itself. (From Field group: Every checkbox of the set is drawn in its own error condition and exposed as invalid.) |
| `disabled` | `boolean` | `false` | `disabled` | Disables every control of the set and draws the frame as disabled. (From Field group: The value is kept, and on the web it is not submitted (CKG-6, CKG-8).) |
| `required` | `boolean` | `false` | `required` | What the form asks of the set: true exposes it as required and draws the required mark beside the label. It never validates: a missing answer is the product's to report with error. (From Field group: means at least one checkbox checked: on the web an empty list is reported to the form's own validation (CKG-8).) |
| `optional` | `boolean` | `false` | `optional` | Draws the optional tag after the label and adds optionalText to the group's description, because assistive technology has no state for it. Ignored while required is true: a set is never both. (From Field group.) |
| `requiredMark` | `enum`: `auto`, `tag`, `asterisk`, `none` | `"auto"` | `required-mark` | How this set shows that it is required: tag is a small filled Tag with requiredText, asterisk is "*" after the label, none draws nothing and keeps the semantics. Auto takes comp.field-group.requirement.mark, which follows sys.behavior.requirement-mark, the system's one answer. Ignored unless required is true. (From Field group.) |
| `requiredText` | `string` | `""` | `required-text` | The text of the required tag, in the product's language ("Required", "必須"). The product's: Tanrei has no word for it. Empty reads the product's words; with neither, FGR-24 says what is drawn. (From Field group.) |
| `optionalText` | `string` | `""` | `optional-text` | The text of the optional tag, in the product's language ("Optional", "任意"); also read to assistive technology as part of the description. The product's: Tanrei has no word for it. Empty reads the product's words. (From Field group.) |
| `orientation` | `enum`: `vertical`, `horizontal` | `"vertical"` | `orientation` | vertical stacks the controls in one column, the default and the form to prefer: every label starts on one edge and is read down. horizontal lays them out in a row that wraps, for two or three short options or a row of fields. (From Field group.) |
| `selectAllLabel` | `string` | `""` | `select-all-label` | Not empty: the group brings one more checkbox before the first, labelled by this text ("All of these", "Every weekday"), that checks or unchecks every enabled checkbox of the set at once and shows checked, unchecked or indeterminate from them (CKG-12, CKG-13). Empty, the default: there is none. The instance's own words: it names this set, so it is not read from the product's words. |

### Events

| Event | Description |
| --- | --- |
| `change` | The person switched a checkbox of the set, or activated the select-all checkbox; carries the new list, which the group already holds. Emitted once per action, by the group and not by its checkboxes. Not emitted when the product sets value. |

### Item: `checkbox`

A choice of the set: the Checkbox component with its own label, checked by the group's value, placed, spaced, disabled and put in its error condition by the group's frame, otherwise drawn and pressed as it is anywhere.

It is a Checkbox.

Names: Web `tanrei-checkbox`, SwiftUI `TanreiCheckbox`, Jetpack Compose `TanreiCheckbox`, Flutter `TanreiCheckbox`, React Native `Checkbox`.

| Prop | Type | Default | Web attribute | Description |
| --- | --- | --- | --- | --- |
| `value` | `string` | `""` | `value` | What the choice stands for: an entry of the group's value while this checkbox is checked. Unique in the group and not empty. The group reads it from the item; it is not a prop of the Checkbox (on the web, the checkbox's own value attribute). |
| `label` | `string` | `""` | `label` | Its own label, as the Checkbox's label. |
| `disabled` | `boolean` | `false` | `disabled` | As the Checkbox's disabled: one checkbox of an enabled group may be disabled by itself. The person cannot switch it, the select-all checkbox leaves it alone, and its entry stays in the value while the value holds it. |

### States

`enabled`, `disabled`, `error`

### Accessibility

- Role: `group`
- Labeling: label names the group, drawn or not (labelHidden). Each checkbox keeps its own name, its own label beside it; the select-all checkbox is named by selectAllLabel. The group's description is the field group's: supportingText, then errorText while error is true, then requiredText while the group is required, then optionalText while it is optional (FGR-11). A group has no required and no invalid state: the requirement is said in the product's words, and each checkbox of a group in error is exposed as invalid and described by errorText. How many are checked is not said unless the product writes it in supportingText.
- Minimum target size: yes

| Keys | Action |
| --- | --- |
| Tab / Shift+Tab | Moves focus to each enabled checkbox of the set in the order given — the select-all checkbox first where there is one — and out of the group. Every checkbox is a stop; the group is not (CKG-5). |
| Space | Switches the focused checkbox (CHK-1); on the select-all checkbox, checks every enabled checkbox of the set, or unchecks them all where all were checked (CKG-13). |
| Arrow keys | Nothing: a set of checkboxes has no arrow keys on any platform, because moving among them must not change them and each is a stop already. |
| Enter | Nothing on the web, where it submits the form; switches the focused checkbox where the platform activates any focused control with Enter (Android, tvOS), as CHK-1 says. |

- Not a target itself: the group takes no press and is not a focus stop (CKG-5), so minimumTarget holds through its checkboxes, each of which keeps a whole touch target of its own at the distance the field group puts between them (CHK-7, FGR-3).
- The checkbox group is a field group of checkboxes that owns their value (composed: the field group frames it, its checkboxes hold its value). What it adds to the two is what every product otherwise writes: the list of checked values and one change for it, one name in a form with reset and the form's own check that a required set is not empty, and the select-all checkbox's three states. It adds no look and has no tokens of its own.
- One level down is always open: a field group around checkboxes, each checked by the product, is the same picture, the same group, the same names, descriptions and states. Take it where each checkbox must have a name of its own in the form, where the set holds something that is not a checkbox, or where the product wants its own rule for what "all" means.
- Beside the radio group. A radio group has one answer, so it is one stop in the focus order, its arrow keys move and select, and its role has a required and an invalid state. A checkbox group has a list: every checkbox is a stop and Space switches it, there are no arrow keys, and its role is the plain group's — the requirement is said in words and each checkbox says invalid for itself. A radio group in error leaves its rings as they are; a checkbox group in error marks every box, because a checkbox has an error condition and a radio button has none.
- required means "at least one". A least or a greatest number of choices ("choose up to three") is the product's rule: it reads the length of the list in change, sets error and writes errorText with the number in its own words, or disables what may no longer be chosen. The group knows no number, because it would have no words to say it with.
- The select-all checkbox stands for the checkboxes the group holds and a person can switch, never for choices the product has not given it. It is one more stop before the set. It is not the place for a hierarchy: choices inside choices are a tree.
- A disabled group is not submitted; a checkbox disabled by itself in an enabled group keeps its entry in the value and is submitted with it. A choice that is always on ("Email, required for your account") is therefore one disabled, checked checkbox, and needs no hidden field beside it.
- When a checkbox group, and when something else. Several of a few choices that are all worth seeing at once, in a form or a setting: a checkbox group, of up to about seven. One of a few: a radio group. One thing on or off, on its own: a checkbox, or a switch where it takes effect at once. Several of many, or where there is little room: a list with multiple selection, or a select. Rows of a table chosen for an action: the table's own selection. Things on or off that are not one answer — each with a name of its own in the form — or a set of switches: a field group.
- On the web the group's shadow root holds the field group's own element around a slot, and the checkboxes stay the product's children: the group is the field group's `fieldset` with its `legend`, exposed once.
- Heights are the texts' and the checkboxes': at 200% text size every text wraps, a row becomes a column, and nothing is cut (WCAG 1.4.4, 1.4.10).
- How a state looks is a value decision of the active design system; components read the state's tokens and never compute the effect themselves.

### Behaviour

Requirements in EARS form, each with its stable id.

- **CKG-1** THE checkbox group SHALL be a field group whose spacing is controls, around its checkboxes: the field group itself, with the field group's own tokens, measures and sentences, and no copy of it — its rows, their order and their gaps (FGR-1), the label (FGR-2), the requirement, its mark and its words (FGR-4, FGR-5, FGR-6, FGR-24), the description, which stays while the group is in error (FGR-7), the error text (FGR-8), the column and the row that wraps (FGR-3, FGR-13), the disabled frame (FGR-14) and what an unknown value of required, optional, requiredMark or orientation does (FGR-22), each from the prop of the same name, which is the field group's. It SHALL have no look and no token of its own: it SHALL draw no container, edge, fill or elevation, SHALL add no padding around its rows and no distance between its checkboxes but the field group's, and SHALL leave each checkbox drawn from the checkbox's own tokens at the checkbox's own size (CHK-4, CHK-12, CHK-15).
- **CKG-2** THE checkbox group SHALL hold one value, a list of strings, and show as checked exactly those of its checkboxes whose value is in the list; WHILE the list is empty none SHALL be checked. WHEN the product sets value THE checkbox group SHALL check and uncheck its checkboxes to match, each over the checkbox's own selection motion (CHK-13), without emitting change and without moving focus. THE checkbox group SHALL keep in value an entry that is the value of none of its checkboxes, because the checkbox it belongs to may arrive later or be out of the set for now (a list of choices narrowed by a search), and SHALL give it back with the rest in change (CKG-4) and to a form (CKG-8). IF value holds one entry twice THEN THE checkbox group SHALL treat it as held once; IF two checkboxes carry one value THEN both SHALL be shown checked or unchecked together, and switching either SHALL switch the entry; IF a checkbox carries no value THEN it SHALL be drawn unchecked and a press on it SHALL change nothing and emit nothing, because the list has no entry to stand for it; and in debug builds THE checkbox group SHALL report each of the three.
- **CKG-3** WHERE the platform lets the product mark one checkbox as checked by itself (on the web, checked on a child element) and the product has not set value, THE checkbox group SHALL take as its value the values of the checkboxes so marked, in the order given, so that markup that says checked on some of its checkboxes is a group with that value, and a checkbox that arrives so marked SHALL add its value to the list. WHEN the product has set value, value SHALL decide: a checkbox that is first drawn or arrives marked against it SHALL be shown as value says. WHEN the product sets or clears checked on a checkbox the checkbox group already holds THE checkbox group SHALL add that checkbox's value to the list or take it out, without emitting change, so that the two never disagree.
- **CKG-4** WHEN the person switches one of the group's checkboxes — by pointer, touch, Space, a click on its label or assistive technology, as CHK-1 says of the checkbox — THE checkbox group SHALL add that checkbox's value to the list where the checkbox became checked and take it out where it became unchecked, and SHALL emit change exactly once, carrying the new list, with value already set when the event is received. The list it then holds SHALL be the values of its checked checkboxes in the order the checkboxes are given, not the order in which they were checked, followed by the entries that belong to no checkbox, in the order they had. No other checkbox SHALL change, and nothing the checkbox emits SHALL reach a listener on the group as a second change (CHK-23). A person can empty the list: unchecking the last checked checkbox emits change with an empty list.
- **CKG-5** THE checkbox group SHALL leave every enabled checkbox its own place in the focus order, in the order given, as the platform has it for checkboxes: Tab and Shift+Tab SHALL move from one checkbox to the next and out of the group at either end, and THE checkbox group SHALL handle no key — no arrow key moves focus or checks, and there is no single stop and no roving focus — because each checkbox is a choice of its own that a person may stop at and switch; Space is the checkbox's (CHK-1), and Enter is left to the form on the web. THE checkbox group SHALL NOT be a stop itself, and a press on its label, its requirement mark, its description or its error text SHALL check nothing and move no focus into the group (FGR-12). WHEN the product asks the group itself for focus, where the platform has such a call, THE checkbox group SHALL give focus to the first enabled checkbox it shows, the select-all checkbox where one is drawn.
- **CKG-6** WHILE disabled is true THE checkbox group SHALL be a disabled field group (FGR-14): its frame drawn from the field group's disabled tokens and every checkbox it holds disabled as the checkbox's own disabled does (CHK-3, CHK-8), the checked ones still shown checked; it SHALL emit no change and SHALL keep value. WHILE disabled is false THE checkbox group SHALL NOT enable a checkbox the product disabled itself: that one keeps its entry in the list WHILE the list holds it, is shown checked or unchecked as value says, cannot be switched by the person, and is left as it is by the select-all checkbox (CKG-13). THE checkbox group SHALL have no read-only state, because a checkbox has none and a composed component adds no state its members have not.
- **CKG-7** THE checkbox group SHALL take from required and optional what the form asks of the set, as a field group does (FGR-4): required means that the list holds at least one entry.
- **CKG-8** WHERE the platform has forms that submit named values (the web) THE checkbox group SHALL take part in its form as one control: WHILE it is not disabled it SHALL submit one entry under its own name for each entry of value, in the list's order, and nothing WHILE the list is empty — what checkboxes of one name submit by themselves, so that whatever receives the form reads the same list from either; SHALL return to the value it was first given WHEN the form is reset, without emitting change; SHALL be disabled WHILE the form disables the set it is in; SHALL report a missing value to the form's own validation WHILE required is true and the list is empty, in the product's words, because the platform has a message for one unchecked checkbox and none for a set; and its checkboxes, the select-all checkbox among them, SHALL submit nothing of their own, whatever name they carry (CHK-23), so that no entry is submitted twice. A checkbox the product disabled itself SHALL still have its entry submitted WHILE the list holds it, because the value is the group's and that checkbox's disabled says only that the person cannot switch it. WHERE the platform has no such forms value and change are the whole of it.
- **CKG-9** THE checkbox group SHALL be exposed to assistive technology as the field group it is, and SHALL add nothing to it: one group that contains its checkboxes, named by label whether or not the label is drawn (FGR-10); described by supportingText, errorText WHILE error is true, requiredText WHILE required is true and optionalText WHILE optional is true (FGR-11); and disabled WHILE disabled is true (FGR-14). A group has neither a required nor an invalid state, so the requirement is said in the product's words and each checkbox says invalid for itself. Each checkbox SHALL expose its own role, its name and its checked state (CHK-14), and the select-all checkbox its mixed state WHILE it is indeterminate. THE checkbox group SHALL NOT expose a second group around the first, SHALL give its checkboxes no position in a set, and SHALL NOT say how many are checked: a product that wants the count said writes it in supportingText.
- **CKG-10** WHEN a checkbox is added to the group, removed from it or moved, or its value or its disabled changes, while the group is shown THE checkbox group SHALL be laid out again as a field group is (FGR-23); SHALL check by value again (CKG-2), so that a checkbox that arrives with a value the list holds is shown checked, and SHALL keep the entry of a checked checkbox that leaves; SHALL set the select-all checkbox again (CKG-12); SHALL emit no change; and SHALL leave focus on the checkbox that has it.
- **CKG-11** THE checkbox group SHALL hold checkboxes and nothing else that can be operated. IF it holds no checkbox THEN THE checkbox group SHALL draw its rows without one and in debug builds report it (FGR-20); IF it holds another control, or a group of controls inside it, THEN in debug builds THE checkbox group SHALL report it and SHALL make no entry of its value from what that one holds. THE checkbox group SHALL NOT restyle a checkbox or change how it answers hover, focus or a press (CHK-5 to CHK-10), and SHALL NOT set indeterminate on a checkbox of the set: a checkbox of the set is in the list or it is not.
- **CKG-12** WHILE selectAllLabel is not empty THE checkbox group SHALL bring one more checkbox, the select-all checkbox: the Checkbox component itself, labelled by selectAllLabel, placed before the first checkbox of the set as one more control of the field group — the same distance from the next, in a column or in a row, the set not indented under it — and so disabled and put in its error condition with the others (CKG-6, CKG-7). It SHALL stand for the checkboxes of the set that the product did not disable one by one: checked WHILE each of them is checked, indeterminate WHILE some are and some are not, unchecked WHILE none is (CHK-4), and disabled WHILE there is no such checkbox. It SHALL have no value, SHALL be no entry of the list, and SHALL follow every change of value and of the set at once, whoever made the change. WHILE selectAllLabel is empty THE checkbox group SHALL bring none.
- **CKG-13** WHEN the person activates the select-all checkbox while it is unchecked or indeterminate THE checkbox group SHALL check every checkbox of the set that the product did not disable itself, as CHK-2 says of an indeterminate checkbox, and while it is checked SHALL uncheck every one of them; a checkbox the product disabled SHALL stay as it is, and an entry of value that belongs to no checkbox SHALL stay in the list. THE checkbox group SHALL emit change exactly once, carrying the list after the change (CKG-4), and nothing of the select-all checkbox's own SHALL reach a listener on the group.
- **CKG-14** THE checkbox group SHALL add nothing of its own to how its members answer the layout direction, enlarged text and forced colors. WHERE the layout direction is right-to-left it SHALL be mirrored as the field group is (FGR-18), SHALL take its direction from where it is used and follow a change of it at once (FGR-19), keeping its value and its focus, and SHALL lay out each of its texts by that text's own direction (FGR-21), each checkbox mirroring itself (CHK-15, CHK-20, CHK-21); it has no key to exchange (CKG-5). WHILE the text size is enlarged it SHALL grow as the field group does (FGR-16): every text wraps, a row becomes a column where one line no longer holds two checkboxes, and the boxes keep their size. WHERE the platform draws with a person's own forced colors it SHALL be drawn as the field group is there (FGR-17), the error carried by the words of errorText and by each checkbox's invalid state, never by a color of the group's.
- **CKG-15** WHERE the platform has ARIA attributes (the web), WHEN the product sets an aria- attribute on the host, by the attribute or by the ARIA property that reflects it, THE checkbox group SHALL move it to the data-aria- attribute of the same name, so that the host is no second node with its name, description or state, and SHALL expose it on the group of the field group it is (CKG-9) — an id reference by the elements it names in the host's tree or a tree around it, a description after its own — except those the checkbox group owns (aria-label, aria-labelledby, aria-disabled, aria-required, aria-invalid, aria-errormessage), which it SHALL move and not expose: its name is label and its states are its props (CKG-9). WHEN the product removes the aria- attribute or its data-aria- twin, or sets the ARIA property that reflects it (ariaLabel, ariaDescribedByElements…) to null, THE checkbox group SHALL take it off that element. WHEN the product sets an aria- attribute the checkbox group owns THE checkbox group SHALL report it in debug builds.

### Tokens

As written in the token documents (DTCG): an alias in braces names the token it takes its value from, which each design system resolves. On the web each token is a CSS custom property: `--tanrei` and the name after `tanrei`, joined by hyphens (`tanrei.comp.field-group.…` → `--tanrei-comp-field-group-…`).

#### `tanrei.comp.field-group`

| Token | Type | Value |
| --- | --- | --- |
| `comp.field-group.label.typescale` | `typography` | `{tanrei.sys.typescale.label}` |
| `comp.field-group.label.gap` | `dimension` | `{tanrei.sys.space.space100}` |
| `comp.field-group.requirement.mark` | `number` | `{tanrei.sys.behavior.requirement-mark}` |
| `comp.field-group.requirement.gap` | `dimension` | `{tanrei.sys.space.space100}` |
| `comp.field-group.requirement.asterisk-gap` | `dimension` | `{tanrei.sys.space.space25}` |
| `comp.field-group.supporting-text.typescale` | `typography` | `{tanrei.sys.typescale.label-small}` |
| `comp.field-group.supporting-text.gap` | `dimension` | `{tanrei.sys.space.space50}` |
| `comp.field-group.error-text.gap` | `dimension` | `{tanrei.sys.space.space50}` |
| `comp.field-group.gap.controls` | `dimension` | `{tanrei.sys.space.space100}` |
| `comp.field-group.gap.fields` | `dimension` | `{tanrei.sys.space.space300}` |
| `comp.field-group.gap-inline` | `dimension` | `{tanrei.sys.space.space200}` |
| `comp.field-group.field.min-width` | `dimension` | `144px` |
| `comp.field-group.standard.label.color` | `color` | `{tanrei.sys.color.on-surface}` |
| `comp.field-group.standard.supporting-text.color` | `color` | `{tanrei.sys.color.on-surface-variant}` |
| `comp.field-group.standard.required-asterisk.color` | `color` | `{tanrei.sys.color.primary}` |
| `comp.field-group.standard.error-text.color` | `color` | `{tanrei.sys.color.error}` |
| `comp.field-group.standard.error.label.color` | `color` | `{tanrei.sys.color.on-surface}` |
| `comp.field-group.standard.error.supporting-text.color` | `color` | `{tanrei.sys.color.on-surface-variant}` |
| `comp.field-group.standard.error.required-asterisk.color` | `color` | `{tanrei.sys.color.primary}` |

Per-state tokens, valued by the design system's pattern rules (not written by hand):

- `comp.field-group.<variant>.<state>.<property>` for the variants `standard`, the states `disabled` and the properties `label.color`, `supporting-text.color`, `required-asterisk.color`, `error-text.color`, `error.label.color`, `error.supporting-text.color`, `error.required-asterisk.color`.

### Qué dibuja la web de otra manera

- El shadow root del elemento contiene un `<tanrei-field-group>` alrededor de la casilla de seleccionar todo, mientras se da `select-all-label`, y de un slot. Las casillas siguen siendo tus propios hijos. Importar el elemento define los dos miembros.
- `value` es solo una propiedad: un array, que se copia al entrar y al salir. No hay atributo `value`, porque una lista en un atributo necesita un separador que un valor puede contener, y HTML ya tiene la forma: `checked` en cada hijo.
- Una casilla sin atributo `value` y sin un valor definido no tiene ninguno: el «on» que envía una casilla sin nada más no se lee en un grupo. Se dibuja sin marcar, y una compilación de depuración lo notifica.
- El elemento está asociado a formularios. Recibe `name` y `form`, y envía con el nombre `name` una entrada por cada valor: lo mismo que envían las casillas nativas de un mismo nombre. Sus casillas no envían nada, lleven el `name` que lleven. Se restablece a su primer valor, sigue a un `<fieldset>` deshabilitado y notifica que falta un valor mientras `required` está activado y la lista está vacía. El mensaje es `error-text`; si no lo hay, el texto de obligatorio, y si tampoco, `label`: da `error-text` desde el principio, no solo cuando `error` está activado.
- `change` es un CustomEvent cuyo `detail.value` es una copia de la lista; antes de él se emite `input`. Un listener en una casilla dentro del grupo no oye ninguno de los dos.
- Partes: `field-group` es el marco, y sus partes tienen aquí los mismos nombres que un nivel más abajo: `label`, `requirement-label`, `supporting-text`, `error-text`. `select-all` es la casilla de seleccionar todo, y sus partes son `select-all-container`, `select-all-box`, `select-all-mark` y `select-all-label`. A tus propias casillas se les da estilo como en cualquier otro lugar, con `tanrei-checkbox::part(…)`.
- El elemento no puede recibir el foco por sí mismo. `focus()` va a la primera casilla habilitada, y un `<label for>` propio de la página lleva el foco allí.
- El elemento no tiene rol. Dale nombre con `label` —con `label-hidden` donde la etiqueta se dibuja en otro lugar— y nunca con `aria-label`, que aquí no nombra nada.
- Los atributos van en kebab-case: `label-hidden`, `supporting-text`, `error-text`, `required-mark`, `required-text`, `optional-text`, `select-all-label`.
- Antes de que el elemento esté definido, y sin script, no se dibuja nada propio: las casillas se muestran como contenido corriente. Una página que deba funcionar sin script escribe un `<fieldset>` nativo con casillas nativas de un mismo `name`, que envía las mismas entradas.

## Pautas

### Recomendado

- Escribe la etiqueta como la pregunta, y la etiqueta de cada casilla como una respuesta a ella.
- Di cómo responder en `supportingText`: «Elige todas las opciones que correspondan.»
- Prefiere una columna. Usa una fila solo para unas pocas etiquetas cortas.
- Da `errorText` siempre que el grupo sea obligatorio, para que la validación propia del formulario lo diga con tus palabras.
- Usa la casilla de seleccionar todo para un conjunto de seis o más, para filtros y para conjuntos de permisos.

### Evita

- No uses un grupo de casillas de verificación para una sola respuesta entre unas pocas: eso es un [grupo de botones de opción](https://tanrei.chnotchy.com/es/components/radio-group/).
- No des un `name` a las casillas para enviarlas: el grupo envía la lista con el suyo.
- No pongas dentro nada que se pueda accionar y no sea una casilla de verificación. Una compilación de depuración lo notifica, y no crea ninguna entrada del valor.
- No sangres el conjunto bajo la casilla de seleccionar todo para mostrar una jerarquía. Unas opciones dentro de otras son un árbol.
- No des nombre al grupo con `aria-label`. Donde no deba dibujarse ninguna etiqueta, da `label` y `labelHidden`.

## Accesibilidad

### De qué se encarga el componente

- Es un solo grupo, el del grupo de campos, que contiene sus casillas. Su nombre es `label`, se dibuje o no; su descripción es el texto de apoyo, el texto de error mientras está en error, y la obligatoriedad con los textos del producto.
- Un grupo no tiene estado obligatorio ni estado no válido. La obligatoriedad se dice con palabras, y cada casilla de un grupo en error se expone como no válida y tiene el texto de error como descripción.
- Cada casilla conserva su propio rol, su nombre y su estado de marcada. La casilla de seleccionar todo dice que es indeterminada mientras algunas están marcadas. Ninguna casilla tiene una posición en un conjunto, y no se dice cuántas están marcadas.
- Cada casilla habilitada es una parada propia para la tecla Tab, y la de seleccionar todo es la primera. Espacio cambia la que tiene el foco. No hay teclas de flecha, y Enter se deja al formulario.
- Una pulsación en la etiqueta, en la marca de obligatoriedad, en la descripción o en el texto de error no marca nada y no mueve el foco.
- Un error que aparece mientras el foco está en una de las casillas lo anuncia el grupo de campos una vez, de forma cortés.
- La dirección, el texto ampliado y los colores forzados los resuelve cada miembro por su cuenta: en un diseño de derecha a izquierda, el marco y cada casilla se reflejan; con el texto al 200%, todos los textos saltan de línea, una fila se convierte en una columna y nada se desplaza hacia los lados.
- No tiene textos propios. `requiredText` y `optionalText`, si se dejan vacíos, los lee el grupo de campos de los textos del producto.

Carencias conocidas en la web: lo que dicen del grupo los lectores de pantalla se ha leído en los árboles de accesibilidad de los navegadores, no se ha oído con un lector de pantalla. Chromium da al elemento un nodo propio en su árbol de accesibilidad, porque el formulario lo valida: un nodo sin rol y sin nombre, alrededor del grupo.

### Qué debes hacer tú

- Da `label`, siempre, y un `label` en cada casilla.
- Da `requiredText` y `optionalText` —en el grupo o una sola vez para el producto— donde el grupo sea obligatorio u opcional: un grupo no tiene estado obligatorio, así que son las palabras las que lo dicen.
- Di en `errorText` qué está mal y cómo corregirlo. El color nunca es la única señal.
- Donde importe el número de opciones («2 de 5 elegidas»), escríbelo en `supportingText`: de otro modo no se anuncia.
