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!

14 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

  2. Naomi Janzen says:

    Hi, thanks for this! I want to hide my media player on selected blog posts (but still have its data readable to my podcasting plugin). My theme allows me to add CSS to each page so I am wondering if there is any CSS which can hide the media player.

    1. Vassilis Mastorostergios says:

      Hi, yes of course this should be easy!

      Let’s try adding the following rule:

      .mejs-container { display: none !important; }

      That should do the trick.

  3. Todd Jones says:

    Is it possible to use this tutorial for any theme that uses the default wordpress player? New to the process and I was wondering if these instructions would only work within the CI theme?

    1. Nik Vourvachis says:

      Hello Todd.

      Yes, this can be used in any WordPress theme, not just one of ours. Just keep in mind that if you see something wrong with the styling, it could be that the theme in use applies its own styles to the player which clash with your custom ones.

  4. mo says:

    hi, i want to make the sound audio as a small icon like this one for google translate

    1. Vassilis Mastorostergios says:

      Heya, you’d have to edit the SVG provided in this article with the app of your choice (i.e. Illustrator, Sketch, or some other vector design application) and replace it :)

  5. Mark says:

    Thanks for this guide Vassilis. I have a very long playlist that will be getting longer so I added:

    `.wp-playlist {
    max-height: 200px;
    overflow: auto;
    }`

  6. Mark says:

    I added the functions to Twenty Seventeen’s functions file and the css to its assets/css directory. All the styling is fine except that no play or volume icon displays on the player of the playlist.

    BTW, how do I change the image at the top left? I tried using display: none on the img tag and using a background image instead but failed with being unable to hide the music symbol but getting the background to show under a semi opaque layer of the symbol image.

    Thanks.

  7. Tekno Freek says:

    hi, is it possible that i can add custom title/caption in the default wordpress audio playlists? I am using the same audio file in multiple playlists – and I need the playlists to display different captions/titles.

    1. Vassilis Mastorostergios says:

      Hello, I’m afraid I don’t see WordPress allowing that, any time you change the title to a track it changes across all playlists.

  8. Kosiso says:

    This was very very helpful thank you

  9. Olamide says:

    Thats why i love this blog

Leave a Reply

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