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: obliqueis 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
obliqueoritalic. - 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'sslntaxis, so11deghere targets'slnt' -11, nofont-variation-settingsneeded) 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 barefont-style: obliquekeyword in Chromium and WebKit, even at the font's own trueslntbounds — 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-weightproperty (font-weight: 700), not by stating'wght'throughfont-variation-settings. Measured: a lone'wght'value alone doesn't breakslnteither, in a flat, unpinned case — but the property can never interact withslntat 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 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) 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 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) 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) 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) -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) 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) 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 } 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 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-style → slnt 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