VizChitra Fonts

Browser compatibility matrix

These are already asserted in Chromium, WebKit and Firefox by src/lib/fonts/slant.browser.test.ts, which measures rendered shear on every pnpm test; results are in docs/compat.md. This page exists for the one thing that cannot automate: real Safari, which Playwright's WebKit is not. Cairo has no italic masters — its italic is the slnt axis — so every row is a different way of asking for the same lean.

TL;DR

Summary: ship font-style: oblique + explicit font-variation-settings: 'slnt' -11 (the recommended row below) — it is correct everywhere today, with no per-use-site angle to get right. The theoretically "purer" end state — a face that declares its true slnt range, matched with an exact angle at the use site — also measures correct in every automated engine, but fonts.css does not ship it: it demands every consumer discover and state the exact angle, and it has not been checked on real Safari.

  • font-style: oblique is the semantically correct, spec-preferred way to ask for Cairo's italic — Cairo has no italic masters, this is the axis, matched via CSS Fonts 4's font-style-matching algorithm.
  • Confirmed correct on Chromium, Firefox, Playwright's WebKit, and real desktop Safari 27.0.
  • Confirmed broken on real, older iOS Safari (18.7, iPhone XR) — the automatic mapping does not happen there at all, for either oblique or italic.
  • For deterministic correctness across every version, pair it with an explicit font-variation-settings: 'slnt' -11. That is the recommended pattern below.
  • Stating the exact angle instead — font-style: oblique 11deg — is tempting (CSS's angle sign is the opposite of OpenType's slnt axis, so 11deg here targets 'slnt' -11, no font-variation-settings needed) but do not use it: against the shipped family, which carries both a normal and an oblique face, Chromium and WebKit pick the wrong one and render upright. Only Firefox honours it. See the trap row below.
  • Declaring an oblique angle range in @font-face (e.g. font-style: oblique -11deg 11deg) breaks the bare font-style: oblique keyword in Chromium and WebKit, even at the font's own true slnt bounds — CSS's implied default angle for "no angle stated" is 14deg, outside this face's ±11deg range, so the browser falls back to synthesis on top of the axis. Correction to an earlier version of this page: it is not "any range breaks it regardless" — stating the exact angle the range covers (oblique 11deg, not bare) resolves correctly in every engine. fonts.css still ships the bare keyword against a range-free face, because that needs no such per-use-site angle discipline. See both rows below.
  • Change weight with the font-weight property (font-weight: 700), not by stating 'wght' through font-variation-settings. Measured: a lone 'wght' value alone doesn't break slnt either, in a flat, unpinned case — but the property can never interact with slnt at all, in any component tree, so it carries none of the font-variation-settings-replaces-not-merges risk (the "second real-device finding" below) by construction.

Independently corroborated: Safari has lagged on this exact CSS Fonts 4 behaviour since at least 2020 — see ArrowType's vf-slnt-test, a community test suite (last run November 2022) that found Safari passing almost nothing except font-variation-settings itself against variable fonts, across three years of retests. Its recommendation to fall back to transform: skew() is for fonts missing the requested axis value entirely — a different, spec-acknowledged ambiguous case — not this one: Cairo has a real slnt axis and font-style: oblique matches it directly.

Quick matrix

Rows are what the @font-face declares; columns are what's set at the use site. Each populated cell overlays two renderings of the same glyphs: pink is the always-correct control (font-variation-settings: 'slnt' -11 directly, on the same family), the other colour is the technique being tested. Aligned, the two colours screen together into a pale blend — visible pink is a failure, not a subtle one. Blank cells are untested or not a meaningful combination for that face, not "assumed fine." Full detail — why each populated cell is what it is — is in "Slant techniques" below.

@font-face { font-style: oblique } — shipped by fonts.css@font-face { font-style: oblique -11deg 11deg } — the trap, the font's real bounds@font-face { font-variation-settings: 'slnt' -11 } descriptor
(nothing set)
VizChitra VizChitra

trivial — nothing here requests a slant, not a meaningful test

VizChitra VizChitra

trivial — nothing here requests a slant, not a meaningful test

VizChitra VizChitra
font-style: italic
VizChitra VizChitra
font-style: oblique
VizChitra VizChitra
VizChitra VizChitra
font-style: oblique 11deg
VizChitra VizChitra
VizChitra VizChitra
font-variation-settings: 'slnt' -11
VizChitra VizChitra
VizChitra VizChitra
oblique + 'slnt' -11
VizChitra VizChitra
  • pass
  • fail
  • pass, reasoned — not separately device-confirmed
  • untested on a real device
  • Hover a badge for the exact device/version/confirmation status.

The iOS Safari backslant

The original bug, and the reason this page exists. Reported on Safari 18.7, iPhone XR: Cairo renders leaning backwards when slnt is left to the font's default. Desktop engines look fine, so this only shows up on an actual old device.

The cause is that font-variation-settings replaces the inherited value rather than merging with it — so declaring only 'wght' silently discards any upright reset. The @font-face descriptor cannot save you here: it is measurably inert in WebKit.

Left: axis unpinned — Right: 'slnt' 0 stated

font-variation-settings: 'wght' 600  vs  'wght' 600, 'slnt' 0
unpinned — backslants on old iOS
VIZCHITRA
pinned — correct everywhere
VIZCHITRA

These must look identical. If the left one leans back, this device has the bug. The fix is component-level, not a global reset: any rule that states 'wght' via font-variation-settings must also state 'slnt' (src/lib/fonts/axis-pinning.test.ts enforces this for this repo's own code). An earlier version of this fix instead pinned 'slnt' 0 on html in app.css — that broke font-style: italic everywhere, on every engine, because an ancestor's explicit slnt blocks the browser's automatic mapping from font-style onto the axis. The "Hazard demo" section further down re-checks that on this device.

Slant techniques — judge by eye

Each row shows the technique on the left and an untouched upright control on the right. A failure looks like the two halves matching.

font-style: oblique + font-variation-settings: 'slnt' -11 — RECOMMENDED

font-style: oblique; font-variation-settings: 'slnt' -11 (face declares: font-style: oblique)
technique
upright control
Hamburgefonstiv

Leans correctly everywhere, by construction: font-style: oblique is the semantically correct, spec-preferred way to ask for this (CSS Fonts 4 font-style-matching) and is future-proof as engines converge — confirmed already fixed between Safari 18.7 and 27.0 (see the two rows below). The explicit slnt value is what makes it deterministic today, on every version, without waiting for old Safari to disappear from the field: an element's own explicit font-variation-settings always wins over any automatic mapping, so whichever half an engine honours, the other covers it.

font-variation-settings: 'slnt' -11 alone — the deterministic ingredient

font-variation-settings: 'slnt' -11
technique
Hamburgefonstiv
upright control
Hamburgefonstiv

Leans forward. This sets the axis directly — no automatic font-style mapping involved, so nothing to disagree about across engines or versions, and immune to the ancestor-pin hazard below (unlike the two rows underneath). It is one half of the recommended pattern above, not a replacement for font-style: pairing it with font-style: oblique keeps the semantic layer without giving up determinism.

font-style: oblique alone (bare, no italic) — version-dependent

font-style: oblique (face declares: font-style: oblique)
technique
Hamburgefonstiv
upright control
Hamburgefonstiv

Per the CSS Fonts 4 font-style-matching algorithm, this is an exact match against the face's own font-style: oblique descriptor — no italic-to-oblique fallback step, one less layer of indirection than the row below. Leans ~11deg in Chromium, current WebKit and Firefox. On real devices this is confirmed version-dependent, not a permanent Safari limitation: broken on Safari 18.7 (iPhone XR), fixed by Safari 27.0 (macOS desktop) — the exact version this changed in is unknown, only these two endpoints are confirmed. Pair with explicit slnt (the row above) for determinism on anything between those two.

font-style: italic against a bare `oblique` face — version-dependent

font-style: italic (face declares: font-style: oblique)
technique
Hamburgefonstiv
upright control
Hamburgefonstiv

Leans ~11deg in Chromium, current WebKit and Firefox — but confirmed UPRIGHT on real Safari 18.7 (iPhone XR): it does not perform this automatic font-style -> slnt mapping at all, even with no ancestor pin in the way. Not separately re-tested on Safari 27.0 — probably fixed alongside the bare-oblique row above, since both reach the same underlying axis-mapping capability just via a different matching path, but that is a guess, not a measurement, so it is left unconfirmed here rather than assumed. Playwright’s WebKit is a different, newer build and reproduces neither gap — this is a real engine divergence you can only catch by hand. font-style: italic is fine as a semantic hint; pair it with explicit slnt (or prefer bare oblique, one layer less indirect) for determinism.

font-style: oblique (BARE) against the font's TRUE -11deg..11deg range — still the trap

font-style: oblique; font-synthesis: weight style (face declares: font-style: oblique -11deg 11deg)
technique
Hamburgefonstiv
upright control
Hamburgefonstiv

-11deg..11deg is not an arbitrary or mismatched range — it's Cairo's actual fvar slnt bounds (min -11, default 0, max 11). The trap is specifically the BARE keyword: CSS Fonts 4 says lack of an angle implies 14deg, which is OUTSIDE this face's declared bounds, so Chromium and WebKit fall back to synthesis to reach 14deg on top of the (correctly matched) axis — Chromium leans roughly twice as far, WebKit prefers synthesis over the axis outright and lands near the same overshoot. Only Firefox is correct. Correction to an earlier version of this row: this is NOT "declaring ANY oblique range breaks it, correct bounds or not" — state the EXACT angle the range covers instead of the bare keyword, and it resolves correctly everywhere (the row below). fonts.css still ships the bare keyword against a range-FREE face regardless: that stays correct with no extra use-site discipline required, where a ranged face would require every consumer to know and state the exact angle. font-synthesis is forced back to weight+style here — this site's own font-synthesis: weight would otherwise neutralise the trap by accident. Not re-tested on a real device.

font-style: oblique 11deg against the font's TRUE range — the real end state, and it works

font-style: oblique 11deg (face declares: font-style: oblique -11deg 11deg)
technique
Hamburgefonstiv
upright control
Hamburgefonstiv

This is the combination the trap row above is actually missing, and the genuine spec-ideal end state: a face that declares its true slnt bounds, and a use site that asks for a specific point inside them. Unlike the bare keyword, 11deg IS within this face's declared range, so there is nothing for the engine to fall back to synthesis for — resolves via the axis alone, correctly, in Chromium, Firefox and Playwright WebKit, whether or not font-synthesis allows style synthesis. fonts.css does not ship this yet: it requires every consumer to discover and state the exact angle rather than just writing font-style: oblique, and it has not been checked on real Safari. Kept as a documented possibility for when Cairo (or a successor) is ready to declare its axis range properly, not a recommendation to adopt today.

font-style: oblique 11deg against the shipped family — do not use

font-style: oblique 11deg (family declares BOTH font-style: normal and font-style: oblique faces, like fonts.css)
technique
Hamburgefonstiv
upright control
Hamburgefonstiv

CSS Fonts 4: OpenType's slnt axis is positive counter-clockwise, CSS's oblique angle is positive clockwise — so stating the angle here SHOULD target 'slnt' -11, the same destination as the recommended row, via font-style-matching alone, no font-variation-settings needed. It does, in Firefox. It does NOT in Chromium or WebKit: caught first as a false pass in an isolated test that only declared the oblique face with no normal sibling; re-measured against a family carrying both, the way fonts.css actually ships Cairo, and both engines pick the wrong face and render upright — not a partial lean, no lean at all. Confirmed live on this page: the matrix cell below reproduces it. Stick with the recommended row above.

font-variation-settings as an @font-face DESCRIPTOR

@font-face { font-variation-settings: 'slnt' -11 }
technique
Hamburgefonstiv
upright control
Hamburgefonstiv

The old hand-written font.css relied on this. Measured DEAD in WebKit — upright, no lean at all — so Cairo italic was silently broken in Safari all along. Not separately device-confirmed in isolation from the original backslant bug report; not re-tested here.

Faux-oblique synthesis on top of a real axis

font-style: italic + font-variation-settings: 'slnt' -11
technique
Hamburgefonstiv
upright control
Hamburgefonstiv

Should look the SAME as the recommended row above. If it leans noticeably further, the engine is synthesising a skew on top of the real axis — fix with font-synthesis: none.

Self-checking

These change measurable metrics, so the page tests them itself.

  • Measuring…

Hazard demo: an ancestor pin kills italic (deliberately broken — not our CSS)

The middle half is expected to look upright, unlike the left — this is the exact bug an earlier version of the iOS fix reintroduced by pinning slnt on html. If app.css ever adds such a pin back, this middle half is how it would show up. Both use a bare specimen with no font-variation-settings of their own — not .sample .m-oblique, which now states font-variation-settings: normal on itself and so is deliberately immune to any ancestor, this one included.

On real Safari 18.7 (iPhone XR), the left half shows upright too — not because of the ancestor pin, but because that engine does not perform the automatic font-styleslnt mapping at all, pin or no pin (fixed by Safari 27.0 — see the TL;DR above). The left and middle halves become visually indistinguishable there, which is itself the case for pairing font-style with an explicit value rather than relying on the mapping alone: you cannot tell "blocked by an ancestor" apart from "this version never supported it" by eye. The right half sidesteps the whole question — it sets font-variation-settings: 'slnt' -11 directly, so there is no automatic mapping to be blocked or unsupported, and it leans correctly under the very same pinned ancestor, on every engine and version including old real Safari. This is the deterministic half of the recommended pattern above, not a reason to drop font-style entirely.

Left: italic, no ancestor pin. Middle: same, under a pinned ancestor — stays upright. Right: explicit slnt, under the SAME pinned ancestor — leans correctly regardless

font-style: italic  vs  (ancestor: font-variation-settings: 'slnt' 0) font-style: italic  vs  (same ancestor) font-variation-settings: 'slnt' -11
no ancestor pin — correct only where italic is supported
VIZCHITRA
nested under a slnt-pinned ancestor — broken on purpose
VIZCHITRA
same pinned ancestor — explicit slnt stays immune
VIZCHITRA