Checkbox

ui-checkbox renders a native <input type="checkbox"> and a <label> in its shadow root. The element's text is the label, and clicking the label toggles the box. It is form-associated. When checked, it submits its value (or on) under its name, like a native checkbox.

Previewindex.html
Accept terms and conditions
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Checkbox demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/checkbox.html"></import-element>

  <ui-checkbox>Accept terms and conditions</ui-checkbox>
</body>
</html>

Examples

Checked

Previewindex.html
Email me about product updates
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Checkbox demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/checkbox.html"></import-element>

  <ui-checkbox checked>Email me about product updates</ui-checkbox>
</body>
</html>

Indeterminate

indeterminate shows a dash instead of a check mark, for a "select all" box whose items are partly checked. Like the native property, it changes only the look. The box still submits according to checked. Clicking the box clears indeterminate, as it does on a native checkbox.

Previewindex.html
Select all
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Checkbox demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/checkbox.html"></import-element>

  <ui-checkbox indeterminate>Select all</ui-checkbox>
</body>
</html>

Disabled

Previewindex.html
Disabled Disabled and checked
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Checkbox demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/checkbox.html"></import-element>

  <ui-checkbox disabled>Disabled</ui-checkbox>
  <ui-checkbox disabled checked>Disabled and checked</ui-checkbox>
</body>
</html>

Without visible text

When the checkbox has no text, name it with aria-label.

Previewindex.html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Checkbox demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/checkbox.html"></import-element>

  <ui-checkbox aria-label="Select row"></ui-checkbox>
</body>
</html>

Listening for changes

change bubbles from the element after each toggle. Read checked from the element.

Previewindex.htmlcheckbox-change-demo.html
Enable notifications

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Checkbox demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/checkbox.html"></import-element>

  <import-element from="./checkbox-change-demo.html"></import-element>
  <checkbox-change-demo></checkbox-change-demo>
</body>
</html>
<custom-element name="checkbox-change-demo">
  <script>
    const settings = useState({ status: "Notifications are off." });

    function changed(checkbox) {
      settings.status = checkbox.checked ? "Notifications are on." : "Notifications are off.";
    }
  </script>

  <style>
    :host {
      display: grid;
      gap: calc(var(--spacing) * 2);
    }
    p {
      margin: 0;
      font-size: var(--text-sm);
      color: var(--muted-foreground);
    }
  </style>

  <ui-checkbox onchange="changed(this)">Enable notifications</ui-checkbox>
  <p role="status"><slot name="#settings.status"></slot></p>
</custom-element>

In a form

Only checked boxes are submitted. Several checkboxes can share a name, and FormData.getAll() returns every checked value. required blocks submission until the box is checked, and form.reset() restores the checked attribute.

Previewindex.htmlcheckbox-form-demo.html
Releases Security advisories Events I agree to the terms
Subscribe Reset
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Checkbox demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/button.html"></import-element>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/checkbox.html"></import-element>

  <import-element from="./checkbox-form-demo.html"></import-element>
  <checkbox-form-demo></checkbox-form-demo>
</body>
</html>
<custom-element name="checkbox-form-demo">
  <script>
    const result = useState({ text: "Submit the form to see its data." });

    function submit(event) {
      event.preventDefault();
      const entries = [...new FormData(event.target)].map(([name, value]) => `${name}=${value}`);
      result.text = entries.join("\n") || "(nothing submitted)";
    }

    function reset() {
      result.text = "The form was reset.";
    }
  </script>

  <style>
    :host {
      display: grid;
      gap: calc(var(--spacing) * 4);
      width: 100%;
      max-width: calc(var(--spacing) * 80);
    }
    form {
      display: grid;
      gap: calc(var(--spacing) * 3);
    }
    .actions {
      display: flex;
      gap: calc(var(--spacing) * 2);
    }
    output {
      display: block;
      padding: calc(var(--spacing) * 3);
      border-radius: var(--radius-md);
      background: var(--muted);
      font-family: var(--font-mono);
      font-size: var(--text-xs);
      white-space: pre-wrap;
    }
  </style>

  <form onsubmit="submit(event)" onreset="reset()">
    <ui-checkbox name="topics" value="releases" checked>Releases</ui-checkbox>
    <ui-checkbox name="topics" value="security">Security advisories</ui-checkbox>
    <ui-checkbox name="topics" value="events">Events</ui-checkbox>
    <ui-checkbox name="terms" required>I agree to the terms</ui-checkbox>
    <div class="actions">
      <ui-button type="submit">Subscribe</ui-button>
      <ui-button type="reset" variant="outline">Reset</ui-button>
    </div>
  </form>
  <output><slot name="#result.text"></slot></output>
</custom-element>

API

Attributes

Attribute Type Default Description
checked boolean false Whether the box is checked. The attribute is the initial and reset state. The property follows the user.
value string Value submitted when checked. Without it the form submits on.
indeterminate boolean false Shows the mixed state. Visual only. Cleared when the user toggles the box.
disabled boolean false Disables the box. Also true inside a disabled <fieldset>.
required boolean false The form is invalid until the box is checked.
aria-label string Accessible name when there is no visible text. Property name: label.

The name attribute is read by the form, as on any form-associated element.

Properties

Property Type Description
checked boolean Current state. Updated when the user toggles the box. Setting it updates the box and the submitted value.
value, indeterminate, disabled, required Mirror the attributes above.

Events

Event Detail Description
change none Bubbles from the element after the user toggles the box. checked is already updated.
input none The native input event of the inner checkbox, which is composed and reaches the page.

Slots

Slot Description
(default) The label text. It is rendered inside a native <label> for the inner checkbox.

CSS parts

Part Description
control The inner <input type="checkbox">.
label The <label> around the slotted text.

CSS states

State Description
:state(attr-checked) Set while the box is checked, for example ui-checkbox:state(attr-checked).

Forms

ui-checkbox is form-associated. When checked it submits value, or on without one. When unchecked it submits nothing. Validity is mirrored from the inner checkbox, so required takes part in checkValidity() and reportValidity(). form.reset() restores the checked attribute, and a disabled <fieldset> disables the box.

Accessibility

The inner native checkbox keeps its role, Space toggles it, and the host delegates focus to it. The slotted text is its label. When there is no text, aria-label is passed to the inner checkbox, and a <ui-label for> pointing at the element sets one too.

Edit this page on GitHub · View as Markdown