WebVTT Cue Settings: Position and Style Captions for Web Video
💡 To position a WebVTT cue, add settings like
line:90% position:5% align:startdirectly after-->on the timestamp line. For global styling, add aSTYLEblock at the top of your .vtt file using::cue { color: white; background-color: rgba(0,0,0,0.75); text-shadow: 1px 1px 2px black; }. These are the two mechanisms WebVTT gives you to control caption position and appearance.
Key takeaways
- Cue settings (
line:,position:,size:,align:) go after-->on the timestamp line, space-separated, one set per cue. STYLEblocks must appear before the first cue. Any STYLE block placed after a cue is silently ignored by conforming parsers.::cuesupportscolor,background-color,font-family,text-shadow, andopacity. It does NOT supportmargin,padding,transform, or layout properties.line:90%(percentage) places the cue at 90% of the viewport height.line:-1(integer) snaps to the last rendered line at the bottom.- Browser support for
::cueis Baseline / widely available since January 2020.::cue-regionis in the spec but has no browser support yet.
What are WebVTT cue settings?
WebVTT (Web Video Text Tracks) is the native timed-text format for the HTML <video> element. A .vtt file holds a sequence of cues. Each cue has a timestamp line, an optional identifier, and a text payload. The cue settings live on the timestamp line, after -->, and tell the browser where to render that cue on screen.
Unlike SRT, which carries no positioning data, WebVTT gives you five spatial controls per cue. These are not CSS: they are part of the WebVTT cue settings syntax defined in the W3C WebVTT Level 1 specification, and they apply only to the cue they are attached to.
I use WebVTT for all web-native caption and subtitle work. Every modern browser supports it natively without JavaScript, and it handles both per-cue positioning and file-level CSS styling through STYLE blocks in one clean text format.
The five cue settings and their values
All cue settings go on the timestamp line after -->, separated by spaces. Here is a working .vtt snippet showing two differently positioned cues:
WEBVTT
cue-1
00:00:05.000 --> 00:00:10.000 line:90% position:5% size:90% align:start
Caption at the bottom-left.
cue-2
00:00:12.000 --> 00:00:17.000 line:10% align:center
Title card near the top.
| Setting | Values | Effect |
|---|---|---|
line | Integer (-1, 0, 3) or percentage (90%) | Vertical placement. Integer: snap to line count from top (positive) or bottom (negative). Percentage: fraction of viewport. Default: auto (near bottom). |
position | Percentage (5% to 100%) | Horizontal placement of the cue box. 0% = left edge, 100% = right edge. Default: auto. |
size | Percentage (80%, 50%) | Width of the cue box. A narrower box wraps text sooner. Default: 100%. |
align | start, center, end, left, right | Text alignment within the cue box. Default: center. |
vertical | rl or lr | Vertical text direction for CJK scripts. Omit for standard horizontal text. |
The key distinction with line: an integer activates snap-to-lines mode, where the browser counts rendered line-heights from the edge. A percentage uses a raw fraction of the viewport height. I use percentages for most web projects because they scale predictably across different viewport sizes.
How do I write a STYLE block and ::cue CSS?
A STYLE block goes at the top of the .vtt file, before the first cue timestamp. It uses the ::cue pseudo-element with standard CSS properties:
WEBVTT
STYLE
::cue {
color: white;
background-color: rgba(0, 0, 0, 0.75);
font-family: Arial, sans-serif;
font-size: 1.1em;
text-shadow: 1px 1px 2px black;
}
STYLE
::cue(b) {
color: #FFD700;
}
STYLE
::cue(v[voice="Narrator"]) {
color: #ADD8E6;
}
00:00:05.000 --> 00:00:10.000
Welcome to this <b>tutorial</b>.
00:00:12.000 --> 00:00:18.000
<v Narrator>The story begins here.</v>
- The
STYLEkeyword appears alone on its own line, immediately followed by the CSS block with no blank line between them. - A blank line ends the STYLE block. Multiple STYLE blocks are valid and applied in order.
- Any STYLE block placed after a cue timestamp is silently ignored. Always put all STYLE blocks at the top.
::cue(b) targets <b> inline tags in the cue payload. You can also use ::cue(i), ::cue(c.classname), and ::cue(v[voice="Name"]) for speaker-colored text. For projects where these captions also need to be translated into Vietnamese or another language, I connect the same .vtt format to the subtitle translation workflow.
Which CSS properties work inside ::cue?
The W3C spec restricts the property set inside ::cue intentionally. Allowing arbitrary CSS could cause a single opaque block to cover an unpredictable area of the video frame.
- Supported:
color,backgroundand allbackground-*longhands,fontand allfont-*,line-height,opacity,outline,text-decorationand longhands,text-shadow,visibility,white-space. - Not supported:
margin,padding,border,width,height,display,position,top,left,transform,animation,transition.
The most common mistake I see: adding padding inside ::cue to inset the background box around the text. It does not work. The fix is to narrow the cue box with the size cue setting, center it with position, and apply background-color in ::cue.
Worth noting: background-color inside ::cue applies per-cue-box individually, not as a single block across all cues. This is by design. It prevents one large opaque rectangle from appearing when multiple cues overlap on screen. The MDN ::cue reference lists the complete supported property set.
Embedding the track element, and what to handle in-house
A .vtt file connects to a <video> element via a <track> tag. Use kind="captions" for same-language captions (including sound descriptions for accessibility) and kind="subtitles" for a translated-language track:
<video controls src="video.mp4">
<track kind="captions" src="captions.en.vtt" srclang="en" label="English" default />
<track kind="subtitles" src="subtitles.vi.vtt" srclang="vi" label="Tiếng Việt" />
</video>
The srclang attribute takes a BCP 47 language tag: en, vi, zh, fr. The default attribute activates the track if the viewer has no saved preference. Only one track per video should carry default. One CORS note: the .vtt file must be served from the same origin as the page, or the server must send Access-Control-Allow-Origin: *. Testing with file:// URLs will fail for this reason.
For a single-language web video with standard placement, producing a .vtt file is a manageable in-house task: text editor, any modern browser for testing, and the W3C spec as a reference. The work gets more involved in these cases:
- Multi-language delivery: each language needs its own .vtt file. Vietnamese subtitles translated from English tend to run longer and need re-breaking to keep reading speed comfortable.
- Platform delivery: streaming platforms and broadcasters rarely accept WebVTT as the primary deliverable. Netflix specifies SRT or TTML/IMSC. Broadcast clients need SCC for English 608 closed captions, or SMPTE-TT for 708.
- SDH: adding sound effect descriptions and speaker labels on top of the dialogue is a craft decision beyond the format work itself.
If you need translated, platform-ready subtitle files, the professional subtitle translation service handles format conversion and localization together.
FAQ
Can I use ::cue to change the caption font size?
Yes. font-size is a supported property inside ::cue. I recommend em or rem units so the size scales with the viewer's browser text settings rather than being locked to a fixed pixel value.
Why does my STYLE block seem to have no effect?
Check its position in the file. Any STYLE block placed after the first cue timestamp is silently ignored by conforming parsers. Move all STYLE blocks to the top of the file, immediately after the WEBVTT header line.
Does ::cue work on mobile browsers?
::cue is Baseline / widely available and has been supported in Safari iOS, Chrome Android, and Firefox Android for several years. Complex selectors like ::cue(v[voice="..."]) have slightly wider variation in support across browsers, so test on your target devices if you use voice-colored text.
Can I have more than one STYLE block in a .vtt file?
Yes. Multiple STYLE blocks are valid and additive. I usually put global cue styles in the first block and speaker-color rules in a second block for readability.
Is WebVTT accepted as a deliverable by Netflix or broadcast clients?
Not as the primary format. Netflix specifies SRT or TTML/IMSC depending on the language. Broadcast clients need SCC for English 608 closed captions or SMPTE-TT for 708 delivery. WebVTT is the right format for a web player but needs to be converted for platform delivery.
Official Sources
- W3C WebVTT Level 1 Specification - cue settings syntax, STYLE blocks, ::cue, and <track> integration. Verified October 2026.
- MDN WebVTT API Reference - format overview, inline cue tags, browser compatibility. Verified October 2026.
- MDN ::cue pseudo-element - complete list of supported and unsupported CSS properties. Verified October 2026.
Written by Dao Huy (Lucas), Vietnamese translator & localization specialist (EN · ZH · FR → Vietnamese). See translation services →
