From be2b3681fc84e1fecfcd4d94a66723b751cc28b1 Mon Sep 17 00:00:00 2001 From: Jack Henderson Date: Fri, 26 Jun 2026 12:51:39 -0400 Subject: [PATCH 1/5] Foundations: light-surface fix, locked palette, rhythm + type tokens MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Additive, low-risk foundation for the refinement work — no surface repaint here (the bg-golden-fizz → white change lands in its own PR). - global.css: set color-scheme:light and a white background so pages that don't paint their own surface no longer fall through to the browser's dark canvas (which rendered black body text invisible in OS dark mode — Updates, articles, Search, Wiki). Set on low-specificity selectors so utilities (bg-black, bg-golden-fizz, a page's bodyBg) still override — intentionally dark/yellow bands are unaffected. - tailwind.config.js: lock the brand palette to black/white/golden-fizz; remove single-use off-brand light-gold/light-black; quarantine gray/red (kept only for the CWNYC vote-bar data-viz) under a "restricted, non- brand" comment so they're not used on brand surfaces. - popup.njk / about: swap the two light-gold / light-black usages to brand golden-fizz / black. - link.css: de-gray .see-more-link (brand black at reduced opacity). - tailwind.config.js: add documented section/block/element spacing-rhythm tokens for consistent gaps between structural blocks. - README: document the palette, contrast rule, surface default, type roles (one Messer weight → hierarchy by size), and the spacing scale. npm run build passes. Dark-mode fix verified in-browser (Updates renders white; 404's bodyBg yellow still wins). Co-Authored-By: Claude Opus 4.8 --- README.md | 68 +++++++++++++++++++++++++ src/site/_includes/components/popup.njk | 2 +- src/site/_includes/css/global.css | 20 ++++++++ src/site/_includes/css/link.css | 6 ++- src/site/about/index.njk | 2 +- tailwind.config.js | 27 ++++++++-- 6 files changed, 116 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 960ac4d2..a7794357 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,41 @@ The RadicalxChange www site is built on [11ty](https://www.11ty.dev/). It's styl ## Design +> A refinement effort is tightening the site's craft _within_ the existing brand +> (hierarchy, spacing, component consistency, restraint). See `DESIGN_DIRECTION.md` +> for the audit and plan. The tokens below are the shared foundation that work builds on. + +### Palette + +Three brand colors, nothing else: + +| Token | Hex | Role | +| ---------------- | --------- | ----------------------------------------------------------- | +| `black` | `#000000` | Primary ink; black section bands ("the big moments"). | +| `white` | `#FFFFFF` | Default page surface. | +| `golden-fizz` | `#EDFF38` | Brand **yellow** — punctuation only. | + +**Yellow is punctuation, not wallpaper.** Use it for accents (underlines, icon +circles, highlight marks, number circles, a single key callout) and the rare, +countable full-bleed jolt (newsletter, donate CTA, 404). Reach for **black +sections** — not yellow fills — for big confident moments. Default surface is white. + +**Contrast rule (ADA), from the Brand Book:** black-on-yellow, black-on-white, or +white/yellow-on-black only — **never** yellow/white on yellow/white, and never +yellow text on white. + +`gray` and `red` remain in the config but are **restricted, non-brand** colors kept +only for functional data-viz on the Community Week NYC microsite. Don't use them on +brand surfaces. + +### Surface & color scheme + +`global.css` sets `color-scheme: light` and a white `body` background so no page can +fall through to the browser's dark canvas (which previously made black text invisible +in OS dark mode). These live on low-specificity selectors, so any utility +(`bg-black`, `bg-golden-fizz`, a page's `bodyBg`) still overrides them — intentionally +dark or yellow bands are unaffected. + ### Grid Pages are laid out on grids. A grid has fixed-width margins, fixed-width gutters and fluid columns. Desktop pages are on a 12-column or 16-column grid. Mobile pages are on a 4-column grid. @@ -48,12 +83,45 @@ We use TailWind's built-in scale for general size and space. These utilities use For typography heavy pages, we use a multiple of line height for vertical spacing. The utilities use `em` units. +#### Section rhythm + +For the gaps **between** structural blocks, prefer the documented rhythm tokens over +ad-hoc `mb-16` / `mb-8` one-offs, so every page shares one calm cadence: + +| Token | Value | Use | +| --------- | ------------------------ | ------------------------------------- | +| `section` | `clamp(4rem, 8vw, 8rem)` | Between major page sections. | +| `block` | `clamp(2rem, 4vw, 3rem)` | Between blocks within a section. | +| `element` | `1.5rem` | Between elements within a block. | + +Apply as any Tailwind spacing utility, e.g. `mb-section`, `space-y-block`, `py-section`, +`gap-element`. + ### Typography We use a [fluid type scale](https://utopia.fyi/blog/designing-with-fluid-type-scales) for almost all text. This scale provides a base text size and "steps" up and down from that. If you assign a step size to text, it will automatically scale relative to the width of the browser. For text written in Messer font, use font-size in the `vw` unit instead. +#### Type roles + +Two families, a small set of roles. **Only one Messer weight is licensed for web** +(`MesserV2.0-Condensed`, 400) — so display hierarchy comes from **size, not weight**. +Keep to these roles rather than inventing per-page sizes: + +| Role | Font (`font-…`) | Treatment | Notes | +| ------------------- | ---------------------- | ------------------------------------------- | ------------------------------------------------ | +| Display / page title | `display` (Messer) | **ALL CAPS**, `size-lg/display`+ | One per page. Caps only — Messer is a caps face. | +| Hero statement | `display` (Messer caps) | short, oversized | Keep it short (≈4–8 words); long copy → standfirst. | +| Section header | `body` bold (Suisse) | `size-2`, with a hairline rule beneath | Pairs with the eyebrow below. | +| Eyebrow / kicker | `body` bold (Suisse) | uppercase, `size--1`, letter-spaced, quiet | A small label, **not** a headline. | +| Body | `body` (Suisse) | `size-0` | Brand body face is Suisse Book.¹ | +| Meta (author/date/tag) | `body` (Suisse) | `size--1` | One consistent size for all metadata. | + +¹ The Brand Book specifies **Suisse Int'l Book** for paragraph text; today `font-body` +resolves to Suisse Int'l _Regular_. A small, deliberate drift to reconcile in a later +typography pass — flagged, not silently changed. + ## Code Layout 11ty builds from _src/site_. Read 11ty's documentation to understand a lot about this directory. We outline only notable quirks here: diff --git a/src/site/_includes/components/popup.njk b/src/site/_includes/components/popup.njk index 550b1eed..60afa251 100644 --- a/src/site/_includes/components/popup.njk +++ b/src/site/_includes/components/popup.njk @@ -38,7 +38,7 @@
-
+ + + {% set abCards = [ + {tag:"Paper", title:"The Plural Stack: Rebuilding our Digital Foundations from Protocol Up", m1:"Andreas Fauler, et al", m2:"June 17, 2026"}, + {tag:"Announcement", title:"Civic AI Launches at Oxford", m1:"RxC Team", m2:"March 31, 2026"}, + {tag:"Blog Post", title:"Europe Doesn't Need Its Own Big Tech. It Needs Protocols Nobody Can Capture.", m1:"Andreas Fauler, Anja von Rosenstiel et al", m2:"June 17, 2026"}, + {tag:"Podcast", title:"Takahiro Anno: Fixing Bugs in Democracy", m1:"Takahiro Anno, Jess Scully", m2:"November 11, 2025"}, + {tag:"Announcement", title:"Germany Launches a Digital Democracy Lab", m1:"RxC Team", m2:"February 3, 2026"}, + {tag:"Blog Post", title:"Reactive vs. Co-Creative Democracy", m1:"Martin Rausch, Tom Atlee, Jenna Büchy", m2:"April 29, 2026"}, + {tag:"Announcement", title:"Co-Creative Democracy, From Taipei to NYC", m1:"RxC Team", m2:"May 5, 2026"}, + {tag:"Podcast", title:"What Protests Want, With Yuting Jiang", m1:"Yuting Jiang, Jess Scully", m2:"November 26, 2025"} + ] %} +
+ {{ sectionHeader.render(title="A/B #1 — Card title weight", eyebrow="Judge as a grid, not one card") }} +

The decision hinges on the tiled effect, so both are rendered as full 8-card grids. Hypothesis: regular for density — bold can turn into a heavy, vibrating field when tiled.

+ +

A · Suisse regular (canonical / shipped)

+
+ + {% for c in abCards %}{{ card.render(href="#", tag=c.tag, title=c.title, meta1=c.m1, meta2=c.m2) }}{% endfor %} +
+ +

B · Suisse bold

+
+ + {% for c in abCards %}{{ card.render(href="#", tag=c.tag, title=c.title, meta1=c.m1, meta2=c.m2) }}{% endfor %} +
+
+ + +
+ {{ sectionHeader.render(title="A/B #2 — Rule placement", eyebrow="Rollout consistency check") }} +

Top hairline over the tag (the stronger system, matches the brand-book header-with-rule logic) vs the bottom rule used on Updates today — so we can see whether a mixed idiom mid-rollout reads as intentional or just transitional.

+ +

A · Top rule over tag (canonical / shipped)

+
+ + {% for c in abCards %}{% if loop.index <= 4 %}{{ card.render(href="#", tag=c.tag, title=c.title, meta1=c.m1, meta2=c.m2) }}{% endif %}{% endfor %} +
+ +

B · Bottom rule under meta (today's Updates idiom)

+
+ + {% for c in abCards %}{% if loop.index <= 4 %}{{ card.render(href="#", tag=c.tag, title=c.title, meta1=c.m1, meta2=c.m2) }}{% endif %}{% endfor %} +
+
+ + +
+ {{ sectionHeader.render(title="Resolved", eyebrow="Locked, for the record") }} +
    +
  • Tag / meta opacity — locked at 60%. Effective #666 on white = 5.74:1, passes WCAG AA for normal text (4.5:1). Description at 80% = 12.6:1.
  • +
  • Eyebrow — optional, used only where it earns it. Standard-on-every-section would just re-create the stacked-label noise from audit finding #2.
  • +
+
+
{{ sectionHeader.render(title="Black-section moment", eyebrow="Big gestures use black, not yellow fills") }} From f462c17f505d5cef1c280fc4f9bc6a7eac91f921 Mon Sep 17 00:00:00 2001 From: Jack Henderson Date: Fri, 26 Jun 2026 16:41:21 -0400 Subject: [PATCH 5/5] chore: trigger Netlify deploy preview