@oshon-ai/primitives
v0.9.1
Published
Oshon headless behavior primitives — unstyled accessible building blocks. Behavior, a11y, keyboard, focus, RBAC hooks, audit hooks, i18n slots. No styling (Phase 4 wraps these with Oshon's visual language).
Downloads
292
Maintainers
Readme
@oshon-ai/primitives
Headless behavior primitives for the Oshon design system. Behavior, accessibility, keyboard handling, focus management, RBAC hooks, audit hooks, i18n slots — no styling. Consumers who want the Oshon visual language should pull from @oshon-ai/components (Phase 4); this package is the substrate.
See OSHON.md §3 for the layering rationale.
Install
pnpm add @oshon-ai/primitivesPeer dependencies: react, react-dom (18.x or 19.x).
Usage
import { Button, Dialog, Tooltip } from '@oshon-ai/primitives';
function Example() {
return (
<Tooltip.Provider>
<Dialog.Root>
<Dialog.Trigger asChild>
<Button>Delete record</Button>
</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay />
<Dialog.Content>
<Dialog.Title>Are you sure?</Dialog.Title>
<Dialog.Description>This cannot be undone.</Dialog.Description>
<Dialog.Close asChild>
<Button>Cancel</Button>
</Dialog.Close>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
</Tooltip.Provider>
);
}What's in Phase 3a
Three primitives — one proprietary, two Radix-wrapped:
| Primitive | Strategy | ADR |
|---|---|---|
| Button | Proprietary headless, RSC-safe | ADR-001 |
| Dialog | Thin wrap over @radix-ui/react-dialog | ADR-002 |
| Tooltip | Thin wrap over @radix-ui/react-tooltip | ADR-003 |
Phase 3b will add Input, Select, Popover, Tabs, and Toggle using the same template.
Package-level contract
Three shared primitives that every component consumes:
ComponentManifestSchema— Zod schema for per-component manifests, ported verbatim from MCP_SCHEMA.md §6. Everymanifest.tsin this package is validated at test time viascripts/validate-manifests.mjs.PermissionContext+usePermissions()+PermissionProvider— RBAC plumbing. Every primitive respectscanView(hides the UI) andcanEdit(disables interaction).AuditProvider+useAudit()— audit event plumbing. Every interactive primitive fires events with shape{ component, action, meta?, timestamp }. Button and Dialog emit; Tooltip does not (see ADR-003).
Plus resolveMessage() for i18n-aware string resolution. The full i18n runtime lands in Phase 5.
Design principles honored
- #3 RBAC + audit by default — every primitive accepts
permissionsand readsAuditProvider(Tooltip carve-out documented in ADR-003) - #5 RSC-default — Button stays a server component; Dialog and Tooltip declare
'use client'because they wrap Radix - #8 Manifest-driven — every primitive ships
manifest.tsvalidated againstComponentManifestSchema - #9 Trust-boundary Zod — manifests parse at test time; malformed manifests fail CI
- #11 i18n by default — primitives take a
messagesprop instead of inlining user-visible strings (skeleton; full runtime in Phase 5)
Development
pnpm --filter @oshon-ai/primitives test # validates manifests + runs vitest
pnpm --filter @oshon-ai/primitives build # tsup + tsc + .d.cts emit
pnpm --filter @oshon-ai/primitives typecheck # tsc --noEmitLicense
MIT.
