-
Notifications
You must be signed in to change notification settings - Fork 779
Description
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:
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:
<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>.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
@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:
prefers-color-schemewent through this debate ([css-mediaqueries] Expose User Preference Media Queries as HTTP Client Hint or HTTP Header #4162) and shipped. Language preference is not meaningfully more sensitive.- The
Accept-Languageheader is sent on every HTTP request. Servers already see this passively. CSS parity does not expand the attack surface for server operators. - 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.
- Browsers could expose a coarsened value (primary subtag only) rather than the full
Accept-Languagelist, limiting the entropy.
Prior art
prefers-color-scheme,prefers-reduced-motion,prefers-contrast— user preferences exposed via media features, shipped cross-browser- [css-mediaqueries] Expose User Preference Media Queries as HTTP Client Hint or HTTP Header #4162 —
prefers-color-schemeas HTTP Client Hint, fingerprinting debate, shipped in Chrome asSec-CH-Prefers-Color-Scheme - [selectors]
:langfor documents without content language and for elements of unknown language; consider:lang("")over:not(:lang("*"))#6915 —:lang()for documents without content language Sec-CH-LangClient Hint — proposed but not widely adopted; no CSS equivalent-webkit-locale— internal Blink/WebKit property mappinglangattribute 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