Skip to main content

max / makeover-webview

Let a progress bar carry a tone Release 0.4.0. The trough was already a Well; the fill was a single hardcoded --action, which threw away information goingson encodes on purpose: subtask progress is success-coloured and an over-estimate is danger-coloured, and the second of those is the bar warning rather than reporting. So the fill takes data-tone variants off Tone, the same shape the badge rules already use. The untoned bar stays --action rather than following the badge rules onto Tone::Neutral: a badge with no status is a muted label, while a bar with no status is still reporting progress, and content-muted would read as disabled.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-07-29 23:26 UTC
Signed with PGP, not checked
Commit: 483d805bed9f3cfcad6245ac69858bd23bf82bef
Parent: 7d13a93
2 files changed, +44 insertions, -5 deletions
M Cargo.toml +1 -1
@@ -1,6 +1,6 @@
1 1 [package]
2 2 name = "makeover-webview"
3 - version = "0.3.0"
3 + version = "0.4.0"
4 4 edition = "2024"
5 5 description = "The webview renderer for makeover-layout. Emits CSS, and is the one renderer that needs no palette: var() is the late binding, so resolution stays with the browser."
6 6 license = "MIT"
M src/lib.rs +43 -4
@@ -402,10 +402,29 @@
402 402 // catch things that have nothing to do with progress. goingson already
403 403 // calls it `.progress-fill`, so this is also the name that deletes.
404 404 let fill = class("progress-fill", opts);
405 - format!(
406 - "{}.{progress} > .{fill} {{\n background: var(--action);\n}}\n",
407 - depth_rule(&progress, Depth::Well)
408 - )
405 + let mut css = depth_rule(&progress, Depth::Well);
406 +
407 + // The untoned bar is `--action`, not [`Tone::Neutral`]. That is the one
408 + // place this differs from the badge rules, and deliberately: a badge with
409 + // no status is a muted label, while a bar with no status is still
410 + // reporting progress, and `content-muted` would read as disabled.
411 + let _ = writeln!(
412 + css,
413 + ".{progress} > .{fill} {{\n background: var(--action);\n}}"
414 + );
415 +
416 + // A bar can be saying something, same as a badge: goingson colours subtask
417 + // progress as success and an over-estimate as danger, which is real
418 + // information rather than decoration. Emitting the tones is what lets that
419 + // survive adoption instead of staying hand-written.
420 + for tone in [Tone::Info, Tone::Success, Tone::Warning, Tone::Danger] {
421 + let _ = writeln!(
422 + css,
423 + ".{progress} > .{fill}[data-tone=\"{0}\"] {{\n background: var(--{0});\n}}",
424 + tone.token()
425 + );
426 + }
427 + css
409 428 }
410 429
411 430 /// The component layer: every named thing phase A emits.
@@ -676,6 +695,26 @@
676 695 assert!(!css.contains("> .fill "));
677 696 }
678 697
698 + #[test]
699 + fn a_progress_bar_can_carry_a_tone_and_defaults_to_action() {
700 + let css = progress_rules(&Emit::default());
701 + // Untoned is --action, not Tone::Neutral's content-muted: a bar with no
702 + // status is still reporting progress, and muted would read as disabled.
703 + assert!(css.contains(".progress > .progress-fill {\n background: var(--action);"));
704 + assert!(!css.contains("progress-fill {\n color: var(--content-muted)"));
705 + for tone in ["info", "success", "warning", "danger"] {
706 + assert!(
707 + css.contains(&format!(
708 + ".progress > .progress-fill[data-tone=\"{tone}\"]"
709 + )),
710 + "missing progress tone {tone}"
711 + );
712 + }
713 + // goingson's two live cases, which is why the tones are emitted at all.
714 + assert!(css.contains("[data-tone=\"success\"] {\n background: var(--success);"));
715 + assert!(css.contains("[data-tone=\"danger\"] {\n background: var(--danger);"));
716 + }
717 +
679 718 #[test]
680 719 fn no_scrollbar_track_is_emitted() {
681 720 // Decision 3's negative half. It was on the phase A list and came off;