max / alloy
- Co-Authored-By
- Claude Opus 5 (1M context) <noreply@anthropic.com>
- Claude-Session
- https://claude.ai/code/session_01EhbCuw3Z8VHWq6VNZ4XK2B
5 files changed,
+193 insertions,
-88 deletions
| @@ -88,43 +88,81 @@ | |||
| 88 | 88 | ||
| 89 | 89 | ## Verified contrast: Akari Dawn | |
| 90 | 90 | ||
| 91 | - | Method: OKLCH-agnostic; reads hex, computes WCAG 2.1 relative luminance directly. | |
| 91 | + | Method: OKLCH-agnostic; reads hex, computes WCAG 2.1 relative luminance directly. Every number below comes out of `tools/wcag_audit.py`; regenerate rather than hand-edit. | |
| 92 | 92 | ||
| 93 | 93 | **Text on surfaces:** | |
| 94 | 94 | ||
| 95 | 95 | | Pair | vs sunken | vs page | vs raised | vs overlay | | |
| 96 | 96 | |---|---:|---:|---:|---:| | |
| 97 | - | | `content.primary` | 10.31 | 13.25 | 14.40 | 15.03 | | |
| 98 | - | | `content.secondary` | 8.15 | 10.48 | 11.39 | 11.88 | | |
| 99 | - | | `content.muted` | 5.01 | 6.44 | 7.00 | 7.30 | | |
| 97 | + | | `content.primary` | 11.19 | 14.38 | 16.74 | 18.06 | | |
| 98 | + | | `content.secondary` | 8.96 | 11.51 | 13.39 | 14.45 | | |
| 99 | + | | `content.muted` | 3.72 | 4.78 | 5.56 | 6.00 | | |
| 100 | 100 | ||
| 101 | - | All twelve pairings clear AA-text (4.5); most clear AAA (7). | |
| 101 | + | `primary` and `secondary` are AAA on every tier. `muted` is AA-text everywhere but the sunken well, where it holds AA-UI, which is what a deliberately recessive tone is for. | |
| 102 | 102 | ||
| 103 | 103 | **Borders on surfaces:** | |
| 104 | 104 | ||
| 105 | 105 | | Pair | vs page | vs raised | vs overlay | | |
| 106 | 106 | |---|---:|---:|---:| | |
| 107 | - | | `border-strong` (derived) | 3.27 | 3.55 | 3.71 | | |
| 108 | - | | `line.border` | 1.37 | 1.49 | 1.55 | | |
| 109 | - | | `border-subtle` (derived) | 1.12 | 1.22 | 1.27 | | |
| 107 | + | | `border-strong` (derived) | 6.34 | 7.38 | 7.96 | | |
| 108 | + | | `line.border` | 3.11 | 3.62 | 3.90 | | |
| 109 | + | | `border-subtle` (derived) | 1.37 | 1.59 | 1.72 | | |
| 110 | 110 | ||
| 111 | - | `border-strong` clears AA-UI on all three surfaces; the derivation formula works for Akari's soft border. `line.border` and `border-subtle` are decorative dividers, not required to meet 3.0. | |
| 111 | + | `line.border` now clears AA-UI on its own, so a control's edge is a boundary a reader can find rather than a hint the derivation has to rescue. `border-strong` is the emphatic tier above it and `border-subtle` stays decorative, not required to meet 3.0. | |
| 112 | 112 | ||
| 113 | 113 | **Accents on surfaces:** | |
| 114 | 114 | ||
| 115 | - | | Accent | vs page | vs raised | vs sunken | vs overlay | Notes | | |
| 115 | + | | Accent | vs sunken | vs page | vs raised | vs overlay | Notes | | |
| 116 | 116 | |---|---:|---:|---:|---:|---| | |
| 117 | - | | `action.primary` (lantern `#8a4530`) | 5.28 | 5.74 | **4.11** | 5.99 | AA-text on three of four; AA-UI on `sunken`, glyph-safe on the darkest tier | | |
| 118 | - | | `status.danger` (`#6a2828`) | 8.07 | 8.78 | 6.28 | 9.15 | AAA everywhere | | |
| 119 | - | | `status.success` (`#3a5830`) | 6.00 | 6.53 | 4.67 | 6.81 | AA-text everywhere | | |
| 120 | - | | `status.warning` (`#b07840`) | **2.80** | **3.05** | **2.18** | **3.18** | Fails AA-text on all tiers; AA-UI only on `raised`/`overlay`. **Amber-on-cream is inherent to Akari's aesthetic**; per the accent-on-glyph rule, warning is expressed as glyph + edge marker only. | | |
| 121 | - | | `status.info` (`#304050`) | 7.96 | 8.65 | 6.19 | 9.03 | AAA / AA-text everywhere | | |
| 117 | + | | `action.primary` (`#5a41a2`) | 4.68 | 6.02 | 7.00 | 7.56 | AA-text on all four | | |
| 118 | + | | `status.danger` (`#8f2420`) | 5.20 | 6.69 | 7.78 | 8.40 | AA-text or AAA everywhere | | |
| 119 | + | | `status.success` (`#2f5424`) | 5.26 | 6.76 | 7.87 | 8.49 | AA-text or AAA everywhere | | |
| 120 | + | | `status.warning` (`#875000`) | **4.00** | 5.14 | 5.98 | 6.45 | AA-text on three of four; AA-UI in the sunken well | | |
| 121 | + | | `status.info` (`#25415c`) | 6.39 | 8.20 | 9.55 | 10.30 | AAA / AA-text everywhere | | |
| 122 | 122 | ||
| 123 | - | Both failure modes are Akari-inherent rather than derivation bugs, and both survive Alloy's discipline: `action.primary` on `surface.sunken` still clears AA-UI (glyph-safe on the darkest recessed panel), and `status.warning`'s amber is a glyph-only accent. | |
| 123 | + | One tier misses AA-text, and it misses on the one surface a light theme cannot help: `surface.sunken` is a recessed cream, and an amber legible against it is no longer an amber. Warning keeps AA-UI there and stays a glyph-plus-edge accent in a well, per the accent-on-glyph rule. | |
| 124 | 124 | ||
| 125 | 125 | ## Verified contrast: Akari Night | |
| 126 | 126 | ||
| 127 | - | Text on surfaces all AAA or AA-text. Borders derived at 7+ contrast (dark themes have more headroom because the border tone is against a light content.primary). Notable accent behavior: `status.danger` at `#d25046` lands at 3.3–4.1:1 across surfaces: AA-UI territory, glyph-safe under the same rule. Full audit reproducible via the script. | |
| 127 | + | **Text on surfaces:** | |
| 128 | + | ||
| 129 | + | | Pair | vs sunken | vs page | vs raised | vs overlay | | |
| 130 | + | |---|---:|---:|---:|---:| | |
| 131 | + | | `content.primary` | 16.68 | 14.29 | 11.90 | 10.15 | | |
| 132 | + | | `content.secondary` | 12.81 | 10.97 | 9.14 | 7.79 | | |
| 133 | + | | `content.muted` | 5.96 | 5.10 | 4.25 | 3.63 | | |
| 134 | + | ||
| 135 | + | **Borders on surfaces:** | |
| 136 | + | ||
| 137 | + | | Pair | vs page | vs raised | vs overlay | | |
| 138 | + | |---|---:|---:|---:| | |
| 139 | + | | `border-strong` (derived) | 10.77 | 8.97 | 7.65 | | |
| 140 | + | | `line.border` | 4.33 | 3.61 | 3.08 | | |
| 141 | + | | `border-subtle` (derived) | 2.33 | 1.94 | 1.65 | | |
| 142 | + | ||
| 143 | + | **Accents on surfaces:** | |
| 144 | + | ||
| 145 | + | | Accent | vs sunken | vs page | vs raised | vs overlay | Notes | | |
| 146 | + | |---|---:|---:|---:|---:|---| | |
| 147 | + | | `action.primary` (`#b9a3f2`) | 8.87 | 7.59 | 6.32 | 5.39 | AA-text or AAA everywhere | | |
| 148 | + | | `status.danger` (`#f78077`) | 7.69 | 6.58 | 5.48 | 4.68 | AA-text everywhere | | |
| 149 | + | | `status.success` (`#96c97e`) | 10.13 | 8.68 | 7.23 | 6.16 | AA-text or AAA everywhere | | |
| 150 | + | | `status.warning` (`#e5ac62`) | 9.63 | 8.25 | 6.87 | 5.86 | AA-text or AAA everywhere | | |
| 151 | + | | `status.info` (`#93aec6`) | 8.43 | 7.22 | 6.01 | 5.13 | AA-text or AAA everywhere | | |
| 152 | + | ||
| 153 | + | Nothing on the dark side falls below AA-text. The overlay column is the tight one, since a popover face is the lightest surface a dark theme has and every accent is read against it. | |
| 154 | + | ||
| 155 | + | ## Why the accent is a lavender | |
| 156 | + | ||
| 157 | + | `action.primary` is the one hue the whole system wears: the focus ring, the caret, the pressed button, the selected row, `category.one`, and the keyword class in every generated editor theme. Akari's own accent is a lantern orange, which put the accent in the same corner of the wheel as `status.warning` and `status.danger` — so the color meaning "this is where you are" and the color meaning "something is wrong" were neighbors, and telling them apart on a chip or a one-cell gutter marker came down to saturation. | |
| 158 | + | ||
| 159 | + | A violet accent has no semantic neighbor. Red stays available for danger and amber for warning, and the ring is unmistakably not either of them. The warm tones are still the theme's ground: `surface.*` is cream on the light side and a warm near-black on the dark, and the selection wash stays amber precisely so that a lavender caret sitting inside it does not disappear into its own hue. | |
| 160 | + | ||
| 161 | + | The consequence to know about: on a light theme a lavender legible as text has to be a deep violet (`#5a41a2`), not the pale tint the word "lavender" suggests. A pale lavender is available to a consumer as a *surface* tint via `mix(surface.page, action.primary, ...)`; it is not what the ink can be. | |
| 162 | + | ||
| 163 | + | ## Why bright red is no longer the accent | |
| 164 | + | ||
| 165 | + | ANSI slot 9 used to resolve to `action.primary`, on the reasoning that the accent was a warm orange-red and so was bright red. That held only while the accent happened to be warm; pointed at a violet it turns every `\e[91m` in the terminal purple. Slot 9 is now `status.danger`, the same relationship slots 10, 11 and 12 have with their normal-intensity counterparts. A program painting an error in bright red is naming red, not naming the theme's accent. | |
| 128 | 166 | ||
| 129 | 167 | ## What's deferred | |
| 130 | 168 |
| @@ -74,8 +74,66 @@ | |||
| 74 | 74 | max(0, min(255, g)), | |
| 75 | 75 | max(0, min(255, b))) | |
| 76 | 76 | ||
| 77 | + | # ---------------------------------------------------------------- oklab | |
| 78 | + | ||
| 79 | + | def _to_oklab(h): | |
| 80 | + | r, g, b = (_linearize(c) for c in hex_to_srgb(h)) | |
| 81 | + | l = 0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b | |
| 82 | + | m = 0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b | |
| 83 | + | s = 0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b | |
| 84 | + | l_, m_, s_ = (v ** (1 / 3) if v >= 0 else -((-v) ** (1 / 3)) for v in (l, m, s)) | |
| 85 | + | return (0.2104542553 * l_ + 0.7936177850 * m_ - 0.0040720468 * s_, | |
| 86 | + | 1.9779984951 * l_ - 2.4285922050 * m_ + 0.4505937099 * s_, | |
| 87 | + | 0.0259040371 * l_ + 0.7827717662 * m_ - 0.8086757660 * s_) | |
| 88 | + | ||
| 89 | + | def _from_oklab(lab): | |
| 90 | + | L, A, B = lab | |
| 91 | + | l_ = L + 0.3963377774 * A + 0.2158037573 * B | |
| 92 | + | m_ = L - 0.1055613458 * A - 0.0638541728 * B | |
| 93 | + | s_ = L - 0.0894841775 * A - 1.2914855480 * B | |
| 94 | + | l, m, s = l_ ** 3, m_ ** 3, s_ ** 3 | |
| 95 | + | def _delin(c): | |
| 96 | + | c = 12.92 * c if c <= 0.0031308 else 1.055 * (max(c, 0.0) ** (1 / 2.4)) - 0.055 | |
| 97 | + | return max(0, min(255, round(c * 255))) | |
| 98 | + | return "#{:02x}{:02x}{:02x}".format( | |
| 99 | + | _delin(4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s), | |
| 100 | + | _delin(-1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s), | |
| 101 | + | _delin(-0.0041960863 * l - 0.7034186147 * m + 1.7076147010 * s)) | |
| 102 | + | ||
| 103 | + | def oklab_mix(a_hex, b_hex, t): | |
| 104 | + | """Perceptual mix, the one makeover derives with. t=0 => a, t=1 => b.""" | |
| 105 | + | x, y = _to_oklab(a_hex), _to_oklab(b_hex) | |
| 106 | + | return _from_oklab(tuple(x[i] + (y[i] - x[i]) * t for i in range(3))) | |
| 107 | + | ||
| 77 | 108 | # ---------------------------------------------------------------- derivation | |
| 78 | 109 | ||
| 110 | + | # makeover's tonal-step constants (makeover `src/emphasis.rs`). The ratios are | |
| 111 | + | # a starting point: each step is pushed further toward the page until it clears | |
| 112 | + | # STEP_FLOOR against the ink, so a theme gets a step that can be seen rather | |
| 113 | + | # than a step of the agreed size. | |
| 114 | + | STEP_RATIO = {"secondary": 0.12, "muted": 0.42} | |
| 115 | + | STEP_FLOOR = 1.21 | |
| 116 | + | PROBE = 0.005 | |
| 117 | + | ||
| 118 | + | def tonal_steps(ink, page): | |
| 119 | + | """content.secondary and content.muted, as makeover derives them at load. | |
| 120 | + | ||
| 121 | + | They are not authored. A theme file names `content.primary` and nothing | |
| 122 | + | else in that family, so this reproduces `makeover::derive_tonal_steps` | |
| 123 | + | rather than reading keys that are no longer in the file. The two must agree; | |
| 124 | + | makeover is the source of truth and this is the port. | |
| 125 | + | """ | |
| 126 | + | out, reached = {}, 0.0 | |
| 127 | + | for key in ("secondary", "muted"): | |
| 128 | + | ratio = max(STEP_RATIO[key], reached) | |
| 129 | + | while True: | |
| 130 | + | color = oklab_mix(ink, page, min(max(ratio, 0.0), 1.0)) | |
| 131 | + | if contrast(color, ink) >= STEP_FLOOR or ratio >= 1.0: | |
| 132 | + | break | |
| 133 | + | ratio += PROBE | |
| 134 | + | out[key], reached = color, ratio | |
| 135 | + | return out | |
| 136 | + | ||
| 79 | 137 | def derive(theme): | |
| 80 | 138 | """Compute Alloy's extended tokens from a makeover theme. | |
| 81 | 139 | ||
| @@ -92,10 +150,12 @@ | |||
| 92 | 150 | # text gets there on both crisp (dark border) and soft (Akari-tier) | |
| 93 | 151 | # borders. border-subtle is decorative — 60% toward surface reads | |
| 94 | 152 | # as "hint of a divider" without adding contrast. | |
| 95 | - | return { | |
| 153 | + | out = { | |
| 96 | 154 | "border-subtle": mix_hex(border, surface, 0.60), | |
| 97 | 155 | "border-strong": mix_hex(border, primary, 0.65), | |
| 98 | 156 | } | |
| 157 | + | out.update(tonal_steps(primary, surface)) | |
| 158 | + | return out | |
| 99 | 159 | ||
| 100 | 160 | # ---------------------------------------------------------------- reporting | |
| 101 | 161 | ||
| @@ -130,8 +190,9 @@ | |||
| 130 | 190 | print("Text on surfaces (target >= 4.5 for text; muted target 3.0)") | |
| 131 | 191 | for key in ("primary", "secondary", "muted"): | |
| 132 | 192 | target = UI_TARGET if key == "muted" else TEXT_TARGET | |
| 193 | + | ink = text[key] if key == "primary" else derived[key] | |
| 133 | 194 | for s_key in ("page", "raised", "sunken", "overlay"): | |
| 134 | - | r = contrast(text[key], surf[s_key]) | |
| 195 | + | r = contrast(ink, surf[s_key]) | |
| 135 | 196 | _row(f"content.{key} on surface.{s_key}", r, target) | |
| 136 | 197 | ||
| 137 | 198 | # Borders on surfaces |
| @@ -220,7 +220,7 @@ | |||
| 220 | 220 | if let Some(inner) = value.strip_prefix('{').and_then(|v| v.strip_suffix('}')) { | |
| 221 | 221 | // A rule that sets its own background is contrasted against *that*, | |
| 222 | 222 | // not against the page — `ui.statusline.insert` puts the page color | |
| 223 | - | // on a lantern chip, which is correct and would fail a check | |
| 223 | + | // on an accent chip, which is correct and would fail a check | |
| 224 | 224 | // against the page. Only rules that let the page show through are | |
| 225 | 225 | // this test's business. | |
| 226 | 226 | if inner.contains("bg") { |
| @@ -1,6 +1,7 @@ | |||
| 1 | - | # Author: Shu Kutsuzawa <cappyzawa@gmail.com> | |
| 2 | - | # License: MIT | |
| 3 | - | # @{meta.name} — inspired by Japanese alleys lit by round lanterns. | |
| 1 | + | # Descended from Akari by Shu Kutsuzawa <cappyzawa@gmail.com>, MIT. | |
| 2 | + | # https://github.com/cappyzawa/akari-theme | |
| 3 | + | # The values below are makeover's own and no longer Akari's; the ancestry is. | |
| 4 | + | # @{meta.name} — the warm ground of a lantern-lit alley, under a violet accent. | |
| 4 | 5 | # | |
| 5 | 6 | # Generated by skelgen from makeover's theme of the same name. Every tone | |
| 6 | 7 | # below is derived; nothing here is a hex literal. Edit the theme, not this. | |
| @@ -17,7 +18,7 @@ | |||
| 17 | 18 | "ui.cursor.normal" = { fg = "cursor-text", bg = "cursor" } | |
| 18 | 19 | "ui.cursor.primary" = { fg = "cursor-text", bg = "cursor" } | |
| 19 | 20 | "ui.cursor.primary.normal" = { fg = "cursor-text", bg = "cursor" } | |
| 20 | - | "ui.cursor.match" = { fg = "lantern", bg = "sunken", modifiers = ["bold"] } | |
| 21 | + | "ui.cursor.match" = { fg = "accent", bg = "sunken", modifiers = ["bold"] } | |
| 21 | 22 | "ui.cursor.insert" = { fg = "cursor-text", bg = "green" } | |
| 22 | 23 | "ui.cursor.primary.insert" = { fg = "cursor-text", bg = "green" } | |
| 23 | 24 | "ui.cursor.select" = { fg = "cursor-text", bg = "magenta" } | |
| @@ -34,12 +35,12 @@ | |||
| 34 | 35 | "ui.gutter" = { bg = "background" } | |
| 35 | 36 | "ui.gutter.selected" = { bg = "cursorline" } | |
| 36 | 37 | "ui.linenr" = { fg = "bright-black" } | |
| 37 | - | "ui.linenr.selected" = { fg = "lantern" } | |
| 38 | + | "ui.linenr.selected" = { fg = "accent" } | |
| 38 | 39 | ||
| 39 | 40 | "ui.statusline" = { fg = "foreground", bg = "surface" } | |
| 40 | 41 | "ui.statusline.inactive" = { fg = "comment", bg = "surface" } | |
| 41 | 42 | "ui.statusline.normal" = { fg = "background", bg = "green", modifiers = ["bold"] } | |
| 42 | - | "ui.statusline.insert" = { fg = "background", bg = "lantern", modifiers = ["bold"] } | |
| 43 | + | "ui.statusline.insert" = { fg = "background", bg = "accent", modifiers = ["bold"] } | |
| 43 | 44 | "ui.statusline.select" = { fg = "background", bg = "magenta", modifiers = ["bold"] } | |
| 44 | 45 | "ui.statusline.separator" = { fg = "bright-black", bg = "surface" } | |
| 45 | 46 | ||
| @@ -63,7 +64,7 @@ | |||
| 63 | 64 | "ui.virtual.jump-label" = { fg = "bright-red", modifiers = ["bold"] } | |
| 64 | 65 | ||
| 65 | 66 | "ui.debug.breakpoint" = { fg = "red" } | |
| 66 | - | "ui.debug.active" = { fg = "lantern", modifiers = ["bold"] } | |
| 67 | + | "ui.debug.active" = { fg = "accent", modifiers = ["bold"] } | |
| 67 | 68 | ||
| 68 | 69 | "ui.highlight" = { bg = "match-bg" } | |
| 69 | 70 | "ui.highlight.frameline" = { bg = "surface" } | |
| @@ -74,22 +75,22 @@ | |||
| 74 | 75 | ||
| 75 | 76 | "ui.picker.header" = { fg = "bright-blue", modifiers = ["bold"] } | |
| 76 | 77 | "ui.picker.header.column" = { fg = "bright-blue", modifiers = ["bold"] } | |
| 77 | - | "ui.picker.header.column.active" = { fg = "lantern", modifiers = ["bold"] } | |
| 78 | + | "ui.picker.header.column.active" = { fg = "accent", modifiers = ["bold"] } | |
| 78 | 79 | ||
| 79 | 80 | # Diagnostics | |
| 80 | 81 | "error" = { fg = "red", modifiers = ["bold"] } | |
| 81 | - | "warning" = { fg = "lantern" } | |
| 82 | + | "warning" = { fg = "yellow" } | |
| 82 | 83 | "info" = { fg = "blue" } | |
| 83 | 84 | "hint" = { fg = "comment" } | |
| 84 | 85 | ||
| 85 | 86 | "diagnostic.error" = { underline = { color = "red", style = "curl" } } | |
| 86 | - | "diagnostic.warning" = { underline = { color = "lantern", style = "curl" } } | |
| 87 | + | "diagnostic.warning" = { underline = { color = "yellow", style = "curl" } } | |
| 87 | 88 | "diagnostic.info" = { underline = { color = "blue", style = "curl" } } | |
| 88 | 89 | "diagnostic.hint" = { underline = { color = "comment", style = "curl" } } | |
| 89 | 90 | "diagnostic.unnecessary" = { modifiers = ["dim"] } | |
| 90 | 91 | "diagnostic.deprecated" = { modifiers = ["crossed_out"] } | |
| 91 | 92 | ||
| 92 | - | "tabstop" = { underline = { color = "lantern", style = "dashed" } } | |
| 93 | + | "tabstop" = { underline = { color = "accent", style = "dashed" } } | |
| 93 | 94 | ||
| 94 | 95 | # Diff | |
| 95 | 96 | "diff.plus" = "green" | |
| @@ -102,16 +103,16 @@ | |||
| 102 | 103 | "diff.delta.conflict" = { fg = "conflict", modifiers = ["bold"] } | |
| 103 | 104 | ||
| 104 | 105 | # Markup | |
| 105 | - | "markup.heading" = { fg = "lantern", modifiers = ["bold"] } | |
| 106 | + | "markup.heading" = { fg = "accent", modifiers = ["bold"] } | |
| 106 | 107 | "markup.heading.marker" = { fg = "comment" } | |
| 107 | - | "markup.heading.1" = { fg = "lantern", modifiers = ["bold"] } | |
| 108 | + | "markup.heading.1" = { fg = "accent", modifiers = ["bold"] } | |
| 108 | 109 | "markup.heading.2" = { fg = "amber", modifiers = ["bold"] } | |
| 109 | 110 | "markup.heading.3" = { fg = "ember", modifiers = ["bold"] } | |
| 110 | 111 | "markup.heading.4" = { fg = "amber" } | |
| 111 | 112 | "markup.heading.5" = { fg = "comment", modifiers = ["bold"] } | |
| 112 | 113 | "markup.heading.6" = { fg = "blue" } | |
| 113 | 114 | "markup.list" = { fg = "cyan" } | |
| 114 | - | "markup.list.numbered" = { fg = "lantern" } | |
| 115 | + | "markup.list.numbered" = { fg = "accent" } | |
| 115 | 116 | "markup.list.unnumbered" = { fg = "cyan" } | |
| 116 | 117 | "markup.list.checked" = { fg = "green" } | |
| 117 | 118 | "markup.list.unchecked" = { fg = "comment" } | |
| @@ -127,8 +128,8 @@ | |||
| 127 | 128 | "markup.raw.block" = { fg = "bright-green" } | |
| 128 | 129 | "markup.normal.completion" = "foreground" | |
| 129 | 130 | "markup.normal.hover" = "foreground" | |
| 130 | - | "markup.heading.completion" = { fg = "lantern", modifiers = ["bold"] } | |
| 131 | - | "markup.heading.hover" = { fg = "lantern", modifiers = ["bold"] } | |
| 131 | + | "markup.heading.completion" = { fg = "accent", modifiers = ["bold"] } | |
| 132 | + | "markup.heading.hover" = { fg = "accent", modifiers = ["bold"] } | |
| 132 | 133 | "markup.raw.inline.completion" = { fg = "bright-green" } | |
| 133 | 134 | "markup.raw.inline.hover" = { fg = "bright-green" } | |
| 134 | 135 | ||
| @@ -145,7 +146,7 @@ | |||
| 145 | 146 | "constant" = { fg = "constant" } | |
| 146 | 147 | "constant.builtin" = { fg = "constant" } | |
| 147 | 148 | "constant.builtin.boolean" = { fg = "constant" } | |
| 148 | - | "constant.character" = { fg = "lantern" } | |
| 149 | + | "constant.character" = { fg = "accent" } | |
| 149 | 150 | "constant.character.escape" = { fg = "escape" } | |
| 150 | 151 | "constant.numeric" = { fg = "constant" } | |
| 151 | 152 | "constant.numeric.integer" = { fg = "constant" } | |
| @@ -180,19 +181,19 @@ | |||
| 180 | 181 | "punctuation.delimiter" = "foreground" | |
| 181 | 182 | "punctuation.special" = { fg = "amber" } | |
| 182 | 183 | ||
| 183 | - | "keyword" = { fg = "lantern" } | |
| 184 | - | "keyword.control" = { fg = "lantern" } | |
| 185 | - | "keyword.control.conditional" = { fg = "lantern" } | |
| 186 | - | "keyword.control.repeat" = { fg = "lantern" } | |
| 187 | - | "keyword.control.import" = { fg = "lantern" } | |
| 188 | - | "keyword.control.return" = { fg = "lantern" } | |
| 189 | - | "keyword.control.exception" = { fg = "lantern" } | |
| 190 | - | "keyword.operator" = { fg = "lantern" } | |
| 184 | + | "keyword" = { fg = "accent" } | |
| 185 | + | "keyword.control" = { fg = "accent" } | |
| 186 | + | "keyword.control.conditional" = { fg = "accent" } | |
| 187 | + | "keyword.control.repeat" = { fg = "accent" } | |
| 188 | + | "keyword.control.import" = { fg = "accent" } | |
| 189 | + | "keyword.control.return" = { fg = "accent" } | |
| 190 | + | "keyword.control.exception" = { fg = "accent" } | |
| 191 | + | "keyword.operator" = { fg = "accent" } | |
| 191 | 192 | "keyword.directive" = { fg = "macro" } | |
| 192 | - | "keyword.function" = { fg = "lantern" } | |
| 193 | - | "keyword.storage" = { fg = "lantern" } | |
| 194 | - | "keyword.storage.type" = { fg = "lantern" } | |
| 195 | - | "keyword.storage.modifier" = { fg = "lantern" } | |
| 193 | + | "keyword.function" = { fg = "accent" } | |
| 194 | + | "keyword.storage" = { fg = "accent" } | |
| 195 | + | "keyword.storage.type" = { fg = "accent" } | |
| 196 | + | "keyword.storage.modifier" = { fg = "accent" } | |
| 196 | 197 | ||
| 197 | 198 | "operator" = "foreground" | |
| 198 | 199 | ||
| @@ -203,7 +204,7 @@ | |||
| 203 | 204 | "function.macro" = { fg = "macro" } | |
| 204 | 205 | "function.special" = { fg = "bright-magenta" } | |
| 205 | 206 | ||
| 206 | - | "tag" = { fg = "lantern" } | |
| 207 | + | "tag" = { fg = "accent" } | |
| 207 | 208 | "tag.builtin" = { fg = "blue" } | |
| 208 | 209 | ||
| 209 | 210 | "namespace" = { fg = "amber" } | |
| @@ -220,8 +221,10 @@ | |||
| 220 | 221 | ||
| 221 | 222 | cursor = "@{action.primary}" | |
| 222 | 223 | cursor-text = "@{surface.page}" | |
| 223 | - | # The selection is the page carrying a wash of the theme's warm accent, which | |
| 224 | - | # is what makes a selected run read as lit rather than merely shaded. | |
| 224 | + | # The selection is the page carrying a wash of the theme's warm tone, which is | |
| 225 | + | # what makes a selected run read as lit rather than merely shaded. Warm on | |
| 226 | + | # purpose, and not the accent: the caret is the accent, and a selection tinted | |
| 227 | + | # the same hue as the thing sitting inside it hides the caret in its own wash. | |
| 225 | 228 | selection-bg = "@{mix(surface.page, status.warning, 0.25)}" | |
| 226 | 229 | selection-fg = "@{content.primary}" | |
| 227 | 230 | # One step off the page: enough to find the caret's line, not enough to stripe | |
| @@ -267,8 +270,8 @@ | |||
| 267 | 270 | ||
| 268 | 271 | # Named roles. These are what the highlight rules above actually reference; the | |
| 269 | 272 | # ANSI names are here for the handful of rules that speak in them. | |
| 270 | - | lantern = "@{action.primary}" | |
| 271 | - | ember = "@{mix(action.primary, status.danger, 0.5)}" | |
| 273 | + | accent = "@{action.primary}" | |
| 274 | + | ember = "@{mix(status.warning, status.danger, 0.5)}" | |
| 272 | 275 | amber = "@{status.warning}" | |
| 273 | 276 | constant = "@{category.six}" | |
| 274 | 277 | # Comments want to recede, which is what `muted` is for. The hand-written |
| @@ -1,7 +1,8 @@ | |||
| 1 | 1 | @{! theme = night } | |
| 2 | - | # Author: Shu Kutsuzawa <cappyzawa@gmail.com> | |
| 3 | - | # License: MIT | |
| 4 | - | # @{meta.name} — inspired by Japanese alleys lit by round lanterns. | |
| 2 | + | # Descended from Akari by Shu Kutsuzawa <cappyzawa@gmail.com>, MIT. | |
| 3 | + | # https://github.com/cappyzawa/akari-theme | |
| 4 | + | # The values below are makeover's own and no longer Akari's; the ancestry is. | |
| 5 | + | # @{meta.name} — the warm ground of a lantern-lit alley, under a violet accent. | |
| 5 | 6 | # | |
| 6 | 7 | # Generated by skelgen from makeover's theme of the same name. Every tone | |
| 7 | 8 | # below is derived; nothing here is a hex literal. Edit the theme, not this. | |
| @@ -18,7 +19,7 @@ | |||
| 18 | 19 | "ui.cursor.normal" = { fg = "cursor-text", bg = "cursor" } | |
| 19 | 20 | "ui.cursor.primary" = { fg = "cursor-text", bg = "cursor" } | |
| 20 | 21 | "ui.cursor.primary.normal" = { fg = "cursor-text", bg = "cursor" } | |
| 21 | - | "ui.cursor.match" = { fg = "lantern", bg = "sunken", modifiers = ["bold"] } | |
| 22 | + | "ui.cursor.match" = { fg = "accent", bg = "sunken", modifiers = ["bold"] } | |
| 22 | 23 | "ui.cursor.insert" = { fg = "cursor-text", bg = "green" } | |
| 23 | 24 | "ui.cursor.primary.insert" = { fg = "cursor-text", bg = "green" } | |
| 24 | 25 | "ui.cursor.select" = { fg = "cursor-text", bg = "magenta" } | |
| @@ -35,12 +36,12 @@ | |||
| 35 | 36 | "ui.gutter" = { bg = "background" } | |
| 36 | 37 | "ui.gutter.selected" = { bg = "cursorline" } | |
| 37 | 38 | "ui.linenr" = { fg = "bright-black" } | |
| 38 | - | "ui.linenr.selected" = { fg = "lantern" } | |
| 39 | + | "ui.linenr.selected" = { fg = "accent" } | |
| 39 | 40 | ||
| 40 | 41 | "ui.statusline" = { fg = "foreground", bg = "surface" } | |
| 41 | 42 | "ui.statusline.inactive" = { fg = "comment", bg = "surface" } | |
| 42 | 43 | "ui.statusline.normal" = { fg = "background", bg = "green", modifiers = ["bold"] } | |
| 43 | - | "ui.statusline.insert" = { fg = "background", bg = "lantern", modifiers = ["bold"] } | |
| 44 | + | "ui.statusline.insert" = { fg = "background", bg = "accent", modifiers = ["bold"] } | |
| 44 | 45 | "ui.statusline.select" = { fg = "background", bg = "magenta", modifiers = ["bold"] } | |
| 45 | 46 | "ui.statusline.separator" = { fg = "bright-black", bg = "surface" } | |
| 46 | 47 | ||
| @@ -64,7 +65,7 @@ | |||
| 64 | 65 | "ui.virtual.jump-label" = { fg = "bright-red", modifiers = ["bold"] } | |
| 65 | 66 | ||
| 66 | 67 | "ui.debug.breakpoint" = { fg = "red" } | |
| 67 | - | "ui.debug.active" = { fg = "lantern", modifiers = ["bold"] } | |
| 68 | + | "ui.debug.active" = { fg = "accent", modifiers = ["bold"] } | |
| 68 | 69 | ||
| 69 | 70 | "ui.highlight" = { bg = "match-bg" } | |
| 70 | 71 | "ui.highlight.frameline" = { bg = "surface" } | |
| @@ -75,22 +76,22 @@ | |||
| 75 | 76 | ||
| 76 | 77 | "ui.picker.header" = { fg = "bright-blue", modifiers = ["bold"] } | |
| 77 | 78 | "ui.picker.header.column" = { fg = "bright-blue", modifiers = ["bold"] } | |
| 78 | - | "ui.picker.header.column.active" = { fg = "lantern", modifiers = ["bold"] } | |
| 79 | + | "ui.picker.header.column.active" = { fg = "accent", modifiers = ["bold"] } | |
| 79 | 80 | ||
| 80 | 81 | # Diagnostics | |
| 81 | 82 | "error" = { fg = "red", modifiers = ["bold"] } | |
| 82 | - | "warning" = { fg = "lantern" } | |
| 83 | + | "warning" = { fg = "yellow" } | |
| 83 | 84 | "info" = { fg = "blue" } | |
| 84 | 85 | "hint" = { fg = "comment" } | |
| 85 | 86 | ||
| 86 | 87 | "diagnostic.error" = { underline = { color = "red", style = "curl" } } | |
| 87 | - | "diagnostic.warning" = { underline = { color = "lantern", style = "curl" } } | |
| 88 | + | "diagnostic.warning" = { underline = { color = "yellow", style = "curl" } } | |
| 88 | 89 | "diagnostic.info" = { underline = { color = "blue", style = "curl" } } | |
| 89 | 90 | "diagnostic.hint" = { underline = { color = "comment", style = "curl" } } | |
| 90 | 91 | "diagnostic.unnecessary" = { modifiers = ["dim"] } | |
| 91 | 92 | "diagnostic.deprecated" = { modifiers = ["crossed_out"] } | |
| 92 | 93 | ||
| 93 | - | "tabstop" = { underline = { color = "lantern", style = "dashed" } } | |
| 94 | + | "tabstop" = { underline = { color = "accent", style = "dashed" } } | |
| 94 | 95 | ||
| 95 | 96 | # Diff | |
| 96 | 97 | "diff.plus" = "green" | |
| @@ -103,16 +104,16 @@ | |||
| 103 | 104 | "diff.delta.conflict" = { fg = "conflict", modifiers = ["bold"] } | |
| 104 | 105 | ||
| 105 | 106 | # Markup | |
| 106 | - | "markup.heading" = { fg = "lantern", modifiers = ["bold"] } | |
| 107 | + | "markup.heading" = { fg = "accent", modifiers = ["bold"] } | |
| 107 | 108 | "markup.heading.marker" = { fg = "comment" } | |
| 108 | - | "markup.heading.1" = { fg = "lantern", modifiers = ["bold"] } | |
| 109 | + | "markup.heading.1" = { fg = "accent", modifiers = ["bold"] } | |
| 109 | 110 | "markup.heading.2" = { fg = "amber", modifiers = ["bold"] } | |
| 110 | 111 | "markup.heading.3" = { fg = "ember", modifiers = ["bold"] } | |
| 111 | 112 | "markup.heading.4" = { fg = "amber" } | |
| 112 | 113 | "markup.heading.5" = { fg = "comment", modifiers = ["bold"] } | |
| 113 | 114 | "markup.heading.6" = { fg = "blue" } | |
| 114 | 115 | "markup.list" = { fg = "cyan" } | |
| 115 | - | "markup.list.numbered" = { fg = "lantern" } | |
| 116 | + | "markup.list.numbered" = { fg = "accent" } | |
| 116 | 117 | "markup.list.unnumbered" = { fg = "cyan" } | |
| 117 | 118 | "markup.list.checked" = { fg = "green" } | |
| 118 | 119 | "markup.list.unchecked" = { fg = "comment" } | |
| @@ -128,8 +129,8 @@ | |||
| 128 | 129 | "markup.raw.block" = { fg = "bright-green" } | |
| 129 | 130 | "markup.normal.completion" = "foreground" | |
| 130 | 131 | "markup.normal.hover" = "foreground" | |
| 131 | - | "markup.heading.completion" = { fg = "lantern", modifiers = ["bold"] } | |
| 132 | - | "markup.heading.hover" = { fg = "lantern", modifiers = ["bold"] } | |
| 132 | + | "markup.heading.completion" = { fg = "accent", modifiers = ["bold"] } | |
| 133 | + | "markup.heading.hover" = { fg = "accent", modifiers = ["bold"] } | |
| 133 | 134 | "markup.raw.inline.completion" = { fg = "bright-green" } | |
| 134 | 135 | "markup.raw.inline.hover" = { fg = "bright-green" } | |
| 135 | 136 | ||
| @@ -146,7 +147,7 @@ | |||
| 146 | 147 | "constant" = { fg = "constant" } | |
| 147 | 148 | "constant.builtin" = { fg = "constant" } | |
| 148 | 149 | "constant.builtin.boolean" = { fg = "constant" } | |
| 149 | - | "constant.character" = { fg = "lantern" } | |
| 150 | + | "constant.character" = { fg = "accent" } | |
| 150 | 151 | "constant.character.escape" = { fg = "escape" } | |
| 151 | 152 | "constant.numeric" = { fg = "constant" } | |
| 152 | 153 | "constant.numeric.integer" = { fg = "constant" } | |
| @@ -181,19 +182,19 @@ | |||
| 181 | 182 | "punctuation.delimiter" = "foreground" | |
| 182 | 183 | "punctuation.special" = { fg = "amber" } | |
| 183 | 184 | ||
| 184 | - | "keyword" = { fg = "lantern" } | |
| 185 | - | "keyword.control" = { fg = "lantern" } | |
| 186 | - | "keyword.control.conditional" = { fg = "lantern" } | |
| 187 | - | "keyword.control.repeat" = { fg = "lantern" } | |
| 188 | - | "keyword.control.import" = { fg = "lantern" } | |
| 189 | - | "keyword.control.return" = { fg = "lantern" } | |
| 190 | - | "keyword.control.exception" = { fg = "lantern" } | |
| 191 | - | "keyword.operator" = { fg = "lantern" } | |
| 185 | + | "keyword" = { fg = "accent" } | |
| 186 | + | "keyword.control" = { fg = "accent" } | |
| 187 | + | "keyword.control.conditional" = { fg = "accent" } | |
| 188 | + | "keyword.control.repeat" = { fg = "accent" } | |
| 189 | + | "keyword.control.import" = { fg = "accent" } | |
| 190 | + | "keyword.control.return" = { fg = "accent" } | |
| 191 | + | "keyword.control.exception" = { fg = "accent" } | |
| 192 | + | "keyword.operator" = { fg = "accent" } | |
| 192 | 193 | "keyword.directive" = { fg = "macro" } | |
| 193 | - | "keyword.function" = { fg = "lantern" } | |
| 194 | - | "keyword.storage" = { fg = "lantern" } | |
| 195 | - | "keyword.storage.type" = { fg = "lantern" } | |
| 196 | - | "keyword.storage.modifier" = { fg = "lantern" } | |
| 194 | + | "keyword.function" = { fg = "accent" } | |
| 195 | + | "keyword.storage" = { fg = "accent" } | |
| 196 | + | "keyword.storage.type" = { fg = "accent" } | |
| 197 | + | "keyword.storage.modifier" = { fg = "accent" } | |
| 197 | 198 | ||
| 198 | 199 | "operator" = "foreground" | |
| 199 | 200 | ||
| @@ -204,7 +205,7 @@ | |||
| 204 | 205 | "function.macro" = { fg = "macro" } | |
| 205 | 206 | "function.special" = { fg = "bright-magenta" } | |
| 206 | 207 | ||
| 207 | - | "tag" = { fg = "lantern" } | |
| 208 | + | "tag" = { fg = "accent" } | |
| 208 | 209 | "tag.builtin" = { fg = "blue" } | |
| 209 | 210 | ||
| 210 | 211 | "namespace" = { fg = "amber" } | |
| @@ -221,8 +222,10 @@ | |||
| 221 | 222 | ||
| 222 | 223 | cursor = "@{action.primary}" | |
| 223 | 224 | cursor-text = "@{surface.page}" | |
| 224 | - | # The selection is the page carrying a wash of the theme's warm accent, which | |
| 225 | - | # is what makes a selected run read as lit rather than merely shaded. | |
| 225 | + | # The selection is the page carrying a wash of the theme's warm tone, which is | |
| 226 | + | # what makes a selected run read as lit rather than merely shaded. Warm on | |
| 227 | + | # purpose, and not the accent: the caret is the accent, and a selection tinted | |
| 228 | + | # the same hue as the thing sitting inside it hides the caret in its own wash. | |
| 226 | 229 | selection-bg = "@{mix(surface.page, status.warning, 0.25)}" | |
| 227 | 230 | selection-fg = "@{content.primary}" | |
| 228 | 231 | # One step off the page: enough to find the caret's line, not enough to stripe | |
| @@ -268,8 +271,8 @@ | |||
| 268 | 271 | ||
| 269 | 272 | # Named roles. These are what the highlight rules above actually reference; the | |
| 270 | 273 | # ANSI names are here for the handful of rules that speak in them. | |
| 271 | - | lantern = "@{action.primary}" | |
| 272 | - | ember = "@{mix(action.primary, status.danger, 0.5)}" | |
| 274 | + | accent = "@{action.primary}" | |
| 275 | + | ember = "@{mix(status.warning, status.danger, 0.5)}" | |
| 273 | 276 | amber = "@{status.warning}" | |
| 274 | 277 | constant = "@{category.six}" | |
| 275 | 278 | # Comments want to recede, which is what `muted` is for. The hand-written |