OTTODesign System

ComponentsForm

Checkbox

The checkbox allows users to select one or multiple options from a set of options or a single yes/no option.

Configurator

LiveCheckbox: size, width, label position, texts and state
HTML
<oc-checkbox-v2><input type="checkbox" aria-label="oc-auto"><label slot="label">Ich möchte den OTTO-Newsletter erhalten.</label></oc-checkbox-v2>

Usage

Anatomy

1 2
  1. Checkbox
  2. Label
LiveAnatomy
HTML
<div class="anatomy" style="display:block;width:max-content;margin:28px auto">
<oc-checkbox-v2 width-behavior="fit"><input type="checkbox" aria-label="oc-auto"><label slot="label">Label</label></oc-checkbox-v2>
<span class="anatomy-pin" style="left:-16px;top:50%">1</span>
<span class="anatomy-pin" style="left:calc(100% + 16px);top:50%">2</span>
<ol class="anatomy-key"><li data-n="1">Checkbox</li><li data-n="2">Label</li></ol>

Variants

There are two variants of the checkbox. Both come in size50 and size100.

  • Default
  • Error: The error variant is not only visualized by the error color but also by an error icon, which is due to accessibility.

Default, size 100

Error, size 100

Bitte akzeptiere die AGB.

Default, size 50

Error, size 50

Bitte akzeptiere die AGB.
Livedefault and error, sizes 100 and 50
HTML
<p class="demo-label">Default, size 100</p>
<oc-checkbox-v2 width-behavior="fit"><input type="checkbox" aria-label="oc-auto"><label slot="label">Newsletter abonnieren</label></oc-checkbox-v2>
<p class="demo-label">Error, size 100</p>
<oc-checkbox-v2 width-behavior="fit"><input type="checkbox" aria-label="oc-auto"><label slot="label">AGB akzeptieren</label><span slot="error">Bitte akzeptiere die AGB.</span></oc-checkbox-v2>
<p class="demo-label">Default, size 50</p>
<oc-checkbox-v2 size="50" width-behavior="fit"><input type="checkbox" aria-label="oc-auto"><label slot="label">Newsletter abonnieren</label></oc-checkbox-v2>
<p class="demo-label">Error, size 50</p>
<oc-checkbox-v2 size="50" width-behavior="fit"><input type="checkbox" aria-label="oc-auto"><label slot="label">AGB akzeptieren</label><span slot="error">Bitte akzeptiere die AGB.</span></oc-checkbox-v2>

Behavior

States

Enabled (hover, press, focus it)

Error

Disabled

LiveStates
HTML
<p class="demo-label">Enabled (hover, press, focus it)</p>
<oc-checkbox-v2><input type="checkbox" aria-label="oc-auto"><label slot="label">Label</label></oc-checkbox-v2>
<oc-checkbox-v2><input type="checkbox" checked aria-label="oc-auto"><label slot="label">Label</label></oc-checkbox-v2>
<p class="demo-label">Error</p>
<oc-checkbox-v2><input type="checkbox" aria-label="oc-auto"><label slot="label">Label</label><span slot="error"></span></oc-checkbox-v2>
<oc-checkbox-v2><input type="checkbox" checked aria-label="oc-auto"><label slot="label">Label</label><span slot="error"></span></oc-checkbox-v2>
<p class="demo-label">Disabled</p>
<oc-checkbox-v2><input type="checkbox" disabled aria-label="oc-auto"><label slot="label">Label</label></oc-checkbox-v2>
<oc-checkbox-v2><input type="checkbox" checked disabled aria-label="oc-auto"><label slot="label">Label</label></oc-checkbox-v2>

Interaction

Users can check a checkbox by tapping anywhere on it. The label is also part of the hitbox and ensures proper support in assistive technologies, improving usability and accessibility. To uncheck it, it may be tapped again. There is also a disabled variant of the checkbox that cannot be clicked or tapped.

Indeterminate

An indeterminate state indicates that a checkbox's value is neither fully checked nor unchecked, typically used in nested lists to show that only some sub-items are selected. It serves as a visual "mixed" status, ensuring the parent element accurately reflects the partial selection of its children without implying a checked or default state.

In a nested list, there are three specific states possible: Indeterminate (only some sub-elements are selected), Checked (all sub-elements are selected), and Unchecked (no sub-elements are selected).

When

Fitting

The checkbox is 1.5rem high and 1.5rem wide. The label is set to fit-content by default, but it can be overwritten to fill-parent. Note, that the whole line is interactive in this case.

fit

fill (default)

Long label wraps

Livefitting
HTML
<p class="demo-label">fit</p>
<oc-checkbox-v2 width-behavior="fit"><input type="checkbox" aria-label="oc-auto"><label slot="label">Label</label></oc-checkbox-v2>
<p class="demo-label">fill (default)</p>
<oc-checkbox-v2><input type="checkbox" aria-label="oc-auto"><label slot="label">Label</label></oc-checkbox-v2>
<p class="demo-label">Long label wraps</p>
<oc-checkbox-v2><input type="checkbox" aria-label="oc-auto"><label slot="label">Ich möchte über Angebote und Neuheiten per E-Mail informiert werden.</label></oc-checkbox-v2>

Placement

The checkbox has a mandatory label which can be placed left or right with a small gap to the checkbox. Ideally, there should be a 24px gap between checkboxes if they are placed vertically or horizontally.

Label right (default)

Label left

Livelabel placement
HTML
<p class="demo-label">Label right (default)</p>
<oc-checkbox-v2 width-behavior="fit"><input type="checkbox" aria-label="oc-auto"><label slot="label">Label</label></oc-checkbox-v2>
<p class="demo-label">Label left</p>
<oc-checkbox-v2 width-behavior="fit" label-placement="left"><input type="checkbox" aria-label="oc-auto"><label slot="label">Label</label></oc-checkbox-v2>

Vertical list, 24px gap

Liveplacement
HTML
<p class="demo-label">Vertical list, 24px gap</p>
<oc-form-group-v1 orientation="vertical" gap="var(--oc-base-dimension-24)" oc-aria-label="Kategorie">
<oc-checkbox-v2><input type="checkbox" aria-label="oc-auto"><label slot="label">Label</label></oc-checkbox-v2>
<oc-checkbox-v2><input type="checkbox" aria-label="oc-auto"><label slot="label">Label</label></oc-checkbox-v2>
<oc-checkbox-v2><input type="checkbox" aria-label="oc-auto"><label slot="label">Label</label></oc-checkbox-v2>
</oc-form-group-v1>

Checkbox vs. radio button

Use the checkbox when multiple options can be selected (multiple selection) or a standalone option can be checked. When only one option can be selected from a list use radio buttons (single selection).

Checkbox: several choices

Radio button: one choice

Livemultiple selection and single selection
HTML
<p class="demo-label">Checkbox: several choices</p>
<oc-checkbox-v2><input type="checkbox" checked aria-label="oc-auto"><label slot="label">Damen</label></oc-checkbox-v2>
<oc-checkbox-v2><input type="checkbox" checked aria-label="oc-auto"><label slot="label">Herren</label></oc-checkbox-v2>
<oc-checkbox-v2><input type="checkbox" aria-label="oc-auto"><label slot="label">Kinder</label></oc-checkbox-v2>
<p class="demo-label">Radio button: one choice</p>
<oc-radio-button-v2><input type="radio" name="cb-vs-rb" aria-label="oc-auto"><label slot="label">Standardversand</label></oc-radio-button-v2>
<oc-radio-button-v2><input type="radio" name="cb-vs-rb" checked aria-label="oc-auto"><label slot="label">Expressversand</label></oc-radio-button-v2>
<oc-radio-button-v2><input type="radio" name="cb-vs-rb" aria-label="oc-auto"><label slot="label">Abholung</label></oc-radio-button-v2>

Checkbox vs. switch

Use the checkbox when a separate interaction is needed to confirm a choice, such as "Speichern" or "Bestätigen", or when multiple options can be selected simultaneously as part of a larger form.

Use the switch for immediate actions, such as toggling a setting on or off. The action happens the moment the user interacts with it.

Checkbox: confirmed with a button

Weiter

Switch: takes effect at once

Livesingle selection on desktop and mobile
HTML
<p class="demo-label">Checkbox: confirmed with a button</p>
<oc-checkbox-v2><input type="checkbox" checked aria-label="oc-auto"><label slot="label">Ich möchte den OTTO-Newsletter erhalten.</label></oc-checkbox-v2>
<oc-button-v1 variant="primary">Weiter</oc-button-v1>
<p class="demo-label">Switch: takes effect at once</p>
<oc-switch-v3><input type="checkbox" checked aria-label="oc-auto"><label slot="label">OTTO-Newsletter erhalten</label></oc-switch-v3>

Do not use switches for lists consisting of multiple options. Instead, use checkboxes. Checkboxes imply that the items are related and take up less visual space than switches.

Do

Kategorie

LiveDo
HTML
<p class="demo-title">Kategorie</p>
<oc-checkbox-v2><input type="checkbox" checked aria-label="oc-auto"><label slot="label">Fernseher</label></oc-checkbox-v2>
<oc-checkbox-v2><input type="checkbox" aria-label="oc-auto"><label slot="label">TV-Wandhalterung</label></oc-checkbox-v2>
<oc-checkbox-v2><input type="checkbox" aria-label="oc-auto"><label slot="label">TV-Wände</label></oc-checkbox-v2>

Don't

Kategorie

LiveDon't
HTML
<p class="demo-title">Kategorie</p>
<oc-switch-v3><input type="checkbox" aria-label="oc-auto"><label slot="label">Fernseher</label></oc-switch-v3>
<oc-switch-v3><input type="checkbox" checked aria-label="oc-auto"><label slot="label">TV-Wandhalterung</label></oc-switch-v3>
<oc-switch-v3><input type="checkbox" aria-label="oc-auto"><label slot="label">TV-Wände</label></oc-switch-v3>

Best Practices

CautionAvoid using horizontal Checkbox lists.

Even if they are placed far enough from each other, the associated label for a checkbox may be mixed up with a different one by users.

LiveCaution
HTML
<oc-checkbox-v2 width-behavior="fit"><input type="checkbox" aria-label="oc-auto"><label slot="label">Rot</label></oc-checkbox-v2>
<oc-checkbox-v2 width-behavior="fit"><input type="checkbox" aria-label="oc-auto"><label slot="label">Blau</label></oc-checkbox-v2>
<oc-checkbox-v2 width-behavior="fit"><input type="checkbox" aria-label="oc-auto"><label slot="label">Grün</label></oc-checkbox-v2>
CautionOnly nest checkboxes if you are using the indeterminate state.
DoUse the actual label of the checkbox.

This is the only way to ensure consistent behavior and layout.

LiveDo
HTML
<oc-checkbox-v2 width-behavior="fit"><input type="checkbox" aria-label="oc-auto"><label slot="label">Auch der Text ist klickbar</label></oc-checkbox-v2>
Newsletter
Don'tUse custom text as a label. The spacing may be off, and proper user interaction (i.e., a hitbox spanning the entire element and interactive states) cannot be ensured.

Content Guidelines

DoUse short, precise labels to describe the options.
Don'tUse overly descriptive labels to describe the options.

Accessibility

For information on accessibility, refer to the technical documentation.

Status

Implementation

Note: For full technical documentation of this component, visitStorybook/Checkbox.

Live demo

Newsletter abonnieren

Neuheiten, Trends und Angebote, passend zu dem, was dich interessiert.

E-Mail-Adresse

Was interessiert dich?

Du kannst dich jederzeit wieder abmelden. Jetzt anmelden
LiveReal OTTO components, rendered by the OTTO component runtime
HTML
<div style="max-width:720px;margin:0 auto;display:grid;grid-template-columns:repeat(auto-fit,minmax(15rem,1fr));gap:32px;align-items:center">
<img src="/previews/imagery/samples/otto-colour-field/colour-field-parcel.webp" alt="" style="display:block;width:100%;aspect-ratio:4/5;object-fit:cover;border-radius:12px">
<div style="display:grid;gap:20px">
  <div style="display:grid;gap:4px">
    <p class="oc-headline-100">Newsletter abonnieren</p>
    <p class="oc-copy-100 oc-text-color-secondary">Neuheiten, Trends und Angebote, passend zu dem, was dich interessiert.</p>
  <oc-text-field-v1 type="email" autocomplete="email">E-Mail-Adresse</oc-text-field-v1>
  <div style="display:grid;gap:12px">
    <p class="oc-copy-100"><b>Was interessiert dich?</b></p>
    <oc-form-group-v1 orientation="vertical" gap="var(--oc-base-dimension-16)" oc-aria-label="Interessen">
      <oc-checkbox-v2><input type="checkbox" checked aria-label="oc-auto"><label slot="label">Mode</label></oc-checkbox-v2>
      <oc-checkbox-v2><input type="checkbox" checked aria-label="oc-auto"><label slot="label">Wohnen</label></oc-checkbox-v2>
      <oc-checkbox-v2><input type="checkbox" aria-label="oc-auto"><label slot="label">Technik</label></oc-checkbox-v2>
    </oc-form-group-v1>
  <oc-divider-v1></oc-divider-v1>
  <oc-checkbox-v2 size="50"><input type="checkbox" required aria-label="oc-auto"><label slot="label">Ja, ich möchte den OTTO-Newsletter per E-Mail erhalten.</label><span slot="hint">Du kannst dich jederzeit wieder abmelden.</span></oc-checkbox-v2>
  <oc-button-v1 variant="primary">Jetzt anmelden</oc-button-v1>

Code

Version Tag Status API
v2 <oc-checkbox-v2> Stable, allowed for generation CheckboxV2
v1 <oc-checkbox-v1> Deprecated, NOT allowed for generation CheckboxV1

Only the latest version (v2) is allowed for generation. Older versions are kept for reference and are deprecated.

Overview (v2)

Checkbox

The checkbox component lets users select one or more options while keeping the label and validation messaging aligned with OTTO design tokens. It pairs a slotted input element with optional hint and validation slots so that you can keep native form semantics.

Default variation
HTML
<oc-checkbox-v2>
<label slot="label">my label</label>
<input type="checkbox" aria-label="oc-auto" />
</oc-checkbox-v2>
Configuration

This component is configurable, allowing you to tailor its features and appearance to your specific needs. To explore all the available options and adjust the component, use the component configurator and see the changes affect the component in real-time.

Usage guidelines

Before integrating the checkbox component into your project, make sure you have correctly installed the OTTO components package. Look through the variations page for examples of possible component variations. Here, you can discover both common and specific variations that address different use cases.

Info

See the Checkbox UX documentation for detailed user experience guidelines.

Layout considerations

This component has visual overflow. It extends beyond its bounding box and is clipped by parent containers with overflow: hidden. Ensure the parent container has sufficient padding to accommodate the component's full visual area.

Accessibility

The checkbox component relies on the attributes applied to the slotted input, so always provide aria-label or an associated <label> element and propagate required, disabled, and checked states through the native control. Reference the built-in accessibility features guide for keyboard expectations and screen-reader behavior.

Configuration (v2)

Checkbox configuration

Configure the checkbox component with the controls below and see the changes live in the preview canvas. Click the Show code button within the preview canvas to see the source code for the current component configuration.

HTML
<oc-checkbox-v2>
<label slot="label">my label</label>
<input type="checkbox" aria-label="oc-auto" />
</oc-checkbox-v2>

Interactive configurator (Storybook controls); every option is listed in the API section of this file.

Migration (v2)

Migration from checkbox v1 to v2

The oc-checkbox component has been updated from oc-checkbox-v1 to oc-checkbox-v2. This migration guide provides step-by-step instructions to update your project to the latest version.

Skip to:

What's new
Native input element

The component now uses a native <input type="checkbox"> element, providing:

  • Better form integration and native form submission
  • Native validation support
  • Improved accessibility with screen readers
  • Support for standard input attributes like autofocus, form, formnovalidate
Events

All native checkbox events are now available:

  • click
  • input
  • change
  • focus
  • blur
Flexible label content

The label slot allows for more flexible label content, including HTML elements:

HTML
<oc-checkbox-v2>
<input type="checkbox" name="option" aria-label="oc-auto" />
<label slot="label"> Option <span class="oc-text-color-secondary">(123)</span> </label>
</oc-checkbox-v2>
API changes
Changed attributes
v1 Attribute v2 Attribute Notes
fit-content width-behavior="fit" Default is "fill"
label-placement label-placement No change
Changed slots
v1 Slot v2 Slot Notes
default (label text) label Label text now uses the label slot
— default Now contains the native <input type="checkbox"> element
— hint New slot for hint text
— error New slot for validation message
Removed attributes

The following attributes have been removed and should now be set on the native <input> element:

v1 Attribute v2 Equivalent Notes
checked Set checked on the <input> element Moved to native input
disabled Set disabled on the <input> element Moved to native input
name Set name on the <input> element Moved to native input
value Set value on the <input> element Moved to native input
required Set required on the <input> element Moved to native input
oc-aria-label Set aria-label on the <input> element Renamed to standard attribute
hint Use the hint slot Moved to slot
validation-message Use the error slot Moved to slot
variant The error state is now automatically applied when the error slot has content Auto-applied based on slot
How to migrate

The main change is that v2 uses a native <input type="checkbox"> element in the default slot instead of managing the checkbox state internally. This provides better accessibility, form integration, and native event support.

Migrate a basic checkbox
<!-- From: -->
<oc-checkbox-v1 name="terms" value="accepted" checked>Accept terms</oc-checkbox-v1>
<!-- To: -->
<oc-checkbox-v2>
  <input type="checkbox" name="terms" value="accepted" checked aria-label="oc-auto" />
  <label slot="label">Accept terms</label>
</oc-checkbox-v2>
Migrate a checkbox with hint and validation message
<!-- From: -->
<oc-checkbox-v1
  name="terms"
  hint="Please read the terms carefully"
  validation-message="You must accept the terms"
  variant="error"
>
  Accept terms
</oc-checkbox-v1>
<!-- To: -->
<oc-checkbox-v2>
  <input type="checkbox" name="terms" aria-label="oc-auto" />
  <label slot="label">Accept terms</label>
  <span slot="hint">Please read the terms carefully</span>
  <span slot="error">You must accept the terms</span>
</oc-checkbox-v2>
Migrate a checkbox with a disabled state
<!-- From: -->
<oc-checkbox-v1 disabled>Disabled checkbox</oc-checkbox-v1>
<!-- To: -->
<oc-checkbox-v2>
  <input type="checkbox" disabled aria-label="oc-auto" />
  <label slot="label">Disabled checkbox</label>
</oc-checkbox-v2>
Migrate a checkbox with width behavior
<!-- From: -->
<oc-checkbox-v1 fit-content>Fit content</oc-checkbox-v1>
<!-- To: -->
<oc-checkbox-v2 width-behavior="fit">
  <input type="checkbox" aria-label="oc-auto" />
  <label slot="label">Fit content</label>
</oc-checkbox-v2>

API v1 (v1, deprecated, not for generation)

Checkbox v1 API

API: <oc-checkbox-v1> (CheckboxV1)

The Checkbox component allows users to select one or more options from a set, with properties for state, styling, and accessibility, and a slot for setting the Checkbox label text.

Attributes / properties
Attribute Type Default Required Description
variant "default" | "error" "default" no Sets the main styling variant of the checkbox. Can be one of: "default", "error".
value string "on" no Sets the initial value of a ticked checkbox to the provided value for form processing. If this attribute is not specified, the default value of value="on" is used as the submitted data.
hint string undefined no Provides additional information related to the checkbox below the element.
validation-message string undefined no Provides a validation message related to the checkbox below the element.

Implicitly sets the checkbox to the error state.
checked boolean false no Indicates whether the checkbox is checked. When enabled, the checkbox is initially ticked.

Accessible in CSS via the custom-state mixin.
disabled boolean false no Indicates whether the checkbox is disabled, preventing user interaction and input.
required boolean false no Indicates whether the input is required for screen readers. Doesn't prevent form submission on its own.
fit-content boolean no Sets the width behavior of the checkbox.
name string undefined no Sets the name tag to identify the checkbox when submitting a form. Mandatory if single-selection is set to true.
label-placement "left" | "right" "right" no Sets the placement direction of the label provided via the default slot relative to the checkbox.
oc-aria-label string undefined no Sets the ARIA label of the checkbox.
Slots
Slot Required Description
default yes Sets the text content for the checkbox label to provide a brief description of the input field.
Events
Event Detail type Description
oc-property-change OcCheckboxV1Events["oc-property-change"] Whenever a property value changes, this event triggers. Use this event to track all property changes within the component.

Refer to the Events documentation for more information.
oc-mount { component: string; } Fired when the component is mounted to the DOM. The event is fired when the onMount hook of the component is called by the runtime.
oc-unmount { component: string; } Fired when the component is unmounted from the DOM. The event is fired when the function returned by the onMount hook of the component is called by the runtime.

API v2

Checkbox v2 API

API: <oc-checkbox-v2> (CheckboxV2)

The Checkbox component allows users to select one or more options from a set. Provides attributes for state, styling, validation, accessibility, and slots for the label, input element, hints, and errors.

Attributes / properties
Attribute Type Default Required Description
size "50" | "100" "100" no The size of the checkbox.
width-behavior "fill" | "fit" "fill" no Defines the width behavior of the checkbox component. - "fill": The component expands to fill the available width. - "fit": The component width fits its content.
label-placement "left" | "right" "right" no The placement direction of the label provided via the default slot relative to the checkbox indicator.
Slots
Slot Required Description
default yes Requires the following interactive element: - <input type="checkbox"> for multi-selection

For more information on interactive components, refer to the interactive component slots documentation.
label no The text content of the checkbox label. Provides a brief description of the option represented by the checkbox.
hint no A hint text displayed below the checkbox. Provides additional information to help users understand the option.
error no An error message displayed below the checkbox. Informs users about validation errors or requirements.
Events
Event Detail type Description
oc-property-change OcCheckboxV2Events["oc-property-change"] Whenever a property value changes, this event triggers. Use this event to track all property changes within the component.

Refer to the Events documentation for more information.
oc-mount { component: string; } Fired when the component is mounted to the DOM. The event is fired when the onMount hook of the component is called by the runtime.
oc-unmount { component: string; } Fired when the component is unmounted from the DOM. The event is fired when the function returned by the onMount hook of the component is called by the runtime.

Variations (v2)

Variations

Listed below are the most common variations of the checkbox component as well as specific component variations for different use cases.

You can explore all available options using the component configurator, adjust the component to your needs, and see the changes live in a preview canvas.

Default

Story: components-checkbox-variations--default · tags: components

The standard configuration with the label placed on the right side.

HTML
<oc-checkbox-v2>
<label slot="label">my label</label>
<input type="checkbox" aria-label="oc-auto" />
</oc-checkbox-v2>
Indeterminate

Story: components-checkbox-variations--indeterminate · tags: components

Indeterminate variant of the checkbox.

Args: labelSlot=<label slot="label"> Alle Optionen </label>

<oc-checkbox-v2 id="indeterminate_parent" >
  <input type="checkbox" aria-label="oc-auto" /><label slot="label"> Alle Optionen </label>${unsafeHTML(hintSlot)}${unsafeHTML(errorSlot)}
</oc-checkbox-v2>

<div style="display: flex; gap: .5rem; padding: .75rem; flex-direction: column;">
  <oc-checkbox-v2 id="indeterminate_option1">
    <input type="checkbox" aria-label="oc-auto" />
    <label slot="label"> Option 1 </label>
  </oc-checkbox-v2>
  <oc-checkbox-v2 id="indeterminate_option2">
    <input type="checkbox" aria-label="oc-auto" />
    <label slot="label"> Option 2 </label>
  </oc-checkbox-v2>
  <oc-checkbox-v2 id="indeterminate_option3">
    <input type="checkbox" aria-label="oc-auto" />
    <label slot="label"> Option 3 </label>
  </oc-checkbox-v2>
</oc-form-group-v1>

<script>
  (() => {
    const parentEl = document.querySelector("#indeterminate_parent");
    const parentInput = parentEl.querySelector("input");
    const optionIds = [
      "#indeterminate_option1",
      "#indeterminate_option2",
      "#indeterminate_option3",
    ];
    const optionInputs = optionIds.map((id) =>
      document.querySelector(id).querySelector("input"),
    );

    function updateParent() {
      const checkedCount = optionInputs.filter((i) => i.checked).length;
      if (checkedCount === 0) {
        parentInput.checked = false;
        parentInput.indeterminate = false;
      } else if (checkedCount === optionInputs.length) {
        parentInput.checked = true;
        parentInput.indeterminate = false;
      } else {
        parentInput.checked = false;
        parentInput.indeterminate = true;
      }
    }

    function updateOptions(checked) {
      optionInputs.forEach((i) => {
        i.checked = checked;
        i.dispatchEvent(new Event("change"));
      });
    }

    parentEl.addEventListener("change", (e) => {
      updateOptions(e.target.checked);
    });

    optionInputs.forEach((input) => {
      input.addEventListener("change", updateParent);
    });

    // Initial state: indeterminate
    optionInputs[0].checked = true;
    optionInputs[1].checked = false;
    optionInputs[2].checked = true;
    updateParent();
  })();
</script>
Story source (TypeScript, verbatim from Storybook)
{
  name: "Indeterminate",
  args: {
    labelSlot: `<label slot="label"> Alle Optionen </label>`
  },
  render({
    defaultSlot,
    labelSlot,
    hintSlot,
    errorSlot,
    ...props
  }) {
    return html`
      <oc-checkbox-v2 id="indeterminate_parent" ${spread(props)}>
        ${unsafeHTML(defaultSlot)}${unsafeHTML(labelSlot)}${unsafeHTML(hintSlot)}${unsafeHTML(errorSlot)}
      </oc-checkbox-v2>

      <div style="display: flex; gap: .5rem; padding: .75rem; flex-direction: column;">
        <oc-checkbox-v2 id="indeterminate_option1">
          <input type="checkbox" aria-label="oc-auto" />
          <label slot="label"> Option 1 </label>
        </oc-checkbox-v2>
        <oc-checkbox-v2 id="indeterminate_option2">
          <input type="checkbox" aria-label="oc-auto" />
          <label slot="label"> Option 2 </label>
        </oc-checkbox-v2>
        <oc-checkbox-v2 id="indeterminate_option3">
          <input type="checkbox" aria-label="oc-auto" />
          <label slot="label"> Option 3 </label>
        </oc-checkbox-v2>
      </oc-form-group-v1>

      <script>
        (() => {
          const parentEl = document.querySelector("#indeterminate_parent");
          const parentInput = parentEl.querySelector("input");
          const optionIds = [
            "#indeterminate_option1",
            "#indeterminate_option2",
            "#indeterminate_option3",
          ];
          const optionInputs = optionIds.map((id) =>
            document.querySelector(id).querySelector("input"),
          );

          function updateParent() {
            const checkedCount = optionInputs.filter((i) => i.checked).length;
            if (checkedCount === 0) {
              parentInput.checked = false;
              parentInput.indeterminate = false;
            } else if (checkedCount === optionInputs.length) {
              parentInput.checked = true;
              parentInput.indeterminate = false;
            } else {
              parentInput.checked = false;
              parentInput.indeterminate = true;
            }
          }

          function updateOptions(checked) {
            optionInputs.forEach((i) => {
              i.checked = checked;
              i.dispatchEvent(new Event("change"));
            });
          }

          parentEl.addEventListener("change", (e) => {
            updateOptions(e.target.checked);
          });

          optionInputs.forEach((input) => {
            input.addEventListener("change", updateParent);
          });

          // Initial state: indeterminate
          optionInputs[0].checked = true;
          optionInputs[1].checked = false;
          optionInputs[2].checked = true;
          updateParent();
        })();
      </script>
    `;
  }
}
Label placement: left

Story: components-checkbox-variations--label-placement-left · tags: components

The standard configuration with the label placed on the left side.

Args: label-placement=left

HTML
<oc-checkbox-v2 label-placement="left">
<label slot="label">my label</label>
<input type="checkbox" aria-label="oc-auto" />
</oc-checkbox-v2>
Error

Story: components-checkbox-variations--error · tags: components

Displays an error state with a hint and error message.

Args: hintSlot=<span slot='hint'>This is a hint message.</span>, errorSlot=<span slot='error'>This is an error message.</span>, defaultSlot=<input type="checkbox" aria-label="oc-auto" />

This is a hint message. This is an error message.
HTML
<oc-checkbox-v2>
<label slot="label">my label</label>
<span slot='hint'>This is a hint message.</span>
<span slot='error'>This is an error message.</span>
<input type="checkbox" aria-label="oc-auto" />
</oc-checkbox-v2>
Disabled

Story: components-checkbox-variations--disabled · tags: components

The disabled state prevents user interaction.

Args: defaultSlot=<input type="checkbox" aria-label="oc-auto" disabled />

HTML
<oc-checkbox-v2>
<label slot="label">my label</label>
<input type="checkbox" aria-label="oc-auto" disabled />
</oc-checkbox-v2>
Size 50

Story: components-checkbox-variations--size-50 · tags: components

Size 50 variant of the checkbox.

Args: defaultSlot=<input type="checkbox" aria-label="oc-auto" />, size=50

HTML
<oc-checkbox-v2 size="50">
<label slot="label">my label</label>
<input type="checkbox" aria-label="oc-auto" />
</oc-checkbox-v2>
Demo: filter

Story: components-checkbox-variations--demo-filter · tags: components

The demo filter showcases how to use Checkboxes for filtering.

HTML
<oc-form-group-v1 orientation="vertical">
<oc-checkbox-v2>
  <input type="checkbox" name="zielgruppe" value="Damen" aria-label="oc-auto" />
  <label slot="label" class="my-label"
    >Damen<span class="oc-text-color-secondary">374637</span></label
  >
</oc-checkbox-v2>
<oc-checkbox-v2>
  <input type="checkbox" name="zielgruppe" value="Herren" aria-label="oc-auto" />
  <label slot="label" class="my-label"
    >Herren<span class="oc-text-color-secondary">234324</span></label
  >
</oc-checkbox-v2>
<oc-checkbox-v2>
  <input type="checkbox" name="zielgruppe" value="Mädchen" aria-label="oc-auto" />
  <label slot="label" class="my-label"
    >Mädchen<span class="oc-text-color-secondary">2323</span></label
  >
</oc-checkbox-v2>
<oc-checkbox-v2>
  <input type="checkbox" name="zielgruppe" value="Jungen" aria-label="oc-auto" />
  <label slot="label" class="my-label"
    >Jungen<span class="oc-text-color-secondary">1224</span></label
  >
</oc-checkbox-v2>
</oc-form-group-v1>
<oc-form-group-v1 class="oc-mt-200" orientation="vertical">
<oc-checkbox-v2 label-placement="left">
  <input type="checkbox" name="zielgruppe" value="Damen" aria-label="oc-auto" />
  <label slot="label" class="my-label"
    >Damen<span class="oc-text-color-secondary">374637</span></label
  >
</oc-checkbox-v2>
<oc-checkbox-v2 label-placement="left">
  <input type="checkbox" name="zielgruppe" value="Herren" aria-label="oc-auto" />
  <label slot="label" class="my-label"
    >Herren<span class="oc-text-color-secondary">234324</span></label
  >
</oc-checkbox-v2>
<oc-checkbox-v2 label-placement="left">
  <input type="checkbox" name="zielgruppe" value="Mädchen" aria-label="oc-auto" />
  <label slot="label" class="my-label"
    >Mädchen<span class="oc-text-color-secondary">2323</span></label
  >
</oc-checkbox-v2>
<oc-checkbox-v2 label-placement="left">
  <input type="checkbox" name="zielgruppe" value="Jungen" aria-label="oc-auto" />
  <label slot="label" class="my-label"
    >Jungen<span class="oc-text-color-secondary">1224</span></label
  >
</oc-checkbox-v2>
</oc-form-group-v1>
<style>
.my-label {
  display: flex;
  justify-content: space-between;
}
</style>
Story source (TypeScript, verbatim from Storybook)
{
  name: "Demo: filter",
  parameters: {
    controls: {
      disabled: true
    }
  },
  argTypes: hideControlsBadge(Metadata),
  render() {
    return html`
      <oc-form-group-v1 orientation="vertical">
        <oc-checkbox-v2>
          <input type="checkbox" name="zielgruppe" value="Damen" aria-label="oc-auto" />
          <label slot="label" class="my-label"
            >Damen<span class="oc-text-color-secondary">374637</span></label
          >
        </oc-checkbox-v2>
        <oc-checkbox-v2>
          <input type="checkbox" name="zielgruppe" value="Herren" aria-label="oc-auto" />
          <label slot="label" class="my-label"
            >Herren<span class="oc-text-color-secondary">234324</span></label
          >
        </oc-checkbox-v2>
        <oc-checkbox-v2>
          <input type="checkbox" name="zielgruppe" value="Mädchen" aria-label="oc-auto" />
          <label slot="label" class="my-label"
            >Mädchen<span class="oc-text-color-secondary">2323</span></label
          >
        </oc-checkbox-v2>
        <oc-checkbox-v2>
          <input type="checkbox" name="zielgruppe" value="Jungen" aria-label="oc-auto" />
          <label slot="label" class="my-label"
            >Jungen<span class="oc-text-color-secondary">1224</span></label
          >
        </oc-checkbox-v2>
      </oc-form-group-v1>

      <oc-form-group-v1 class="oc-mt-200" orientation="vertical">
        <oc-checkbox-v2 label-placement="left">
          <input type="checkbox" name="zielgruppe" value="Damen" aria-label="oc-auto" />
          <label slot="label" class="my-label"
            >Damen<span class="oc-text-color-secondary">374637</span></label
          >
        </oc-checkbox-v2>
        <oc-checkbox-v2 label-placement="left">
          <input type="checkbox" name="zielgruppe" value="Herren" aria-label="oc-auto" />
          <label slot="label" class="my-label"
            >Herren<span class="oc-text-color-secondary">234324</span></label
          >
        </oc-checkbox-v2>
        <oc-checkbox-v2 label-placement="left">
          <input type="checkbox" name="zielgruppe" value="Mädchen" aria-label="oc-auto" />
          <label slot="label" class="my-label"
            >Mädchen<span class="oc-text-color-secondary">2323</span></label
          >
        </oc-checkbox-v2>
        <oc-checkbox-v2 label-placement="left">
          <input type="checkbox" name="zielgruppe" value="Jungen" aria-label="oc-auto" />
          <label slot="label" class="my-label"
            >Jungen<span class="oc-text-color-secondary">1224</span></label
          >
        </oc-checkbox-v2>
      </oc-form-group-v1>

      <style>
        .my-label {
          display: flex;
          justify-content: space-between;
        }
      </style>
    `;
  }
}

Interaction tests (CheckboxV2.interactions.stories.ts)

Interaction test stories (automated tests, not usage patterns).

Should Handle User Selection

Story: components-checkbox-interaction-tests--should-handle-user-selection · tags: play-fn

HTML
<oc-checkbox-v2>
<input type="checkbox" aria-label="oc-auto" name="brand" value="Samsung" />
<label slot="label">Samsung</label>
</oc-checkbox-v2>
<oc-checkbox-v2>
<input type="checkbox" aria-label="oc-auto" name="brand" value="LG" />
<label slot="label">LG</label>
</oc-checkbox-v2>
<oc-checkbox-v2>
<input type="checkbox" aria-label="oc-auto" name="size" value="S" />
<label slot="label">S</label>
</oc-checkbox-v2>
<oc-checkbox-v2>
<input type="checkbox" aria-label="oc-auto" name="size" value="M" />
<label slot="label">M</label>
</oc-checkbox-v2>
Story source (TypeScript, verbatim from Storybook)
{
  parameters: {
    docs: {
      disable: true
    }
  },
  render() {
    return html`
      <oc-checkbox-v2>
        <input type="checkbox" aria-label="oc-auto" name="brand" value="Samsung" />
        <label slot="label">Samsung</label>
      </oc-checkbox-v2>
      <oc-checkbox-v2>
        <input type="checkbox" aria-label="oc-auto" name="brand" value="LG" />
        <label slot="label">LG</label>
      </oc-checkbox-v2>
      <oc-checkbox-v2>
        <input type="checkbox" aria-label="oc-auto" name="size" value="S" />
        <label slot="label">S</label>
      </oc-checkbox-v2>
      <oc-checkbox-v2>
        <input type="checkbox" aria-label="oc-auto" name="size" value="M" />
        <label slot="label">M</label>
      </oc-checkbox-v2>
    `;
  },
  async play({
    canvasElement
  }) {
    const [brand1, brand2, size1, size2] = Array.from(canvasElement.getElementsByTagName("oc-checkbox-v2"));
    const getInput = (element: Element) => element.querySelector("input") as HTMLInputElement;
    await expect(getInput(brand1).checked).toBeFalsy();
    await expect(getInput(brand2).checked).toBeFalsy();
    await expect(getInput(size1).checked).toBeFalsy();
    await expect(getInput(size2).checked).toBeFalsy();
    await click(getInput(brand1));
    await click(getInput(size1));
    await expect(getInput(brand1).checked).toBeTruthy();
    await expect(getInput(brand2).checked).toBeFalsy();
    await expect(getInput(size1).checked).toBeTruthy();
    await expect(getInput(size2).checked).toBeFalsy();
    await click(getInput(brand2));
    await click(getInput(size2));
    await expect(getInput(brand1).checked).toBeTruthy();
    await expect(getInput(brand2).checked).toBeTruthy();
    await expect(getInput(size1).checked).toBeTruthy();
    await expect(getInput(size2).checked).toBeTruthy();
    await click(getInput(brand1));
    await click(getInput(size1));
    await click(getInput(brand2));
    await click(getInput(size2));
    await expect(getInput(brand1).checked).toBeFalsy();
    await expect(getInput(brand2).checked).toBeFalsy();
    await expect(getInput(size1).checked).toBeFalsy();
    await expect(getInput(size2).checked).toBeFalsy();
  }
}

Should Have Correct Form Data

Story: components-checkbox-interaction-tests--should-have-correct-form-data · tags: play-fn

HTML
<form>
<oc-checkbox-v2>
  <input type="checkbox" aria-label="oc-auto" name="brand" value="Samsung" />
  <label slot="label">Samsung</label>
</oc-checkbox-v2>
<oc-checkbox-v2>
  <input type="checkbox" aria-label="oc-auto" name="brand" value="LG" />
  <label slot="label">LG</label>
</oc-checkbox-v2>
<oc-checkbox-v2>
  <input type="checkbox" aria-label="oc-auto" name="size" value="S" />
  <label slot="label">S</label>
</oc-checkbox-v2>
<oc-checkbox-v2>
  <input type="checkbox" aria-label="oc-auto" name="size" value="M" />
  <label slot="label">M</label>
</oc-checkbox-v2>
</form>
Story source (TypeScript, verbatim from Storybook)
{
  parameters: {
    docs: {
      disable: true
    }
  },
  render() {
    return html` <form>
      <oc-checkbox-v2>
        <input type="checkbox" aria-label="oc-auto" name="brand" value="Samsung" />
        <label slot="label">Samsung</label>
      </oc-checkbox-v2>
      <oc-checkbox-v2>
        <input type="checkbox" aria-label="oc-auto" name="brand" value="LG" />
        <label slot="label">LG</label>
      </oc-checkbox-v2>
      <oc-checkbox-v2>
        <input type="checkbox" aria-label="oc-auto" name="size" value="S" />
        <label slot="label">S</label>
      </oc-checkbox-v2>
      <oc-checkbox-v2>
        <input type="checkbox" aria-label="oc-auto" name="size" value="M" />
        <label slot="label">M</label>
      </oc-checkbox-v2>
    </form>`;
  },
  async play({
    canvasElement
  }) {
    const [brand1, brand2, size1, size2] = Array.from(canvasElement.getElementsByTagName("oc-checkbox-v2"));
    const getInput = (element: Element) => element.querySelector("input") as HTMLInputElement;
    await click(getInput(brand1));
    await click(getInput(size1));
    await click(getInput(brand2));
    await click(getInput(size2));
    const form = canvasElement.getElementsByTagName("form").item(0);

    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
    // @ts-ignore
    const data = Array.from(new FormData(form).entries());
    await expect(data).toStrictEqual([["brand", "Samsung"], ["brand", "LG"], ["size", "S"], ["size", "M"]]);
  }
}

Should Have Pointer Events Set

Story: components-checkbox-interaction-tests--should-have-pointer-events-set · tags:

V1 (v1, deprecated, not for generation)

Checkbox v1

Important

This is a deprecated version of the checkbox component. For the latest version, see the updated component documentation. Refer to this migration guide to update your project to the latest version.

The checkbox component allows users to select one or more options from a set, with properties for state, styling, and accessibility, and a slot for setting the checkbox label text.

Default variation
<oc-checkbox-v1 name="myName" value="on" label-placement="right" variant="default">some label</oc-checkbox-v1>
Configuration

The checkbox component offers a variety of styling variants such as default and error. This component is configurable, allowing you to tailor its features and appearance to your specific needs. To explore all the available options and adjust the component, use the component configurator and see the changes affect the component in real-time.

Usage guidelines

Before integrating the checkbox component into your project, make sure you have correctly installed the OTTO components package. Look through the variations page for examples of possible component variations. Here, you can discover both common and specific variations that address different use cases.

Layout considerations

This component has visual overflow. It extends beyond its bounding box and is clipped by parent containers with overflow: hidden. Ensure the parent container has sufficient padding to accommodate the component's full visual area.

Accessibility

The checkbox component comes with a set of built-in accessibility features to ensure a seamless experience for all users.

Important

If the checkbox component is used in a form, pressing Enter while focused on the checkbox triggers the submit action immediately (implicit submit) if an oc-button component or a normal HTML button with the type submit is also present in the form.

Use oc-aria-label

To make the checkbox recognizable for screen readers, use the oc-aria-label attribute to provide clear and descriptive context information. See the general accessibility documentation for guidance on using oc-aria-label, including how it works with link and masked link behavior.

V1/Configuration (v1, deprecated, not for generation)

Checkbox configuration

Configure the checkbox component with the controls below and see the changes live in the preview canvas. Click the Show code button within the preview canvas to see the source code for the current component configuration.

<oc-checkbox-v1 name="myName" value="on" label-placement="right" variant="default">some label</oc-checkbox-v1>

Interactive configurator (Storybook controls); every option is listed in the API section of this file.

V1/Interaction tests (CheckboxV1.form.interactions.stories.ts)

Interaction test stories (automated tests, not usage patterns).

Should Fire Keydown Event

Story: components-checkbox-v1-interaction-tests--should-fire-keydown-event · tags: play-fn

<form>
  <oc-checkbox-v1 name="name">{Enter}</oc-checkbox-v1>
  <button type="submit"></button>
</form>
Story source (TypeScript, verbatim from Storybook)
{
  render() {
    return html`
      <form>
        <oc-checkbox-v1 name="name">{Enter}</oc-checkbox-v1>
        <button type="submit"></button>
      </form>
    `;
  },
  async play({
    canvasElement
  }) {
    canvasElement.getElementsByTagName("form").item(0)!.addEventListener("submit", ev => ev.preventDefault());
    const checkbox = canvasElement.getElementsByTagName("oc-checkbox-v1").item(0)!;
    let fired = 0;
    checkbox.addEventListener("keydown", () => fired += 1);
    checkbox.dispatchEvent(new KeyboardEvent("keydown", {
      key: "Enter"
    }));
    await expect(fired, "keydown should be fired one time").toBe(1);
  }
}

Should Submit Like Native Inputs

Story: components-checkbox-v1-interaction-tests--should-submit-like-native-inputs · tags: play-fn

<form id="form1">
  <oc-checkbox-v1 name="name">{Enter}</oc-checkbox-v1>
  <button type="submit" data-should-submit></button>
  <button type="submit" data-should-not-submit></button>
</form>

<form id="form2">
  <oc-checkbox-v1 name="name">{Enter}</oc-checkbox-v1>
  <input type="submit" data-should-submit />
  <button type="submit" data-should-not-submit></button>
</form>

<form id="form3">
  <oc-checkbox-v1 name="name">{Enter}</oc-checkbox-v1>
  Should submit the form
</form>

<form id="form4">Should submit the form</form>
<oc-checkbox-v1 name="name" form="form4">{Enter}</oc-checkbox-v1>

<form id="form5">
  <button type="submit" data-should-submit></button>
  <button type="submit" data-should-not-submit></button>
</form>
<oc-checkbox-v1 name="name" form="form5">{Enter}</oc-checkbox-v1>

<form id="form6">
  <input type="submit" data-should-submit />
  <button type="submit" data-should-not-submit></button>
</form>
<oc-checkbox-v1 name="name" form="form6">{Enter}</oc-checkbox-v1>

<form id="form7">
  <oc-checkbox-v1 name="name">{Enter}</oc-checkbox-v1>
  <button type="submit" disabled data-should-not-submit></button>
  <button type="submit" data-should-not-submit></button>
</form>

<form id="form8">
  <oc-checkbox-v1 name="name">{Enter}</oc-checkbox-v1>
  <input type="submit" disabled data-should-not-submit />
  <input type="submit" data-should-not-submit />
</form>
Story source (TypeScript, verbatim from Storybook)
{
  render() {
    return html`
      <form id="form1">
        <oc-checkbox-v1 name="name">{Enter}</oc-checkbox-v1>
        <button type="submit" data-should-submit></button>
        <button type="submit" data-should-not-submit></button>
      </form>

      <form id="form2">
        <oc-checkbox-v1 name="name">{Enter}</oc-checkbox-v1>
        <input type="submit" data-should-submit />
        <button type="submit" data-should-not-submit></button>
      </form>

      <form id="form3">
        <oc-checkbox-v1 name="name">{Enter}</oc-checkbox-v1>
        Should submit the form
      </form>

      <form id="form4">Should submit the form</form>
      <oc-checkbox-v1 name="name" form="form4">{Enter}</oc-checkbox-v1>

      <form id="form5">
        <button type="submit" data-should-submit></button>
        <button type="submit" data-should-not-submit></button>
      </form>
      <oc-checkbox-v1 name="name" form="form5">{Enter}</oc-checkbox-v1>

      <form id="form6">
        <input type="submit" data-should-submit />
        <button type="submit" data-should-not-submit></button>
      </form>
      <oc-checkbox-v1 name="name" form="form6">{Enter}</oc-checkbox-v1>

      <form id="form7">
        <oc-checkbox-v1 name="name">{Enter}</oc-checkbox-v1>
        <button type="submit" disabled data-should-not-submit></button>
        <button type="submit" data-should-not-submit></button>
      </form>

      <form id="form8">
        <oc-checkbox-v1 name="name">{Enter}</oc-checkbox-v1>
        <input type="submit" disabled data-should-not-submit />
        <input type="submit" data-should-not-submit />
      </form>
    `;
  },
  async play({
    canvasElement
  }) {
    const submits: {
      form: string;
      submitter: HTMLElement | null;
    }[] = [];
    canvasElement.querySelectorAll("form").forEach(form => form.addEventListener("submit", ev => {
      ev.preventDefault();
      submits.push({
        form: form.id,
        submitter: ev.submitter
      });
    }));
    const checkboxes = Array.from(canvasElement.querySelectorAll(`oc-checkbox-v1[name="name"]`)) as (HTMLOcTextFieldV1Element & {
      internals: ElementInternals;
    })[];
    for (let i = 0; i < checkboxes.length; i += 1) {
      const checkbox = checkboxes[i];
      // eslint-disable-next-line no-await-in-loop
      await expect(checkbox.internals.form?.id, "checkbox should have correct associated form").toBe(`form${i + 1}`);
    }

    // eslint-disable-next-line no-restricted-syntax
    for (const checkbox of checkboxes) {
      const input = checkbox.shadowRoot!.querySelector("input")!;
      // eslint-disable-next-line no-await-in-loop
      input.dispatchEvent(new KeyboardEvent("keydown", {
        key: "Enter"
      }));
    }
    await expect(submits.length, "number of submits").toBe(6);
    await expect(submits[0].form).toBe("form1");
    await expect(submits[0].submitter?.dataset.shouldSubmit).toBeDefined();
    await expect(submits[1].form).toBe("form2");
    await expect(submits[1].submitter?.dataset.shouldSubmit).toBeDefined();
    await expect(submits[2].form).toBe("form3");
    await expect(submits[2].submitter).toBe(null);
    await expect(submits[3].form).toBe("form4");
    await expect(submits[3].submitter).toBe(null);
    await expect(submits[4].form).toBe("form5");
    await expect(submits[4].submitter?.dataset.shouldSubmit).toBeDefined();
    await expect(submits[5].form).toBe("form6");
    await expect(submits[5].submitter?.dataset.shouldSubmit).toBeDefined();
  }
}

V1/Interaction tests (CheckboxV1.interactions.stories.ts)

Interaction test stories (automated tests, not usage patterns).

Should Handle User Selection

Story: components-checkbox-v1-interaction-tests--should-handle-user-selection · tags: play-fn

<oc-checkbox-v1 name="brand" value="Samsung">Samsung</oc-checkbox-v1>
<oc-checkbox-v1 name="brand" value="LG">LG</oc-checkbox-v1>
<oc-checkbox-v1 name="size" value="S">S</oc-checkbox-v1>
<oc-checkbox-v1 name="size" value="M">M</oc-checkbox-v1>
Story source (TypeScript, verbatim from Storybook)
{
  parameters: {
    docs: {
      disable: true
    }
  },
  render() {
    return html`
      <oc-checkbox-v1 name="brand" value="Samsung">Samsung</oc-checkbox-v1>
      <oc-checkbox-v1 name="brand" value="LG">LG</oc-checkbox-v1>
      <oc-checkbox-v1 name="size" value="S">S</oc-checkbox-v1>
      <oc-checkbox-v1 name="size" value="M">M</oc-checkbox-v1>
    `;
  },
  async play({
    canvasElement
  }) {
    const [brand1, brand2, size1, size2] = Array.from(canvasElement.getElementsByTagName("oc-checkbox-v1"));
    await expect(brand1.checked).toBeFalsy();
    await expect(brand2.checked).toBeFalsy();
    await expect(size1.checked).toBeFalsy();
    await expect(size2.checked).toBeFalsy();
    await click(deepQuerySelector(brand1, ".checkbox")!);
    await click(deepQuerySelector(size1, ".checkbox")!);
    await expect(brand1.checked).toBeTruthy();
    await expect(brand2.checked).toBeFalsy();
    await expect(size1.checked).toBeTruthy();
    await expect(size2.checked).toBeFalsy();
    await click(deepQuerySelector(brand2, ".checkbox")!);
    await click(deepQuerySelector(size2, ".checkbox")!);
    await expect(brand1.checked).toBeTruthy();
    await expect(brand2.checked).toBeTruthy();
    await expect(size1.checked).toBeTruthy();
    await expect(size2.checked).toBeTruthy();
    await click(deepQuerySelector(brand1, ".checkbox")!);
    await click(deepQuerySelector(size1, ".checkbox")!);
    await click(deepQuerySelector(brand2, ".checkbox")!);
    await click(deepQuerySelector(size2, ".checkbox")!);
    await expect(brand1.checked).toBeFalsy();
    await expect(brand2.checked).toBeFalsy();
    await expect(size1.checked).toBeFalsy();
    await expect(size2.checked).toBeFalsy();
  }
}

Should Be Accessible With Keyboard

Story: components-checkbox-v1-interaction-tests--should-be-accessible-with-keyboard · tags: play-fn

<oc-checkbox-v1 name="size" value="S">S</oc-checkbox-v1>
<oc-checkbox-v1 name="size" value="M">M</oc-checkbox-v1>
<oc-checkbox-v1 name="size" value="L">L</oc-checkbox-v1>
Story source (TypeScript, verbatim from Storybook)
{
  parameters: {
    docs: {
      disable: true
    }
  },
  render() {
    return html`
      <oc-checkbox-v1 name="size" value="S">S</oc-checkbox-v1>
      <oc-checkbox-v1 name="size" value="M">M</oc-checkbox-v1>
      <oc-checkbox-v1 name="size" value="L">L</oc-checkbox-v1>
    `;
  },
  async play({
    canvasElement
  }) {
    const [size1, size2, size3] = Array.from(canvasElement.getElementsByTagName(`oc-checkbox-v1`));
    size1.focus();
    await expect(size1).toHaveFocus();
    await expect(size1.checked).toBeFalsy();
    await expect(size2.checked).toBeFalsy();
    await expect(size3.checked).toBeFalsy();
    await click();
    await expect(size1.checked).toBeTruthy();
    await expect(size2.checked).toBeFalsy();
    await expect(size3.checked).toBeFalsy();
    await tab();
    await expect(size2).toHaveFocus();
    await expect(size1.checked).toBeTruthy();
    await expect(size2.checked).toBeFalsy();
    await expect(size3.checked).toBeFalsy();
    await click();
    await expect(size1.checked).toBeTruthy();
    await expect(size2.checked).toBeTruthy();
    await expect(size3.checked).toBeFalsy();
    await tab();
    await expect(size3).toHaveFocus();
    await click();
    await expect(size1.checked).toBeTruthy();
    await expect(size2.checked).toBeTruthy();
    await expect(size3.checked).toBeTruthy();
    await tab(true);
    await click();
    await expect(size1.checked).toBeTruthy();
    await expect(size2.checked).toBeFalsy();
    await expect(size3.checked).toBeTruthy();
  }
}

Should Have Correct Form Data

Story: components-checkbox-v1-interaction-tests--should-have-correct-form-data · tags: play-fn

<form>
  <oc-checkbox-v1 name="brand" value="Samsung">Samsung</oc-checkbox-v1>
  <oc-checkbox-v1 name="brand" value="LG">LG</oc-checkbox-v1>
  <oc-checkbox-v1 name="size" value="S">S</oc-checkbox-v1>
  <oc-checkbox-v1 name="size" value="M">M</oc-checkbox-v1>
</form>
Story source (TypeScript, verbatim from Storybook)
{
  parameters: {
    docs: {
      disable: true
    }
  },
  render() {
    return html` <form>
      <oc-checkbox-v1 name="brand" value="Samsung">Samsung</oc-checkbox-v1>
      <oc-checkbox-v1 name="brand" value="LG">LG</oc-checkbox-v1>
      <oc-checkbox-v1 name="size" value="S">S</oc-checkbox-v1>
      <oc-checkbox-v1 name="size" value="M">M</oc-checkbox-v1>
    </form>`;
  },
  async play({
    canvasElement
  }) {
    const [brand1, brand2, size1, size2] = Array.from(canvasElement.getElementsByTagName("oc-checkbox-v1"));
    await click(deepQuerySelector(brand1, ".checkbox")!);
    await click(deepQuerySelector(size1, ".checkbox")!);
    await click(deepQuerySelector(brand2, ".checkbox")!);
    await click(deepQuerySelector(size2, ".checkbox")!);
    const form = canvasElement.getElementsByTagName("form").item(0);

    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
    // @ts-ignore
    const data = Array.from(new FormData(form).entries());
    await expect(data).toStrictEqual([["brand", "Samsung"], ["brand", "LG"], ["size", "S"], ["size", "M"]]);
  }
}

Handle Default Value And Default Checked

Story: components-checkbox-v1-interaction-tests--handle-default-value-and-default-checked · tags: play-fn

<oc-checkbox-v1>no defaults set</oc-checkbox-v1>
<oc-checkbox-v1 value="test" checked>Test Label</oc-checkbox-v1>
Story source (TypeScript, verbatim from Storybook)
{
  parameters: {
    chromatic: {
      disableSnapshot: true
    }
  },
  render() {
    return html`
      <oc-checkbox-v1>no defaults set</oc-checkbox-v1>
      <oc-checkbox-v1 value="test" checked>Test Label</oc-checkbox-v1>
    `;
  },
  async play({
    canvasElement
  }) {
    const [sutWithoutAttributes, sutWithAttributes] = Array.from(canvasElement.getElementsByTagName("oc-checkbox-v1")) as (HTMLOcCheckboxV1Element & {
      defaultChecked: boolean;
      defaultValue: string;
    })[];

    // TEST sutWithoutAttributes
    await expect(sutWithoutAttributes.checked).toBeFalsy();
    await expect(sutWithoutAttributes.defaultChecked).toBeFalsy();
    await expect(sutWithoutAttributes.hasAttribute("checked")).toBeFalsy();
    await expect(sutWithoutAttributes.value).toBe("on");
    await expect(sutWithoutAttributes.defaultValue).toBe("");
    await expect(sutWithoutAttributes.hasAttribute("value")).toBeFalsy();
    sutWithoutAttributes.checked = true;
    sutWithoutAttributes.value = "new on";
    await expect(sutWithoutAttributes.checked).toBeTruthy();
    await expect(sutWithoutAttributes.defaultChecked).toBeFalsy();
    await expect(sutWithoutAttributes.hasAttribute("checked")).toBeFalsy();
    await expect(sutWithoutAttributes.value).toBe("new on");
    // await expect(sutWithoutAttributes.defaultValue).toBe("new on");
    // await expect(sutWithoutAttributes.getAttribute("value")).toBe("new on");

    // TEST sutWithAttributes
    await expect(sutWithAttributes.checked).toBeTruthy();
    await expect(sutWithAttributes.defaultChecked).toBeTruthy();
    await expect(sutWithAttributes.hasAttribute("checked")).toBeTruthy();
    await expect(sutWithAttributes.value).toBe("test");
    await expect(sutWithAttributes.defaultValue).toBe("test");
    await expect(sutWithAttributes.getAttribute("value")).toBe("test");
    sutWithAttributes.checked = false;
    sutWithAttributes.value = "new test";
    await expect(sutWithAttributes.checked).toBeFalsy();
    await expect(sutWithAttributes.defaultChecked).toBeTruthy();
    await expect(sutWithAttributes.hasAttribute("checked")).toBeTruthy();
    await expect(sutWithAttributes.value).toBe("new test");
    // await expect(sutWithAttributes.defaultValue).toBe("new test");
    // await expect(sutWithAttributes.getAttribute("value")).toBe("new test");

    // TEST programmatic created
    const sutCreated = document.createElement("oc-checkbox-v1") as HTMLOcCheckboxV1Element & {
      defaultChecked: boolean;
      defaultValue: string;
    };
    sutCreated.innerText = "created";
    sutCreated.checked = true;
    canvasElement.append(sutCreated);
    await expect(sutCreated.checked).toBeTruthy();
    await expect(sutCreated.defaultChecked).toBeFalsy();
    await expect(sutCreated.hasAttribute("checked")).toBeFalsy();
    await expect(sutCreated.value).toBe("on");
    await expect(sutCreated.defaultValue).toBe("");
    await expect(sutCreated.hasAttribute("value")).toBeFalsy();
    sutCreated.defaultChecked = true;
    sutCreated.defaultValue = "foo value";
    await expect(sutCreated.checked).toBeTruthy();
    await expect(sutCreated.defaultChecked).toBeTruthy();
    await expect(sutCreated.hasAttribute("checked")).toBeTruthy();
    await expect(sutCreated.value).toBe("foo value");
    await expect(sutCreated.defaultValue).toBe("foo value");
    await expect(sutCreated.getAttribute("value")).toBe("foo value");
  }
}

V1/Variations (v1, deprecated, not for generation)

Variations

Listed below are the most common variations of the checkbox component as well as specific component variations for different use cases.

You can explore all available options using the component configurator, adjust the component to your needs, and see the changes live in a preview canvas.

Default

Story: components-checkbox-v1-variations--default · tags: components

The default configuration uses the default variant with the default value "on".

<oc-checkbox-v1 name="myName" value="on" label-placement="right" variant="default">some label</oc-checkbox-v1>
Label placement: left

Story: components-checkbox-v1-variations--label-placement-left · tags: components

The default configuration uses the default variant with the default value "on" as well as the label placed on the left hand side.

Args: label-placement=left

<oc-checkbox-v1 name="myName" value="on" label-placement="left" variant="default">some label</oc-checkbox-v1>
Error

Story: components-checkbox-v1-variations--error · tags: components

The error variant uses the color red to indicate that the checkbox is in an error state.

Args: variant=error

<oc-checkbox-v1 name="myName" value="on" label-placement="right" variant="error">some label</oc-checkbox-v1>
Disabled

Story: components-checkbox-v1-variations--disabled · tags: components

The default variant with the disabled attribute set to true, preventing user interaction.

Args: disabled=true

<oc-checkbox-v1 name="myName" value="on" label-placement="right" variant="default" disabled>
  some label
</oc-checkbox-v1>
Demo form

Story: components-checkbox-v1-variations--demo-form · tags: components

The Demo form showcases a form with multiple checkboxes, a reset button to uncheck all checkboxes, and a submit button.

<form novalidate>
  <fieldset class="oc-mb-150">
    <legend>Marke*</legend>
    <oc-form-group-v1 orientation="horizontal" flex-behavior="shrink">
      <oc-checkbox-v1 name="brand" value="Samsung">Samsung</oc-checkbox-v1>
      <oc-checkbox-v1 name="brand" value="LG">LG</oc-checkbox-v1>
      <oc-checkbox-v1 name="brand" value="Philips">Philips</oc-checkbox-v1>
      <oc-checkbox-v1 name="brand" value="Sony">Sony</oc-checkbox-v1>
      <oc-checkbox-v1 name="brand" value="Hisense">Hisense</oc-checkbox-v1>
    </oc-form-group-v1>
  </fieldset>
  <fieldset class="oc-mb-150">
    <legend>Größe*</legend>
    <oc-form-group-v1 orientation="horizontal" flex-behavior="shrink">
      <oc-checkbox-v1 name="size" value="S">S</oc-checkbox-v1>
      <oc-checkbox-v1 name="size" value="M">M</oc-checkbox-v1>
      <oc-checkbox-v1 name="size" value="L">L</oc-checkbox-v1>
      <oc-checkbox-v1 name="size" value="XL">XL</oc-checkbox-v1>
      <oc-checkbox-v1 name="size" value="XXL">XXL</oc-checkbox-v1>
    </oc-form-group-v1>
  </fieldset>
  <fieldset class="oc-mb-150">
    <legend>Lorem*</legend>
    <oc-form-group-v1 orientation="horizontal" flex-behavior="shrink">
      <oc-checkbox-v1 name="lorem" value="1"
        >Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod
        tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero
        eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea
        takimata sanctus est Lorem ipsum dolor sit amet.</oc-checkbox-v1
      >
      <oc-checkbox-v1 name="lorem" value="2"
        >At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren,
        no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet,
        consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et
        dolore magna aliquyam erat, sed diam voluptua.</oc-checkbox-v1
      >
      <oc-checkbox-v1 name="lorem" value="3"
        >Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
        Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor
        invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.</oc-checkbox-v1
      >
    </oc-form-group-v1>
  </fieldset>
  <div style="display: flex; gap: 16px">
    <oc-button-v1 variant="secondary" type="reset">Reset</oc-button-v1>
    <oc-button-v1 type="submit">Submit</oc-button-v1>
  </div>
</form>

<script>
  (() => {
    const [form] = document.getElementsByTagName("form");
    const [group1, group2, group3] = document.getElementsByTagName("oc-form-group-v1");

    form.addEventListener("change", async () => {
      // wait for the next microtask to get the updated checked state
      await new Promise((resolve) => setTimeout(resolve));

      let formData = new FormData(form);
      if (formData.has("brand")) group1.validationMessage = "";
      if (formData.has("size")) group2.validationMessage = "";
      if (formData.has("lorem")) group3.validationMessage = "";
    });

    form.addEventListener("submit", (ev) => {
      let formData = new FormData(form);

      group1.validationMessage = formData.has("brand") ? "" : "Please select a brand.";
      group2.validationMessage = formData.has("size") ? "" : "Please select a size.";
      group3.validationMessage = formData.has("lorem") ? "" : "Please select a lorem.";

      if (formData.has("brand") && formData.has("size") && formData.has("lorem")) {
        let data = "";
        formData.forEach((value, key) => (data += key + "=" + value + "\\n"));
        console.log(data);
        alert("sending:\\n" + data);
      }

      ev.preventDefault();
    });
  })();
</script>
Story source (TypeScript, verbatim from Storybook)
{
  name: "Demo form",
  parameters: {
    controls: {
      disabled: true
    }
  },
  argTypes: hideControlsBadge(Metadata),
  render() {
    return html`
      <form novalidate>
        <fieldset class="oc-mb-150">
          <legend>Marke*</legend>
          <oc-form-group-v1 orientation="horizontal" flex-behavior="shrink">
            <oc-checkbox-v1 name="brand" value="Samsung">Samsung</oc-checkbox-v1>
            <oc-checkbox-v1 name="brand" value="LG">LG</oc-checkbox-v1>
            <oc-checkbox-v1 name="brand" value="Philips">Philips</oc-checkbox-v1>
            <oc-checkbox-v1 name="brand" value="Sony">Sony</oc-checkbox-v1>
            <oc-checkbox-v1 name="brand" value="Hisense">Hisense</oc-checkbox-v1>
          </oc-form-group-v1>
        </fieldset>
        <fieldset class="oc-mb-150">
          <legend>Größe*</legend>
          <oc-form-group-v1 orientation="horizontal" flex-behavior="shrink">
            <oc-checkbox-v1 name="size" value="S">S</oc-checkbox-v1>
            <oc-checkbox-v1 name="size" value="M">M</oc-checkbox-v1>
            <oc-checkbox-v1 name="size" value="L">L</oc-checkbox-v1>
            <oc-checkbox-v1 name="size" value="XL">XL</oc-checkbox-v1>
            <oc-checkbox-v1 name="size" value="XXL">XXL</oc-checkbox-v1>
          </oc-form-group-v1>
        </fieldset>
        <fieldset class="oc-mb-150">
          <legend>Lorem*</legend>
          <oc-form-group-v1 orientation="horizontal" flex-behavior="shrink">
            <oc-checkbox-v1 name="lorem" value="1"
              >Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod
              tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero
              eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea
              takimata sanctus est Lorem ipsum dolor sit amet.</oc-checkbox-v1
            >
            <oc-checkbox-v1 name="lorem" value="2"
              >At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren,
              no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet,
              consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et
              dolore magna aliquyam erat, sed diam voluptua.</oc-checkbox-v1
            >
            <oc-checkbox-v1 name="lorem" value="3"
              >Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
              Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor
              invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.</oc-checkbox-v1
            >
          </oc-form-group-v1>
        </fieldset>
        <div style="display: flex; gap: 16px">
          <oc-button-v1 variant="secondary" type="reset">Reset</oc-button-v1>
          <oc-button-v1 type="submit">Submit</oc-button-v1>
        </div>
      </form>

      <script>
        (() => {
          const [form] = document.getElementsByTagName("form");
          const [group1, group2, group3] = document.getElementsByTagName("oc-form-group-v1");

          form.addEventListener("change", async () => {
            // wait for the next microtask to get the updated checked state
            await new Promise((resolve) => setTimeout(resolve));

            let formData = new FormData(form);
            if (formData.has("brand")) group1.validationMessage = "";
            if (formData.has("size")) group2.validationMessage = "";
            if (formData.has("lorem")) group3.validationMessage = "";
          });

          form.addEventListener("submit", (ev) => {
            let formData = new FormData(form);

            group1.validationMessage = formData.has("brand") ? "" : "Please select a brand.";
            group2.validationMessage = formData.has("size") ? "" : "Please select a size.";
            group3.validationMessage = formData.has("lorem") ? "" : "Please select a lorem.";

            if (formData.has("brand") && formData.has("size") && formData.has("lorem")) {
              let data = "";
              formData.forEach((value, key) => (data += key + "=" + value + "\\n"));
              console.log(data);
              alert("sending:\\n" + data);
            }

            ev.preventDefault();
          });
        })();
      </script>
    `;
  }
}
Demo: filter

Story: components-checkbox-v1-variations--demo-filter · tags: components

The demo filter showcases how to use Checkboxes for filtering.

<div lang="de">
  <oc-form-group-v1 orientation="vertical">
    <oc-checkbox-v1
      name="zielgruppe"
      value="Damen"
      oc-aria-label="Damen 374637 Treffer"
      class="flex-label"
      >Damen<span class="number">374637</span>
    </oc-checkbox-v1>
    <oc-checkbox-v1
      name="zielgruppe"
      value="Herren"
      oc-aria-label="Herren 234324 Treffer"
      class="flex-label"
      >Herren<span class="number">234324</span>
    </oc-checkbox-v1>
    <oc-checkbox-v1
      name="zielgruppe"
      value="Mädchen"
      oc-aria-label="Mädchen 2323 Treffer"
      class="flex-label"
      >Mädchen<span class="number">2323</span>
    </oc-checkbox-v1>
    <oc-checkbox-v1
      name="zielgruppe"
      value="Jungen"
      oc-aria-label="Jungen 1224 Treffer"
      class="flex-label"
      >Jungen<span class="number">1224</span>
    </oc-checkbox-v1>
  </oc-form-group-v1>

  <oc-form-group-v1 class="oc-mt-200" orientation="vertical">
    <oc-checkbox-v1
      label-placement="left"
      name="zielgruppe"
      value="Damen"
      oc-aria-label="Damen 374637 Treffer"
      class="flex-label"
      >Damen<span class="number">374637</span>
    </oc-checkbox-v1>
    <oc-checkbox-v1
      label-placement="left"
      name="zielgruppe"
      value="Herren"
      oc-aria-label="Herren 234324 Treffer"
      class="flex-label"
      >Herren<span class="number">234324</span>
    </oc-checkbox-v1>
    <oc-checkbox-v1
      label-placement="left"
      name="zielgruppe"
      value="Mädchen"
      oc-aria-label="Mädchen 2323 Treffer"
      class="flex-label"
      >Mädchen<span class="number">2323</span>
    </oc-checkbox-v1>
    <oc-checkbox-v1
      label-placement="left"
      name="zielgruppe"
      value="Jungen"
      oc-aria-label="Jungen 1224 Treffer"
      class="flex-label"
      >Jungen<span class="number">1224</span>
    </oc-checkbox-v1>
  </oc-form-group-v1>
</div>

<style>
  .flex-label::part(default) {
    display: flex;
    justify-content: space-between;
  }

  .number {
    color: var(--oc-base-color-gray-200);
  }
</style>
Story source (TypeScript, verbatim from Storybook)
{
  name: "Demo: filter",
  parameters: {
    controls: {
      disabled: true
    }
  },
  argTypes: hideControlsBadge(Metadata),
  render() {
    return html`
      <div lang="de">
        <oc-form-group-v1 orientation="vertical">
          <oc-checkbox-v1
            name="zielgruppe"
            value="Damen"
            oc-aria-label="Damen 374637 Treffer"
            class="flex-label"
            >Damen<span class="number">374637</span>
          </oc-checkbox-v1>
          <oc-checkbox-v1
            name="zielgruppe"
            value="Herren"
            oc-aria-label="Herren 234324 Treffer"
            class="flex-label"
            >Herren<span class="number">234324</span>
          </oc-checkbox-v1>
          <oc-checkbox-v1
            name="zielgruppe"
            value="Mädchen"
            oc-aria-label="Mädchen 2323 Treffer"
            class="flex-label"
            >Mädchen<span class="number">2323</span>
          </oc-checkbox-v1>
          <oc-checkbox-v1
            name="zielgruppe"
            value="Jungen"
            oc-aria-label="Jungen 1224 Treffer"
            class="flex-label"
            >Jungen<span class="number">1224</span>
          </oc-checkbox-v1>
        </oc-form-group-v1>

        <oc-form-group-v1 class="oc-mt-200" orientation="vertical">
          <oc-checkbox-v1
            label-placement="left"
            name="zielgruppe"
            value="Damen"
            oc-aria-label="Damen 374637 Treffer"
            class="flex-label"
            >Damen<span class="number">374637</span>
          </oc-checkbox-v1>
          <oc-checkbox-v1
            label-placement="left"
            name="zielgruppe"
            value="Herren"
            oc-aria-label="Herren 234324 Treffer"
            class="flex-label"
            >Herren<span class="number">234324</span>
          </oc-checkbox-v1>
          <oc-checkbox-v1
            label-placement="left"
            name="zielgruppe"
            value="Mädchen"
            oc-aria-label="Mädchen 2323 Treffer"
            class="flex-label"
            >Mädchen<span class="number">2323</span>
          </oc-checkbox-v1>
          <oc-checkbox-v1
            label-placement="left"
            name="zielgruppe"
            value="Jungen"
            oc-aria-label="Jungen 1224 Treffer"
            class="flex-label"
            >Jungen<span class="number">1224</span>
          </oc-checkbox-v1>
        </oc-form-group-v1>
      </div>

      <style>
        .flex-label::part(default) {
          display: flex;
          justify-content: space-between;
        }

        .number {
          color: var(--oc-base-color-gray-200);
        }
      </style>
    `;
  }
}