| 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 | Interactive states (hover, press) have no tokens of their own: components |
| 27 | derive them by applying the fill or foreground color at reduced opacity, |
| 28 | which adapts to both color schemes automatically. |
| 29 | |
| 30 | Steid is dark-first — the layout sets `class="dark"` on <html>. Light mode |
| 31 | is supported and secondary. */ |
| 32 | |
| 33 | @import "tailwindcss"; |
| 34 | |
| 35 | /* Scan the project's Rust sources for the Tailwind classes they use. This |
| 36 | file is installed at the project root, so `./src` is the crate's source. */ |
| 37 | @source "./src/**/*.rs"; |
| 38 | |
| 39 | /* Dark mode is opted into by putting the `dark` class on an ancestor, |
| 40 | typically <html>. */ |
| 41 | @custom-variant dark (&:is(.dark *)); |
| 42 | |
| 43 | /* Expose the tokens as Tailwind utilities (`bg-background`, `text-primary`, |
| 44 | `border-border`, ...). The indirection through plain variables on `:root` |
| 45 | and `.dark` is what lets the values swap when the color scheme changes. */ |
| 46 | @theme inline { |
| 47 | --font-sans: "Geist", ui-sans-serif, system-ui, sans-serif; |
| 48 | /* Paths, commit hashes, branch names, and code. */ |
| 49 | --font-mono: "IBM Plex Mono", ui-monospace, "SF Mono", monospace; |
| 50 | --color-background: var(--background); |
| 51 | --color-surface: var(--surface); |
| 52 | --color-foreground: var(--foreground); |
| 53 | --color-muted-foreground: var(--muted-foreground); |
| 54 | --color-primary: var(--primary); |
| 55 | --color-primary-foreground: var(--primary-foreground); |
| 56 | --color-success: var(--success); |
| 57 | --color-success-foreground: var(--success-foreground); |
| 58 | --color-warning: var(--warning); |
| 59 | --color-warning-foreground: var(--warning-foreground); |
| 60 | --color-destructive: var(--destructive); |
| 61 | --color-destructive-foreground: var(--destructive-foreground); |
| 62 | --color-border: var(--border); |
| 63 | --color-ring: var(--ring); |
| 64 | --shadow-xs: var(--shadow-xs); |
| 65 | --shadow-sm: var(--shadow-sm); |
| 66 | } |
| 67 | |
| 68 | /* Light mode. Secondary, but it should not look like an afterthought. */ |
| 69 | :root { |
| 70 | --background: oklch(0.99 0.003 260); |
| 71 | --surface: oklch(1 0 0); |
| 72 | --foreground: oklch(0.24 0.012 260); |
| 73 | --muted-foreground: oklch(0.51 0.014 260); |
| 74 | --primary: oklch(0.5 0.18 250); |
| 75 | --primary-foreground: oklch(0.99 0.003 260); |
| 76 | --success: oklch(0.52 0.15 145); |
| 77 | --success-foreground: oklch(0.99 0.01 145); |
| 78 | --warning: oklch(0.62 0.14 85); |
| 79 | --warning-foreground: oklch(0.99 0.01 85); |
| 80 | --destructive: oklch(0.55 0.2 27); |
| 81 | --destructive-foreground: oklch(0.99 0.012 27); |
| 82 | --border: oklch(0.9 0.008 260); |
| 83 | --ring: oklch(0.5 0.18 250); |
| 84 | --shadow-xs: 0 1px 2px oklch(0.24 0.02 260 / 12%); |
| 85 | --shadow-sm: |
| 86 | 0 1px 2px oklch(0.24 0.02 260 / 10%), |
| 87 | 0 2px 8px -2px oklch(0.24 0.02 260 / 9%); |
| 88 | } |
| 89 | |
| 90 | /* Dark mode. The primary surface for this product. */ |
| 91 | .dark { |
| 92 | --background: oklch(0.16 0.02 260); |
| 93 | --surface: oklch(0.2 0.02 260); |
| 94 | --foreground: oklch(0.9 0.01 260); |
| 95 | --muted-foreground: oklch(0.66 0.014 260); |
| 96 | --primary: oklch(0.65 0.15 250); |
| 97 | --primary-foreground: oklch(0.16 0.02 260); |
| 98 | --success: oklch(0.7 0.17 145); |
| 99 | --success-foreground: oklch(0.16 0.02 145); |
| 100 | --warning: oklch(0.8 0.15 85); |
| 101 | --warning-foreground: oklch(0.16 0.02 85); |
| 102 | --destructive: oklch(0.65 0.2 25); |
| 103 | --destructive-foreground: oklch(0.14 0.02 25); |
| 104 | --border: oklch(0.3 0.012 260); |
| 105 | --ring: oklch(0.65 0.15 250); |
| 106 | /* Light-mode shadows disappear on a dark background, so these are deeper. */ |
| 107 | --shadow-xs: 0 1px 2px oklch(0 0 0 / 35%); |
| 108 | --shadow-sm: 0 1px 2px oklch(0 0 0 / 35%), 0 2px 8px -2px oklch(0 0 0 / 30%); |
| 109 | } |
| 110 | |
| 111 | @layer base { |
| 112 | body { |
| 113 | @apply bg-background font-sans text-foreground; |
| 114 | } |
| 115 | } |