Slab
SkillFiles & storageWriting, editing, and rendering Slab documents (.slab) — the declarative design language for app screens, posters, terminal UIs, and interactive components. Use when authoring or modifying .slab files, rendering via the slab CLI (`bunx @stencil-hq/slab` or `slab-cli`: svg/png/apng/tui), bundling via Vite/Bun plugins (`@stencil-hq/slab/vite`, `@stencil-hq/slab/bun`), embedding via generated web components (`slab gen wc`), typed React components (`slab gen react`), typed Rust modules (`slab gen rust`), typed Go modules (`slab gen go`), `include_doc!` proc-macros, or Ratatui widgets (`slab-ratatui`), declaring the typed host surface (params, list/each, holes, signals, themes, rich-field runs), building rich-text or block editors on kernel fields, working on the conformance corpus, or debugging Slab diagnostics and layout.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Slab skill
What this skill tells your AI
The instructions your AI receives, as published by stencil-hq/slab in skill/SKILL.md and read by ahel’s review.
A declarative language for designed surfaces that renders faithfully to web,
GPU, TUI, SVG, and PNG from one source. One pipeline: the Rust compiler
(slab-compile) lowers .slab text to SLIR (protobuf + Snappy binary IR);
one Rust kernel (slab-kernel) owns layout, motion, hit testing, focus,
editing, and dispatch on every platform (natively linked, or via WASM in
browsers). Thin drivers paint frames, forward events, and expose the retained
semantic scene through platform accessibility adapters. spec/SPEC.md is
normative; conformance/ is executable (native and WASM match goldens byte
for byte). Hosts never parse .slab and never do layout.
The whole layout model in one sentence:
Every node is a box. Every box receives constraints (min/max width and height), returns a size, and its parent places it. Containers differ only in how they place children.
No margins, no selectors/cascade, no z-index, no absolute positioning, no
display property. Paint order is tree order; overlap requires an explicit
opt-in container (stack, canvas, offset). Mistakes become diagnostics,
not silent overlap.
Quick start
tokens {
color { bg #0e1116; ink #e6edf3; accent oklch(72% 0.16 250) }
text { title { size 18; weight 650 } }
}
def Chip(label, tone=color.accent) {
row pad=4,10 gap=6 radius=999 stroke=tone align=center w=hug {
rect w=6 h=6 radius=3 bg=tone
text label size=12 color=tone nowrap
}
}
col#card w=360 pad=24 gap=12 bg=color.bg radius=12 {
text "Pale Green Things" style=text.title color=color.ink
row gap=8 align=center {
Chip label="FLAC"
spacer
text "4:12" size=12 color=color.ink
}
}
Render (no Rust toolchain needed; the compiler ships as WASM in the npm CLI):
bunx @stencil-hq/slab render doc.slab -o out.png --width 800
bunx @stencil-hq/slab check doc.slab # ALWAYS run after editing
Output kind infers from the extension (.svg .png .apng .txt); --client tui
with no -o prints cells to stdout. check validates the main document and
each export definition through its standalone path. Code generators produce
typed web components (slab gen wc), React wrappers (slab gen react),
Rust modules (slab gen rust), or Go modules (slab gen go). In this repo
the native CLI is
cargo run -p slab-cli -- (adds fmt, conformance, lsp, and
--font).
Fresh releases: bun's minimum-release-age gate blocks packages younger than
24h (… blocked by minimum-release-age). Fix: add [install] minimumReleaseAge = 0 to a project bunfig.toml. bunx ignores a cwd
bunfig — use bun add @stencil-hq/slab then ./node_modules/.bin/slab.
Core vocabulary (memorize; details in references/language.md)
- Containers:
row col wrap grid stack canvas para group—stack(layers) andcanvas(at=x,y) are the ONLY overlap opt-ins. - Leaves/controls:
text,span(insidepara),rect,img,path(canvas only),icon,divider,spacer, andhole. - Sizing per axis:
w=240(request) |hug|fill/fill:2|40%- clamps
min-w max-w min-h max-h. Defaults: main = hug; cross = stretch for containers andrect, hug for other leaves; insidestack/canvaseverything hugs.
- clamps
- Style:
bg stroke stroke-w stroke-align stroke-sides stroke-dash radius smooth shadow blur backdrop backdrop-mask grain mask opacity color family size weight leading tracking strike italic underline code-color code-bg style= align-text rotate scale tilt fit pad gap animate transition scrollbar scrollbar-w scrollbar-fg scrollbar-bg— closed set, nothing else.currentis icon-declaration paint, not a general color token. - Flags/modes:
clip bleed scroll nowrap ellipsis inert focusable multiline drag-ghost; usedrag-ghostonly withdrag=, and usescroll=cross|both,sticky, andeach … virtual item-extent=Nonly in their documented contexts. - Reserved attrs:
key=, signal binders (act=, pointer/drag lifecycle, field/submit, resize),keys=, a11y metadata/state/relation/value attrs, and overlay placementattach= gravity= collide=. - Conditionals:
when hover|dragging|drop|tui|dark|w<600|prop|theme(name) { … }patches its node with attrs/children. Binders on the patched node are statically registered, active only while the condition holds, and stop contributing focus/hit behavior when false. - Components/data:
def Name(params) { body }, Capitalized calls, children splice atslot, andexportdefs become standalone documents and recursivelist(Def)schemas.each param.rowsconsumes a root list; nested templates useeach child_prop. Macro expansion has no arithmetic. - Host surface (typed, compiler-checked): scalar params, recursive lists, holes, runtime image registration, signals, keyed scroll/divider/reveal APIs, field caret/runs/range editing, and the retained scene. There is NO tree injection or selector API.
Rules that prevent 90% of mistakes
fillis a SIZE keyword, never a color — backgrounds usebg=.align=positions a node's CHILDREN; to position the node in its parent useself=(stack children:self=bottom-end offset=4,-4).%needs a determinate parent axis; againsthugit degrades to hug withpct-unbounded. Progress bars live insidefill/fixed tracks.- Node headers end at newlines. End each continued header line with
\; indentation alone does not continue it. - Bare idents in value position are keywords or component props — token
references are ALWAYS dotted (
color.accent, neveraccent). - Numbers are unitless
u(1u = 1px on web/svg). Durations are plain ms. - One shadow inline (
shadow=0,2,6,#0004); layered shadows must be a list of presets/token refs (shadow=shadow.crisp,shadow.lift). - TUI paints one cell per grapheme, but layout uses vector font metrics.
Use cell-multiple geometry and
pad=16,8+ inside borders. Set the full tripletwhen tui { family="mono" size=13.333 leading=1.2 }: the 600/1000em mono advance becomes exactly 8u (one column) and the line box 13.333 × 1.2 = 16u (one row). The default leading 1.4 yields 18.67u lines, so any stacked text column drifts off the 16u row grid. - Quarter-turn
rotate(±90/270) participates in layout; any other angle is ink-only. TUI skips rotated subtrees. - Dynamic rows come from
list(Def)+each, including recursive child lists. Give items stable keys; path-address nested lists by index/field. Use kernel virtualization only for a uniform-height top-leveleach. - Diagnostics are the contract:
squeeze= fixed size clamped,clipped= content truncated, andglyph-missing= static text is absent from its resolved embedded family. Fix the named source; never silence by guessing coordinates.cap-*names a declared client degradation. - Keep policy in the host: Slab owns layout, gesture mechanics and optional drag ghosts, focus, scrolling, text editing (caret, selection, rich runs, IME, bounded undo), scene export, and shipped web/native accessibility adapters. The host owns app state, block structure, popover dismissal, and focus traps.
- Treat
spec/SPEC.mdas normative andspec/FRAME.mdas the exact host ABI. Skill references are procedural guidance, not replacement specs.
Feature selection cues
- Use a recursive
list(Def)plus nestedeach child_propfor trees and grouped rows. Addvirtual item-extent=Nonly to a direct root-listeachunder a main-axis scrollingrow/col; userevealItemfor navigation. - Use
para { each param.runs }for host-supplied rich text; make the run schema body exactly onespan. - Use
field=text nodes for editing: the kernel owns caret, selection, wrapping, IME, and bounded undo. Rich text keeps five inline span sets (bold/italic/underline/strike/code) beside the string via the field-runs API; toggle styles over the selection;code-color/code-bgpaint code runs. For a block editor keep one field per block: Shift-click forms a cross-field range, edits arrive as one pre-mutationrange_editrequest, and structural undo uses the snapshot/commit/restore field transaction. - Use
path d=param.route(insidecanvas) for runtime geometry. Declare reusable staticiconassets at top level and tint them throughcurrent. - Use
img src=param.nameplus host image registration for runtime pixels; do not encode changing image data into document params. - Use
press/context/dblclickfor gesture starts,pointer-move/pointer-upfor routed raw motion/releases, anddrag/drag-update/drag-end/dropfor a complete drag lifecycle. Consume every signal's typedSignalMeta; adddrag-ghostwhen the kernel should paint the moving source duplicate. Useact=for ordinary keyboard-and-pointer activation. - Use
scroll=cross|bothfor two-axis overflow,stickyonly on direct main-scroll children, and keyedrevealinstead of host-computed offsets. - Use
dividerbetween two panes; let it own pointer/keyboard resizing. Supply initial/restored extents through the keyed API, not bespoke dragging. - Use
attach=param.anchoron astack/canvaschild for popovers. Feed a signal's fullmeta.keyback as the anchor; keep dismissal/focus policy in the host. - Author complete a11y roles, names, state, relations, values, and live-region metadata. Shipped web/native adapters build the platform semantic tree; application hosts do not rebuild it from scene records.
References — load on demand
- references/language.md — grammar and node semantics; dynamic paths/icons; para runs; scrolling/sticky; dividers; anchored overlays; accessibility attrs; layout, components, tokens, keys, and diagnostics. Read when authoring beyond basic screens or when a diagnostic is unclear.
- references/styling.md — gradients, icon
currentpaint, layered/inset shadows, glass, blur, stroke geometry, transforms, interaction states/drag ghosts, and motion. Read when styling effects, icons, interactions, or animation. - references/hosts.md — recursive/virtual lists, runtime images, pointer
and drag signals with
SignalMeta, generated web/Rust/Go bindings, the exact clean-cutover Instance APIs, scroll/reveal/divider state, popovers, rich-field runs and caret/range editing, host structural transactions, and framework accessibility adapters. Read when building an interactive or data-driven app. - references/rendering.md — SLIR → kernel → Frame, runtime path/image and scale ops, scene semantics, per-client degradations, TUI rules, CLI, and conformance. Read when implementing a driver, targeting a client, or debugging cross-client differences.
When the repo is available, spec/SPEC.md is normative (spec/SLIR.md and
spec/FRAME.md for the machine interfaces) and examples/*.slab are quality
references — examples/10-settings.slab is the canonical interactive app
(params, signal buttons, kernel-edited field, hole);
examples/12-tracklist.slab shows list/each, themes, and scrollbars;
examples/01-settings.slab and 06-jcard.slab are the visual-quality bar
for static documents; bench/slate/doc/editor.slab is the block-editor
reference (one field per block, rich runs, cross-field ranges). Live
playground: https://stencil-hq.github.io/slab/
Signals
- GitHub stars
- 74
- Forks
- 2
- Last commit
- Aug 2026
ahel review
K1binfo
installs-packages (in references/rendering.md)
Automated review, not a security audit. Ruleset v1+k2.
Advanced
- Catalog kind
- skill
- Gateway key
slab- Source
- github.com/stencil-hq/slab