Skip to content

Concepts

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.

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.

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.

@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.

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.

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.

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.

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) and SpecimenLabel (plate annotation, catalogue metadata, title and summary).
  • @fungi.computer/caps/components/technonomicon: CigaretteCard, Masthead, SectionBar, Folio, IssueMasthead, Spread and 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-link and caps-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 (children data-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, with data-dropcap, a drop cap) and caps-live-portrait (a live avatar traced by a host SVG filter whose caps-live-portrait-ink flood 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-out and caps-page-back-in are for your view transitions. Drop them under reduced motion.