Welcome to Oshon · v1.0  ·  Now in public beta for enterprise teams Read the launch notes
SelectionUpdatedFree

Selection Controls

Checkbox / Radio / Switch / Numeric Selector / Item rows / Lists.

Preview

Live preview
@oshon-ai/components
Email me product updates
Standard
Compact
Notifications

Installation

Install the runtime packages:

pnpm
pnpm add @oshon-ai/components @oshon-ai/tokens @oshon-ai/primitives

Or scaffold the component source directly into your codebase (shadcn-style):

pnpm
pnpm dlx @oshon-ai/cli add selectioncontrols

Wire the tokens into your Tailwind v4 stylesheet:

css
/* app/globals.css */
@import 'tailwindcss';
@import '@oshon-ai/tokens/css';
@import '@oshon-ai/tokens/tailwind';

New here? Walk through the full setup — prereqs, theming, your first render.

Usage

Import the component and render it. Every component supports the standard tier, size, and disabled props where applicable.

tsx
'use client';
import { SelectionControls } from '@oshon-ai/components';

export default function Example() {
  return <SelectionControls />;
}

Default

tsx
<Checkbox size="m" label="Accept terms" />

Size matrix

tsx
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem', alignItems: 'flex-start' }}>
      <div key="xs" data-story-size="xs">
        <Checkbox size="xs" label="Accept terms" />
      </div>
      <div key="s" data-story-size="s">
        <Checkbox size="s" label="Accept terms" />
      </div>
      <div key="m" data-story-size="m">
        <Checkbox size="m" label="Accept terms" />
      </div>
      <div key="l" data-story-size="l">
        <Checkbox size="l" label="Accept terms" />
      </div>
      <div key="mobile" data-story-size="mobile">
        <Checkbox size="mobile" label="Accept terms" />
      </div>
    </div>

API

Every prop is documented here directly from the component's TypeScript interface. Inherited DOM attributes (aria-*, onClick, style, etc.) work as usual but are omitted from this table.

PropTypeDefaultDescription
checkedbooleanControlled checked state.
classNamestringAdditional classes merged after the root defaults.
defaultCheckedbooleanUncontrolled initial checked state.
descriptionReactNodeOptional secondary description rendered under the label.
disabledbooleanDisabled state.
indeterminatebooleanIndeterminate visual + ARIA state. Independent of `checked` per the WAI-ARIA `aria-checked="mixed"` pattern.
labelReactNodeOptional visible label rendered to the right of the box.
onCheckedChange((checked: boolean) => void)Fires on every toggle with the new boolean.
sizeenummVisual size. Default `'m'` (Figma 20×20).
slotClassNameCheckboxSlotClassNamesPer-slot className overrides.

Styling

Three layers of customization, in order of escape-hatch strength: className overrides → data-attribute targeting → CSS custom properties.

Passing Tailwind classes

Every Oshon component accepts a className prop merged AFTER the component's default classes. Use it to override spacing, color, or size without forking the component.

tsx
<SelectionControls
  className="ring-2 ring-offset-2 ring-blue-500"
/>

Data attributes

Oshon components expose their internal state as data-oshon-* attributes so you can target them from CSS without coupling to internal class names. The most common attributes are listed below — see the component's source for the full set.

AttributeValuesDescription
data-oshon-sizexs · s · m · l · mobileVisual size axis. Mirrors the `size` prop.
data-oshon-tierprimary · secondary · tertiaryVisual emphasis tier (Button family). Mirrors the `tier` prop.
data-oshon-stateenabled · active · error · disabledComponent surface state. Set automatically based on props.
data-disabledtrue · (omitted)Set when `disabled` is true. Pair with `:disabled` CSS for native input components.
data-stateopen · closed · checked · unchecked · …Radix-derived state for overlay components (Dialog, Tabs, Toggle, etc.).
css
/* Target the secondary tier specifically */
[data-oshon-tier="secondary"] {
  --oshon-color-primary-700: var(--my-brand-color);
}

Interactive states

Every interactive component supports the standard CSS pseudo- classes plus Tailwind's state variants. Focus rings always use :focus-visible so keyboard users see them but mouse users don't.

  • :hover / hover:* — pointer hover
  • :focus-visible / focus-visible:* — keyboard focus
  • :active / active:* — pressed
  • :disabled / disabled:* — set via the disabled prop