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;
}