What Survives ::cue: A Field Guide to WebVTT Styling Across Chrome, Firefox, and Safari

If you have ever written a ::cue rule, watched it work in Chrome, and then opened the same page in Firefox to find your carefully colored speaker labels rendered as plain white text, you already know the problem this article is about. The rule did not error. The console stayed quiet. The style simply did not apply.

That silence is the trap. ::cue is a pseudo-element with a spec-defined property list, and it is widely available — MDN notes it has been available across browsers since January 2020, with some parts having varying levels of support. But “available” and “predictable” are different claims. This piece is a field guide to what actually survives, grounded in a published per-browser test matrix, so you can design WebVTT styling that degrades instead of disappearing.

What the spec actually promises

The WebVTT specification defines four pseudo-elements for styling cues and regions: ::cue, ::cue(), ::cue-region, and ::cue-region(). The spec’s own example shows a gradient background and text color applied to all cues, plus a separate color for bold objects inside cues.

Critically, the spec does not let you put arbitrary CSS inside these rules. MDN lists the permitted properties: the background family, color, the font family, line-height, opacity, outline, text-decoration, text-shadow, visibility, white-space, ruby-position, and text-combine-upright. Anything outside that list is not supposed to apply.

Here is the first thing to internalize: that list is a ceiling, not a floor. A property being permitted by the spec does not mean a given engine applies it. And when an engine declines, it typically does so without a console warning, without a fallback box, and without any signal you can catch in a test suite. Treat every ::cue rule as an enhancement until you have verified it in the engines your audience uses.

The one thing every engine agrees on

If you want a baseline that survives, the retrieved test matrix at hartman.github.io — a per-browser fixture log that screenshots rendering and dumps TextTrack.cues and getCueAsHTML() — points to a narrow intersection. Bare ::cue with color works across Chrome 151, Firefox 153, Safari 26.6, Safari TP 27.0, and iOS Safari 26.5. Tag selectors for i, b, and u also render their native styling everywhere.

That is your floor. A plain ::cue { color: white; } rule, plus reliance on the built-in italic, bold, and underline markup, is the styling you can ship without a per-browser caveat. It is not glamorous. It is the part that does not vanish.

Where it fractures

The matrix documents several failure modes that are worth knowing by name, because each one fails differently.

Bare ::cue background-color in Chrome

Bare ::cue with background-color is silently ignored in Chrome 151, while the other tested engines apply it. If you rely on a translucent black box behind your captions for legibility over bright video, Chrome will not give it to you through that rule. The matrix shows background-color working in Chrome when an argument selector is present, which is a different code path — not the same feature simply re-enabled.

Argument selectors in Firefox

This is the big one. The matrix reports that ::cue(.class), ::cue(v[voice="..."]), bare tag selectors like ::cue(i), and compound selectors such as ::cue(b.class) all fail in Firefox 153 while working in the other tested browsers. The failure is specific to the default styling method — an in-file STYLE block — which is how a standalone .vtt file declares its own styling.

This matters for a concrete reason. If you want voice-colored captions, ::cue(v[voice="Esme"]) is the natural rule, and the spec’s own examples show exactly that pattern. But if that rule does not apply in Firefox, your speaker color is gone in one engine. A robust design carries a non-color cue as well — speaker labels in the cue text itself — so the meaning survives when the color does not.

The method matters as much as the property

Here is a subtlety that will bite anyone writing a test log. The matrix reports that in-file STYLE-block background-image only works in Chrome; page-level <style> and external stylesheet methods reach the other browsers. The same property, the same selector, a different delivery method, a different result.

That means your experiment log must record which method you used. “::cue background-image works” is not a reproducible statement. “::cue background-image via page-level stylesheet works in Firefox 153″ is. If you do not record the method, your results are not reproducible, and reproducibility is the whole point of a versioned experiment log.

Regions: defined, but not delivered

The spec defines ::cue-region and ::cue-region() for styling cues within a region. MDN is blunt about the reality: the ::cue-region pseudo-element is intended for styling cues in a particular region, but is not supported in any browser.

The matrix confirms the styling gap and adds a layout wrinkle. ::cue-region() background styling is not applied in Firefox, Safari, Safari TP, or iOS Safari. Chrome has no VTTRegion at all — the matrix reports Chrome 151 lacks VTTCue.region and the VTTRegion constructor — so it falls back to normal cue layout. Firefox and Safari do implement region layout, including anchor, width, and multi-cue stacking.

If your design depends on two-column rollup captions for two speakers, the spec’s region example is the right mental model, but the delivery is uneven. Do not assume a region will be styled, and do not assume Chrome will lay one out.

Cue settings are not CSS

This is a category error worth naming explicitly. Cue settings — line, position, align, size, vertical — are parsed by the track engine, not the style engine. They fail differently from CSS properties, and you should test them separately.

The matrix shows real divergence here too. position:P%,line-left and line:P%,start are dropped entirely in Chrome 151, falling back to auto, while working in the other tested browsers. vertical:rl and vertical:lr render horizontally in Firefox 153 while working elsewhere. And Chrome’s narrow-size text wrapping overflows as one line instead of wrapping, while the other engines wrap correctly.

None of these are ::cue failures. They are cue-setting failures. If you conflate the two in your testing, you will chase the wrong fix.

A degradation strategy

Given all of the above, here is the approach I would recommend, stated as a recommendation rather than a finding:

  1. Ship a baseline that the matrix shows working everywhere. Bare ::cue with color, plus native i/b/u styling. This is the layer that does not disappear.
  2. Add enhancements as a second layer. Backgrounds, voice colors, class-based styling, and regions go on top, behind per-browser verification. Do not let an enhancement carry meaning that the baseline cannot.
  3. Carry meaning in the text, not only in the style. Speaker labels, sound-effect brackets, and language tags in the cue text survive when color does not.
  4. Record the method and the version in your log. Note whether you used an in-file STYLE block or a page-level stylesheet, and note the browser versions you tested. The matrix is a dated snapshot — Chrome 151, Firefox 153, Safari 26.6, Safari TP 27.0, iOS Safari 26.5 — not a permanent truth.

What this means for accessibility

WCAG 2.2 Success Criterion 1.2.2 requires captions for all prerecorded audio content in synchronized media, with stated exceptions for media that is a text alternative and clearly labeled as such. The Understanding document also notes that captions should not obscure or obstruct relevant information in the video.

Notice what the criterion is about: captions existing, being synchronized, and being readable. It is not about gradients. A plain, universally supported ::cue color rule that survives in every engine serves the criterion better than a beautiful rule that vanishes in one. Styling is secondary to presence. If you are weighing a design decision, weigh it against that order of priorities.

One more thing: players are not browsers

Everything above assumes native <track> rendering. If you use a JavaScript player, the rules change. The matrix reports that video.js 8.23.9 implements none of the WebVTT CSS styling spec — no ::cue(), no ::cue-region(), no shadow DOM — and paints cues itself as plain HTML. An in-file STYLE block parses fine and is then silently ignored.

That is one player at one version, not a claim about all players. But it is a reminder that “::cue support” is a property of the rendering path, not of the file format. If you swap the player, re-run the test.

FAQ

Does ::cue work in all modern browsers?

MDN describes ::cue as widely available since January 2020, with some parts having varying levels of support. The retrieved matrix shows bare ::cue with color working across all tested engines, but argument selectors and several properties diverge. “Available” is not the same as “uniform.”

Why does my ::cue(.class) rule work in Chrome and Safari but not Firefox?

The matrix reports that argument selectors — ::cue(.class), ::cue(v[voice="..."]), bare tag selectors, and compound selectors — fail in Firefox 153 via the default in-file STYLE block method. The failure is silent. If you need class-based styling, verify it in Firefox specifically, and carry the meaning in the cue text as a fallback.

Can I style captions with a background image?

The matrix reports that in-file STYLE-block background-image only works in Chrome; page-level stylesheet methods reach the other browsers. The delivery method changes the result, so record which method you used.

Is ::cue-region usable?

MDN states ::cue-region is not supported in any browser. The matrix confirms region background styling is not applied in Firefox, Safari, Safari TP, or iOS Safari, and Chrome has no VTTRegion implementation at all.

Do cue settings like position and line behave the same as CSS?

No. Cue settings are parsed by the track engine, not the style engine, and they fail differently. The matrix shows position:P%,line-left and line:P%,start dropped entirely in Chrome 151, and vertical:rl/vertical:lr rendering horizontally in Firefox 153. Test them separately from ::cue rules.

Sources