Skip to main content

max / makeover

Rename the FontFace builders, so weight and style can be read FontFace built with bare verbs while FontOverride and Typography build with with_*, and the collision was not free: weight and style were unreadable because the accessor could not have the obvious name. A renderer loading a variable face directly has to name a weight, and the declaration is where that fact lives. weight()/style() are accessors now; with_weight()/with_style() build. Three call sites in the tree, all forward-fixed in this pass.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-08-18 14:31 UTC
Signed with PGP, not checked
Commit: c20aeaedd0a93e69c09e19406fa215890492ddb3
Parent: da1fd1e
2 files changed, +51 insertions, -8 deletions
M Cargo.toml +1 -1
@@ -1,6 +1,6 @@
1 1 [package]
2 2 name = "makeover"
3 - version = "2.11.0"
3 + version = "3.0.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"
M src/lib.rs +50 -7
@@ -1168,8 +1168,8 @@
1168 1168 };
1169 1169 Some(
1170 1170 FontFace::new(family, [file])
1171 - .weight(HOUSE_WEIGHT_RANGE)
1172 - .style("normal"),
1171 + .with_weight(HOUSE_WEIGHT_RANGE)
1172 + .with_style("normal"),
1173 1173 )
1174 1174 }
1175 1175 }
@@ -1214,18 +1214,33 @@
1214 1214 /// faces do: a `@font-face` with no range makes the browser resolve every
1215 1215 /// weight to the file's default instance.
1216 1216 #[must_use]
1217 - pub fn weight(mut self, weight: impl Into<String>) -> Self {
1217 + pub fn with_weight(mut self, weight: impl Into<String>) -> Self {
1218 1218 self.weight = Some(weight.into());
1219 1219 self
1220 1220 }
1221 1221
1222 1222 /// `font-style`. Omitted when unset, which means `normal`.
1223 1223 #[must_use]
1224 - pub fn style(mut self, style: impl Into<String>) -> Self {
1224 + pub fn with_style(mut self, style: impl Into<String>) -> Self {
1225 1225 self.style = Some(style.into());
1226 1226 self
1227 1227 }
1228 1228
1229 + /// The declared `font-weight`, or `None` when the face never named one.
1230 + ///
1231 + /// A renderer loading a variable face directly has to name a weight — the
1232 + /// file's own default instance is whatever the base shipped, which for the
1233 + /// house faces is ExtraLight — so this is the half of the declaration that
1234 + /// stops the load from being a guess.
1235 + pub fn weight(&self) -> Option<&str> {
1236 + self.weight.as_deref()
1237 + }
1238 +
1239 + /// The declared `font-style`, or `None`, which means `normal`.
1240 + pub fn style(&self) -> Option<&str> {
1241 + self.style.as_deref()
1242 + }
1243 +
1229 1244 /// The family name, as the stack has to spell it.
1230 1245 ///
1231 1246 /// For a renderer that loads faces rather than emitting CSS this is the
@@ -1393,7 +1408,8 @@
1393 1408 ///
1394 1409 /// The other half of [`resolve`](Self::resolve), for a renderer that has
1395 1410 /// to load a file rather than name a stack: `resolve` says which family
1396 - /// wins, this says where the bytes come from and what to call them. The
1411 + /// wins, this says where the bytes come from, what to call them, and at
1412 + /// what weight. The
1397 1413 /// house faces are not here — they belong to the slot rather than to any
1398 1414 /// one product, and [`FontSlot::house_face`] is where they answer.
1399 1415 pub fn faces(&self, slot: FontSlot) -> &[FontFace] {
@@ -3247,7 +3263,7 @@
3247 3263 "Reglo",
3248 3264 ["Reglo-Bold.woff2", "https://cdn.example/reglo.woff2"],
3249 3265 )
3250 - .weight("700"),
3266 + .with_weight("700"),
3251 3267 ),
3252 3268 );
3253 3269 let faces = t.font_face_css();
@@ -3313,7 +3329,7 @@
3313 3329 // the one the stack spells or the two halves drift.
3314 3330 let t = Typography::house("fonts").with_override(
3315 3331 FontOverride::new(FontSlot::Display, "\"RecursiveMono\", monospace").with_face(
3316 - FontFace::new("RecursiveMono", ["RecursiveMonoLnrSt-Bold.ttf"]).weight("700"),
3332 + FontFace::new("RecursiveMono", ["RecursiveMonoLnrSt-Bold.ttf"]).with_weight("700"),
3317 3333 ),
3318 3334 );
3319 3335
@@ -3329,6 +3345,33 @@
3329 3345 );
3330 3346 }
3331 3347
3348 + #[test]
3349 + fn a_weight_and_style_are_readable_now_that_the_builders_are_not_using_the_names() {
3350 + let bold = FontFace::new("Reglo", ["Reglo-Bold.woff2"]).with_weight("700");
3351 + assert_eq!(bold.weight(), Some("700"));
3352 + assert_eq!(
3353 + bold.style(),
3354 + None,
3355 + "unset means normal, not a stated normal"
3356 + );
3357 +
3358 + let italic = FontFace::new("Odd", ["odd.woff2"]).with_style("italic");
3359 + assert_eq!(italic.weight(), None);
3360 + assert_eq!(italic.style(), Some("italic"));
3361 + }
3362 +
3363 + #[test]
3364 + fn the_house_faces_state_the_variable_range_a_direct_loader_has_to_name() {
3365 + // The trap this closes: a variable face's own default instance is
3366 + // whatever the base shipped, which for these is ExtraLight. A loader
3367 + // that does not name a weight gets that and nothing says so.
3368 + for slot in [FontSlot::Mono, FontSlot::Sans] {
3369 + let face = slot.house_face().unwrap();
3370 + assert_eq!(face.weight(), Some(HOUSE_WEIGHT_RANGE));
3371 + assert_eq!(face.style(), Some("normal"));
3372 + }
3373 + }
3374 +
3332 3375 #[test]
3333 3376 fn a_source_is_read_back_unresolved_because_only_the_css_wants_a_url() {
3334 3377 let t = Typography::house("/static/fonts").with_override(young_serif());