Preview
Installation
Install the runtime packages:
pnpm add @oshon-ai/components @oshon-ai/tokens @oshon-ai/primitives
Or scaffold the component source directly into your codebase (shadcn-style):
pnpm dlx @oshon-ai/cli add inputfields
Wire the tokens into your Tailwind v4 stylesheet:
/* 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.
'use client';
import { InputFields } from '@oshon-ai/components';
export default function Example() {
return <InputFields />;
}Basic — controlled
We'll never share your email.
<BasicField />
Validation — live errors
Your legal name as it appears on ID.
8+ characters.
<ValidationPlayground />
State matrix — Desktop (m)
Enabled
Optional Description
Active (focused — click to see)
Optional Description
Filled
Optional Description
ErrorFilled
Mandatory Error Description
ErrorEmpty
Mandatory Error Description
DisabledFilled
Optional Description
DisabledEmpty
Optional Description
<div style={stackCol}>
<p style={sectionHeading}>Enabled</p>
<TextField label="Label" required tooltip description="Optional Description" />
<p style={sectionHeading}>Active (focused — click to see)</p>
<TextField
label="Label"
required
tooltip
description="Optional Description"
autoFocus
defaultValue="User ente"
/>
<p style={sectionHeading}>Filled</p>
<TextField
label="Label"
required
tooltip
description="Optional Description"
defaultValue="User entered text"
/>
<p style={sectionHeading}>ErrorFilled</p>
<TextField
label="Label"
required
tooltip
defaultValue="User entered text"
error="Mandatory Error Description"
/>
<p style={sectionHeading}>ErrorEmpty</p>
<TextField
label="Label"
required
tooltip
error="Mandatory Error Description"
/>
<p style={sectionHeading}>DisabledFilled</p>
<TextField
label="Label"
required
tooltip
description="Optional Description"
defaultValue="User entered text"
disabled
/>
<p style={sectionHeading}>DisabledEmpty</p>
<TextField
label="Label"
required
tooltip
description="Optional Description"
disabled
/>
</div>State matrix — Mobile
Enabled
Optional Description
Filled
Optional Description
ErrorFilled
Mandatory Error Description
DisabledFilled
Optional Description
<div style={stackCol}>
<p style={sectionHeading}>Enabled</p>
<TextField
size="mobile"
label="Label"
required
tooltip
description="Optional Description"
/>
<p style={sectionHeading}>Filled</p>
<TextField
size="mobile"
label="Label"
required
tooltip
description="Optional Description"
defaultValue="User entered text"
/>
<p style={sectionHeading}>ErrorFilled</p>
<TextField
size="mobile"
label="Label"
required
tooltip
defaultValue="User entered text"
error="Mandatory Error Description"
/>
<p style={sectionHeading}>DisabledFilled</p>
<TextField
size="mobile"
label="Label"
required
tooltip
description="Optional Description"
defaultValue="User entered text"
disabled
/>
</div>Size ladder — xs → mobile
Proportional to Figma scale.
Proportional to Figma scale.
Proportional to Figma Desktop.
Proportional to Figma scale.
Proportional to Figma Mobile.
<SizeLadderPlayground />
Secondary action slot
Optional Description
<SecondaryActionPlayground />
Form layout (2-col grid)
Optional
<FormPlayground />
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.
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | — | Classes merged onto the underlying `<input>` (same as `Input`'s `className`). |
description | ReactNode | — | Description shown under the field. Rendered as the "description" slot in Figma. Skipped if `error` is present. |
disabled | boolean | — | Disable the input. Renders with the native `disabled` attribute so screen readers and keyboard users get consistent signal. No change / blur audit events fire while disabled. |
error | ReactNode | — | Error message shown under the field. When present, the helper row switches to the error slot, the input gets `aria-invalid="true"`, and the error is wired via `aria-describedby`. |
invalid | boolean | — | Marks the field as invalid for a11y. `true` → `aria-invalid="true"`. The visual layer surfaces the error styling; primitive only ensures correct ARIA. |
label | ReactNode | — | Label content. If omitted, the label row is skipped. |
permissionAttrs | Record<string, unknown> | — | Attribute bag forwarded to `permissions.can(..., attrs)` for attribute-based access control. Primitives never invent values here — the consumer supplies them. |
permissions | Partial<PermissionContext> | — | Per-instance permission override. Merges over the ambient `PermissionContext`. Example: `permissions={{ mode: 'hidden' }}` switches this one instance to render-as-null when denied. |
readOnly | boolean | — | Non-mutating but still interactive. Renders with native `readOnly` so the field is focus-reachable, copy-friendly, and announced as read-only by assistive tech. Change events are silently dropped. |
required | boolean | — | Render the required asterisk next to the label AND set `aria-required` on the input. Native `required` is intentionally NOT forwarded — we let consumers own validity messaging via the `error` prop instead of letting the browser race with our helper row. |
resource | string | — | Resource name passed to `permissions.can('edit', resource, attrs)`. Default `'input'`. Consumers override for specific semantics, e.g. `resource="field:ssn"` for a sensitive data field. |
secondaryAction | ReactNode | — | Right-justified slot inside the label row — the Figma "secondary action" placeholder. Typical usage: a checkbox or small control. |
size | enum | m | Visual size. Default `'m'`. |
tooltip | ReactNode | — | Tooltip icon content. When provided, an info icon renders next to the required asterisk; consumers wrap this in their own Tooltip component if a hover hint is desired (omitting the wrapper gives a non-interactive visual hint). |
wrapperClassName | string | — | Classes merged onto the outermost wrapper (the vertical stack). |
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.
<InputFields 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.
| Attribute | Values | Description |
|---|---|---|
data-oshon-size | xs · s · m · l · mobile | Visual size axis. Mirrors the `size` prop. |
data-oshon-tier | primary · secondary · tertiary | Visual emphasis tier (Button family). Mirrors the `tier` prop. |
data-oshon-state | enabled · active · error · disabled | Component surface state. Set automatically based on props. |
data-disabled | true · (omitted) | Set when `disabled` is true. Pair with `:disabled` CSS for native input components. |
data-state | open · closed · checked · unchecked · … | Radix-derived state for overlay components (Dialog, Tabs, Toggle, etc.). |
/* 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 thedisabledprop