Skip to main content

max / makeover-geometry

Own density selection: density_css, 0.2.0 The policy belongs in the crate that defines Density, and it was parked in makeover-build only out of a release worry that does not apply while every product is internal. Touch hangs off (hover: none), (pointer: coarse). Density is a capability, not a device and not a width: a narrow window on a desktop still has a pointer in it and a tablet at full width still has a finger. That is the question the platform answers and the one Density is asking. An explicit user choice is emitted last so it wins at equal specificity. Detection is a default, not a verdict: a touchscreen laptop and someone who simply wants roomier targets are both real and neither is visible to a media query. Tested, because ordering is the entire mechanism there. Settles what three consumers answered three ways. Additive, so 0.2.0. 20 tests, clippy clean. Not published.
Author: Max Johnson <me@maxj.phd> · 2026-07-28 22:10 UTC
Signed with PGP, not checked
Commit: 156e003e5706e78abf8b940520f3aa31ddd00e99
Parent: f5c61af
2 files changed, +62 insertions, -1 deletion
M Cargo.toml +1 -1
@@ -1,6 +1,6 @@
1 1 [package]
2 2 name = "makeover-geometry"
3 - version = "0.1.0"
3 + version = "0.2.0"
4 4 edition = "2024"
5 5 description = "The invariant half of the make-family design system: relational spacing, radius, border width and type scale. Geometry never varies by theme, which is why it does not live in makeover."
6 6 license = "MIT"
M src/lib.rs +61
@@ -504,6 +504,42 @@
504 504 )
505 505 }
506 506
507 + /// The whole spacing layer with the canonical density selection, as CSS.
508 + ///
509 + /// **Density is a capability, not a device and not a width.** A narrow window
510 + /// on a desktop still has a pointer in it and a tablet at full width still has
511 + /// a finger, so the touch preset hangs off `(hover: none), (pointer: coarse)`
512 + /// rather than off a breakpoint or a user-agent string. That is the question
513 + /// the platform actually answers, and it is the one [`Density`] is asking.
514 + ///
515 + /// `explicit_touch` names a selector an app sets when the *user* has chosen.
516 + /// It is emitted last and therefore wins at equal specificity, because
517 + /// detection is a default rather than a verdict: a touchscreen laptop and
518 + /// someone who simply wants roomier targets are both real, and neither is
519 + /// visible to a media query.
520 + ///
521 + /// Settles a policy three consumers previously answered three ways. GoingsOn
522 + /// sniffed the user agent behind a mode class, Balanced Breakfast used
523 + /// `(hover: none)` alone, and audiofiles had no switch at all; the first of
524 + /// those asked what device this is as a proxy for a capability already
525 + /// reported.
526 + #[must_use]
527 + pub fn density_css(explicit_touch: Option<&str>) -> String {
528 + let mut css = geometry_css_vars(Density::Pointer);
529 + css.push_str("\n@media (hover: none), (pointer: coarse) {\n");
530 + for line in gap_css_overrides(":root", Density::Touch).lines() {
531 + css.push_str(" ");
532 + css.push_str(line);
533 + css.push('\n');
534 + }
535 + css.push_str("}\n");
536 + if let Some(selector) = explicit_touch {
537 + css.push_str("\n/* An explicit user choice, last so it wins over detection. */\n");
538 + css.push_str(&gap_css_overrides(selector, Density::Touch));
539 + }
540 + css
541 + }
542 +
507 543 /// Emit a density preset as a scoped override block.
508 544 ///
509 545 /// Only the relational layer is emitted: the scale and the base do not change
@@ -524,6 +560,31 @@
524 560 mod tests {
525 561 use super::*;
526 562
563 + #[test]
564 + fn density_is_selected_by_capability_not_by_width_or_agent() {
565 + let css = density_css(None);
566 + assert!(css.contains("@media (hover: none), (pointer: coarse)"));
567 + // The three things density must never be selected by.
568 + assert!(!css.contains("max-width"), "a breakpoint crept in");
569 + assert!(!css.contains("min-width"), "a breakpoint crept in");
570 + assert!(!css.contains("ui-mode"), "a device mode crept in");
571 + }
572 +
573 + #[test]
574 + fn an_explicit_choice_is_emitted_after_the_detection() {
575 + let css = density_css(Some(".ui-mode-mobile"));
576 + let media = css.find("@media").expect("media query");
577 + let explicit = css.find(".ui-mode-mobile").expect("explicit selector");
578 + // Equal specificity, so order is the whole mechanism: the user's
579 + // choice has to come last or detection quietly overrides it.
580 + assert!(explicit > media, "the explicit selector must come last");
581 + }
582 +
583 + #[test]
584 + fn without_an_explicit_selector_there_are_exactly_two_presets() {
585 + assert_eq!(density_css(None).matches("--gap-peer").count(), 2);
586 + }
587 +
527 588 #[test]
528 589 fn the_hig_relationships_land_on_the_hig_values() {
529 590 // Mac OS 8 HIG, Control Layout Guidelines. The ratios are ours, but at