steid

@jamesgill /

steid/styles.css
8.0 KBCode·Blame·Raw
1/* Steid's theme.
2 Started from Topcoat's `neutral` theme and retuned; see plans/ui.md.
3
4 The theme is a small set of design tokens. Components refer to tokens only,
5 never to raw colors, so the whole product restyles by editing `:root` and
6 `.dark` below. A component that hardcodes a color is a bug — it will not
7 follow a palette change and it will not adapt to the color scheme.
8
9 --background the page background
10 --surface raised panels: cards, menus, flash messages
11 --foreground text and iconography on the background
12 --muted-foreground secondary text: captions, descriptions, placeholders
13 --primary the emphasis color: primary actions, selection, links
14 --primary-foreground text on a primary-filled surface
15 --success confirmations, and git additions
16 --success-foreground text on a success-filled surface
17 --warning caution, and git modifications
18 --warning-foreground text on a warning-filled surface
19 --destructive destructive actions, errors, and git deletions
20 --destructive-foreground text on a destructive-filled surface
21 --border hairline borders and dividers
22 --ring the focus ring
23 --shadow-xs a subtle shadow for controls: buttons, inputs
24 --shadow-sm a shadow for raised surfaces: cards, menus
25
26 Syntax highlighting adds a --syntax-* family, derived from the same palette and
27 mapped onto the highlighter's classes at the bottom of this file.
28
29 Interactive states (hover, press) have no tokens of their own: components
30 derive them by applying the fill or foreground color at reduced opacity,
31 which adapts to both color schemes automatically.
32
33 Steid is dark-first — the layout sets `class="dark"` on <html>. Light mode
34 is supported and secondary. */
35
36@import "tailwindcss";
37
38/* Scan the project's Rust sources for the Tailwind classes they use. This
39 file is installed at the project root, so `./src` is the crate's source. */
40@source "./src/**/*.rs";
41
42/* Dark mode is opted into by putting the `dark` class on an ancestor,
43 typically <html>. */
44@custom-variant dark (&:is(.dark *));
45
46/* Expose the tokens as Tailwind utilities (`bg-background`, `text-primary`,
47 `border-border`, ...). The indirection through plain variables on `:root`
48 and `.dark` is what lets the values swap when the color scheme changes. */
49@theme inline {
50 --font-sans: "Geist", ui-sans-serif, system-ui, sans-serif;
51 /* Paths, commit hashes, branch names, and code. */
52 --font-mono: "IBM Plex Mono", ui-monospace, "SF Mono", monospace;
53 --color-background: var(--background);
54 --color-surface: var(--surface);
55 --color-foreground: var(--foreground);
56 --color-muted-foreground: var(--muted-foreground);
57 --color-primary: var(--primary);
58 --color-primary-foreground: var(--primary-foreground);
59 --color-success: var(--success);
60 --color-success-foreground: var(--success-foreground);
61 --color-warning: var(--warning);
62 --color-warning-foreground: var(--warning-foreground);
63 --color-destructive: var(--destructive);
64 --color-destructive-foreground: var(--destructive-foreground);
65 --color-border: var(--border);
66 --color-ring: var(--ring);
67 --shadow-xs: var(--shadow-xs);
68 --shadow-sm: var(--shadow-sm);
69}
70
71/* Light mode. Secondary, but it should not look like an afterthought. */
72:root {
73 --background: oklch(0.99 0.003 260);
74 --surface: oklch(1 0 0);
75 --foreground: oklch(0.24 0.012 260);
76 --muted-foreground: oklch(0.51 0.014 260);
77 --primary: oklch(0.5 0.18 250);
78 --primary-foreground: oklch(0.99 0.003 260);
79 --success: oklch(0.52 0.15 145);
80 --success-foreground: oklch(0.99 0.01 145);
81 --warning: oklch(0.62 0.14 85);
82 --warning-foreground: oklch(0.99 0.01 85);
83 --destructive: oklch(0.55 0.2 27);
84 --destructive-foreground: oklch(0.99 0.012 27);
85 --border: oklch(0.9 0.008 260);
86 --ring: oklch(0.5 0.18 250);
87 --shadow-xs: 0 1px 2px oklch(0.24 0.02 260 / 12%);
88 --shadow-sm:
89 0 1px 2px oklch(0.24 0.02 260 / 10%),
90 0 2px 8px -2px oklch(0.24 0.02 260 / 9%);
91
92 --syntax-comment: oklch(0.55 0.015 260);
93 --syntax-punctuation: oklch(0.46 0.015 260);
94 --syntax-variable: oklch(0.3 0.014 260);
95 --syntax-type: oklch(0.45 0.09 195);
96 --syntax-function: oklch(0.48 0.15 250);
97 --syntax-constant: oklch(0.5 0.12 62);
98 --syntax-string: oklch(0.45 0.12 150);
99 --syntax-keyword: oklch(0.45 0.17 300);
100 --syntax-heading: oklch(0.42 0.16 250);
101}
102
103/* Dark mode. The primary surface for this product. */
104.dark {
105 --background: oklch(0.16 0.02 260);
106 --surface: oklch(0.2 0.02 260);
107 --foreground: oklch(0.9 0.01 260);
108 --muted-foreground: oklch(0.66 0.014 260);
109 --primary: oklch(0.65 0.15 250);
110 --primary-foreground: oklch(0.16 0.02 260);
111 --success: oklch(0.7 0.17 145);
112 --success-foreground: oklch(0.16 0.02 145);
113 --warning: oklch(0.8 0.15 85);
114 --warning-foreground: oklch(0.16 0.02 85);
115 --destructive: oklch(0.65 0.2 25);
116 --destructive-foreground: oklch(0.14 0.02 25);
117 --border: oklch(0.3 0.012 260);
118 --ring: oklch(0.65 0.15 250);
119 /* Light-mode shadows disappear on a dark background, so these are deeper. */
120 --shadow-xs: 0 1px 2px oklch(0 0 0 / 35%);
121 --shadow-sm: 0 1px 2px oklch(0 0 0 / 35%), 0 2px 8px -2px oklch(0 0 0 / 30%);
122
123 --syntax-comment: oklch(0.6 0.014 260);
124 --syntax-punctuation: oklch(0.72 0.014 260);
125 --syntax-variable: oklch(0.86 0.02 260);
126 --syntax-type: oklch(0.82 0.08 195);
127 --syntax-function: oklch(0.78 0.11 240);
128 --syntax-constant: oklch(0.82 0.11 70);
129 --syntax-string: oklch(0.78 0.12 150);
130 --syntax-keyword: oklch(0.75 0.12 300);
131 --syntax-heading: oklch(0.75 0.13 250);
132}
133
134@layer base {
135 body {
136 @apply bg-background font-sans text-foreground;
137 }
138}
139
140/* Syntax highlighting.
141 `infrastructure/highlight.rs` emits classes, never inline colors, so the theme
142 lives here with everything else and follows the color scheme. Tailwind never sees
143 these class names — the markup is generated, not written in `src/` — so this is
144 plain CSS against the tokens above, as ui.md requires.
145
146 The classes are the *atoms* of a TextMate scope, flattened and prefixed: the scope
147 `storage.type.function.rust` becomes `class="hl-storage hl-type hl-function hl-rust"`.
148 Atoms are therefore not positional — `hl-function` is on `meta.function`, which
149 wraps a whole function body, as well as on the name itself — so a single-atom
150 selector is only safe for an atom that leads its scope. The rest are spelled out as
151 the compound they really are.
152
153 Order is load-bearing. Spans nest, so an inner span with two matching rules of equal
154 specificity takes the later one; `punctuation.definition.string` carries both
155 `hl-punctuation` and `hl-string`, and a quote should read as its string. Weakest
156 first, strongest last. */
157
158.hl-punctuation,
159.hl-keyword.hl-operator {
160 color: var(--syntax-punctuation);
161}
162
163.hl-variable,
164.hl-entity.hl-other.hl-attribute-name {
165 color: var(--syntax-variable);
166}
167
168.hl-entity.hl-name,
169.hl-support.hl-function {
170 color: var(--syntax-function);
171}
172
173.hl-entity.hl-name.hl-type,
174.hl-entity.hl-name.hl-class,
175.hl-entity.hl-name.hl-struct,
176.hl-entity.hl-name.hl-enum,
177.hl-entity.hl-name.hl-trait,
178.hl-entity.hl-name.hl-namespace,
179.hl-support.hl-type,
180.hl-support.hl-class {
181 color: var(--syntax-type);
182}
183
184.hl-constant,
185.hl-support.hl-constant {
186 color: var(--syntax-constant);
187}
188
189.hl-storage,
190.hl-keyword,
191.hl-entity.hl-name.hl-tag {
192 color: var(--syntax-keyword);
193}
194
195.hl-string {
196 color: var(--syntax-string);
197}
198
199/* Markdown and other markup: the source is read as prose, so structure carries the
200 color and the body stays at foreground. */
201.hl-markup.hl-heading {
202 color: var(--syntax-heading);
203 font-weight: 600;
204}
205
206.hl-markup.hl-raw {
207 color: var(--syntax-string);
208}
209
210.hl-markup.hl-underline.hl-link {
211 color: var(--syntax-function);
212}
213
214.hl-markup.hl-bold {
215 font-weight: 600;
216}
217
218.hl-markup.hl-italic {
219 font-style: italic;
220}
221
222/* Last, so a comment is a comment whatever it contains. */
223.hl-comment {
224 color: var(--syntax-comment);
225}
226
227.hl-invalid {
228 color: var(--destructive);
229}