All components
Checkbox Card
Selectable cards with label, description, and checkmark indicator in surface, subtle, outline, and solid variants.
AnalyticsTrack page viewsBackupsDaily snapshots
1234567891011121314151617181920import { CheckboxCard } from "#components/ui/base-ui/checkbox-card.tsx";import { indigoPalette } from "@stylexblocks/ui/themes/palettes.ts";
export function Example() { return ( // Root is Base UI's Checkbox.Root styled as a card; it mirrors the // checked state so every part can style itself from it <CheckboxCard.Root defaultChecked variant="outline" size="md" palette={indigoPalette}> <CheckboxCard.Control> <CheckboxCard.Content> <CheckboxCard.Label>Analytics</CheckboxCard.Label> <CheckboxCard.Description>Track page views</CheckboxCard.Description> </CheckboxCard.Content> <CheckboxCard.Indicator /> </CheckboxCard.Control> </CheckboxCard.Root> );}
// controlled: <CheckboxCard.Root checked={on} onCheckedChange={setOn}>Variants
AnalyticsTrack page viewsBackupsDaily snapshots
surfaceAnalyticsTrack page viewsBackupsDaily snapshots
subtleAnalyticsTrack page viewsBackupsDaily snapshots
outlineAnalyticsTrack page viewsBackupsDaily snapshots
solidSizes
AnalyticsTrack page viewsBackupsDaily snapshots
smAnalyticsTrack page viewsBackupsDaily snapshots
mdAnalyticsTrack page viewsBackupsDaily snapshots
lgInstall
1npx stylexblocks add checkbox-cardSource
components/ui/base-ui/checkbox-card.tsx
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547import * as stylex from "@stylexjs/stylex";import { createContext, useContext, useState } from "react";import { Checkbox as BaseCheckbox } from "@base-ui-components/react/checkbox";import { createSlotRecipeContext } from "#lib/base-ui/create-slot-recipe-context.tsx";import { defineSlotRecipe, fromMap, resolveSlotStyles, type SlotRecipeVariants,} 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 checkboxCardRoot = stylex.create({ base: { display: 'flex', flexDirection: 'column', userSelect: 'none', position: 'relative', borderRadius: semanticRadii.panel, flex: 1, 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, ':is([data-invalid], [aria-invalid="true"])': semanticColors.borderError, }, opacity: { default: null, ':is(:disabled, [disabled], [data-disabled])': 0.8, }, borderColor: { default: null, ':is(:disabled, [disabled], [data-disabled])': semanticColors.borderSubtle, }, outline: { default: null, ':is([data-invalid], [aria-invalid="true"])': '2px solid', }, },})
const checkboxCardRootSizes = stylex.create({ sm: { fontSize: fontSizes.sm, lineHeight: lineHeights.sm, }, md: { fontSize: fontSizes.sm, lineHeight: lineHeights.sm, }, lg: { fontSize: fontSizes.md, lineHeight: lineHeights.md, },})
const checkboxCardRootVariants = stylex.create({ surface: { borderWidth: '1px', borderColor: { default: semanticColors.border, ':is(:disabled, [disabled], [data-disabled])': semanticColors.borderSubtle, ':is(:checked, [data-checked], [aria-checked="true"], [data-state="checked"])': colorPalette.muted, }, backgroundColor: { default: null, ':is(:checked, [data-checked], [aria-checked="true"], [data-state="checked"])': colorPalette.subtle, ':is(:disabled, [disabled], [data-disabled])': semanticColors.bgMuted, }, color: { default: null, ':is(:checked, [data-checked], [aria-checked="true"], [data-state="checked"])': colorPalette.fg, }, }, subtle: { backgroundColor: semanticColors.bgMuted, }, outline: { borderWidth: '1px', borderColor: { default: semanticColors.border, ':is(:disabled, [disabled], [data-disabled])': semanticColors.borderSubtle, ':is(:checked, [data-checked], [aria-checked="true"], [data-state="checked"])': colorPalette.solid, }, boxShadow: { default: null, ':is(:checked, [data-checked], [aria-checked="true"], [data-state="checked"])': '0 0 0 1px var(--shadow-color)', }, '--shadow-color': { default: null, ':is(:checked, [data-checked], [aria-checked="true"], [data-state="checked"])': colorPalette.solid, }, }, solid: { borderWidth: '1px', backgroundColor: { default: null, ':is(:checked, [data-checked], [aria-checked="true"], [data-state="checked"])': colorPalette.solid, }, color: { default: null, ':is(:checked, [data-checked], [aria-checked="true"], [data-state="checked"])': colorPalette.contrast, }, borderColor: { default: null, ':is(:disabled, [disabled], [data-disabled])': semanticColors.borderSubtle, ':is(:checked, [data-checked], [aria-checked="true"], [data-state="checked"])': colorPalette.solid, }, },})
const checkboxCardRootJustifys = stylex.create({ start: { '--checkbox-card-justify': 'flex-start', }, end: { '--checkbox-card-justify': 'flex-end', }, center: { '--checkbox-card-justify': 'center', },})
const checkboxCardRootAligns = stylex.create({ start: { '--checkbox-card-align': 'flex-start', }, end: { '--checkbox-card-align': 'flex-end', }, center: { '--checkbox-card-align': 'center', },})
const checkboxCardControl = stylex.create({ base: { display: 'inline-flex', flex: 1, position: 'relative', borderRadius: semanticRadii.panel, justifyContent: 'var(--checkbox-card-justify)', alignItems: 'var(--checkbox-card-align)', },})
const checkboxCardControlSizes = stylex.create({ sm: { padding: spacing._3, gap: spacing._1_5, }, md: { padding: spacing._4, gap: spacing._2_5, }, lg: { padding: spacing._4, gap: spacing._3_5, },})
const checkboxCardControlVariants = stylex.create({ subtle: { backgroundColor: { default: null, ':is(:checked, [data-checked], [aria-checked="true"], [data-state="checked"])': colorPalette.muted, }, color: { default: null, ':is(:checked, [data-checked], [aria-checked="true"], [data-state="checked"])': colorPalette.fg, }, },})
const checkboxCardControlOrientations = stylex.create({ vertical: { flexDirection: 'column', }, horizontal: { flexDirection: 'row', },})
const checkboxCardLabel = stylex.create({ base: { fontWeight: fontWeights.medium, display: 'flex', alignItems: 'center', gap: spacing._2, opacity: { default: null, ':is(:disabled, [disabled], [data-disabled])': 0.5, }, },})
const checkboxCardDescription = stylex.create({ base: { opacity: 0.64, fontSize: fontSizes.sm, lineHeight: lineHeights.sm, },})
const checkboxCardAddon = stylex.create({ base: { opacity: { default: null, ':is(:disabled, [disabled], [data-disabled])': 0.5, }, },})
const checkboxCardAddonSizes = stylex.create({ sm: { paddingInline: spacing._3, paddingBlock: spacing._1_5, borderTopWidth: '1px', }, md: { paddingInline: spacing._4, paddingBlock: spacing._2, borderTopWidth: '1px', }, lg: { paddingInline: spacing._4, paddingBlock: spacing._2, borderTopWidth: '1px', },})
const checkboxCardIndicator = 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 checkboxCardIndicatorSizes = stylex.create({ 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 checkboxCardIndicatorVariants = stylex.create({ surface: { borderColor: { default: semanticColors.borderEmphasized, ':is([data-invalid], [aria-invalid="true"])': semanticColors.borderError, }, }, subtle: {}, outline: { borderColor: { default: semanticColors.borderEmphasized, ':is([data-invalid], [aria-invalid="true"])': semanticColors.borderError, }, }, solid: { borderColor: { default: semanticColors.border, ':is([data-invalid], [aria-invalid="true"])': semanticColors.borderError, }, color: colorPalette.fg, },})
const checkboxCardContent = stylex.create({ base: { display: 'flex', flexDirection: 'column', flex: 1, gap: spacing._1, justifyContent: 'var(--checkbox-card-justify)', alignItems: 'var(--checkbox-card-align)', },})
const checkboxCardContentAligns = stylex.create({ start: { textAlign: 'start', }, end: { textAlign: 'end', }, center: { textAlign: 'center', },})
export type CheckboxCardSize = keyof typeof checkboxCardRootSizes
export type CheckboxCardVariant = keyof typeof checkboxCardRootVariants
export type CheckboxCardJustify = keyof typeof checkboxCardRootJustifys
export type CheckboxCardAlign = keyof typeof checkboxCardRootAligns
export type CheckboxCardOrientation = keyof typeof checkboxCardControlOrientations
const checkboxCardRecipe = defineSlotRecipe({ slots: { root: { styles: checkboxCardRoot, sizes: checkboxCardRootSizes, variants: checkboxCardRootVariants, justifys: checkboxCardRootJustifys, aligns: checkboxCardRootAligns, }, control: { styles: checkboxCardControl, sizes: checkboxCardControlSizes, variants: checkboxCardControlVariants, orientations: checkboxCardControlOrientations, }, label: { styles: checkboxCardLabel }, description: { styles: checkboxCardDescription }, indicator: { styles: checkboxCardIndicator, sizes: checkboxCardIndicatorSizes, variants: checkboxCardIndicatorVariants, }, content: { styles: checkboxCardContent, aligns: checkboxCardContentAligns }, }, defaultVariants: { size: "md", variant: "outline", align: "start", justify: "start", orientation: "horizontal", },});
const cardChecked = stylex.create({ surface: { backgroundColor: { default: null, ":is([data-checked])": colorPalette.subtle }, color: { default: null, ":is([data-checked])": colorPalette.fg }, borderColor: { default: null, ":is([data-checked])": colorPalette.muted }, }, subtle: { backgroundColor: { default: null, ":is([data-checked])": colorPalette.muted }, color: { default: null, ":is([data-checked])": colorPalette.fg }, }, outline: { boxShadow: { default: null, ":is([data-checked])": `0 0 0 1px ${colorPalette.solid}` }, borderColor: { default: null, ":is([data-checked])": colorPalette.solid }, }, solid: { backgroundColor: { default: null, ":is([data-checked])": colorPalette.solid }, color: { default: null, ":is([data-checked])": colorPalette.contrast }, borderColor: { default: null, ":is([data-checked])": colorPalette.solid }, },});
const cardIndicatorChecked = stylex.create({ surface: { backgroundColor: { default: null, ":is([data-checked])": colorPalette.solid }, color: { default: null, ":is([data-checked])": colorPalette.contrast }, borderColor: { default: null, ":is([data-checked])": colorPalette.solid }, }, subtle: { color: { default: null, ":is([data-checked])": colorPalette.fg }, borderColor: { default: null, ":is([data-checked])": colorPalette.solid }, }, outline: { backgroundColor: { default: null, ":is([data-checked])": colorPalette.solid }, color: { default: null, ":is([data-checked])": colorPalette.contrast }, borderColor: { default: null, ":is([data-checked])": colorPalette.solid }, }, solid: { color: { default: null, ":is([data-checked])": colorPalette.solid }, borderColor: { default: null, ":is([data-checked])": "currentcolor" }, },});
const chrome = stylex.create({ cursorPointer: { cursor: "pointer" }, iconFill: { width: "100%", height: "100%" },});
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> );}
const CardContext = createContext<{ checked: boolean }>({ checked: false });
function RootNode({ checked, defaultChecked, onCheckedChange, ...props}: BaseCheckbox.Root.Props) { const [inner, setInner] = useState(defaultChecked === true); const isChecked = checked ?? inner; return ( <CardContext.Provider value={{ checked: isChecked }}> <BaseCheckbox.Root {...props} checked={isChecked} onCheckedChange={(next, eventDetails) => { setInner(next); onCheckedChange?.(next, eventDetails); }} data-checked={isChecked ? "" : undefined} /> </CardContext.Provider> );}
function IndicatorNode({ children, ...props}: React.ComponentProps<"span">) { const { checked } = useContext(CardContext); return ( <span {...props} data-checked={checked ? "" : undefined}> {children ?? (checked ? <CheckIcon /> : null)} </span> );}
const { withProvider, withContext } = createSlotRecipeContext(checkboxCardRecipe);
export type CheckboxCardVariantProps = Partial<SlotRecipeVariants<typeof checkboxCardRecipe>>;
/** * Checkbox Card — Base UI's checkbox styled as a selectable card. * `<CheckboxCard.Root variant size>` resolves the recipe once; every part * inherits it through context. Root mirrors the checked boolean so Root and * Indicator can stamp `data-checked` for `:is([data-checked])` chrome. */export const CheckboxCard = { Root: withProvider( RootNode, (v) => [ ...resolveSlotStyles(checkboxCardRecipe.slots.root, v), fromMap(cardChecked, v.variant), chrome.cursorPointer, ], "CheckboxCardRoot", ), Control: withContext("span", "control", "CheckboxCardControl"), Content: withContext("span", "content", "CheckboxCardContent"), Label: withContext("span", "label", "CheckboxCardLabel"), Description: withContext("span", "description", "CheckboxCardDescription"), Indicator: withContext( IndicatorNode, (v) => [ ...resolveSlotStyles(checkboxCardRecipe.slots.indicator, v), fromMap(cardIndicatorChecked, v.variant), ], "CheckboxCardIndicator", ),};
export type CheckboxCardRootProps = React.ComponentProps<typeof CheckboxCard.Root>;export type CheckboxCardControlProps = React.ComponentProps<typeof CheckboxCard.Control>;export type CheckboxCardContentProps = React.ComponentProps<typeof CheckboxCard.Content>;export type CheckboxCardLabelProps = React.ComponentProps<typeof CheckboxCard.Label>;export type CheckboxCardDescriptionProps = React.ComponentProps<typeof CheckboxCard.Description>;export type CheckboxCardIndicatorProps = React.ComponentProps<typeof CheckboxCard.Indicator>;