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

Join 10,000 users and receive our Website Launch Checklist, discounts, freebies, news & updates straight into your inbox.

24 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. 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

  10. daniele says:

    Hallo, thanks a lot for the tutorial !! Do you know how may i insert a playlist audio that works like different players in sequence, instead of this compact version? ? ( Estetically like if i wanted to add one by one the files , with the thumnails on the left )

    1. Vassilis Mastorostergios says:

      Heya, glad you liked the tutorial. I’m not sure if I got the question right but you can add multiple tracks to the default WordPress audio player and it will look like a playlist. Alternatively you could look at our free audio player plugin AudioIgniter: https://www.cssigniter.com/plugins/audioigniter

  11. Kennedy says:

    This was very helpful thanks for sharing

  12. Godspower says:

    Nice tutorial admin great writeup. Thanks

  13. Tom says:

    Thank you very much! Do you know how to change the color of the time that appears on hover on the audio track progress bar? Right now it defaults to black.

    1. Vassilis Mastorostergios says:

      Hi, can you help me narrow down which element you’re talking about, the progress bar itself or the actual time digits (e.g. 00:34)? And on the main progress or in the track listing underneath?

  14. Annie says:

    Hello! I’ve used this awesome tutorial to style my theme’s players and everything was working great until I updated to WordPress v5. All audio / playlists added using the “Classic Block” are still styled correctly, but any audio added using the “Audio Block” is using the browser’s default audio player. Any ideas on how to extend the styling to work with the new audio block? Thanks!

    1. Vassilis Mastorostergios says:

      Heya, unfortunately I’m not aware of any good ways to style the default HTML5 audio player, this seems not to be possible right now so unless we want to use an external JS plugin (like mediaelementplayer) to override it we’re struck with it I’m afraid.

  15. Jim says:

    Hi. This might sound like a really dumb question, but my audio player in wordpress doesn’t look like your first (black) example at all.

    And strangely enough, it looks different in every browser — Edge, Firefox, and Chrome.

    So it seems to me like for some reason the wordpress audio player is not loading, and it’s just passing it to the browser to load their own built-in audio players.

    How do I correct this, and get back to the wordpress audio player?

    Thanks!

    1. Vassilis Mastorostergios says:

      Heya, unfortunately since WP 5.0 WordPress has decided to use the native browser audio player for single audio files. I’m not aware of any good ways to style the default HTML5 audio player, this seems not to be possible right now so unless we want to use an external JS plugin (like mediaelementplayer) to override it we’re struck with it I’m afraid.

Leave a Reply

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