Skip to main content

max / alloy_tui

9.5 KB · 118 lines History Blame Raw
1 # Research: a design tool for immediate-mode UI
2
3 *A late-term, research-shaped project. No commitment, no timeline. Captured here so the architectural decisions Alloy makes now don't accidentally close the door on it.*
4
5 *Pivot note (2026-07-17): Alloy now authors ratatui, not egui. The question and its architecture are unchanged (both are immediate-mode, function-over-state), but the tool would target terminal compositions rather than GPU-drawn ones. egui appears below as the original immediate-mode example; read it as "immediate-mode UI, egui or ratatui."*
6
7 ## The question
8
9 How do designers collaborate with engineers on immediate-mode UIs without the designers learning Rust? Figma exists because retained-mode reactive frameworks (React, SwiftUI, Flutter, Slint) accept pixel-positioned mockups as a meaningful artifact. Immediate-mode toolkits like egui don't, because immediate-mode UI is not a tree of positioned visual elements. It is a *function* that takes state and produces frames.
10
11 If immediate-mode is the right architectural choice (Alloy's principle 3 says it is), then the design-tool gap is real. Nobody has filled it because immediate-mode UI hasn't been mainstream enough to support a serious tool, and the existing design-tool model doesn't fit. That's both the opportunity and the warning.
12
13 ## Why Figma's model doesn't translate
14
15 Figma's primitives:
16
17 - **Artboard = screen.** The unit of work is a static frame representing one state at one viewport.
18 - **Component variants = states.** Drawing the hover state means drawing another artboard.
19 - **Auto-layout = retained-mode flow.** Children are positioned by an algorithm that runs once per layout pass and caches results.
20 - **Output artifact = pixel-perfect mockup + a token system.** Engineers translate the mockup into a reactive component tree.
21
22 The translation is lossy. Designs drawn this way routinely miss:
23
24 - State transitions (Figma draws snapshots; the actual UX is the motion between them).
25 - Edge cases (long text, empty data, error variants; not drawn means not designed).
26 - Real data shapes (mockup data is always neat).
27 - Viewport variance (pixel-perfect is meaningless when the window resizes).
28
29 Engineers absorb the translation cost and the mismatches show up as bugs months later.
30
31 ## What immediate-mode rewards instead
32
33 Immediate-mode reframes the unit of design work. There is no screen, only a *function rendered every frame from current state*. So the meaningful artifacts a designer produces are:
34
35 1. **Tokens**: palette, type scale, spacing, radii, motion (or its absence). Flat data, already covered by the makeover theme files described in [COMPONENT-LIBRARY.md]COMPONENT-LIBRARY.md.
36 2. **Themed primitives**: buttons, text fields, tags, focus rings. Visual specification of state variants.
37 3. **Composition vocabulary**: "this is a card with these slots," "this is a list row with this arrangement," "this section is dense; this one is breathy." A *grammar*, not a pixel layout.
38 4. **Data shapes the composition accepts.** A `PackageRow` accepts `(name, version, urgency, icon)`. The designer specifies what fields render in what arrangement; the engineer provides the data.
39
40 The screen disappears as a unit of work. A "page" becomes a composition of components against a data source. The designer's contribution is upstream of any specific screen.
41
42 ## Sketch: what the tool might be
43
44 The hypothetical Alloy design tool (working name **`crucible`**; alloys are formed in crucibles, which preserves the metallurgy frame) would:
45
46 ### Component as unit, not screen as unit
47
48 The primary view is a component you're editing, not a canvas of screens. You edit `PackageRow` once; the tool renders it in isolation against multiple sample data sets (`short name`, `extremely long unicode name`, `missing version`, `red urgency tag`, `disabled state`), all visible at once. You do not draw four artboards; you specify variations of one component and the tool shows them live.
49
50 ### Token-first authoring
51
52 Most design work is the token graph. The tool's primary editing surface is the palette, the typography scale, the spacing scale, the radii, the elevation steps. Changes propagate instantly to every component preview. This is already most of Figma's value, and it round-trips cleanly to a makeover theme file (the `cast` tool idea generalized).
53
54 ### Composition grammar, not pixel layout
55
56 When the designer composes a component, they're not positioning pixels, they're choosing layout primitives that map 1:1 to the authored call vocabulary (ratatui's `Layout` constraints plus `alloy_tui`'s `section` / `AlloyForm` / `AlloyList` helpers, or egui's `horizontal` / `vertical` / `card` / `field_row` if authored GUI ever returns). The tool's output is a `.alloy.ron` (or `.alloy.toml`) describing the composition structurally:
57
58 ```
59 PackageRow = card(elevation = raised) {
60 horizontal {
61 icon(slot = "icon")
62 vertical {
63 text(slot = "name", style = body)
64 text(slot = "version", style = muted)
65 }
66 spacer
67 tag(slot = "urgency")
68 button(action = "update", style = primary)
69 }
70 }
71 ```
72
73 The engineer's `impl Widget for PackageRow` reads this composition file (or has it compiled in via `build.rs`) and renders it against the actual data. The interaction logic, what "update" does, is engineer territory. The visual structure is designer territory. The seam between them is the composition file.
74
75 ### Live render against sample data
76
77 The tool embeds an actual render context (a ratatui terminal backend for the TUI target). The designer's preview is not a static mockup; it is the same code path the production app uses, fed sample data the designer authors alongside the component. "What does this look like with the longest possible name?" becomes a typed-in test case, not an unsketched edge case.
78
79 ### Engineer-facing output
80
81 The artifacts produced are: a makeover theme file (tokens), `<Component>.alloy.ron` files (compositions), and a small registry of which components exist. The engineer wires these into the authored TUIs (the console). There is no pixel-perfect mockup to translate; the composition file *is* the spec.
82
83 ## Open research questions
84
85 These are the things that need answering before this is a project worth starting:
86
87 1. **How rich does the composition vocabulary need to be?** Too thin and designers can't express enough; too rich and the tool reinvents Rust visually. The egui call vocabulary is a good first approximation but may not be sufficient.
88 2. **How are conditional renders specified?** "If this field is present, show the button; otherwise hide it" is a code-side concern in egui. Does the composition language need basic conditionals? If yes, where does it stop being design and start being programming?
89 3. **How are state variants specified?** Hover/pressed/focused/disabled are mandatory per the design language. Does the designer author state-specific variants of compositions, or only state-specific tokens (and the composition is state-neutral)?
90 4. **How is custom drawing handled?** A custom sparkline or gauge widget cannot be expressed in any composition vocabulary. The tool should explicitly cede those: they're engineer-authored ratatui code that consumes tokens but isn't designed in the tool.
91 5. **What's the minimum viable scope?** Probably: token editing + a fixed set of primitives + a fixed-shape `card` and `list` composition. Everything else escalates fast.
92 6. **Is the right output format RON, TOML, or a custom DSL?** RON is most expressive; TOML is most readable; a custom DSL is most controllable but most expensive to maintain.
93
94 ## Prior art and adjacent things
95
96 - **Storybook** (web): component-first isolation and state preview. Closest existing parallel, but its primitives are React components, not a domain-independent grammar.
97 - **Penpot**: open-source Figma alternative. Hackable; a better foundation than Figma for this kind of experiment if the tool went the "plugin to an existing design app" route instead of "standalone tool."
98 - **Tokens Studio** (Figma plugin): proves token-sync alone is valuable.
99 - **Lottie**: a grammar for one specific concern (animation). Demonstrates that a constrained grammar can be a real design-tool output.
100 - **rerun.io**: Rust + egui-based data visualization tool. Not a design tool, but a demonstration that immediate-mode UI can support sophisticated tooling.
101 - **Hex / Retool**: visual programming over data. Wrong shape for design (they conflate design with logic) but their data-first ergonomic is correct.
102
103 ## What this commits Alloy to right now
104
105 Almost nothing. The architectural decisions already made in [COMPONENT-LIBRARY.md]COMPONENT-LIBRARY.md keep the door open:
106
107 - Tokens live in a TOML file, not in Rust source. Any tool that emits a valid makeover theme file is a viable producer.
108 - The crate exposes a small, stable set of layout combinators and themed primitives. These would be the vocabulary the composition grammar references.
109
110 The further commitments Alloy *could* make to keep this even more open (small, low-cost, worth considering):
111
112 - Define composition files (`<Component>.alloy.ron`) as a possible authoring surface for components, even if the v1 design system crate only consumes them via `include_str!`. This makes the surface real before any tool exists.
113 - Keep all themed primitives' visual configuration token-driven and avoid hardcoded design decisions in widget code, so a future tool editing tokens has full reach.
114
115 ## Status
116
117 Research-only. v4+ at earliest. Captured here so the question doesn't get lost and so the design-system architecture continues to leave the door open. If pursued, it is its own project (sibling to Alloy, not part of it), with its own repo and probably its own license decision.
118