Skip to content
Navigation

Checkbox

Added 14 October 2026

React

Codecomponents/react/checkbox-demo.tsx
import {useState} from 'react';
import {Checkbox, Label} from '@stroeer-labs/design-library/react';

function IndeterminateCheckbox() {
    // Base UI's `indeterminate` is a controlled prop, so we clear it on the first toggle —
    // this mirrors the Angular brain checkbox, which resets indeterminate when clicked.
    const [checked, setChecked] = useState(false);
    const [indeterminate, setIndeterminate] = useState(true);

    return (
        <Checkbox
            id="terms-indeterminate"
            checked={checked}
            indeterminate={indeterminate}
            onCheckedChange={(value) => {
                setChecked(value);
                setIndeterminate(false);
            }}
        />
    );
}

export function CheckboxDemo() {
    return (
        <div className="space-y-6">
            <div className="space-y-2">
                <p className="text-xs font-medium text-muted-foreground">Default</p>
                <div className="flex items-center gap-3">
                    <Checkbox id="terms" />
                    <Label htmlFor="terms">Accept terms and conditions</Label>
                </div>
            </div>
            <div className="space-y-2">
                <p className="text-xs font-medium text-muted-foreground">Checked with description</p>
                <div className="flex items-start gap-3">
                    <Checkbox id="terms-2" defaultChecked aria-describedby="terms-2-description" />
                    <div className="grid gap-1.5">
                        <Label htmlFor="terms-2">Accept terms and conditions</Label>
                        <p id="terms-2-description" className="text-sm text-muted-foreground">
                            By clicking this checkbox, you agree to the terms and conditions.
                        </p>
                    </div>
                </div>
            </div>
            <div className="space-y-2">
                <p className="text-xs font-medium text-muted-foreground">Indeterminate</p>
                <div className="flex items-center gap-3">
                    <IndeterminateCheckbox />
                    <Label htmlFor="terms-indeterminate">Select all</Label>
                </div>
            </div>
            <div className="space-y-2">
                <p className="text-xs font-medium text-muted-foreground">Disabled</p>
                <div className="flex items-center gap-3">
                    <Checkbox id="terms-disabled" disabled />
                    <Label htmlFor="terms-disabled">Enable notifications</Label>
                </div>
            </div>
            <div className="space-y-2">
                <p className="text-xs font-medium text-muted-foreground">Card</p>
                {/* Selectable card: clicking the whole label toggles the nested checkbox, and the card
                    fills in when checked via has-[[aria-checked=true]]. Upstream shadcn/spartan hardcode
                    blue-* here; we map to semantic tokens (bg-muted highlight, default brand-interactive
                    checkbox) per the no-built-in-palette rule. */}
                <Label className="flex items-start gap-3 rounded-lg border border-border/50 p-3 hover:bg-muted/50 has-[[aria-checked=true]]:bg-muted">
                    <Checkbox id="toggle-2" defaultChecked />
                    <div className="grid gap-1.5 font-normal">
                        <p className="text-sm leading-none font-medium">Enable notifications</p>
                        <p className="text-sm text-muted-foreground">You can enable or disable notifications at any time.</p>
                    </div>
                </Label>
            </div>
        </div>
    );
}

Angular

Codecomponents/angular/checkbox-demo.ts
import '@angular/compiler';
import {Component} from '@angular/core';
import {HlmCheckboxImports, HlmLabelImports} from '@stroeer-labs/design-library/angular';

@Component({
    selector: 'dl-checkbox-demo',
    standalone: true,
    imports: [HlmCheckboxImports, HlmLabelImports],
    template: `
    <div class="space-y-6">
      <div class="space-y-2">
        <p class="text-xs font-medium text-muted-foreground">Default</p>
        <div class="flex items-center gap-3">
          <hlm-checkbox inputId="terms" />
          <label hlmLabel for="terms">Accept terms and conditions</label>
        </div>
      </div>
      <div class="space-y-2">
        <p class="text-xs font-medium text-muted-foreground">Checked with description</p>
        <div class="flex items-start gap-3">
          <hlm-checkbox inputId="terms-2" [checked]="true" aria-describedby="terms-2-description" />
          <div class="grid gap-1.5">
            <label hlmLabel for="terms-2">Accept terms and conditions</label>
            <p id="terms-2-description" class="text-sm text-muted-foreground">By clicking this checkbox, you agree to the terms and conditions.</p>
          </div>
        </div>
      </div>
      <div class="space-y-2">
        <p class="text-xs font-medium text-muted-foreground">Indeterminate</p>
        <div class="flex items-center gap-3">
          <hlm-checkbox inputId="terms-indeterminate" [(indeterminate)]="selectAllIndeterminate" />
          <label hlmLabel for="terms-indeterminate">Select all</label>
        </div>
      </div>
      <div class="space-y-2">
        <p class="text-xs font-medium text-muted-foreground">Disabled</p>
        <div class="flex items-center gap-3">
          <hlm-checkbox inputId="terms-disabled" disabled />
          <label hlmLabel for="terms-disabled">Enable notifications</label>
        </div>
      </div>
      <div class="space-y-2">
        <p class="text-xs font-medium text-muted-foreground">Card</p>
        <!-- Selectable card: clicking the whole label toggles the nested checkbox, and the card fills
             in when checked via has-[[aria-checked=true]]. Upstream shadcn/spartan hardcode blue-* here;
             we map to semantic tokens (bg-muted highlight, default brand-interactive checkbox) per the
             no-built-in-palette rule. Mirrors the React card demo exactly. -->
        <label hlmLabel class="flex items-start gap-3 rounded-lg border border-border/50 p-3 hover:bg-muted/50 has-[[aria-checked=true]]:bg-muted">
          <hlm-checkbox inputId="toggle-2" [checked]="true" />
          <div class="grid gap-1.5 font-normal">
            <p class="text-sm leading-none font-medium">Enable notifications</p>
            <p class="text-sm text-muted-foreground">You can enable or disable notifications at any time.</p>
          </div>
        </label>
      </div>
    </div>
  `,
})
export class CheckboxDemoComponent {
    // Two-way bound so brain's toggle() clearing indeterminate persists — mirrors the React demo.
    protected selectAllIndeterminate = true;
}