@oshon-ai/components
v0.9.3
Published
Oshon styled React components — the commercially-licensed visual layer that wraps @oshon-ai/primitives with Tailwind v4 utility classes driven by @oshon-ai/tokens CSS variables. Proprietary visual language, zero CSS-in-JS, WCAG 2.2 AA floor.
Downloads
548
Maintainers
Readme
@oshon-ai/components
Oshon styled React components (MIT tier) — the open-source visual layer of the Oshon design system.
Every component in this package is a thin Tailwind v4 + @oshon-ai/tokens-driven wrapper over the corresponding headless primitive from @oshon-ai/primitives. Behavior, accessibility, RBAC, and audit plumbing live in the primitives; this package owns only Oshon's proprietary visual language.
Status — Phase 4a (2026-04-20): Button, Dialog, Select. The remaining primitives (Tooltip, Input, Toggle, Popover, Tabs) wrap in Phase 4b. See OSHON.md §4.
Install
pnpm add @oshon-ai/components @oshon-ai/tokens @oshon-ai/primitivesPeer dependencies: react ≥ 18, react-dom ≥ 18. The consumer must also ship Tailwind CSS v4 (or a CSS-variable-aware equivalent) and import @oshon-ai/tokens/tokens.css so the --oshon-* custom properties are defined at runtime.
Usage
Per-component import (recommended — tree-shakable by per-entry path):
import { Button } from '@oshon-ai/components/button';
import { Dialog } from '@oshon-ai/components/dialog';
import { Select } from '@oshon-ai/components/select';Barrel import (bundlers tree-shake by named export):
import { Button, Dialog, Select } from '@oshon-ai/components';Button (hug variant)
<Button onClick={handleSave}>Save</Button>
<Button size="l" leadingIcon={<SaveIcon />}>Save</Button>
<Button loading>Saving…</Button>
<Button resource="row:edit" permissions={{ can: () => canEdit }}>
Edit
</Button>Dialog
<Dialog.Root>
<Dialog.Trigger>Open</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay />
<Dialog.Content size="m">
<Dialog.Title>Confirm</Dialog.Title>
<Dialog.Description>Are you sure?</Dialog.Description>
<Dialog.Close>Cancel</Dialog.Close>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>Select
<Select.Root defaultValue="open">
<Select.Trigger aria-label="Status">
<Select.Value placeholder="Select…" />
<Select.Icon>▾</Select.Icon>
</Select.Trigger>
<Select.Portal>
<Select.Content>
<Select.Viewport>
<Select.Item value="open">
<Select.ItemText>Open</Select.ItemText>
<Select.ItemIndicator>✓</Select.ItemIndicator>
</Select.Item>
<Select.Item value="closed">
<Select.ItemText>Closed</Select.ItemText>
<Select.ItemIndicator>✓</Select.ItemIndicator>
</Select.Item>
</Select.Viewport>
</Select.Content>
</Select.Portal>
</Select.Root>Tailwind configuration
The consumer's Tailwind config must content-scan this package so the utility classes authored in our TSX are picked up:
// tailwind.config.ts
export default {
content: [
'./app/**/*.{ts,tsx}',
'./node_modules/@oshon-ai/components/dist/**/*.js',
],
// @theme is in @oshon-ai/tokens/tailwind.css — @import it in your global CSS.
};Architectural decisions
ADR-001 — Styling mechanismADR-002 — Variant-per-file deferredADR-003 — RSC boundaryADR-004 — Manifest sizes[] extension
Related
@oshon-ai/primitives— headless behavior layer (what these wrap).@oshon-ai/tokens— the CSS custom-property contract (what these reference).@oshon-ai/data(commercial, future) — data-adaptive components (DataTable, Menu, Multiselect, …) that compose this layer.
The MIT ↔ commercial boundary is enforced by @oshon-ai/eslint-plugin/no-commercial-in-core. This package is MIT; do not add imports from @oshon-ai/{data,patterns,governance,brand-forge}.
