Skip to main content

max / makeover-webview

0.37.0: a picture's frame is a border, not a bevel 0.36.0 gave .picture-img Depth::Raised through depth_rule, the same call button and card make. MNW's landing page is where it showed: the frames had no visible edge at all. --bevel-raised is an INSET shadow, drawn inside the box. On a button or a card that box is a surface this crate owns, so an inset edge reads as the surface catching the light. On a picture it lands on top of the picture, over whatever pixels the image has at its border. MNW's screenshots are light-on-light parchment, so the light half fell on a light image and the frame disappeared. You cannot bevel a surface you do not own. A picture's content is the app's, arrives at request time, and can be any colour, so its edge has to sit outside the content. That is a border. The fill stays and is not decoration: it shows through a transparent PNG and stands in the frame's place while the image is still loading. A real limit on Depth rather than a special case. Every other consumer of a depth draws its own surface; a picture is the first member whose surface belongs to someone else.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-08-14 19:28 UTC
Signed with PGP, not checked
Commit: 886165fdd10305efb330d724c9c8b76f66533e73
Parent: 3d84ca6
2 files changed, +32 insertions, -13 deletions
M Cargo.toml +1 -1
@@ -1,6 +1,6 @@
1 1 [package]
2 2 name = "makeover-webview"
3 - version = "0.36.0"
3 + version = "0.37.0"
4 4 edition = "2024"
5 5 # One copy of this renderer per dependency graph, enforced by cargo rather than
6 6 # by remembering. Two versions means the generated stylesheet and the emitted
M src/lib.rs +31 -12
@@ -1012,15 +1012,32 @@
1012 1012 /// `makeover_layout::Image` arrived at 0.21.0, found by trying to describe MNW's
1013 1013 /// carousel and discovering nothing named a picture.
1014 1014 ///
1015 - /// # The frame is a bevel
1015 + /// # The frame is a border, and this is the one place a bevel is wrong
1016 1016 ///
1017 - /// [`Depth::Raised`] through [`depth_rule`], the same call `button` and `card`
1018 - /// make, rather than a border and a radius written out here. MNW's shipped
1019 - /// carousel draws its frames with `1px solid var(--border)` plus a blurred
1020 - /// `rgba()` shadow on the landing page, and the shadow is the defect: in-flow
1021 - /// depth is a bevel, and elevation is for what sits *over* the page. Emitting
1022 - /// the bevel from the depth call is what stops the rgba literal coming back
1023 - /// under a new name.
1017 + /// 0.36.0 emitted [`Depth::Raised`] here through [`depth_rule`], the same call
1018 + /// `button` and `card` make. It was wrong, and MNW's landing page is where it
1019 + /// showed: **the frames had no visible edge at all.**
1020 + ///
1021 + /// `Depth::Raised`'s edge is `--bevel-raised`, which is an *inset* shadow — a
1022 + /// 1px light run at the top-left and a dark one at the bottom-right, drawn
1023 + /// **inside** the element's box. On a button or a card that box is a surface
1024 + /// this crate owns, so an inset edge reads as the surface catching the light.
1025 + /// On a picture it is drawn on top of the picture, over whatever pixels the
1026 + /// image happens to have at its border. MNW's screenshots are light-on-light
1027 + /// parchment, so the light half landed on a light image and the frame
1028 + /// disappeared.
1029 + ///
1030 + /// **You cannot bevel a surface you do not own.** A picture's content is the
1031 + /// app's, arrives at request time, and can be any colour, so its edge has to
1032 + /// sit *outside* the content rather than on it. That is a border.
1033 + ///
1034 + /// The fill stays, and it is not decoration: it is what shows through a
1035 + /// transparent PNG and what stands in the frame's place while the image is
1036 + /// still loading.
1037 + ///
1038 + /// This is a real limit on [`Depth`] rather than a special case. Every other
1039 + /// consumer of a depth draws its own surface; a picture is the first member
1040 + /// whose surface belongs to someone else.
1024 1041 ///
1025 1042 /// # No size
1026 1043 ///
@@ -1034,14 +1051,16 @@
1034 1051 let caption = class("picture-caption", opts);
1035 1052 let mut css = String::new();
1036 1053
1037 - css.push_str(&depth_rule(&img, Depth::Raised));
1038 -
1039 1054 // Block, or an inline image sits on the text baseline and carries a
1040 1055 // descender's worth of space under it that no app ever wants and every app
1041 - // deletes by hand.
1056 + // deletes by hand. The border is the frame; see the type docs for why it is
1057 + // not the bevel every other surface here gets.
1042 1058 let _ = writeln!(
1043 1059 css,
1044 - ".{img} {{\n display: block;\n width: 100%;\n height: auto;\n}}"
1060 + ".{img} {{\n display: block;\n width: 100%;\n height: auto;\n \
1061 + background: var(--{});\n border: {} solid var(--border);\n}}",
1062 + Fill::Raised.token(),
1063 + opts.border_width
1045 1064 );
1046 1065
1047 1066 // The two fits that need a rule. `Fit::Natural` emits no attribute at all,