How to style text tracks in HTML5 video via CSS?
Asked Answered
P

7

19

Is it possible to style text tracks (like subtitles and captions) in HTML5 video players?

I already found a way to do so for Chrome:

video::-webkit-media-text-track-container {
    // Style the container
}

video::-webkit-media-text-track-background {
    // Style the text background
}

video::-webkit-media-text-track-display {
    // Style the text itself
}

This seems to confuse Safari a bit. It works, but the rendering is quite buggy.

But more important: How to achieve do this for Firefox and IE?

Pyelitis answered 27/8, 2015 at 14:36 Comment(2)
some related questions based on audio rather than video #4127208 #17130140Fescue
Thanks. Doesn't solve the question, but interesting anyways... ;-)Pyelitis
A
20

The only cross-browser solution I have found to date is: Hide the video’s text tracks and use your own.

This will allow you to create your own text nodes, with classes, id’s etc. which can then be styled simply via css.

In order to do so, you would utilize the onenter and onexit methods of the text cues in order to implement your own text nodes.

var video   = document.querySelector(‘YOUR_VIDEO_SELECTOR’)
    tracks  = video.textTracks[0],
    tracks.mode = 'hidden', // must occur before cues is retrieved
    cues    = tracks.cues;

  var replaceText = function(text) {
        $('WHERE_TEXT_GETS_INSERTED').html(text);
      },

      showText = function() {
        $('WHERE_TEXT_GETS_INSERTED').show();
      },

      hideText = function() {
        $('WHERE_TEXT_GETS_INSERTED').hide();
      },

      cueEnter = function() {
        replaceText(this.text);
        showText();
      },

      cueExit = function() {
        hideText();
      },

      videoLoaded = function(e) {
        for (var i in cues) {
          var cue = cues[i];
          cue.onenter = cueEnter;
          cue.onexit = cueExit;
        }
      },

      playVideo = function(e) {
        video.play();
      };


  video.addEventListener('loadedmetadata', videoLoaded);
  video.addEventLister('load', playVideo);
  video.load();
Aforementioned answered 13/7, 2017 at 17:41 Comment(2)
You rock!! This worked fantastic as a cross-browser solution for captioning mp3 files.Torino
The only correct answer in 2023Kristiankristiansand
D
13

Use this for Chrome:

video::cue {
  // add style here
}

Firefox:

Not yet supported. Open bug to implement ::cue pseudo-element - https://bugzilla.mozilla.org/show_bug.cgi?id=865395

Edit:
Support for FireFox is available, it works similarly as it does in Chrome and Opera. But support is not yet available for Edge or IE.

Degrade answered 20/10, 2015 at 18:21 Comment(1)
This doesn't seem to work in 2023Kristiankristiansand
G
10

I set out to style my captions to have a black background and be positioned below the video for Safari and Chrome. I have achieved success with the following code combined with editing the .vtt file with the following styles. Note you must add the styles to the .vtt file or else in safari your captions will jump around when the video controls (even if they're hidden) would appear:

4
00:00:09.980 --> 00:00:12.640 line:13 position:50% align:middle 
size:100%
for just the summer but I ended up staying here.

Styles for chrome and safari captions:

Chrome uses the video::cue background-color and opacity.

video::cue {
  opacity: 1;
  background-color: black;
  font-size: 20px !important;
}

Safari uses -webkit-media-text-track-display-backdrop for it's background color. Note the !important which overrides Safari's inherent styling.

video::-webkit-media-text-track-display-backdrop {
  background-color: black !important;
  overflow: visible !important;
}

The following webkit-media-text-track-display overflow is allow for more padding around Chrome's caption text:

video::-webkit-media-text-track-display {
  overflow: visible !important;
}

Overflow visible is important on the following code for Safari and I'm setting the captions below the video with the transform, which is reliant on a fixed font-size:

video::-webkit-media-text-track-container {
 overflow: visible !important;
 transform: translateY(30%) !important;
}

EDIT

With some tweaking I ended up using this for my project:

Important: Delete all inline styling from your .VTT file.

Determine if the user is using chrome or safari.

const rootElement = document.getElementById('root');
const M = ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [];

rootElement.className += "web";
if(M[1] === 'chrome'){
  rootElement.className += " chrome";
} else {
  rootElement.className += " safari";
}

Then in a SASS .scss file use the following styles. NOTE: If you're not using SASS you can simply create a class for the video element and nest the corresponding styles.

.chrome {
  video::cue {
    font-size: 24px;
    opacity: 1;
    background-color: black;
    -webkit-transform: translateY(10%) !important;
    transform: translateY(10%) !important;
  }

  video::-webkit-media-text-track-display {
    overflow: visible !important;
    -webkit-box-sizing: border-box;
    background: black;
    padding: 8px;
    border-radius: 16px;
  }


  video::-webkit-media-text-track-container {
    overflow: visible !important;
    -webkit-transform: translateY(40%) !important;
    transform: translateY(40%) !important;
    position: relative;
  }
}

.safari {
  video::cue {
    font-size: 24px;
    opacity: 1;
    background-color: black;
  }

  video::-webkit-media-text-track-display-backdrop {
    background-color: black !important;
    overflow: visible !important;
  }

  video::-webkit-media-text-track-display {
    overflow: visible !important;
    -webkit-box-sizing: border-box;
  }

  video::-webkit-media-text-track-container {
    overflow: visible !important;
    -webkit-transform: translateY(20%) !important;
    transform: translateY(20%) !important;
    position: absolute;
  }
}
Greenlaw answered 5/2, 2018 at 23:13 Comment(0)
R
1

This is working for chrome,

video::-webkit-media-text-track-container {
    // Style the container
}

video::-webkit-media-text-track-background {
    // Style the text background
}


video::-webkit-media-text-track-display {
    // Style the text itself
}

you can also get some information from those links.

Link 1

Link 2

Restraint answered 27/8, 2015 at 16:33 Comment(2)
the sources don't directly mention that property, but the cross browser web VTT info linked to is good, including the list of plug-ins, you might want to update your answer for this. See also advprog.blogspot.co.uk/2013/07/…Fescue
Sadly the media-text-track stuff doesn't exist for firefox. Link 1 doesn't contain information about styling, and the solution proposed in link 2 needs a rewrite of the subtitle/caption file. So this isn't a pure css solution.Pyelitis
U
1

html

<video >
    <source ref="videoSource">
    <track default kind="subtitles" src="xxx" />
</video>

//custom element for showing subtitle
<div ref='subtitleTrackWrapper' v-show='showTextTrack'></div>

javascript

let textTrack = [your video tag].textTracks[0]
textTrack.mode = 'hidden'
textTrack.addEventListener('cuechange', () => {
const cues = textTrack.activeCues; 

    if(cues.length>0){
        this.showTextTrack = true
        this.$refs.subtitleTrackWrapper.innerHTML = cues[0].text
    }else{
        this.showTextTrack = false
    }

});
Uranology answered 8/11, 2022 at 13:14 Comment(0)
M
0

For a cross-browser solution, forking from Spencer S.'s answer, but in vanilla js and using slightly different event listeners...

/***
  * replace subtitles for cross-browser consistency
  * from Spencer S. - https://mcmap.net/q/632099/-how-to-style-text-tracks-in-html5-video-via-css
***/ 
let track  = topperVideo.textTracks[0];
track.mode = 'hidden'; // must occur before cues is retrieved
let cues    = track.cues;
let subtitleElem = topperVideoWrapper.querySelector('#topper-video-subtitle');
  
let replaceText = function(text) {
  subtitleElem.innerHTML = text;
};

let showText = function() {
  subtitleElem.style.display = "block";
};  

let trackShow = function(text) {
  replaceText(text);
  showText();
};

let trackHide = function() {
  subtitleElem.style.display = "none";
};

track.addEventListener('cuechange', function(event) {

  // test for should-be visible cue
  let activeCue = track.activeCues[0];
    
  // if would be showing a cue
  if(activeCue) {
    // get text
    let activeCueText = activeCue.text;
    // show cue
    trackShow(activeCueText);
  } else {
    // hide cue
    trackHide();
  }
});

// play video here
Maynord answered 4/5, 2023 at 15:17 Comment(0)
S
0

After some research I have been enlightened with this solution, you can style the subtitles however you want.

var subTrack = document.getElementById("video").textTracks[0];
const customSubtitles = document.getElementById("customSubtitles")

subTrack.oncuechange = function(e) {
  var cue = this.activeCues[0];
  if(cue){
    customSubtitles.innerHTML = cue.text
  }
}

And if you wanna hide the original subtitles, set the kind of the track to "metadata".

Significancy answered 1/2, 2024 at 22:38 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.