[csswg-drafts] [mediaqueries-5] Add a prefers-lang media feature to expose user language preference (#13615)

Evoke4350 has just created a new issue for https://github.com/w3c/csswg-drafts:

== [mediaqueries-5] Add a prefers-lang media feature to expose user language preference ==
There is currently no CSS mechanism to match against the user's preferred language. `:lang()` matches the document's declared language (`lang` attribute, `Content-Language` header), not what the user's browser is configured to prefer.

On static hosting (GitHub Pages, S3, any CDN without edge compute), there is no server-side component to read `Accept-Language` and set `<html lang>` dynamically. The only option is JavaScript:

```js
document.documentElement.lang = navigator.language.slice(0, 2);
```

This works but it forces a script dependency for something that should be declarative. Other user preferences already have media features — `prefers-color-scheme`, `prefers-reduced-motion`, `prefers-contrast`, `prefers-reduced-transparency`. Language is missing.

### Use case

All 25 translations live in the DOM. CSS handles the rest — no JS, no server:

```html
<span class="msg" lang="en">android will become a locked-down platform</span>
<span class="msg" lang="fr">android va devenir une plateforme fermée</span>
<span class="msg" lang="de">android wird eine geschlossene plattform</span>
```

```css
.msg { display: none; }
:lang(en) > .banner .msg[lang="en"] { display: inline; }
:lang(fr) > .banner .msg[lang="fr"] { display: inline; }
```

This machinery works today. But `:lang()` only reads what the document declares, not what the user prefers. Bridging that gap requires JS or edge compute — neither should be necessary for what is a styling decision.

### Proposal

```css
@media (prefers-lang: fr) {
  .msg[lang="fr"] { display: inline; }
}

@media (prefers-lang: en) {
  .msg[lang="en"] { display: inline; }
}
```

Matching should follow BCP 47 prefix rules: `(prefers-lang: en)` matches `en`, `en-US`, `en-GB`, etc. This is consistent with how `:lang()` already handles subtag matching.

### Privacy / fingerprinting

`navigator.language` is already exposed to JavaScript unconditionally — no permission, no opt-in. Any page running JS has this signal today. A CSS media feature does make it passive rather than active, which is a real tradeoff.

Counterpoints:

1. `prefers-color-scheme` went through this debate (#4162) and shipped. Language preference is not meaningfully more sensitive.
2. The `Accept-Language` header is sent on every HTTP request. Servers already see this passively. CSS parity does not expand the attack surface for server operators.
3. Sites that need i18n already use JS to detect language. A media feature removes the JS tax without creating a new fingerprinting vector — the information is already available.
4. Browsers could expose a coarsened value (primary subtag only) rather than the full `Accept-Language` list, limiting the entropy.

### Prior art

- `prefers-color-scheme`, `prefers-reduced-motion`, `prefers-contrast` — user preferences exposed via media features, shipped cross-browser
- #4162 — `prefers-color-scheme` as HTTP Client Hint, fingerprinting debate, shipped in Chrome as `Sec-CH-Prefers-Color-Scheme`
- #6915 — `:lang()` for documents without content language
- `Sec-CH-Lang` Client Hint — proposed but not widely adopted; no CSS equivalent
- `-webkit-locale` — internal Blink/WebKit property mapping `lang` attribute for font fallback, confirming engines already track locale internally

### What this enables

- CSS-only i18n on static sites — no JS, no edge compute, no server
- Localized UI labels, banners, and content using only HTML + CSS
- Progressive enhancement for sites that already have `lang`-tagged content in the DOM

Please view or discuss this issue at https://github.com/w3c/csswg-drafts/issues/13615 using your GitHub account


-- 
Sent via github-notify-ml as configured in https://github.com/w3c/github-notify-ml-config

Received on Sunday, 8 March 2026 20:14:47 UTC