A CSS style guide for the default WordPress media player

As theme authors we’re always striving to give our themes a unique design and marry them to the WordPress ecosystem by trying to provide a unified user experience, to the best possible extent. As WordPress comes with its own widgets, shortcodes, and other components, it’s important for any theme to take them into consideration and style them accordingly to its look and feel.

Most of WordPress’s native components (i.e. widgets or shortcodes) do not come with their own predefined styles (i.e. the Gallery shortcode), but other ones (especially JavaScript based ones) do so (i.e. the Audio player). In this post we’ll see how we can visually customize the default WordPress media player and make it feel at home on our theme.

The WordPress media player

The WordPress media player is a wrapper on top of MediaElement.js, a powerful, customizable audio and video player that’s powered by HTML5 features. By default, in WordPress, it looks like this:

What we’ll achieve in this tutorial is to generate a set of predefined selectors for the player (along with WordPress’s audio playlist) and better match it on our theme. Here’s what we’ll be achieving to start with:

At first we’ll convert the player into light-skinned themed, suitable for more themes which are already on a dark-on-white mode.

Scripts and styles

First, let’s create the file my-theme-player.css to host our media player custom styles and enqueue it appropriately (in our theme’s functions.php file):

<?php
add_action( 'wp_footer', 'ci_theme_footer_scripts' );

function ci_theme_footer_scripts() {
	if ( wp_style_is( 'wp-mediaelement', 'enqueued' ) ) {
		wp_enqueue_style( 'my-theme-player', get_template_directory_uri() . '/css/my-theme-player.css', array(
			'wp-mediaelement',
		), '1.0' );
	}
}

The above snippet will ensure that our custom stylesheet will only load when MediaElement’s styles load (i.e. when there’s a media player on the page) and always after them (we need our custom styles to load after the default ones due to the cascade).

And because we’re good citizens, we need a way to isolate our theme’s styles from the rest of the WordPress ecosystem, i.e. assigning a unique class on the player so that we can nest every selector that we’ll use. This is always a good idea, as we’ve discussed previously in Writing effective CSS for WordPress, as it will limit the scope of the new styles just for our theme without possibly breaking other plugins relying on the WordPress media player.

<?php
/**
 * 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.
 */
add_action( 'wp_print_footer_scripts', 'mytheme_mejs_add_container_class' );

function mytheme_mejs_add_container_class() {
	if ( ! wp_script_is( 'mediaelement', 'done' ) ) {
		return;
	}
	?>
	<script>
	(function() {
		var settings = window._wpmejsSettings || {};
		settings.features = settings.features || mejs.MepDefaults.features;
		settings.features.push( 'exampleclass' );
		MediaElementPlayer.prototype.buildexampleclass = function( player ) {
			player.container.addClass( 'mytheme-mejs-container' );
		};
	})();
	</script>
	<?php
}
functions.php

The code was kindly borrowed by cedaro. It will add the class .mytheme-mejs-container in the wrapper element of the media player. Feel free to change the prefix (mytheme) to anything you like (but make sure you replace it in the styles as well).

Styling the player

For this tutorial we’ll be changing the player’s theme to a light-skinned one, which is more suitable for most dark-on-white designs, plus adding a touch of character with an accent color.

/* Player background */
.mytheme-mejs-container.mejs-container,
.mytheme-mejs-container .mejs-controls,
.mytheme-mejs-container .mejs-embed,
.mytheme-mejs-container .mejs-embed body {
  background-color: #efefef;
}

/* Player controls */
.mytheme-mejs-container .mejs-button > button {
  background-image: url(images/mejs-controls-dark.svg);
}

.mytheme-mejs-container .mejs-time {
  color: #888888;
}

/* Progress and audio bars */

/* Progress and audio bar background */
.mytheme-mejs-container .mejs-controls .mejs-horizontal-volume-slider .mejs-horizontal-volume-total,
.mytheme-mejs-container .mejs-controls .mejs-time-rail .mejs-time-total {
  background-color: #fff;
}

/* Track progress bar background (amount of track fully loaded)
  We prefer to style these with the main accent color of our theme */
.mytheme-mejs-container .mejs-controls .mejs-time-rail .mejs-time-loaded {
  background-color: rgba(219, 78, 136, 0.075);
}

/* Current track progress and active audio volume level bar */
.mytheme-mejs-container .mejs-controls .mejs-horizontal-volume-slider .mejs-horizontal-volume-current,
.mytheme-mejs-container .mejs-controls .mejs-time-rail .mejs-time-current {
  background: #db4e88;
}

/* Reduce height of the progress and audio bars */
.mytheme-mejs-container .mejs-time-buffering,
.mytheme-mejs-container .mejs-time-current,
.mytheme-mejs-container .mejs-time-float,
.mytheme-mejs-container .mejs-time-float-corner,
.mytheme-mejs-container .mejs-time-float-current,
.mytheme-mejs-container .mejs-time-hovered,
.mytheme-mejs-container .mejs-time-loaded,
.mytheme-mejs-container .mejs-time-marker,
.mytheme-mejs-container .mejs-time-total,
.mytheme-mejs-container .mejs-horizontal-volume-total,
.mytheme-mejs-container .mejs-time-handle-content {
  height: 3px;
}

.mytheme-mejs-container .mejs-time-handle-content {
  top: -6px;
}

.mytheme-mejs-container .mejs-time-total {
  margin-top: 8px;
}

.mytheme-mejs-container .mejs-horizontal-volume-total {
  top: 19px;
}
my-theme-player.css

The styles above will make the player look like this:

Pretty nifty. We’ve changed the background color, text color, the progress / audio bars pigment to the theme’s accent color, and the buttons. For the buttons, you’ll need to create a new SVG icon-set with darker icons, as the default ones are totally white. You can download the one I’ve prepared here. Add it to an /images directory and you’ll be good to go.

As an additional note, you’ve probably noticed that there’s a lot of nesting. Unfortunately that’s how it has to be, since MediaElement.js is styled that way already and we also need our own container class on top. Using a pre-processor like Sass or LESS would help a lot with readability if you’re comfortable with it.

Styling for media playlists

Every time you create an audio playlist in a post or page and you select more than one audio file, WordPress will create the player in a playlist mode for you. By default it looks like this:

WordPress wraps playlists with a different container (its own, unrelated to MediaElement.js), and the following styles will help give a better visual:

.wp-playlist-light {
  box-shadow: 3px 3px 0 #e2e2e2;
}

/* Captions - Track titles / subtitles, time */
.wp-playlist-light .wp-playlist-caption,
.wp-playlist-light .wp-playlist-item-length {
  color: #787878;
}

/* Captions - Current track */
.wp-playlist-light .wp-playlist-current-item .wp-playlist-item-title {
  font-size: 16px;
}

.wp-playlist-light .wp-playlist-item-album {
  font-style: normal;
}

.wp-playlist-light .wp-playlist-item-artist {
  text-transform: none;
  opacity: .8;
}

/* Playlist items */
.wp-playlist-light .wp-playlist-item {
  padding: 10px 0;
  border-bottom-color: #efefef;
}

.wp-playlist-light .wp-playlist-item:last-child {
  padding-bottom: 0;
}

.wp-playlist-light .wp-playlist-playing {
  font-weight: normal;
  border-bottom-color: #db4e88;
}

.wp-playlist-light .wp-playlist-item-length {
  top: 10px;
}
my-theme-player.css

And here’s how our playlist looks now:

Much better! Be aware that we’re not changing any kind of font-family as we’d generally like them to be the same as our theme, but of course you’re free to do so!

Here’s the full CSS code supporting both a standalone player and the audio playlist:

/* Media Element Player styles */

/* Player background */
.mytheme-mejs-container.mejs-container,
.mytheme-mejs-container .mejs-controls,
.mytheme-mejs-container .mejs-embed,
.mytheme-mejs-container .mejs-embed body {
  background-color: #efefef;
}

/* Player controls */
.mytheme-mejs-container .mejs-button > button {
  background-image: url(images/mejs-controls-dark.svg);
}

.mytheme-mejs-container .mejs-time {
  color: #888888;
}

/* Progress and audio bars */

/* Progress and audio bar background */
.mytheme-mejs-container .mejs-controls .mejs-horizontal-volume-slider .mejs-horizontal-volume-total,
.mytheme-mejs-container .mejs-controls .mejs-time-rail .mejs-time-total {
  background-color: #fff;
}

/* Track progress bar background (amount of track fully loaded)
  We prefer to style these with the main accent color of our theme */
.mytheme-mejs-container .mejs-controls .mejs-time-rail .mejs-time-loaded {
  background-color: rgba(219, 78, 136, 0.075);
}

/* Current track progress and active audio volume level bar */
.mytheme-mejs-container .mejs-controls .mejs-horizontal-volume-slider .mejs-horizontal-volume-current,
.mytheme-mejs-container .mejs-controls .mejs-time-rail .mejs-time-current {
  background: #db4e88;
}

/* Reduce height of the progress and audio bars */
.mytheme-mejs-container .mejs-time-buffering,
.mytheme-mejs-container .mejs-time-current,
.mytheme-mejs-container .mejs-time-float,
.mytheme-mejs-container .mejs-time-float-corner,
.mytheme-mejs-container .mejs-time-float-current,
.mytheme-mejs-container .mejs-time-hovered,
.mytheme-mejs-container .mejs-time-loaded,
.mytheme-mejs-container .mejs-time-marker,
.mytheme-mejs-container .mejs-time-total,
.mytheme-mejs-container .mejs-horizontal-volume-total,
.mytheme-mejs-container .mejs-time-handle-content {
  height: 3px;
}

.mytheme-mejs-container .mejs-time-handle-content {
  top: -6px;
}

.mytheme-mejs-container .mejs-time-total {
  margin-top: 8px;
}

.mytheme-mejs-container .mejs-horizontal-volume-total {
  top: 19px;
}

/* WordPress audio playlist styles */

.wp-playlist-light {
  box-shadow: 3px 3px 0 #e2e2e2;
}

/* Captions - Track titles / subtitles, time */
.wp-playlist-light .wp-playlist-caption,
.wp-playlist-light .wp-playlist-item-length {
  color: #787878;
}

/* Captions - Current track */
.wp-playlist-light .wp-playlist-current-item .wp-playlist-item-title {
  font-size: 16px;
}

.wp-playlist-light .wp-playlist-item-album {
  font-style: normal;
}

.wp-playlist-light .wp-playlist-item-artist {
  text-transform: none;
  opacity: .8;
}

/* Playlist items */
.wp-playlist-light .wp-playlist-item {
  padding: 10px 0;
  border-bottom-color: #efefef;
}

.wp-playlist-light .wp-playlist-item:last-child {
  padding-bottom: 0;
}

.wp-playlist-light .wp-playlist-playing {
  font-weight: normal;
  border-bottom-color: #db4e88;
}

.wp-playlist-light .wp-playlist-item-length {
  top: 10px;
}
my-theme-player.css

Restyling everything

In case you don’t want to override the default styles (as is the approach of this tutorial) you can always completely dequeue WordPress’s default styles and re-write everything as you see fit:

<?php
add_action( 'wp_enqueue_scripts', 'mytheme_deregister_media_element_styles' );

function mytheme_deregister_media_element_styles() {
	wp_deregister_style( 'mediaelement' );
	wp_deregister_style( 'wp-mediaelement' );
}
Dequeueing WordPress media element styles

Doing this will remove the default stylesheet WordPress loads with media element (but the player will still work) and you’d have to completely rewrite them as you see fit, layout and all. An amazing starting point if you’d like to go through this endeavour is Justin Tadlock’s Media Element base stylesheet, which provides the basic structure and layout boilerplate.

And that’s it! Let us know in the comments how you’ve customized the media player in your themes and what’s your approach, we’d love to exchange ideas!

Subscribe to our newsletter.

Get fresh WordPress content straight into your inbox. We hate spam more than you do.

2 comments

  1. michael says:

    Do you know the easiest and best way to set the WordPress built in audio player to set the default volume to 100% and not 80%? I know very little about website editing!

    Thanks!

    1. Vassilis Mastorostergios says:

      Heya, unfortunately I don’t think it’s possible. The only workaround I found is here and requires some custom code: https://github.com/mediaelement/mediaelement/issues/2449

Leave a Reply

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