A Notto library · v0.1.1

React primitives for agent‑adaptive apps.

Notto UI is 14 intent-level primitives and a runtime renderer — shipped as @nottohq/ui. Agents emit JSON; the renderer validates against zod and mounts a safe React tree. No geometry values, no CSS escape hatches — a single uniform vocabulary humans and LLMs both speak.

$npm install @nottohq/ui
License · MITPeer · react ≥ 19Bundle · 14 primitivesSurface · 0.1.x stable
v0.1.1 · STABLEFirst stable surface. 0.1.x contract — patches non-breaking by contract.Next minor · 0.2CHANGELOG.md
01 / PRIMITIVES

Fourteen primitives. One vocabulary.

Every primitive speaks the same intent-level prop DSL — tone · variant · size · gap · padding · align · justify. No geometry values. No style escape hatch. Agents and humans compose the same trees.

Renderer-safe · 9

Available through @nottohq/ui/renderer

Serializable, stateless, safe to emit as agent JSON. Validated against zod schemas before mount.

Page01

Top-level document shell. Theme preset, width, padding.

themewidthpadding
Stack02

One-axis flow. Direction, gap, align, justify. The spine of every composition.

directiongapalignjustify
Box03

Rectangular surface with tone + padding. Used as a container, never for layout math.

tonepaddingborderradius
Text04

All prose. Variant sets role; tone sets foreground meaning.

varianttoneweight
Icon05

Named glyph from the allowlist. Unknown names fail at validation.

namesizelabel
Button06

Primary action. Emits a typed action reference — never an inline handler.

varianttoneaction
Link07

Safe href allowlist. Tabnabbing defense forced on target=_blank.

hreftoneexternal
Badge08

Compact status marker. Tone carries semantic meaning, not colour values.

tonevariantsize
CodeBlock09

Preformatted monospace block. Text is escaped before mount — no HTML injection.

languagetone
React-only · 5

Stateful or render-fn primitives

Outside the renderer surface — used directly in React. Too stateful or too open to serialise.

Field10

Label + control + help + error. Controlled input; not serialisable.

variantsizestate
Card11

Container with header / body / footer render slots. Functions as children.

varianttonepadding
Table12

Rows resolved by a render fn. Sort + select state lives in React, not JSON.

columnsdensity
Modal13

Portal + focus trap + escape. Open state is React-owned, never agent-emitted.

sizevariant
Toast14

Queued notifications via ToastProvider + useToast. Imperative surface — not declarable.

tonesize
02 / RENDERER

JSON in. A safe React tree out.

Edit the document on the left. The renderer validates against zod on every change — invalid trees never reach React. Try a javascript: href, an unknown primitive, a sixteen-level nesting.

document.jsonedit · schema · provenance
Parsed · validated · mounted
rendered.treemounted
CONFIRMED

Booking confirmed

Fri · 7:30 PM · table 4 · 2 guests

Share link
caps · nodes ≤ 1000 · depth ≤ 20 · text ≤ 10 kB
03 / SAFETY

The renderer is a boundary, not a convenience.

Agents produce untrusted input. Everything crossing the boundary is validated, clamped, or rejected. Errors never echo pathological input back to the caller.

CONTROLBEHAVIOURVECTORSTATUS
S.01 · NETurl allowlist
Href schemes filtered to https, mailto, tel, and internal refs. Rejects javascript:, data:, protocol-relative, and user:pass@ phishing forms.
userinfo phishing xss via href
rejects at parse
S.02 · NAVtabnabbing defense
Any target="_blank" link auto-gets rel="noopener noreferrer". Cannot be overridden through the DSL.
window.opener pivot
enforced at mount
S.03 · DOSshape caps
Hard ceilings on node count (1000), depth (20), and leaf text (10 kB). A runaway agent can't hang the renderer.
pathological nesting token flood
rejects before mount
S.04 · REGregistry freeze
Action + icon registries snapshotted at render boundary, then frozen. Late mutation cannot bypass the allowlist.
registry poisoning prototype pollution
snapshot + freeze
S.05 · VALstrict prop validation
Every primitive has a zod schema. Unknown props, wrong types, or out-of-range values reject the node. No silent coercion, ever.
prop smuggling silent mutation
zod · fail-closed
S.06 · LOGerror echo clamps
Validation errors describe the violation, not the payload. User-controlled strings are clamped at 60 chars to prevent log flooding or prompt injection through telemetry.
log injection prompt leakage
structured only
04 / SKILL

Ships with an agent skill. Any LLM learns the library in one file.

SKILL.md is a markdown file the library ships with. Drop it in Claude, Cursor, Cline, or Copilot; the model learns the primitives and emits valid trees on the first try.

@nottohq/ui/skill/SKILL.mdread-only · 12 kB
---
name: nottohq-ui
version: 0.1.1
purpose: emit valid @nottohq/ui JSON trees
---

# Vocabulary
Primitives: Page · Stack · Box · Text · Icon ·
Button · Link · Badge · CodeBlock

Every primitive accepts a subset of:
  tone    · neutral | primary | success | danger | …
  variant · solid | soft | outline | ghost
  size    · sm | md | lg
  gap     · 0 | 1 | 2 | 3 | 4 | 5 | 6 | 8 | 10 | 12
  padding · same scale as gap

# Rules
- Never pass style. Never pass arbitrary CSS.
- Root of a renderer tree is always Page or Stack.
- Layout with Stack, never with Box.
- Links need a scheme (https:, mailto:, tel:).
agent.output.jsonstreaming · valid
{
  "type": "Stack",
  "props": { "gap": 4, "padding": 5 },
  "children": [
    { "type": "Badge",
      "props": { "tone": "success" },
      "children": "CONFIRMED" },
    { "type": "Text",
      "props": { "variant": "title" },
      "children": "Booking confirmed" },
    { "type": "Button",
      "props": { "action": "open" },
      "children": "Open" }
  ]
}
05 / PHILOSOPHY

Small surface, universal runtime, universal onboarding.

§ 01

Intent, not geometry

The DSL talks about tone, variant, gap — roles, not pixels. Pixel decisions live in the theme, once. Agents can't invent a 17px padding.

tonevariantgapsize
§ 02

One runtime, everywhere

React is the universal runtime. No custom VM, no per-provider coupling, no proprietary format. If an LLM emits valid JSON, the renderer mounts it.

react ≥ 19zodmit
§ 03

One skill, any model

Markdown is the universal onboarding. SKILL.md works with Claude, Cursor, Cline, Copilot — anything that reads a file.

claudecursorclinecopilot