All components
Checkbox
Binary selection controls with outline, solid, and subtle boxes and a paired label.
12345678910111213141516import { Checkbox } from "#components/ui/base-ui/checkbox.tsx";import { indigoPalette } from "@stylexblocks/ui/themes/palettes.ts";
export function Example() { return ( // Root is the label wrapper; Control is Base UI's Checkbox.Root and takes // the checked props — checked styling resolves via Base UI's state-aware // className callback <Checkbox.Root variant="solid" size="md" palette={indigoPalette}> <Checkbox.Control defaultChecked /> <Checkbox.Label>Email notifications</Checkbox.Label> </Checkbox.Root> );}
// controlled: <Checkbox.Control checked={checked} onCheckedChange={setChecked} />Variants
outline
solid
subtle
Sizes
xs
sm
md
lg
Install
1npx stylexblocks add checkboxSource
components/ui/base-ui/checkbox.tsx
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261import * as stylex from "@stylexjs/stylex";import { Checkbox as BaseCheckbox } from "@base-ui-components/react/checkbox";import { mergeProps } from "@base-ui-components/react/merge-props";import { createSlotRecipeContext } from "#lib/base-ui/create-slot-recipe-context.tsx";import { defineSlotRecipe, fromMap, resolveSlotStyles,} from "#lib/recipe-props.ts";import { colorPalette } from '@stylexblocks/ui/color-palette.stylex.ts'import { colors } from '@stylexblocks/ui/tokens/colors.stylex.ts'import { fontSizes } from '@stylexblocks/ui/tokens/font-sizes.stylex.ts'import { fontWeights } from '@stylexblocks/ui/tokens/font-weights.stylex.ts'import { lineHeights } from '@stylexblocks/ui/tokens/line-heights.stylex.ts'import { semanticColors } from '@stylexblocks/ui/semantic-tokens/colors.stylex.ts'import { semanticRadii } from '@stylexblocks/ui/semantic-tokens/radii.stylex.ts'import { sizes } from '@stylexblocks/ui/tokens/sizes.stylex.ts'import { spacing } from '@stylexblocks/ui/tokens/spacing.stylex.ts'
const checkboxRoot = stylex.create({ base: { display: 'inline-flex', gap: spacing._2, alignItems: 'center', verticalAlign: 'top', position: 'relative', },})
const checkboxRootSizes = stylex.create({ xs: { gap: spacing._1_5, }, sm: { gap: spacing._2, }, md: { gap: spacing._2_5, }, lg: { gap: spacing._3, },})
const checkboxControl = stylex.create({ base: { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0, color: colors.white, borderWidth: '1px', borderColor: { default: 'transparent', ':is([data-invalid], [aria-invalid="true"])': semanticColors.borderError, }, borderRadius: semanticRadii.control, outlineWidth: { default: null, ':focus-visible': '1px', }, outlineOffset: { default: null, ':focus-visible': '2px', }, outlineStyle: { default: null, ':focus-visible': 'solid', }, outlineColor: { default: null, ':focus-visible': colorPalette.focusRing, }, opacity: { default: null, ':is(:disabled, [disabled], [data-disabled])': 0.5, }, },})
const checkboxControlSizes = stylex.create({ xs: { width: sizes._3, height: sizes._3, }, sm: { width: sizes._3_5, height: sizes._3_5, }, md: { width: sizes._4, height: sizes._4, padding: spacing._0_5, }, lg: { width: sizes._5, height: sizes._5, padding: spacing._0_5, },})
const checkboxControlVariants = stylex.create({ outline: { borderColor: { default: semanticColors.borderEmphasized, ':is([data-invalid], [aria-invalid="true"])': semanticColors.borderError, }, }, solid: { borderColor: { default: semanticColors.borderEmphasized, ':is([data-invalid], [aria-invalid="true"])': semanticColors.borderError, }, }, subtle: { backgroundColor: colorPalette.muted, borderColor: { default: colorPalette.emphasized, ':is([data-invalid], [aria-invalid="true"])': semanticColors.borderError, }, },})
const checkboxLabel = stylex.create({ base: { fontWeight: fontWeights.medium, userSelect: 'none', opacity: { default: null, ':is(:disabled, [disabled], [data-disabled])': 0.5, }, },})
const checkboxLabelSizes = stylex.create({ xs: { fontSize: fontSizes.xs, lineHeight: lineHeights.xs, }, sm: { fontSize: fontSizes.sm, lineHeight: lineHeights.sm, }, md: { fontSize: fontSizes.sm, lineHeight: lineHeights.sm, }, lg: { fontSize: fontSizes.md, lineHeight: lineHeights.md, },})
export type CheckboxSize = keyof typeof checkboxRootSizes
export type CheckboxVariant = keyof typeof checkboxControlVariants
const checkboxRecipe = defineSlotRecipe({ slots: { root: { styles: checkboxRoot, sizes: checkboxRootSizes }, control: { styles: checkboxControl, sizes: checkboxControlSizes, variants: checkboxControlVariants, }, label: { styles: checkboxLabel, sizes: checkboxLabelSizes }, }, defaultVariants: { variant: "solid", size: "md" },});
// Checked-state chrome keyed off Base UI's `data-checked` — the generated// recipe does not style the checked fill.const checkboxChecked = stylex.create({ outline: { color: { default: null, ":is([data-checked])": colorPalette.fg }, borderColor: { default: null, ":is([data-checked])": colorPalette.solid }, }, solid: { backgroundColor: { default: null, ":is([data-checked])": colorPalette.solid }, borderColor: { default: null, ":is([data-checked])": colorPalette.solid }, color: { default: null, ":is([data-checked])": colorPalette.contrast }, }, subtle: { color: { default: null, ":is([data-checked])": colorPalette.fg }, },});
const chrome = stylex.create({ cursorPointer: { cursor: "pointer" }, iconFill: { width: "100%", height: "100%" }, indicator: { display: "inline-flex", width: "100%", height: "100%", alignItems: "center", justifyContent: "center", },});
function CheckIcon() { return ( <svg viewBox="0 0 12 12" fill="none" aria-hidden="true" {...stylex.props(chrome.iconFill)}> <path d="M2.5 6.4 5 8.9l4.5-5.4" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" /> </svg> );}
function ControlNode({ children, ...props}: React.ComponentProps<typeof BaseCheckbox.Root>) { return <BaseCheckbox.Root {...props}>{children ?? <Indicator />}</BaseCheckbox.Root>;}
export type CheckboxIndicatorProps = BaseCheckbox.Indicator.Props;
/** Mounted only while checked — defaults to the check icon. */function Indicator({ children, ...props }: CheckboxIndicatorProps) { return ( <BaseCheckbox.Indicator {...mergeProps(props, stylex.props(chrome.indicator))}> {children ?? <CheckIcon />} </BaseCheckbox.Indicator> );}
const { withProvider, withContext } = createSlotRecipeContext(checkboxRecipe);
/** * Checkbox — Base UI's checkbox wearing the checkbox slot recipe. The Root * label resolves variant and size once; Control (Base UI's Checkbox.Root) * and Label inherit them through context. Checked chrome targets the * engine's `data-checked`. */export const Checkbox = { Root: withProvider( "label", (v) => [...resolveSlotStyles(checkboxRecipe.slots.root, v), chrome.cursorPointer], "CheckboxRoot", ), Control: withContext( ControlNode, (v) => [ ...resolveSlotStyles(checkboxRecipe.slots.control, v), fromMap(checkboxChecked, v.variant), ], "CheckboxControl", ), Indicator, Label: withContext("span", "label", "CheckboxLabel"),};
export type CheckboxRootProps = React.ComponentProps<typeof Checkbox.Root>;export type CheckboxControlProps = React.ComponentProps<typeof Checkbox.Control>;export type CheckboxLabelProps = React.ComponentProps<typeof Checkbox.Label>;