ai-ui.fscss is a presentation layer for AI product surfaces in the FSCSS ecosystem: tokens, layout, and chrome only. No models, no streaming clients, no backend.
Your app still owns data, events, and model I/O. This module owns how the shell, messages, prompt bar, and related pieces look and sit on the page — and it compiles to plain CSS.
What it covers
| Surface | Role |
|---|---|
| Shell | App grid: sidebar + main + room for the prompt |
| Messages | User / assistant rows, bubbles, avatars |
| Prompt | Textarea + send control |
| Stream | Caret on partial assistant text |
| Thinking | Processing dots |
| Citations | Source chips under answers |
| Artifacts | Code / JSON / doc cards |
| Workflow | Step nodes and edges |
| Usage | Token and metric cards |
Same contract as other FSCSS modules: import registers @define helpers, you call them with host selectors, markup uses those class names, design tokens flow through CSS variables.
Install mindset
You need FSCSS 1.2.6+ (CLI, browser runtime, IDE, or the Python wrapper - same language).
Registry-style import:
@import((*) from ai-ui) Or selective, when you only want part of the surface:
@import(( ai-root, ai-theme-research, ai-shell, ai-messages, ai-message, ai-message-assistant, ai-bubble, ai-citations, ai-citation, ai-stream ) from ai-ui) In the browser IDE, package automation can scaffold demos with something like libon ai-ui / libck ai-ui to enable the pack in your workspace.
Tokens and themes
Start from a root once:
@ai-root() /* :root */ /* or @ai-root(root.my-scope) for a scoped tree */ Themes override token values on that root:
| Helper | Character |
|---|---|
@ai-theme-minimal() | Quiet, near-monochrome |
@ai-theme-copilot() | Blues / greens, product-chat feel |
@ai-theme-research() | Slate + cyan, citation-heavy |
@ai-theme-support() | Calm support-desk green |
@ai-theme-dashboard() | Stronger accent for ops / agent boards |
Composition presets apply root + theme in one call:
@ai-compose-copilot() /* same idea as @ai-root() + @ai-theme-copilot() */ Also available: @ai-compose-minimal(), @ai-compose-research(), @ai-compose-support(), @ai-compose-dashboard().
You still call shell / message / prompt helpers for structure. Presets only set the token layer.
Typical tokens you will override locally:
- Surfaces:
--ai-bg,--ai-surface,--ai-card - Brand:
--ai-accent,--ai-accent-2 - Roles:
--ai-user,--ai-assistant - Type:
--ai-text,--ai-text-muted - Layout:
--ai-sidebar-w,--ai-prompt-h,--ai-msg-max
.ai-shell { --ai-accent: #a78bfa; --ai-sidebar-w: 240px; } Minimal copilot shell
Structure helpers take the host selector:
@import((*) from ai-ui) @ai-compose-copilot() @ai-shell(.ai-shell) @ai-sidebar(.ai-sidebar) @ai-main(.ai-main) @ai-messages(.ai-messages) @ai-message(.ai-message) @ai-message-user(.ai-message-user) @ai-message-assistant(.ai-message-assistant) @ai-bubble(.ai-bubble) @ai-avatar(.ai-avatar) @ai-prompt(.ai-prompt) @ai-prompt-box(.ai-prompt-box) @ai-prompt-send(.ai-prompt-send) Markup shape:
<div class="ai-shell"> <aside class="ai-sidebar">/* threads, nav */</aside> <main class="ai-main"> <div class="ai-messages"> <div class="ai-message ai-message-user"> <div class="ai-avatar">U</div> <div class="ai-bubble">Track order 12389</div> </div> <div class="ai-message ai-message-assistant"> <div class="ai-avatar">AI</div> <div class="ai-bubble">Out for delivery…</div> </div> </div> <form class="ai-prompt" onsubmit="return false"> <textarea class="ai-prompt-box" placeholder="Ask…" rows="1"></textarea> <button class="ai-prompt-send" type="submit" aria-label="Send">↑</button> </form> </main> </div> User rows align one way; assistant rows the other. Bubbles and avatars are separate pieces so you can swap markup without fighting the layout grid.
Stream, thinking, citations, artifacts
When the answer is still arriving:
@ai-stream(.ai-stream) @ai-thinking(.ai-thinking) <div class="ai-bubble ai-stream">Partial answer…</div> <div class="ai-thinking"> <span class="ai-dot"></span> <span class="ai-dot"></span> <span class="ai-dot"></span> Thinking… </div> Citations under an answer:
@ai-citations(.ai-citations) @ai-citation(.ai-citation) <div class="ai-citations"> <a class="ai-citation" href="#"><span class="ai-cite-num">1</span> source.api</a> </div> Artifact card (code / JSON / doc):
@ai-artifacts(.ai-artifacts) @ai-artifact-header(.ai-artifact-header) @ai-artifact-body(.ai-artifact-body) <div class="ai-artifacts"> <div class="ai-artifact-header"> <span>status.json</span> <span>JSON</span> </div> <div class="ai-artifact-body">{ /* … */ }</div> </div> Workflow strip for agent steps:
@ai-workflow(.ai-workflow) @ai-node(.ai-node) @ai-edge(.ai-edge) Usage / stats for an ops board:
@ai-usage(.ai-usage) @ai-stat(.ai-stat) <div class="ai-usage"> <div class="ai-stat"> <span class="ai-stat-label">Tokens today</span> <span class="ai-stat-value">48.2k</span> <span class="ai-stat-delta up">+12%</span> </div> </div> Tool chips (search / generate / etc.):
@ai-tools(.ai-tools) @ai-tool-chip(.ai-tool-chip) Utilities when you need a plain card or divider in the same token system: @ai-card, @ai-divider, @ai-scroll-fade.
Compose with others
ai-ui does not draw charts. For an ops dashboard you combine modules:
- ai-ui — shell, message column, prompt, workflow, usage chrome
- st-core@v2 — pure CSS charts from arrays and custom properties
- circle-progress — rings for quotas or completion
- overlapui — avatar stacks in a sidebar or header
Import both (or all) and keep selectors in their own namespaces. Example depth pattern:
@import((*) from ai-ui) @import((*) from st-core@v2) @ai-compose-dashboard() @st-root() /* chart fill / line helpers on a card inside .ai-main */ That is the same idea as a product screen: one token story for the AI chrome, another for the chart series, no shared class collisions by design.
What this is not
- Not a chat backend
- Not a streaming protocol
- Not a replacement for accessibility work in your markup (labels, live regions, focus order stay your job)
It is the CSS layer so you are not rewriting the same shell for every AI experiment.
Mental model
- Register —
@importfromai-ui - Theme —
@ai-compose-*or@ai-root+@ai-theme-* - Structure —
@ai-shell,@ai-messages,@ai-prompt, … with your selectors - Markup — class names that match those selectors
- Ship — compile with CLI / IDE / Python wrapper, or prototype with the browser runtime
Selective imports keep the emitted CSS small when you only need research citations or a dashboard strip.
If you are building copilots, research assistants, support bots, or agent ops boards and you already like FSCSS modules, ai-ui.fscss is the matching piece for the conversation chrome: detailed enough to look intentional, light enough that the framework and the model stay outside the stylesheet.
Source and full helper list live in the ai-ui.fscss repo
try a compose preset first, then add stream, citations, and artifacts only where the product actually needs them.