CSS: HSL + light-dark() + native nesting + :is() + @layer#3
Merged
Conversation
…ight-dark() Switch the colour pipeline from RGB-comma-triplets fed to legacy rgb()/rgba() to HSL space-syntax fed to modern hsl(): more legible values, easier hue/saturation/lightness tweaks, and the modern slash-alpha syntax across the board. All 100+ rgb()/rgba(var(--…)) callsites become hsl(var(--…)) / hsl(var(--…) / N). Collapse the three duplicated `@media (prefers-color-scheme: dark)` blocks (default, peach, retro) into single light-dark() declarations driven by :root's existing color-scheme: light dark. .scheme--light / .scheme--dark now just pin color-scheme so light-dark() resolves predictably. Side-effect: the original .theme--peach block had a bug where the dark variant always won regardless of scheme; light-dark() restores the intended per-scheme behaviour. Breaking change for downstream user CSS that referenced the comma-syntax form (e.g. `rgba(var(--text-color), .5)`). Migration: switch to `hsl(var(--text-color) / .5)`. Pre-1.0 — no semver contract broken.
Modernises the stylesheet against features that have been baseline since 2022-23. screen.css drops 260 lines (-8%, 3265 → 3005); minified output 46 KB → 42.5 KB. * Tier 1.1 — Native CSS nesting. Collapsed 11 prefix groups into nested forms: .gadgets, .video, .sharing, .simple-signup, .gallery, .meta, body > header, body > footer, .articles-list, #search__form / #search__results / #search__input, .taxon, .taxonomy-filter, plus the body > header .menu hover/focus tree. Hugo's minifier (tdewolff) preserves & nesting through `resources.Minify`. * Tier 1.2 — :is(). Collapsed the 12-selector main heading-anchor list (h1..h6[id], a[title], a[href*='.pdf'], ins/del[datetime], abbr/span[title]) and its :hover / :not(:hover) / :target variants into single :is() forms. Same for the 5-place .marginalia / .entry-content aside repetition, the image-link-disable list, and the 1200px hover breakpoint group. ~15 collapse sites total. * Tier 2.1 — @layer reset, structure, theming, typography, helpers, modules. Wraps the 6 named sections in cascade layers. User CSS outside any layer now wins over theme rules without needing !important — strictly improves the override story. Accessibility !importants (reduced-motion, scrollbar overrides, user-select) stay; they're meant to win against everything including unlayered user rules. * Tier 3.1 — `inset`. Three position blocks collapsed: figure:has(img.blend)::after → inset: 0 .icon → inset-inline: 0 .gadgets → inset: 0 0 0 auto * Tier 3.2 — Logical properties. 31 `margin-left/right` and `padding-left/right` sites become `margin-inline-*` / `padding-inline-*` (also 1 in essential.css). Same line count, RTL- ready. * Tier 3.3 — `text-wrap`. Headings get `balance` (no widow words); `main p, main li, main dt, main dd` get `pretty` (no orphan-line rivers).
8 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
assets/styles/screen.csspredates a generation of CSS features that have been baseline since 2022-23. This PR collapses the resulting boilerplate against four such features, plus a small Tier-3 polish pass. Net effect: −260 lines (-8%, 3265 → 3005); minified bundle 46 KB → 42.5 KB; cleaner cascade story.Browser floor stays where it already was. The newest feature here (
light-dark()) ships in Safari 17.5 (May 2024); everything else is 2022-23 or older. The file already uses:has(),aspect-ratio, and:is()in places, so this is incremental modernisation, not a floor change.What
Two commits:
1. HSL color vars +
light-dark()(27471b7)--text-color: 34, 34, 50;) to HSL space-syntax (--text-color: 240 19% 16%;). Conversion is mechanical via Python'scolorsysto avoid eyeballing rounding. HSL is more legible (hue / saturation / lightness vs. opaque RGB) and lets you derive variations by tweaking lightness alone.rgb()/rgba(var(--…), N)callsites becomehsl(var(--…))/hsl(var(--…) / N)(modern slash-alpha syntax).@media (prefers-color-scheme: dark)blocks (default, peach, retro) collapse into singlelight-dark()declarations driven by:root's existingcolor-scheme: light dark..scheme--light/.scheme--darknow just pincolor-scheme..theme--peachhad its dark block winning regardless of scheme (the bare.theme--peachselector appeared in both light and dark blocks; the later one won). Withlight-dark(), scheme actually drives the resolution.essential.cssgets the same treatment.Breaking change for downstream user CSS that referenced the comma-syntax form (
rgba(var(--text-color), .5)). Migration:hsl(var(--text-color) / .5). Pre-1.0 — no semver contract.2. Native nesting +
:is()+@layer+ Tier-3 polish (ad61c07)Tier 1.1 — Native CSS nesting. 11 prefix groups collapsed into nested forms:
.gadgets,.video,.sharing,.simple-signup,.gallery,.meta,body > header,body > footer,.articles-list,#search__form/#search__results/#search__input,.taxon,.taxonomy-filter, plus thebody > header .menuhover/focus tree. Hugo's minifier (tdewolff/minify) preserves&nesting throughresources.Minify.Tier 1.2 —
:is(). ~15 collapse sites: the 12-selectormainheading-anchor list (h1..h6[id],a[title],a[href*='.pdf'],ins/del[datetime],abbr/span[title]) and its:hover/:not(:hover)/:targetvariants; the 5-place.marginalia, .entry-content asiderepetition; the image-link-disable list; the 1200px hover breakpoint group.Tier 2.1 —
@layer reset, structure, theming, typography, helpers, modules. The 6 named sections wrap in cascade layers in declaration order. User CSS outside any layer now wins over theme rules without!important— strictly improves the override story. Accessibility!importants (reduced-motion, scrollbar overrides,user-select) stay; they're meant to win against everything including unlayered user rules.Tier 3.1 —
insetshorthand. Three blocks collapsed:figure:has(img.blend)::after→inset: 0.icon→inset-inline: 0.gadgets→inset: 0 0 0 autoTier 3.2 — Logical properties. 31
margin-left/rightandpadding-left/rightsites becomemargin-inline-*/padding-inline-*(also 1 inessential.css). Same line count, RTL-ready behind a singledir="rtl"flag.Tier 3.3 —
text-wrap. Headings getbalance(no widow words);main p, main li, main dt, main ddgetpretty(no orphan-line rivers).Held back (intentionally)
!importanthacks that@layermakes redundant (taxon colour overrides,.iconoutline). The cascade now favours user overrides regardless, so the!importants are inert rather than harmful — removing them is a tidy follow-up but risks specificity surprises if any internal rule was relying on them.body > header menurewrite. Worth ~10 lines and mechanical, but it makes assumptions about the rendered HTML structure I'd rather verify against the templates first.Both are safe to revisit in a smaller PR.
Test plan
hugo --gc --minifyclean onexampleSite/html-validateclean (.github/html-validate.json)wc -l screen.css: 3265 → 3005theme--peach+theme--retro. Compare againstmaster.body { color: red; }) wins without!important.Generated by Claude Code