Navigation

v0.0.4
Forms

Checkbox

A small native well that fills with ink when checked.

On this page

A checkbox is a small well that fills with ink when checked. Put .checkbox on a native <input type="checkbox">, and wrap it with its text in a <label class="check"> so the whole row is a target. Each checkbox is its own yes or no; for one choice among several, use a radio.

Checkbox

<label class="check">  <input type="checkbox" class="checkbox" checked>Email me about releases</label><label class="check">  <input type="checkbox" class="checkbox">Weekly digest</label>

#In a group

Related checkboxes go in a fieldset with a legend, so the group has a name. Any number of them can be checked.

In a group

<fieldset class="fieldset">  <legend class="fieldset-legend">Notify me about</legend>  <label class="check">    <input type="checkbox" class="checkbox" name="notify" value="failed" checked>Failed deployments</label>  <label class="check">    <input type="checkbox" class="checkbox" name="notify" value="certificates" checked>Expiring certificates</label>  <label class="check">    <input type="checkbox" class="checkbox" name="notify" value="news">Product news</label></fieldset>

#Long labels

The box centers on the first line of its label and stays there when the label wraps, so a sentence can follow it.

Long labels

<label class="check max-w(320)"><input type="checkbox" class="checkbox" required>I have read the terms of service and understand that deployments on the free plan pause after 30 days without traffic.</label>

When an option needs a title and a description of its own, use a choice.

#Indeterminate

A parent checkbox whose children are partly selected shows a dash. Set indeterminate from script; HTML has no attribute for it.

Indeterminate

<label class="check">  <input type="checkbox" class="checkbox" id="select-all">All regions</label><label class="check ml-lg">  <input type="checkbox" class="checkbox" checked>Tokyo</label><label class="check ml-lg">  <input type="checkbox" class="checkbox">Frankfurt</label><script>  document.getElementById('select-all').indeterminate = true</script>

#Disabled

A disabled checkbox fades, and its label drops to disabled ink.

Disabled

<label class="check">  <input type="checkbox" class="checkbox" checked disabled>Required by your plan</label><label class="check">  <input type="checkbox" class="checkbox" disabled>Not available in your region</label>

#Shape

A checkbox is a square with a chip's corner, which says "any of these". Where the browser draws continuous corners, that square takes the shape of an app icon. The check arrives with a short settle and leaves at once; with reduced motion it only fades.

#Accessibility

  • The input is native, so Space toggles it and a form submits it.
  • A checkbox without visible text, such as one in a table row, needs aria-label.
  • A setting that takes effect at once, without a save button, is a switch.

#Classes

ClassDescription
checkA label row around a checkbox, a radio or a switch and its text; it takes disabled ink while its control is disabled
checkboxA native type="checkbox" input: a square well with a chip's corner

#Source

@master/ui/components/checkbox.css
/* * Checkbox: a native input drawn as a small well that fills with ink when checked. * Its corner is a chip's, so with continuous corners it takes the shape of an app icon. * Wrap it with its text in a .check label to make the whole row a target; the same label * serves a radio and a switch. */@layer components {  .check {    display: inline-flex;    align-items: flex-start;    gap: .5rem;    color: var(--color-text-strong);    font-size: .875rem;    line-height: 1.45;    cursor: pointer;    touch-action: manipulation;    -webkit-tap-highlight-color: transparent;    &:has(.checkbox:disabled, .radio:disabled, .switch:disabled) {      color: var(--color-text-disabled);      cursor: not-allowed;    }    /* On touch the label is the target: it reaches 44px around its line without moving what stands near it. */    @media (pointer: coarse) {      padding-block: .75rem;      margin-block: -.75rem;    }  }  .checkbox {    --check-size: 1.125rem;    position: relative;    display: inline-grid;    flex: none;    place-items: center;    width: var(--check-size);    height: var(--check-size);    /* Centres the control on the first line of a label that wraps. */    margin: max(0px, calc((1lh - var(--check-size)) / 2)) 0;    padding: 0;    border: 0;    border-radius: calc((var(--radius-chip) - 1px) * var(--corner-scale));    corner-shape: var(--corner-shape);    background: var(--color-surface-inset);    /* A small control needs a firmer edge than a surface to be found: one step, still half a pixel. */    box-shadow: var(--shadow-pressed), inset 0 0 0 .5px var(--color-line-strong);    vertical-align: middle;    appearance: none;    cursor: inherit;    transition-property: background-color, box-shadow;    transition-duration: var(--duration-quick);    transition-timing-function: var(--easing-glide);    &::after {      content: '';      width: 10px;      height: 10px;      opacity: 0;      scale: .6;      background: var(--color-text-inverse);      /* The mark leaves at once and arrives with a short settle. */      transition: opacity var(--duration-press) linear, scale var(--duration-press) linear;    }    @media (hover: hover) {      &:hover:not(:checked, :indeterminate, :disabled) {        background: var(--color-surface-inset-hover);        box-shadow: var(--shadow-pressed), inset 0 0 0 .5px var(--color-text-muted);      }    }    &:is(:checked, :indeterminate) {      background: var(--color-surface-inverse);      box-shadow: var(--shadow-ink-mark);      &::after {        opacity: 1;        scale: 1;        transition: opacity var(--duration-quick) var(--easing-glide), scale var(--duration-base) var(--easing-glide);      }    }    &:focus-visible {      @apply --focus-ring;    }    &:disabled {      opacity: .5;    }    @media (prefers-reduced-motion: reduce) {      &::after {        scale: 1;      }    }    /* Draw with system colors so the mark survives forced colors. */    @media (forced-colors: active) {      forced-color-adjust: none;      background: Field;      box-shadow: inset 0 0 0 1px FieldText;      &::after {        background: HighlightText;      }      &:is(:checked, :indeterminate) {        background: Highlight;        box-shadow: none;      }      &:disabled {        opacity: 1;        box-shadow: inset 0 0 0 1px GrayText;      }      &:focus-visible {        outline-color: Highlight;      }    }  }  .checkbox::after {    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10'%3E%3Cpath d='M2 5.2 4.1 7.3 8 2.8' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / contain no-repeat;  }  .checkbox:indeterminate::after {    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10'%3E%3Cpath d='M2.5 5h5' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E") center / contain no-repeat;  }}