Concepts
Core concepts
Section titled “Core concepts”Hosts own behaviour
Section titled “Hosts own behaviour”Caps owns rendering plus the interaction, controlled or uncontrolled state, focus, keyboard handling, teardown and accessibility that belong to a UI primitive. Your app owns product data and state, authority, routes, persistence, fetching, effects, commands, policy and copy. Blocks receive props, slots and callbacks. They never fetch.
Components keep their native contracts. Button forwards native props and its
ref, adds variant, size, cut and loading, and merges your className
last. Checkbox, Switch and Progress keep their native elements.
Explicit subpaths
Section titled “Explicit subpaths”Every component, block, helper and stylesheet has its own subpath. There is no barrel, alias or hidden provider, so you import exactly what you render and nothing else ends up in your bundle.
Themes and CSS
Section titled “Themes and CSS”Stipe keeps daisyUI’s whole theme catalog open. Set any daisyUI theme with
data-theme. latte, mocha, field-guide, field-guide-night, embed,
embed-dark, phosphor, codec, pirate-radio and pirate-radio-night are
Stipe’s maintained presets.
<html data-theme="mocha"> ...</html>Your CSS can add a preset or local treatment by overriding daisyUI or Stipe variables. Keep it CSS-only:
[data-theme="workbench"] { --color-primary: #8b5cf6; --color-primary-content: #ffffff; --stipe-surface: var(--color-base-200); --stipe-fg-muted: #4c4f69;}Caps reads its material from the theme vocabulary below. Choose the look with
data-theme. Do not layer component CSS over Caps.
| Token | Caps material role |
|---|---|
daisyUI --depth |
Scales molded highlights, bevels, recesses, and elevation shadows. 1 preserves the molded latte/mocha material. 0 removes dimensional effects for flat linework. |
daisyUI --border |
Structural border and linework thickness. |
daisyUI --radius-field, --radius-selector, --radius-box |
Caps control, selection, and container corner geometry. |
daisyUI --color-base-*, --color-base-content, --color-neutral, semantic colors |
Surface faces, ink, neutral controls, and state accents. |
Stipe --stipe-linework |
Structural line color. The default follows --color-base-300, while field-guide uses ink. |
Stipe --card-paper, --card-rule, --card-ink, --card-rule-width |
Collector card faces, ruled borders, text, and frame weight. |
Stipe --card-frame, --card-frame-ink, --card-accent, --card-window, --card-window-ink, --card-texture, --card-shadow |
Card frame band and its ink, the accent rule and series mark, the art window stock and its ink, paper grain, and the single drop shadow. |
Stipe --font-display |
Fraunces, the self-hosted display serif for headlines, decks, card titles and flavor text. |
Stipe --font-nameplate |
Grenze Gotisch, the self-hosted blackletter for a Masthead’s nameplate. |
Stipe --newsprint-column-rule, --marginalia-ink |
Issue column separators and notes in the margin. |
Stipe --growth-spore, --growth-mycelium, --growth-fruiting |
Growth-stage badge colors. |
Caps adds no parallel material vocabulary, because daisyUI already supplies
depth, border weight, radii and semantic colors. field-guide sets
--depth: 0, thin borders, small radii, cream paper colors and muted naturalist
accents. field-guide-night keeps the same material on dark blue-green paper
with cream ink.
Screen supplies display material and keeps the inherited interface font. Use
font-mono on machine values, identifiers and code, not on a whole screen. Use
opaque semantic tokens for text hierarchy and ordinary surfaces. Keep opacity
for real compositing such as scrims, overlays and visual effects.
Filled and outline buttons read finish tokens: --caps-button-border-width
(length), --caps-button-weight (font weight), --caps-button-press (active
travel), --caps-button-drop and --caps-button-drop-active (complete shadow
lists), and --caps-button-cut (a CSS clip path, none keeps the uncut
silhouette). Unset tokens keep the default rendering. Roles still choose the
colour. Joined input actions keep their shared seam.
Class merging
Section titled “Class merging”@fungi.computer/caps/lib/cn merges classes with cn@0.2.6 and Stipe’s
named spacing scale. It accepts conditional
strings, arrays and objects and keeps caller-last Tailwind overrides, so
cn("text-xs px-xs py-sm", "px-md") gives "text-xs py-sm px-md". The helper
keeps typography and spacing names apart.
Dialogs and menus
Section titled “Dialogs and menus”Dialog and DropdownMenu use Base UI’s event details, focus management and
open state. Their styled parts accept Base UI’s render composition and
state-based className callbacks. You can place the portal inside a window and
choose focus targets. When the container mounts with the overlay, pass the
mounted element through a React callback ref. Passing null keeps the portal
waiting for that element.
DialogContent is the convenient portal, backdrop and popup composition. It
keeps child state across closes by default. Its backdrop follows Base UI’s full
modal mode. modal={false} and modal="trap-focus" have no backdrop. For a
different composition, use DialogPortal, DialogBackdrop and DialogPopup
directly. The raw portal keeps Base UI’s default mount lifetime. You can retain
closing content with details.preventUnmountOnClose() and finish its animation
with the native actionsRef.current.unmount().
DropdownMenuContent exposes placement and portal-container options. Use
DropdownMenuPortal, DropdownMenuPositioner and DropdownMenuPopup directly
for custom anchors or collision boundaries. Both overlays support
interactionOwner="host": your app receives Escape and owns focus restoration.
Local overlays use Base UI’s normal closing behaviour.
CSS inherits through the DOM, not through React portals. For a nested theme, render popups into an unclipped container inside that theme.
Selection contracts
Section titled “Selection contracts”Use rootRef to receive the root div of a generic Combobox or Listbox.
Combobox also exposes inputRef and triggerRef for its input and popup
trigger.
Import ComboboxProps<T> from @fungi.computer/caps/components/combobox and
ResourceSelectProps from @fungi.computer/caps/blocks/resource-select. Use
Extract or Exclude to select a mode, and indexed access to declare reusable
callbacks or native trigger metadata. The public props keep the inline or popup,
controlled or uncontrolled, and single or multiple constraints, so you need no
private aliases. The selection contract notes have
worked examples.
Windows
Section titled “Windows”WindowFrame, WindowHeader, WindowActions and WindowBody draw a rounded
application window. Caps owns presentation, not a window manager. Your app
supplies positioning, active and floating state, drag-handle events and
lifecycle commands, and owns tiling, floating geometry and layout focus.
GameWindowContent adds padded reading rhythm for a game’s headings, readouts
and action keys inside WindowBody.
Only the callbacks you pass produce action buttons. Omit handleProps for a
static title. Otherwise the title is a native button, separate from the actions.
Name the frame with aria-label or aria-labelledby. A window is a non-modal
section, not a dialog or focus trap. WindowFrame variant="ink" draws it in an
ink line with a hard offset shadow, for windows over a game world.
Editorial and print components
Section titled “Editorial and print components”Some components serve long-form, magazine-style pages. They read Stipe’s card,
marginalia, growth and type tokens, so they look right in field-guide,
field-guide-night and the other editorial themes.
@fungi.computer/caps/components/field-guide:Keycap(a native keyboard key),StatusLed(a static labelled light),CartridgeLabel(a ruled cartridge face) andSpecimenLabel(plate annotation, catalogue metadata, title and summary).@fungi.computer/caps/components/technonomicon:CigaretteCard,Masthead,SectionBar,Folio,IssueMasthead,Spreadand related editorial pieces.
CigaretteCard is a framed collector card at the playing-card ratio 63:88. Its
content scales and clamps to fit instead of stretching the card: a display-serif
title and type line, a hexagonal series mark, an inset art window, a stat plate
on an accent rule, a flavor line and a collector line with an optional set
symbol. The reverse keeps the same frame around a ruled measurement ledger with
sources. finish="full-art" runs the drawing under paper panels inside an
accent border. A null stat value prints “not measured”. Caps never invents a
number. Give a card href to make it a preview: a link to the card’s own page
that shows only its face. The card on its own page is a pressed button that
turns over.
Masthead sets a nameplate in --font-nameplate blackletter with an optional
printed mark, a tagline, an edition line between a hairline and a double
rule, and two ears (startEar, endEar) that flank the nameplate on wide pages
and sit under it on phones. size="front" prints the large front-page head and
size="running" the smaller inner-page head. SectionBar is its section
navigation: small capitals between rules, the current section printed in
reverse, an optional letter marker, a shortcut announced through
aria-keyshortcuts, and tools at the end. It scrolls sideways on narrow pages
instead of wrapping. Folio is the running foot. IssueMasthead names its
publication in the kicker. Spread layout="lead" opens its first teaser
paragraph on a drop cap.
Server-rendered pages without React hydration can opt in to two small browser
enhancements. enhanceCigaretteCards() from
@fungi.computer/caps/enhance/cigarette-card makes click, tap, Enter and Space
toggle a card’s aria-pressed, and ignores clicks a hydrated card already
handled. sketch() from @fungi.computer/caps/enhance/sketch draws rough.js
pencil marks inside every [data-sketch-root]. Mark inline text with
data-sketch="circle" or data-sketch="underline", and a note with
data-sketch-leader-x/y to draw a leader to the data-sketch-target inside the
same data-sketch-item. The layout turns leaders on with
--caps-sketch-leader: on. Strokes use currentColor, so theme switches need
no redraw. SpecimenLabel sketch marks its plate and detail, and its ruled CSS
marks stay until the pencil layer draws.
Static sites (for example Astro) can use plain-HTML class contracts from the stylesheet without React:
- Journal:
caps-meta(catalogue line),caps-entry-row(contents row,data-thumb="none"without a plate),caps-toc,caps-backlinks,caps-tool-call,caps-collage(specimen sketchbook with--art-*and--note-*placement variables),caps-note-teaser,caps-inset(data-align="end"floats a card beside prose),caps-nav-link,caps-shortcuts,caps-skip-linkandcaps-pencil-reveal. - Print:
caps-headline(data-size="banner|lead|story|brief"),caps-deck,caps-byline,caps-jump(the “continued on” line),caps-story-head,caps-section-head(data-size="front"for a section front),caps-edition(childrendata-edition-slot="lead|aside|wide", the aside ruled beside the lead),caps-column-grid(one ruled column per topic,--caps-columns),caps-column-head,caps-column-empty,caps-brief,caps-box,caps-numeral,caps-print-prose(display headings, pull-quote blockquotes, an ornamental break and, withdata-dropcap, a drop cap) andcaps-live-portrait(a live avatar traced by a host SVG filter whosecaps-live-portrait-inkflood reads--card-ink).caps-note-teaser[data-size="lead"]sets a teaser as a front-page lead. - Page turns: keyframes
caps-page-out,caps-page-in,caps-page-back-outandcaps-page-back-inare for your view transitions. Drop them under reduced motion.