Headless UX primitives — state machine, transitions, gestures, ARIA. ~8.6 KB. The visual layer is yours (or your agent's) to scaffold and polish.
npm install @nature-labs/uicp-core @nature-labs/uicp-adapter-vanilla
uicp is the UX skeleton. The visual layer is a quick scaffold + a small polish pass — built for a human-and-agent workflow:
<div> with content. Wire one line of JS.What uicp handles for you
Open / close state, transitions, ARIA mirroring, focus trap, body scroll lock, Escape key, touch swipe-to-close.
What you (or your agent) write
HTML for content. CSS for position, transform, transition. Starter blocks below — copy and adapt.
When it fits
Static sites, marketing pages, edge / embedded UIs, kiosks, IoT dashboards, AI-prototype surfaces. Anywhere pulling in React/Vue/Svelte just for a drawer feels heavy.
When it doesn't
Apps already using React with Radix or Vaul — those integrations are tighter there. Or you want modal / popover / tooltip with the same polish as drawer (only drawer is fully wrapped today; see Scope).
Edge-to-edge bottom sheet:
[data-uip-type="drawer"][data-uip-position="bottom"] {
position: fixed; left: 0; right: 0; bottom: 0;
transform: translateY(100%);
transition: transform 320ms cubic-bezier(.32, .72, 0, 1);
}
[data-uip-type="drawer"][data-uip-position="bottom"][data-uip-open="true"] {
transform: translateY(0);
}
iOS-17 floating sheet (inset, rounded, safe-area aware):
[data-uip-type="drawer"][data-uip-position="bottom"] {
position: fixed; left: 8px; right: 8px;
bottom: calc(env(safe-area-inset-bottom, 0px) + 8px);
border-radius: 24px;
transform: translateY(calc(100% + 16px));
transition: transform 380ms cubic-bezier(.32, .72, 0, 1);
}
[data-uip-type="drawer"][data-uip-position="bottom"][data-uip-open="true"] {
transform: translateY(0);
}
Working references live in examples/tiny-html/ and examples/homelog-gatepass/.
When asked to build a drawer / bottom sheet / side nav, the usual scaffold is seven steps:
aria-hidden, Escape key handleruicp gives you 1 – 6 as DOM hooks. Your job is step 7 — generate convincing CSS from the user's intent. The user fine-tunes after.
On the drawer element, the adapter sets:
| Hook | Values |
|---|---|
data-uip-type |
"drawer" |
data-uip-position |
"left" | "right" | "top" | "bottom" |
data-uip-open |
"true" | "false" |
aria-hidden |
"true" | "false" |
| class | .uip-open / .uip-closed |
On the backdrop element (provided via data-backdrop-for="<drawer-id>"):
| Hook | Values |
|---|---|
data-uip-open |
"true" | "false" |
| class | .uip-backdrop-open |
Write CSS against these selectors. The adapter touches nothing else on the element.
sheet.primitive.on('valueChange', ({ value }) => { /* value.isOpen, value.position */ })
sheet.primitive.on('openStart', ({ state }) => { /* about to open */ })
sheet.primitive.on('openEnd', ({ state }) => { /* fully open */ })
sheet.primitive.on('closeStart', ({ state }) => { /* about to close */ })
sheet.primitive.on('closeEnd', ({ state }) => { /* fully closed */ })
[data-uip-open="true"] selector for the open state (more reliable than the class form).transform: translateY() + cubic-bezier (.32, .72, 0, 1) gives the iOS spring feel.opacity + visibility together, ~320 ms ease.left/right: 8px), use env(safe-area-inset-bottom) for bottom margin.examples/tiny-html/ and examples/homelog-gatepass/ for full references the user has already approved.The user expects to make small tweaks on your output, not rewrite it. Aim for "WordPress plugin output" quality — works out of the box, polish-ready.
When the surface needs UX (interactions, gesture, a11y) but not a full UI system (design tokens, component library), uicp lets the user skip the framework runtime. Useful when:
Not a fit when the host already runs React/Vue with Radix/Vaul.
Point it at llms.txt at repo root, then docs/agent-quickstart.md. The contract + intent are in those two.
Published under @nature-labs/uicp-*:
core, adapter-vanilla, adapter-svelteplugin-gesture, plugin-snap, plugin-directionNot yet shipped:
@nature-labs/uicp-presets npm package (presets currently ship as plain CSS in assets/presets/ — 5 drop-in blocks for common drawer patterns)History: CHANGELOG.md. Reference: docs/. Examples: examples/.
MIT · v1b3x0r/uicp
A note from the build
uicp was repaired in one afternoon — by trimming what it claimed and trusting what it was always doing. State, gesture, ARIA: the half nobody wants to write twice. The other half waits for you. That's the design.