diff --git a/public/og-default.png b/public/og-default.png index f605ed37..64fd3e4c 100644 Binary files a/public/og-default.png and b/public/og-default.png differ diff --git a/public/og/checklist.png b/public/og/checklist.png index 7c0ba54d..70f48514 100644 Binary files a/public/og/checklist.png and b/public/og/checklist.png differ diff --git a/public/og/spec.png b/public/og/spec.png index 783fafbd..20c058ae 100644 Binary files a/public/og/spec.png and b/public/og/spec.png differ diff --git a/public/og/spec/accessibility.png b/public/og/spec/accessibility.png index 27d4d66d..31562dd7 100644 Binary files a/public/og/spec/accessibility.png and b/public/og/spec/accessibility.png differ diff --git a/public/og/spec/accessibility/forced-colors.png b/public/og/spec/accessibility/forced-colors.png new file mode 100644 index 00000000..6b990712 Binary files /dev/null and b/public/og/spec/accessibility/forced-colors.png differ diff --git a/src/components/HeadMeta.astro b/src/components/HeadMeta.astro index 40d3b2e8..0b7d1c08 100644 --- a/src/components/HeadMeta.astro +++ b/src/components/HeadMeta.astro @@ -29,9 +29,18 @@ const { const canonicalUrl = canonical ?? new URL(Astro.url.pathname, site.url).toString(); const fullTitle = title === site.name ? title : `${title} · ${site.shortName}`; +// On a Cloudflare Pages preview build the production OG images don't exist yet +// (they ship with the PR that's still in preview), so point the social image at +// the deployment's own origin. Production (the `main` branch) keeps site.url so +// the card stays on the canonical domain. Canonical itself never changes. +const cfBranch = process.env.CF_PAGES_BRANCH; +const ogBase = + cfBranch && cfBranch !== "main" + ? (process.env.CF_PAGES_URL ?? site.url) + : site.url; const ogImage = new URL( ogImageOverride ?? "/og-default.png", - site.url, + ogBase, ).toString(); --- diff --git a/src/content/changelog/2026-06-22-forced-colors.md b/src/content/changelog/2026-06-22-forced-colors.md new file mode 100644 index 00000000..86db1477 --- /dev/null +++ b/src/content/changelog/2026-06-22-forced-colors.md @@ -0,0 +1,8 @@ +--- +title: New page on forced colours mode +date: "2026-06-22" +type: added +relatedSlugs: [forced-colors] +--- + +Added a page on [forced colours mode](/spec/accessibility/forced-colors/) — the `forced-colors` media feature (CSS Media Queries Level 5) that signals when Windows High Contrast and similar palettes are active. It sits alongside [colour contrast](/spec/accessibility/color-contrast/) and [`color-scheme`](/spec/foundations/color-scheme/): use system colour keywords to repair boundaries the user's palette flattens, without overriding their choice. diff --git a/src/content/spec/accessibility/color-contrast.md b/src/content/spec/accessibility/color-contrast.md index ada1cf00..aa5b6df9 100644 --- a/src/content/spec/accessibility/color-contrast.md +++ b/src/content/spec/accessibility/color-contrast.md @@ -6,7 +6,7 @@ summary: "Text and meaningful non-text elements must have enough contrast agains status: required order: 10 appliesTo: [all] -relatedSlugs: [focus-indicators, semantic-html] +relatedSlugs: [focus-indicators, semantic-html, forced-colors] updated: "2026-05-29T09:13:20.000Z" sources: - title: "WCAG 1.4.3 — Contrast (Minimum) (Level AA)" diff --git a/src/content/spec/accessibility/forced-colors.md b/src/content/spec/accessibility/forced-colors.md new file mode 100644 index 00000000..6c8274d3 --- /dev/null +++ b/src/content/spec/accessibility/forced-colors.md @@ -0,0 +1,65 @@ +--- +title: "Forced colours mode" +slug: forced-colors +category: accessibility +summary: "Respect forced colours mode (Windows High Contrast and similar). The `forced-colors` media feature lets you repair UI the user's palette would otherwise flatten — without overriding their choice." +status: recommended +order: 15 +appliesTo: [all] +relatedSlugs: [color-contrast, color-scheme, reduced-motion] +updated: "2026-06-22T00:00:00.000Z" +sources: + - title: "CSS Media Queries Level 5 — forced-colors" + url: "https://drafts.csswg.org/mediaqueries-5/#forced-colors" + publisher: "W3C" + - title: "CSS Color Adjustment Module Level 1 — forced-color-adjust" + url: "https://drafts.csswg.org/css-color-adjust-1/#forced" + publisher: "W3C" + - title: "MDN — forced-colors" + url: "https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@media/forced-colors" + publisher: "MDN" +--- + +## What it is + +Some users override every site's colours with a small, high-contrast palette of their own. Windows High Contrast mode is the best-known example; the operating system or browser substitutes the user's chosen foreground, background, link, and button colours for whatever the page declared. The browser exposes that state through the `forced-colors` media feature, which is `active` when a forced palette is in effect. + +```css +@media (forced-colors: active) { + .card { + /* box-shadow is forced to none — restore the boundary with a border */ + border: 1px solid CanvasText; + } +} +``` + +Forced colours mode works automatically for most semantic markup. The feature exists for the cases where the substitution removes information — and for the rare element that should opt out via `forced-color-adjust`. + +## Why it matters + +The high contrast of a reduced palette is essential for some users with low vision, light sensitivity, or cognitive needs to read a page at all. When forced colours apply, the browser drops `box-shadow`, flattens background images behind text, and recolours borders and text to system colours. UI that relied on a shadow, a background-colour swatch, or a coloured-but-borderless control can lose its visible boundary entirely. + +Respecting the mode means two things: never fighting the user's palette, and repairing the few places where their palette erases a distinction your design carried in colour alone. + +## How to implement + +- **Build on semantic HTML.** Real `