Skip to main content

max / alloy

9.1 KB · 83 lines History Blame Raw
1 # Alloy Design Language
2
3 The visual rules Alloy's authored TUIs follow, and that adopted apps are themed toward where possible. "Authored" means ratatui terminal surfaces. The principles below are toolkit-agnostic, and the mechanics are stated for a terminal cell grid, with notes for any adopted GUI surface where they differ.
4
5 Reference imagery: [inspo/]inspo/: terminal rices that demonstrate most of these principles.
6
7 ## Core principle: tinted greyscale chrome, color as information
8
9 The UI is monochrome in a single tinted greyscale ramp. "Tinted" is the load-bearing word: the ramp is biased toward a hue (warm beige in light mode, warm charcoal in dark), never pure white or black at the extremes. Surfaces, borders, box-drawing, scrollbars and focus borders all live on this ramp.
10
11 **The ink is the exception, and it is neutral.** `content.primary` is a greyscale — `#111111` light, `#f0f0f0` dark. Colour on text has to mean something, and an ink tinted toward the theme's own hue spends that channel on nothing while making the warm ground read as a wash over everything rather than as ground. The tonal steps below it (`content.secondary`, `content.muted`) are derived by travelling from that ink toward the page, so they pick up a trace of the tint as they recede, which is the ramp working rather than a tinted ink. Still never pure black or pure white: `#000000` collapses the step derivation, and the reasoning is in [TOKENS.md]TOKENS.md#why-the-ink-is-a-greyscale.
12
13 Color is reserved for information. A red value means something is wrong. A green bar means utilization. A syntax-highlighted token carries semantic weight worth the eye's attention. If a color does not encode information, it does not appear.
14
15 This is the rule that organizes everything else, and it is a natural fit for a terminal, where color is scarce and legible precisely because it is rare.
16
17 ## Light and dark are polarity flips of the same tint
18
19 Dark mode is not a different palette. It is the light-mode ramp with its lightness inverted along the same hue axis. A warm-beige light mode flips to a warm-charcoal dark mode; the tint hue is preserved. Components reference ramp positions (`surface`, `surface-raised`, `text-primary`, `text-muted`, `border`) and never raw values. The mode toggle inverts lightness only.
20
21 ## The information palette
22
23 A small, fixed set of accent hues, used only when their meaning is the point:
24
25 - **Red**: error, over-threshold, destructive action confirmed
26 - **Yellow / amber**: warning, near-threshold, attention without alarm
27 - **Green**: healthy, success, in-bounds utilization
28 - **Blue**: informational, link, in-progress
29 - **Magenta / violet**: categorical accent for syntax or data series where the other four are taken
30
31 Saturation stays moderate. Accents sit as foreground on the tinted-greyscale surface; they do not become the surface. A dense data readout is fully colored where it encodes severity or series identity, with every cell of chrome remaining tinted grey.
32
33 ## What this rules out
34
35 - Branded accent colors on non-data chrome (no "Alloy" accent on pane borders, headers, or focus).
36 - Ambient color washes, gratuitous reverse-video blocks, rainbow chrome.
37 - Per-app theming that introduces a new hue. An app picks a position on the existing ramp; it does not extend the palette.
38 - Status communicated by color alone. Color-as-information is paired with a label or glyph, never color alone (accessibility floor, and doubly important in a terminal where a user may have remapped the 16-color base).
39
40 ## Typography
41
42 Mono, by nature: a terminal renders one fixed-width font (`Quasi Mono`, the house face). Hierarchy comes from the levers a terminal has: **weight** (`Modifier::BOLD` for emphasis and section headers), **case** (`UPPERCASE` letter-labels above readouts, in the mil-spec placard tradition), and **the house marks** (ICONOGRAPHY.md's functional tier: triangles, arrows, box drawing, block elements). Column alignment is free: the terminal font is already tabular, so numeric columns align without a special display font. The exact tokens live in [TOKENS.md]TOKENS.md#typography.
43
44 ## Geometry
45
46 Rectilinear, and a terminal is rectilinear by construction. Box-drawing borders (`BorderType::Plain`) in a low-contrast ramp position. No shadows (a terminal has none to give, which suits the no-decorative-shadows rule). No corner radius (cells cannot round). Generous blank-cell padding around tabular regions so the data, which carries the color, has room to breathe against the grey chrome.
47
48 ## Affordance: depth via ramp, not via color
49
50 Monochrome chrome, no shadows, and color-reserved-for-information remove the usual vocabularies for signaling interactivity. In the Apple HIG tradition of *discoverable depth*, the replacement vocabulary:
51
52 **Surface elevation is the depth language.** Use at least three ramp positions that read as elevation: `surface` (base, the terminal background), `surface-raised` (bordered controls), `surface-overlay` (popovers, menus, dialogs). The step between levels is tonal. A control announces itself by sitting on a raised, bordered surface; a data panel sits flush and borderless on `surface`. Elevation is a static affordance for control chrome, not a focus ring: the focused pane does *not* swap its surface.
53
54 **State variants are mandatory, not optional.** A terminal has no pointer, so the states reduce to what keyboard focus expresses: `default`, `focused`, `selected`, `disabled`. Focused switches the pane's border to `border-strong`, and nothing else changes: no surface swap, no title reweighting, no glyph. Selected does the same at row scope: a `border-strong` leading-edge stripe (a border-like cue) and no surface change. Disabled drops text to `text-muted`. No Alloy widget ships with only a default state. (Hover/pressed remain documented for any adopted GUI surface but do not apply to authored TUIs.)
55
56 **Borders are the boundary cue.** A single box-drawing border at `border` announces a control's edge; switching that border to `border-strong` announces focus. This is functional chrome, not decoration.
57
58 **Focus is obligatory, unambiguous, and border-only.** Keyboard focus is shown by the focused pane's border going to `border-strong`, with enough contrast to be unmistakable at arm's length, and that is the *entire* cue. No surface swap, no accent, no glyph, no title styling change. Focus uses the ramp, not the information palette; it is chrome, not data. Making focus subtle as a "clean" choice is a regression; stacking extra signals on top of the border is also a regression, in the other direction.
59
60 **Square-and-flush is for data; bordered is for controls.** Data panels and tabular regions are borderless and flush on `surface`. Interactive controls (fields, list rows that respond to Enter, buttons) carry a box border. The presence or absence of a border is itself the affordance cue.
61
62 ## Composition patterns
63
64 Binding wherever the layout applies.
65
66 **Action hierarchy: polarity-driven.** A primary action inverts polarity (its cell run is `text-primary` background with `surface-raised` text), visually heavy without recruiting an accent. Secondary actions sit on `surface` with normal text. Disabled actions hold `text-muted`. A pane never shows more than one primary action.
67
68 **List and pane selection: leading-edge stripe.** The current selection in a list is marked with a `border-strong` stripe (a single reverse or block cell) along the leading edge of the row. The row's surface does not change, and no glyph or reweighting is added; the stripe is the only cue, matching the pane-border rule at row scope. It rhymes with the focus border (also `border-strong`) but reads as an edge, so selection and focus stay distinguishable when they coincide. Selection is chrome; never an accent.
69
70 **Inline messages: accent only at the edge and glyph.** A color-as-information message (error, warn, healthy, info) carries a single-cell accent bar at the leading edge and an accent glyph; the message body stays `text-primary`. The accent never fills the message's surface (that would turn chrome into data).
71
72 **Titlebars / header rows: one step above raised.** A window or pane header sits on `surface-overlay` to mark its perimeter as distinct from content on `surface-raised`.
73
74 **Tabular numerics: the terminal font, right-aligned.** Numeric columns align for free in a fixed-width terminal font; right-align them and pad with blank cells. No special display font is needed or available.
75
76 ## Iconography
77
78 Two tiers with non-overlapping roles. Functional icons are the house glyph set drawn into the face itself — triangles, arrows, box drawing, block elements — used inline at cell size, and are the primary tier for TUIs. There is no Private Use Area in the image. Hero illustrations survive only on the few graphical surfaces that remain (splash, swaylock background). Full rules in [ICONOGRAPHY.md]ICONOGRAPHY.md.
79
80 ## Scope
81
82 Binding for the ratatui surfaces Alloy authors (`alloy_tui`, the console). Adopted apps (sway chrome, swaylock, mako, helix, yazi, bottom) are themed toward the ramp where their theming permits, and accepted as-is where it does not. Heterogeneity at the seam between authored and adopted is a known trade, called out in [MANIFESTO.md]MANIFESTO.md#what-we-author-vs-what-we-adopt.
83