Considerations when Customizing MediaElement.js in WordPress

We’ve worked with MediaElement.js extensively at AudioTheme ever since WordPress bundled it in version 3.6 to provide native support for audio and video. We’ve used it to create custom audio players, style the default players provided by WordPress core, and have even open-sourced projects based on it.

Unfortunately, we’ve also run into a number of issues with themes and plugins conflicting with each other, so I’d like to share a few tips we’ve developed to try to prevent issues in our own themes.

Core Media Players

MediaElement.js consists of a couple of different pieces: A shim that aims to smooth some of the inconsistencies between browsers in the HTML5 audio and video APIs, as well as a default media player interface. It’s possible to use the shim without the player, but as far as themes and plugins are concerned, WordPress includes both.

In order to create a player in WordPress, one of the native shortcodes needs to be used: [audio], [video], or [playlist].

Custom styling on the custom MediaElement.js tracklist in AudioTheme's Promenade theme.
Custom styling on the tracklist in AudioTheme’s Promenade WordPress theme.

Many theme authors like to provide custom styles for the players generated by those shortcodes to create an extra polished feel. However, most themes and plugins don’t properly account for other players that might appear on the page, which can wreak havoc on their appearance and function.

The problem stems from the fact that it’s plain difficult to style the MediaElement player in the first place. Making things worse, styles are coming from all over the place. To start, core has a couple of style sheets that need to be accounted for:

  • wp-includes/js/mediaelement/mediaelementplayer.min.css
  • wp-includes/js/mediaelement/wp-mediaelement.css

When a theme then provides its own custom styles and a plugin comes along with its own player, it has no idea what to expect.

Some themes completely remove the core styles, some try to override the core initialization routine, and some use !important rules. All of these approaches are less than ideal and bound to fail.


  • Themes want to style the default media players
  • Themes want to provide custom media players
  • Plugins want to provide custom media players
  • Plugins want to provide custom styles for the default media players

In each scenario, developers should be able to reasonably expect that the core styles are available and that they should only have to do the minimum amount of work necessary to override the core styles.

In order for those assumptions to be valid:

  • Custom styles should be isolated to the elements being targeted
  • The .mejs-container selector should be considered global and should never be used as the root selector

Isolating MediaElement.js Styles

Properly targeting the various default players requires at least three selectors:

.wp-playlist.mejs-container {}

And as you target deeper elements, it becomes necessary to write selectors that look like this:

.wp-audio-shortcode.mejs-container .mejs-controls .mejs-time-rail .mejs-time-float-corner,
.wp-video-shortcode.mejs-container .mejs-controls .mejs-time-rail .mejs-time-float-corner,
.wp-playlist.mejs-container .mejs-controls .mejs-time-rail .mejs-time-float-corner {}

Nesting in a preprocessor like LESS or Sass eases the pain of writing these sorts of selector chains over and over, but the compiled output quickly becomes bloated, sometimes adding extra kilobytes to the page weight. That’s not good.

It would be nice to have a common HTML class in the core-generated media elements for styling purposes. Fortunately, MediaElement.js provides its own plugin API, which we can use to write a simple feature that will insert a common class.

* Add an HTML class to MediaElement.js container elements to aid styling.
* Extends the core _wpmejsSettings object to add a new feature via the
* MediaElement.js plugin API.
function example_mejs_add_container_class() {
if ( ! wp_script_is( 'mediaelement', 'done' ) ) {
(function() {
var settings = window._wpmejsSettings || {};
settings.features = settings.features || mejs.MepDefaults.features;
settings.features.push( 'exampleclass' );
MediaElementPlayer.prototype.buildexampleclass = function( player ) {
player.container.addClass( 'example-mejs-container' );
add_action( 'wp_print_footer_scripts', 'example_mejs_add_container_class' );

This snippet pushes the custom MediaElement.js “exampleclass” plugin to WordPress’ settings object, which is used when initializing players created by the native shortcodes. Instead of requiring three separate selectors like earlier, this approach simplifies our root selector:

.example-mejs-container.mejs-container {}

The common class serves a few purposes:

  • Isolates styles to prevent conflicts with custom media players in the theme or plugins
  • Gives the selector a higher specificity to override default styles
  • Simplifies the CSS to make it easer to read and write
  • Reduces the amount of code, improving performance

It’s not a perfect solution, but in the absence of anything better, it’s helped us prevent issues common in other themes.

Things to Avoid

Overly specific selectors and using !important in rules create similar problems, however conflicts from both can be mitigated by never targeting global selectors like .mejs-container without a context-limiting class. Remember, prefix everything!

Never initialize players using something like this: $( 'audio, video' ).mediaelementplayer();. Use a unique selector if you need to initialize a custom media element.

Custom Players

Custom players are typically initialized separately and should implement their own prefixed HTML class to properly isolate styles in order to prevent conflicts with the default players.

Custom audio player in AudioTheme's Encore theme.
Custom audio player in AudioTheme’s Encore WordPress theme.

It’s also possible to use MediaElement.js as a compatibility shim for interfacing with the HTML5 audio and video API, while not relying on the built-in player UI or functionality. Doing so makes it easier to control the markup and reduce the selectors, but does require wiring all the controls yourself. As an example, this is the approach we took by creating a Backbone-powered audio player in our Huesos WordPress theme.


  1. ive done it just by enqueue the custom css, and ive tried with 20+ themes, and it has no issues what so ever.

  2. Would I place this php into the functions file or would it be best to add it into a plugin? Currently I just used the three css selectors first mentioned as I am currently only wanting to change the #222 background to a color used mostly in the theme.

    Thanks for this article and any help.

  3. I’m trying to add some styling to the default audio playlists in wordpress and was wondering if you could clarify what you are referring to with the ‘the other script enqueuing functions’.

    When I do an inspect element on the audio playlist I can see that classes that I need to target are .wp-playlist-light and .wp-playlist and these seem to sit outside the .example-mejs-container.mejs-container {}

    Any tips would be greatly appreciated.

    1. Themes usually enqueue the main style sheet and any other scripts needed to make it work in the functions.php like the default themes. So we just drop the snippet from the article alongside those functions, but you can put it anywhere in functions.php.

      Most plugins don’t integrate with the core playlists, so you’re usually pretty safe using the .wp-playlist selectors in your theme, especially if you’re not planning on distributing the theme or using it with any plugins.

  4. “It’s also possible to use MediaElement.js as a compatibility shim for interfacing with the HTML5 audio and video API, while not relying on the built-in player UI or functionality. ”

    How would you do that? How can I use the shortcode without the player classes or acces the media library with JS? I want to use the audio web api.
    Thank you!

Leave a Reply

Your email address will not be published. Required fields are marked *