Architecture
Workspace
guise/
├── Cargo.toml # workspace manifest (plain crates.io gpui)
├── docs/ # human docs (this directory)
├── site/ # docs-website generator (Bun; one page per docs/*.md, via render/nav.ts)
└── crates/
├── guise/ # the library — published as `guise-ui`, lib name `guise`
└── gallery/ # a live showcase (cargo run -p gallery)
The gpui dependency
Everything builds against crates.io gpui = "0.2.2" — no git pins and
no [patch.crates-io] section, so guise-ui installs as a plain registry
dependency and publishes cleanly. The few style/scroll APIs the crates.io
snapshot lacks are shimmed in style.rs (FlexExt) via gpui's raw
StyleRefinement. (thirdparty/block/ is a leftover vendored crate; no
manifest references it.)
The library package is guise-ui — the guise name was taken on
crates.io — with [lib] name = "guise". Cargo commands address the package as
-p guise-ui, while code imports stay use guise::....
Library module map (crates/guise/src)
| Module | Contents |
|---|---|
theme/ |
Theme, Color, Palette, Scale, Size, ColorScheme, JSON theme files (Theme::from_json), prebuilt presets (Theme::preset) |
style.rs |
the Variant system and surface() resolver |
layout/ |
themed Stack, Group, Center, SimpleGrid, AppShell, Container, Space, plus Breakpoint/Responsive |
flex/ |
Flutter-style Row, Column, Container, Expanded, … |
input/ |
TextInput, TextArea, NumberInput, PasswordInput, PinInput, Select, Combobox, Checkbox, Switch, Radio, RadioGroup, CheckboxGroup, SegmentedControl, Slider, RangeSlider, Rating, ColorInput, TagsInput, Field, Autocomplete, Calendar, DatePicker, TimePicker, FileInput, Dropzone, Transfer, the Date/Time models, the TextEdit model, the shared single-line key map (keys.rs) |
editor/ |
Editor entity, the EditorModel buffer, Language highlighters (Rust, SQL, JSON, TOML, Python, JS/TS, Go, C, Markdown), Diagnostic/Severity |
markdown/ |
MarkdownEditor entity (live-preview markdown) over pure block / inline / layout passes |
data/ |
Avatar, AvatarGroup, List, VirtualList, Table, TableView, DataView, TreeView, TabBar, Timeline, Tabs, Accordion |
chart/ |
Sparkline, LineChart, AreaChart, BarChart, ScatterChart, PieChart — canvas-painted builders with optional axes/legends/hover |
feedback/ |
Alert, Loader, Progress, RingProgress, Notification, ToastStack |
overlay/ |
Modal, ConfirmModal, Drawer, Menu, MenuBar, ContextMenu, Popover, HoverCard, LoadingOverlay, Spotlight, Tooltip, Tour, OverlayHost (window-level modal stack + toasts) |
nav/ |
Breadcrumbs, NavLink, NavigationMenu, Stepper, Pagination, StatusBar |
reactive/ |
Signal, Binding, Context/Provider, hooks (use_state/watch/use_memo/use_effect), Form (per-field signals) + FormState |
macros.rs |
the row!/col!/… layout macros |
anim/ |
Easing curves, Spring physics, Presence (exit animations) |
dnd/ |
Draggable, DropTarget, SortableList — typed drag payloads |
transition.rs |
Transition / Collapse (true height) animations |
webview.rs |
WebView — native embedded web view via wry (default-on webview feature) |
| root files | Button, Badge, Card, Paper, Panel, SplitPanel, Image, Mark, Blockquote, Spoiler, Text, Title, Anchor, Code, Kbd, Icon, ActionIcon, ThemeIcon, CloseButton, CopyButton, Chip, Indicator, Skeleton, Divider, ScrollArea, Carousel |
Conventions
- One component per file, lowercase names, no
-/_/spaces; group with directories (input/select.rs), not concatenated names. - Read everything from the theme via
guise::theme::theme(cx)— never hardcode a color or size. This is what makes light/dark switching free. - Builder methods take
mut selfand returnSelf(chainable). - Container components implement
ParentElement(justextend);.child/.childrencome for free. - Resolve all theme values into locals before any
cx.listener(...)or content-builder call —theme(cx)borrowscximmutably and those need it mutably, so a latetheme(cx)read overlaps the borrow and won't compile. - Closures stored on elements (
.hover,.on_click) must be'static— capture resolvedHsla/f32values, not the&Themeborrow.
Adding a component
- Create a file under the right module (or the crate root for a loose one).
- Define a
#[derive(IntoElement)]builder +impl RenderOnce, or aRender+EventEmitterentity if it owns state. Resolve visuals fromtheme(cx). - Re-export it from the module's
mod.rs, then fromlib.rs, then add it to theprelude. - Add a showcase to
crates/gallery/. - Write the component's docs section on the right
docs/page — and if that page is new, register it insite/render/nav.tsso the website picks it up. - For pure logic (parsing, range math, an editing model), add
#[cfg(test)]tests next to the code.
See the component model for the two patterns in detail.
Commands
cargo run -p gallery # launch the showcase
cargo check -p guise-ui # fast type-check (package is guise-ui; lib name is guise)
cargo test -p guise-ui # unit tests
cargo build -p gallery # full build of the binary