max / makeover
- Co-Authored-By
- Claude Opus 5 (1M context) <noreply@anthropic.com>
- Claude-Session
- https://claude.ai/code/session_01EhbCuw3Z8VHWq6VNZ4XK2B
7 files changed,
+104 insertions,
-51 deletions
| @@ -1,6 +1,6 @@ | |||
| 1 | 1 | [package] | |
| 2 | 2 | name = "makeover" | |
| 3 | - | version = "3.2.1" | |
| 3 | + | version = "3.3.0" | |
| 4 | 4 | edition = "2024" | |
| 5 | 5 | description = "Shared theme loading for the make-family apps: TOML theme files parsed into intent-based color tokens, with perceptual derivations and WCAG contrast." | |
| 6 | 6 | license = "MIT" |
| @@ -81,8 +81,11 @@ | |||
| 81 | 81 | ||
| 82 | 82 | ## Original themes | |
| 83 | 83 | ||
| 84 | - | `akari-dawn` and `akari-night` are adaptations, listed above. The following are | |
| 85 | - | this project's own work and carry no third-party claim: | |
| 84 | + | `akari-dawn` and `akari-night` are listed above as adaptations, and the credit | |
| 85 | + | stands: they began as Akari re-expressed into this schema. Their current values | |
| 86 | + | are this project's own, having moved for contrast and for a violet accent, so | |
| 87 | + | what remains owed upstream is the ancestry rather than the palette. The | |
| 88 | + | following carry no third-party claim at all: | |
| 86 | 89 | ||
| 87 | 90 | - `makenotwork` — the makenot.work platform palette | |
| 88 | 91 | - `goingson` — the GoingsOn application palette |
| @@ -128,6 +128,14 @@ | |||
| 128 | 128 | /// or dark, since red is the theme's danger tone either way, which is exactly | |
| 129 | 129 | /// why the four achromatic slots are not in this table. | |
| 130 | 130 | /// | |
| 131 | + | /// `action.primary` is deliberately absent. A theme's accent is whatever hue | |
| 132 | + | /// the theme wants it to be, and slot 9 is *bright red* — a program painting an | |
| 133 | + | /// error in it is naming red, not naming the accent. Binding the two only ever | |
| 134 | + | /// looked right while the accent happened to be a warm orange; point the accent | |
| 135 | + | /// at a lavender and every `\e[91m` in the terminal turns purple. Bright red is | |
| 136 | + | /// the danger tone, the same way slots 10, 11 and 12 are their normal-intensity | |
| 137 | + | /// counterparts. | |
| 138 | + | /// | |
| 131 | 139 | /// Here rather than in each consumer, so a program that paints its own palette | |
| 132 | 140 | /// at runtime resolves the same slots as one reading a generated config. Slot | |
| 133 | 141 | /// 14 is `category.six`. | |
| @@ -138,7 +146,7 @@ | |||
| 138 | 146 | (4, "status.info"), | |
| 139 | 147 | (5, "category.five"), | |
| 140 | 148 | (6, "category.six"), | |
| 141 | - | (9, "action.primary"), // bright red, the theme's warm accent | |
| 149 | + | (9, "status.danger"), | |
| 142 | 150 | (10, "status.success"), | |
| 143 | 151 | (11, "status.warning"), | |
| 144 | 152 | (12, "status.info"), |
| @@ -267,6 +267,13 @@ | |||
| 267 | 267 | fn adapted_themes_carry_inline_attribution() { | |
| 268 | 268 | // Each adapted file must name its upstream in-file, so the credit | |
| 269 | 269 | // survives someone copying a single .toml out of the crate. | |
| 270 | + | // | |
| 271 | + | // The upstream URL is the anchor, not a phrase. What the credit says | |
| 272 | + | // varies with how far a file has travelled from its source — a theme | |
| 273 | + | // whose values we have since moved is descended from its upstream | |
| 274 | + | // rather than adapted from it — and a test that pinned the wording | |
| 275 | + | // would be asking the header to lie about that rather than asking it | |
| 276 | + | // to carry the credit. | |
| 270 | 277 | const ORIGINALS: [&str; 5] = [ | |
| 271 | 278 | "makenotwork", | |
| 272 | 279 | "goingson", | |
| @@ -279,7 +286,7 @@ | |||
| 279 | 286 | continue; | |
| 280 | 287 | } | |
| 281 | 288 | assert!( | |
| 282 | - | source.contains("adapted from"), | |
| 289 | + | source.lines().take(6).any(|l| l.contains("https://")), | |
| 283 | 290 | "adapted theme `{id}` is missing its inline attribution header" | |
| 284 | 291 | ); | |
| 285 | 292 | } |
| @@ -242,7 +242,30 @@ | |||
| 242 | 242 | derived.push(("hover-surface".into(), sunken)); | |
| 243 | 243 | } | |
| 244 | 244 | if let Some(border) = get(&intents, "border") { | |
| 245 | - | derived.push(("border-strong".into(), darken(border, 0.05))); | |
| 245 | + | // The emphatic border tier: one step further from the page than | |
| 246 | + | // `border` is, which is not the same as one step darker. | |
| 247 | + | // | |
| 248 | + | // Direction is read off `content` against `surface-page`, the way | |
| 249 | + | // `surface-well` reads it, because "stronger" means "further from the | |
| 250 | + | // ground it is drawn on" and a dark theme's ground is at the bottom of | |
| 251 | + | // the ramp. Darkening unconditionally moves a dark theme's border | |
| 252 | + | // *toward* its page, so the strong tier came out weaker than the plain | |
| 253 | + | // one — invisible for as long as the border itself was, and obvious the | |
| 254 | + | // moment a theme gave the border a real value. | |
| 255 | + | // | |
| 256 | + | // A theme with no page or no content keeps the darkening, which is the | |
| 257 | + | // light-theme answer and the one the majority of a half-written file | |
| 258 | + | // will want. | |
| 259 | + | let away_from_page = match (get(&intents, "content"), get(&intents, "surface-page")) { | |
| 260 | + | (Some(content), Some(page)) => content.to_oklab().l > page.to_oklab().l, | |
| 261 | + | _ => false, | |
| 262 | + | }; | |
| 263 | + | let strong = if away_from_page { | |
| 264 | + | lighten(border, 0.05) | |
| 265 | + | } else { | |
| 266 | + | darken(border, 0.05) | |
| 267 | + | }; | |
| 268 | + | derived.push(("border-strong".into(), strong)); | |
| 246 | 269 | } | |
| 247 | 270 | ||
| 248 | 271 | for (token, rgb) in derived { | |
| @@ -494,17 +517,23 @@ | |||
| 494 | 517 | // cannot hold a bevel, not that it is wrong. Shrinking the list is the fix; | |
| 495 | 518 | // growing it is a regression in the theme, not in this derivation. | |
| 496 | 519 | // | |
| 497 | - | // An entry leaves this list only when the fix is upstream's own, never a | |
| 498 | - | // color we picked. The remaining entries are shallow ramps in published | |
| 499 | - | // palettes, deferred until every app is migrated and eyeballed. | |
| 520 | + | // An imported palette leaves this list only when the fix is upstream's own, | |
| 521 | + | // never a color we picked, or the list becomes a record of what we were | |
| 522 | + | // willing to repaint rather than of what cannot hold a bevel. The remaining | |
| 523 | + | // entries are shallow ramps in published palettes, deferred until every app | |
| 524 | + | // is migrated and eyeballed. | |
| 525 | + | // | |
| 526 | + | // The akari pair is not an imported palette in that sense and is exempt. | |
| 527 | + | // Upstream ships no file in this schema, so its surface ramp was ours to | |
| 528 | + | // choose from the start, and it is the pair every app defaults to: a depth | |
| 529 | + | // model the default theme cannot render is a broken default, not a palette | |
| 530 | + | // we are borrowing on someone else's terms. | |
| 500 | 531 | #[test] | |
| 501 | 532 | fn raised_is_distinct_from_page() { | |
| 502 | 533 | // Below this, a raised surface and the page under it are one surface to | |
| 503 | 534 | // a reader, whichever direction the theme ramps in. | |
| 504 | 535 | const MIN_DELTA_L: f32 = 0.05; | |
| 505 | 536 | const CANNOT_LIFT_OFF_THE_PAGE: &[&str] = &[ | |
| 506 | - | "akari-dawn", | |
| 507 | - | "akari-night", | |
| 508 | 537 | "ayu-light", | |
| 509 | 538 | "ayu-mirage", | |
| 510 | 539 | "catppuccin-latte", |
| @@ -1,39 +1,42 @@ | |||
| 1 | - | # Colors adapted from Akari, used under the MIT License. | |
| 1 | + | # Descended from Akari, used under the MIT License. | |
| 2 | 2 | # https://github.com/cappyzawa/akari-theme | |
| 3 | 3 | # Copyright (c) 2025 Shu Kutsuzawa | |
| 4 | 4 | # | |
| 5 | - | # Adaptation only: the palette is re-expressed into makeover's intent | |
| 6 | - | # schema. Upstream ships no file in this format. See THIRD-PARTY-NOTICES.md. | |
| 5 | + | # Upstream ships no file in this schema, so this began as a re-expression of | |
| 6 | + | # Akari's palette into makeover's intents and has since departed from it: the | |
| 7 | + | # surfaces and ink were pushed apart for contrast, and the accent moved off | |
| 8 | + | # Akari's lantern orange to a violet. The structure and the debt are Akari's; | |
| 9 | + | # the values are no longer. See THIRD-PARTY-NOTICES.md. | |
| 7 | 10 | ||
| 8 | 11 | [meta] | |
| 9 | 12 | name = "Akari Dawn" | |
| 10 | 13 | variant = "light" | |
| 11 | 14 | ||
| 12 | 15 | [surface] | |
| 13 | - | page = "#e4ded6" | |
| 14 | - | raised = "#ede7de" | |
| 15 | - | sunken = "#cfc4b6" | |
| 16 | - | overlay = "#f0ece4" | |
| 16 | + | page = "#e8e2da" | |
| 17 | + | raised = "#f7f3ed" | |
| 18 | + | sunken = "#d2c8b9" | |
| 19 | + | overlay = "#fefcf8" | |
| 17 | 20 | ||
| 18 | 21 | [content] | |
| 19 | - | primary = "#1a1816" | |
| 22 | + | primary = "#161310" | |
| 20 | 23 | ||
| 21 | 24 | [action] | |
| 22 | - | primary = "#8a4530" | |
| 25 | + | primary = "#5a41a2" | |
| 23 | 26 | ||
| 24 | 27 | [status] | |
| 25 | - | danger = "#6a2828" | |
| 26 | - | success = "#3a5830" | |
| 27 | - | warning = "#b07840" | |
| 28 | - | info = "#304050" | |
| 28 | + | danger = "#8f2420" | |
| 29 | + | success = "#2f5424" | |
| 30 | + | warning = "#875000" | |
| 31 | + | info = "#25415c" | |
| 29 | 32 | ||
| 30 | 33 | [line] | |
| 31 | - | border = "#cabeae" | |
| 34 | + | border = "#8d7d65" | |
| 32 | 35 | ||
| 33 | 36 | [category] | |
| 34 | - | one = "#8a4530" | |
| 35 | - | two = "#3a5830" | |
| 36 | - | three = "#304050" | |
| 37 | - | four = "#b07840" | |
| 38 | - | five = "#806080" | |
| 39 | - | six = "#305858" | |
| 37 | + | one = "#5a41a2" | |
| 38 | + | two = "#2f5424" | |
| 39 | + | three = "#25415c" | |
| 40 | + | four = "#875000" | |
| 41 | + | five = "#8c2f6a" | |
| 42 | + | six = "#1f5450" |
| @@ -1,39 +1,42 @@ | |||
| 1 | - | # Colors adapted from Akari, used under the MIT License. | |
| 1 | + | # Descended from Akari, used under the MIT License. | |
| 2 | 2 | # https://github.com/cappyzawa/akari-theme | |
| 3 | 3 | # Copyright (c) 2025 Shu Kutsuzawa | |
| 4 | 4 | # | |
| 5 | - | # Adaptation only: the palette is re-expressed into makeover's intent | |
| 6 | - | # schema. Upstream ships no file in this format. See THIRD-PARTY-NOTICES.md. | |
| 5 | + | # Upstream ships no file in this schema, so this began as a re-expression of | |
| 6 | + | # Akari's palette into makeover's intents and has since departed from it: the | |
| 7 | + | # surfaces and ink were pushed apart for contrast, and the accent moved off | |
| 8 | + | # Akari's lantern orange to a violet. The structure and the debt are Akari's; | |
| 9 | + | # the values are no longer. See THIRD-PARTY-NOTICES.md. | |
| 7 | 10 | ||
| 8 | 11 | [meta] | |
| 9 | 12 | name = "Akari Night" | |
| 10 | 13 | variant = "dark" | |
| 11 | 14 | ||
| 12 | 15 | [surface] | |
| 13 | - | page = "#25231f" | |
| 14 | - | raised = "#2e2c27" | |
| 15 | - | sunken = "#1c1a17" | |
| 16 | - | overlay = "#35322c" | |
| 16 | + | page = "#201e1a" | |
| 17 | + | raised = "#302c27" | |
| 18 | + | sunken = "#0e0d0b" | |
| 19 | + | overlay = "#3b3731" | |
| 17 | 20 | ||
| 18 | 21 | [content] | |
| 19 | - | primary = "#e6ded3" | |
| 22 | + | primary = "#f3ede3" | |
| 20 | 23 | ||
| 21 | 24 | [action] | |
| 22 | - | primary = "#e26a3b" | |
| 25 | + | primary = "#b9a3f2" | |
| 23 | 26 | ||
| 24 | 27 | [status] | |
| 25 | - | danger = "#d25046" | |
| 26 | - | success = "#7faf6a" | |
| 27 | - | warning = "#d4a05a" | |
| 28 | - | info = "#7a8fa2" | |
| 28 | + | danger = "#f78077" | |
| 29 | + | success = "#96c97e" | |
| 30 | + | warning = "#e5ac62" | |
| 31 | + | info = "#93aec6" | |
| 29 | 32 | ||
| 30 | 33 | [line] | |
| 31 | - | border = "#3d3a35" | |
| 34 | + | border = "#8a8173" | |
| 32 | 35 | ||
| 33 | 36 | [category] | |
| 34 | - | one = "#e26a3b" | |
| 35 | - | two = "#7faf6a" | |
| 36 | - | three = "#7a8fa2" | |
| 37 | - | four = "#d4a05a" | |
| 38 | - | five = "#8e7ba0" | |
| 39 | - | six = "#6f8f8a" | |
| 37 | + | one = "#b9a3f2" | |
| 38 | + | two = "#96c97e" | |
| 39 | + | three = "#93aec6" | |
| 40 | + | four = "#e5ac62" | |
| 41 | + | five = "#dd94d0" | |
| 42 | + | six = "#84b5ae" |