fix(ui): Cyrillic/Greek weights, html lang, weight normalisation (#1780)

Load Roboto 600/700 and DM Sans 700 so Cyrillic and Greek headings render
real semibold and bold faces instead of a synthetic bold, keep
<html lang> in step with the UI language, and move every font weight onto
the 400/500/600/700 scale (JetBrains Mono at 500 or lighter; the dashboard
LIVE badge now uses the interface font at 700).

Add the `styles:font-weights:validate` ratchet guard and its CI step. It
reads stylesheets much as Sass and the browser do (cascade, layers,
mixins, content blocks, `@extend`, `@at-root`, `:is()`/`:where()`,
keyframes) and lists what it deliberately does not trace in its header.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
4grayandClaude Opus 5.5 authored and GitHub committed 2026-10-04 08:55:23 +02:00
1 parent ab8460338a
commit e8b181fcea
52 files changed
+11216 -97

No files matched your search

@@ -892,6 +892,32 @@ Prefer removing a control over shrinking everything around it:
Never drop the only way back to a hidden surface. A collapse toggle that is
reachable by touch needs its restore affordance to be reachable too.
## Typography
The app stack is DM Sans with Roboto behind it (`$app-font-stack` in
`apps/web/src/m3-theme.scss`). DM Sans covers Latin only, so Cyrillic and Greek
UI text (ru, by, el) renders in Roboto. `apps/web/src/styles.scss` bundles both
families in 400, 500, 600 and 700, and JetBrains Mono in 400 and 500 (its
heavier faces would exceed the initial-bytes ratchet).
- Use only those four weights: in `font-weight`, in the `font` shorthand, and in
any custom property, Sass variable or token map that feeds one. A weight
between faces snaps to a neighbour (650 renders as 700), and 600 or more with
no face of at least 600 gets Chromium's synthetic bold. CI runs
`pnpm run styles:font-weights:validate`, which rejects any other value.
- JetBrains Mono text stays at 500 or lighter, also where it is a fallback
behind `ui-monospace` (only macOS resolves that). The check enforces this in
any rule that sets the family, directly or through a variable, or inherits
it from an enclosing rule. It cannot see what a mono modifier class inherits
from its base rule; set `font-weight: 500` there.
- Import whole `@fontsource/<family>/<weight>.css` files. The single-script
files such as `cyrillic-600.css` have no `unicode-range`, so a Cyrillic-only
face wins the weight match for Latin text in `Roboto, …` stacks and sends it
to the next family.
- `<html lang>` follows the UI language (`syncDocumentLanguage()` maps `by` to
`be` and `zhtw` to `zh-TW`), so `hyphens`, `text-transform` (Turkish İ) and
Han glyph fallback use the right locale.
## Theme Guidance
### Light Theme