VizChitra Fonts

Testing Cairo's oblique italic across browsers

Cairo has no italic masters — its italic is entirely the slnt variable axis, not a separate face. Asking a browser to slant it (font-style: italic, oblique, an explicit axis value, or some combination) can resolve very differently depending on how you ask and which engine is rendering it. This page tests every viable technique, in real engines and on real devices, to work out which ones are safe to rely on and which ones are traps — and to justify the pattern /use tells consumers to write.

Every technique below is 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.

TL;DR

Summary: ship font-style: oblique 11deg + explicit font-variation-settings: 'slnt' -11 (the recommended row below) — this is what fonts.css now ships, confirmed correct on every tested case: Chromium, Firefox, Playwright's WebKit, and real Safari 18.7 AND 27.0, including against the normal-style sibling face fonts.css always ships alongside this one. It fixes two SEPARATE problems at once — see below.

  • Two separate problems, easy to conflate: (1) a Chromium/WebKit synthesis bug, triggered when a use-site keyword's angle falls outside a declared @font-face range; (2) real Safari 18.7 performing no automatic font-style → slnt mapping at all, for any keyword, exact angle or not — a device/version gap, not an engine bug. Stating the exact angle fixes problem 1. Pairing it with an explicit slnt fixes problem 2. You need both.
  • font-style: oblique <angle> is the CSS Fonts 4 spec's own mechanism for matching a specific point inside a declared slnt range — not a workaround. Bare oblique (no angle) falls back to a stated default of 14deg; italic's angle is spec-defined as unspecified. Neither is guaranteed to fall inside this face's declared -11deg..11deg bounds, and neither does — that's problem 1 above. State the exact angle instead.
  • The exact angle alone is still not enough — confirmed BROKEN on real Safari 18.7 (that's problem 2). Pair it with font-variation-settings: 'slnt' -11: an element's own explicit value directly sets the axis, regardless of whether font-style-matching succeeds on a given engine or version. Confirmed correct on both real Safari 18.7 and 27.0.
  • This required checking the tested versions, not "every version" — only two real Safari data points exist (18.7 and 27.0); the exact version the gap closed in between them is unknown.
  • A family carrying both a normal and a bare oblique face together is a separate, historical hazard: Chromium and WebKit pick the wrong face for an angled request and render upright. This is no longer what fonts.css ships — the recommended row's ranged face does not have this ambiguity — but it's a real trap if you ever hand-roll a face this way yourself. See "historical hazard" 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 -11deg 11deg; }The font's real slnt bounds — shipped by fonts.css@font-face { font-style: oblique; }The font's style only — historical, no longer shipped
0. (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

1. font-style: italic;
VIZChitra VIZChitra
VIZChitra VIZChitra
2. font-style: oblique;
VIZChitra VIZChitra
VIZChitra VIZChitra
3. font-style: oblique 11deg;
VIZChitra VIZChitra
VIZChitra VIZChitra
4. font-variation-settings: 'slnt' -11;
VIZChitra VIZChitra
VIZChitra VIZChitra
5. font-style: oblique 11deg;
font-variation-settings: 'slnt' -11;
VIZChitra VIZChitra
VIZChitra VIZChitra
  • pass
  • fail
  • untested on a real device
  • Hover a badge for the exact device/version.
  • Firefox 155.0
  • Chromium 153.0.8010.12
  • WebKit 26.6 (Playwright)
  • Safari 27.0 (macOS desktop)
  • Safari 18.7 (iPhone XR)

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.

⚠ Incomplete font-style: oblique 11deg ALONE against the TRUE range — correct, but not sufficient by itself

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

This is what you get if you drop the slnt half of the recommended row above — do not. Unlike the bare keyword, 11deg IS within this face's declared range, so there is nothing for Chromium or WebKit to fall back to synthesis for — resolves via the axis alone, correctly, in every automated engine, and confirmed on real Safari 27.0. But confirmed BROKEN on real Safari 18.7: that device performs no automatic font-style -> slnt mapping at all, exact angle or not, the same gap every other font-style-only row in this matrix hits. Getting the angle right solves the synthesis trap; it does not solve the device-support gap — those are two different problems (see the recommended row above).

✕ Avoid font-style: italic against the TRUE -11deg..11deg range — broken, differently

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

Per spec, italic's angle is not 14deg like bare oblique's — it is explicitly "unspecified" (CSS Fonts 4) — so this is a DIFFERENT gap from the row below, not the same one restated: whatever angle an engine picks for italic, it is not guaranteed to fall inside this face's declared -11..11 bounds either, and it doesn't here. Broken in the same two engines as the row below, but by a DIFFERENT amount: Chromium leans ~0.44, the same double-stack shape as bare oblique (axis correctly set to -11, plus a synthetic skew on top). WebKit leans ~0.25 this time, not ~0.44 — that's tan(14deg), a PURE synthetic skew with NO axis contribution at all, a third, genuinely different failure mode. Measured, not assumed to match the row below just because both are "the trap." Only Firefox is correct. Confirmed broken on real Safari, both 18.7 and 27.0. font-synthesis is forced back to weight+style here — this site's own font-synthesis: weight would otherwise neutralise the trap by accident.

✕ Avoid font-style: oblique (BARE) against the TRUE range — still the trap, avoid

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

-11deg..11deg is not an arbitrary or mismatched range — it's Cairo's actual fvar slnt bounds (min -11, default 0, max 11), and it is what fonts.css now ships. 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. This is exactly why the recommended pattern above is not optional: writing bare font-style: oblique against the face fonts.css actually ships, without the exact angle, hits this trap. font-synthesis is forced back to weight+style here — this site's own font-synthesis: weight would otherwise neutralise the trap by accident. Confirmed broken on real Safari, both 18.7 and 27.0.

✕ Avoid font-style: oblique 11deg against a family with BOTH normal and BARE oblique — historical hazard

font-style: oblique 11deg (family declares font-style: normal AND a BARE font-style: oblique, no range — no longer what fonts.css ships)
technique
VIZChitra
upright control
VIZChitra

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 via font-style-matching alone. It does, in Firefox. It does NOT in Chromium or WebKit: both pick the wrong face and render upright — not a partial lean, no lean at all. This is specifically about a BARE oblique descriptor competing ambiguously with a normal sibling for an angled request — fonts.css no longer ships a face shaped like this (see the recommended row above), but the hazard stays real if you ever hand-roll a face this way yourself: a bare oblique descriptor gives the matching algorithm no bounds to prefer it unambiguously. A RANGED oblique descriptor does not have this problem (see 'oblique 11deg ALONE' above) — confirmed broken on real Safari, both 18.7 and 27.0.

○ Legacy font-style: oblique + font-variation-settings: 'slnt' -11 — historical, still valid

font-style: oblique; font-variation-settings: 'slnt' -11 (face declares: font-style: oblique, no range)
technique
upright control
VIZChitra

What fonts.css recommended before the migration to a declared range (see the RECOMMENDED row above) — still correct today, against a face with no declared range, and still a valid choice if you ever ship or consume a face shaped this way. font-style: oblique is the semantically correct way to ask for this (CSS Fonts 4 font-style-matching); the explicit slnt value is what makes it deterministic regardless of whether a given engine performs that mapping at all — an element's own explicit font-variation-settings always wins over any automatic mapping, so whichever half an engine honours, the other covers it. Confirmed by hand on both real Safari 18.7 and 27.0.

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

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

Leans forward. This sets the axis directly — no automatic font-style mapping involved, so nothing to disagree about across engines, versions, or which face is in play, and immune to the ancestor-pin hazard below. It is one half of the recommended pattern above, not a replacement for font-style: pairing it with font-style keeps the semantic layer without giving up determinism. Confirmed by hand on both real Safari 18.7 and 27.0, not just reasoned from the mechanism.

○ Legacy font-style: oblique alone (bare, no italic), against a range-free face — historical

font-style: oblique (face declares: font-style: oblique, no range)
technique
VIZChitra
upright control
VIZChitra

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. 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 for determinism on anything between those two — this is exactly the same lesson as the RECOMMENDED row at the top, just against a range-free face rather than the one fonts.css now actually ships.

○ Legacy font-style: italic against a range-free bare `oblique` face — historical

font-style: italic (face declares: font-style: oblique, no range)
technique
VIZChitra
upright control
VIZChitra

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. Confirmed fixed on Safari 27.0, matching the bare-oblique row above — this time measured, not the earlier guess that it was "probably fixed alongside it." 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.

ℹ Diagnostic HISTORICAL, DO NOT USE — font-variation-settings as an @font-face DESCRIPTOR

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

Not a live recommendation — kept as a regression guard. 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, the whole time that file was live. Demoted out of the Quick matrix above: it's not a viable alternative worth comparing side by side with the working techniques, just a documented reason not to reintroduce it. Not separately device-confirmed in isolation from the original backslant bug report; not re-tested here.

ℹ Diagnostic Faux-oblique synthesis on top of a real axis

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

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
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