This article was originally posted on my personal blog
Audio elements can be tricky to style. There is no straightforward way to style them, as applying CSS styles on audio
directly does not work.
In this tutorial, we'll learn 2 ways of styling audios. We'll check how we can style audio elements with their pseudo-element selectors, then we'll see how we can style them completely from scratch.
This tutorial uses CodePens to show examples on the go. You can check out the full collection, as well.
The audio used in this tutorial is free audio from ZapSplat.
Using Pseudo-Element Selectors
Audio elements, by default, are not visible. You need to add the controls
attribute for their controls to be visible.
This is how a basic audio player looks like:
Audio elements have the following pseudo-element selectors in CSS:
audio::-webkit-media-controls-panel
audio::-webkit-media-controls-mute-button
audio::-webkit-media-controls-play-button
audio::-webkit-media-controls-timeline-container
audio::-webkit-media-controls-current-time-display
audio::-webkit-media-controls-time-remaining-display
audio::-webkit-media-controls-timeline
audio::-webkit-media-controls-volume-slider-container
audio::-webkit-media-controls-volume-slider
audio::-webkit-media-controls-seek-back-button
audio::-webkit-media-controls-seek-forward-button
audio::-webkit-media-controls-fullscreen-button
audio::-webkit-media-controls-rewind-button
audio::-webkit-media-controls-return-to-realtime-button
audio::-webkit-media-controls-toggle-closed-captions-button
Using these selectors, you can give basic styling to audio elements. However, these only work on select browsers like Chrome.
We'll see a few examples of how we can use some of these selectors to style the audio element.
All the examples below will only work on Chrome. So, if you want to see how the audio element's style changes, please use Chrome.
Styling the Control Panel
To style the control panel, which is the container of all the audio's controls, you can use the selector audio::-webkit-media-controls-panel
. In the example below, we use the selector to change the background color.
Styling the Mute Button
To style the mute button, you can use the selector audio::-webkit-media-controls-mute-button
. In the example below, we change the background color of the mute button as well as add a border-radius.
Styling the Play Button
To style the play button, you can use the selector audio::-webkit-media-controls-play-button
. In the example below, we change the background color of the play button as well as add a border-radius.
Style the Current Time
To style the current time you can use the selector audio::-webkit-media-controls-current-time-display
. In the example below, we change the color of the text.
Style the Remaining Time
To style the remaining time you can use the selector audio::-webkit-media-controls-time-remaining-display
. In the example below, we change the color of the text.
Style the Timeline
To style the timeline you can use the selector audio::-webkit-media-controls-timeline
. In the example below, we add a background color and a border radius.
Styling the Volume Slider
To style the volume slider, which on Chrome appears after hovering the mute button, you can use the selector audio::-webkit-media-controls-volume-slider
. In the example below, we add a background color, a border radius, and some padding.
Styling a Custom Audio Player
In this section, we'll create our own custom audio player. We'll a nice looking player that uses different elements to achieve a good style. Then with the help of Javascript bind the audio element's functionalities to these elements.
All the icons used in this section are from Heroicons.
You can see the full demo on CodePen at the end of the section.
Create the Track Image
Usually, audio players have an image of the track playing. It gives a nice style to the audio player. We'll just an icon from Heroicons to simulate that.
We'll start by adding in the HTML the container .audio-player
. Inside that container, we'll add the track "image" element.
<div class="audio-player">
<div class="icon-container">
<svg xmlns="http://www.w3.org/2000/svg" class="audio-icon" viewBox="0 0 20 20" fill="currentColor">
<path d="M18 3a1 1 0 00-1.196-.98l-10 2A1 1 0 006 5v9.114A4.369 4.369 0 005 14c-1.657 0-3 .895-3 2s1.343 2 3 2 3-.895 3-2V7.82l8-1.6v5.894A4.37 4.37 0 0015 12c-1.657 0-3 .895-3 2s1.343 2 3 2 3-.895 3-2V3z" />
</svg>
<audio src="https://www.zapsplat.com/wp-content/uploads/2015/sound-effects-61905/zapsplat_multimedia_alert_chime_short_musical_notification_cute_child_like_001_64918.mp3?_=1"></audio>
</div>
</div>
Then, we'll add some CSS to style these elements.
.audio-player {
width: 15rem;
height: 15rem;
}
.icon-container {
width: 100%;
height: 100%;
background-color: #DE5E97;
color: #fff;
display: flex;
justify-content: center;
align-items: center;
}
.audio-icon {
width: 90%;
height: 90%;
}
This will create the following audio track image.
This has nothing to do with the actual functionality of the audio. It's just to make the visual nice.
Add the Play Button
Next, we'll add the play button. There are 3 phases of adding the play button: adding the HTML elements, adding the CSS styling, then implementing the Javascript functionality.
Add the HTML elements
Add the following inside the .audio-player
element:
<div class="controls">
<button class="player-button">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="#3D3132">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM9.555 7.168A1 1 0 008 8v4a1 1 0 001.555.832l3-2a1 1 0 000-1.664l-3-2z" clip-rule="evenodd" />
</svg>
</button>
</div>
This will add a container .controls
element, then inside it, we're adding a button that has a play icon inside.
Add the CSS styles
Next, we'll add the CSS styles for the .controls
element and the button.
First, add the following CSS Variable inside .audio-player
:
.audio-player {
--player-button-width: 3em;
...
}
Then, add the following CSS to style the .controls
and .player-button
elements:
.controls {
display: flex;
flex-direction: row;
align-items: center;
width: 100%;
margin-top: 10px;
}
.player-button {
background-color: transparent;
border: 0;
width: var(--player-button-width);
height: var(--player-button-width);
cursor: pointer;
padding: 0;
}
This will style the .controls
element to be a flexbox element. This will allow us to align the controls (which we will add more later) inside nicely.
The player button just has a transparent background and no border, as we just want to show the icon inside.
This will produce the following UI:
However, clicking the button now does nothing. We need to use Javascript to bind the functionalities to the audio.
Bind the Functionality with Javascript
In Javascript, we'll first define some variables:
const playerButton = document.querySelector('.player-button'),
audio = document.querySelector('audio'),
playIcon = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="#3D3132">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM9.555 7.168A1 1 0 008 8v4a1 1 0 001.555.832l3-2a1 1 0 000-1.664l-3-2z" clip-rule="evenodd" />
</svg>
`,
pauseIcon = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="#3D3132">
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zM7 8a1 1 0 012 0v4a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v4a1 1 0 102 0V8a1 1 0 00-1-1z" clip-rule="evenodd" />
</svg>
`;
We've defined playerButton
which is the player button element, audio
which is the audio this player is for, playIcon
and pauseIcon
which we will use to toggle the icon of the button.
Then, we'll create the function that should be triggered when the button is clicked:
function toggleAudio () {
if (audio.paused) {
audio.play();
playerButton.innerHTML = pauseIcon;
} else {
audio.pause();
playerButton.innerHTML = playIcon;
}
}
This function checks whether the audio is paused or playing, then it either plays or pauses it. It also changes the icon of playerButton
.
Next, add the function as an event listener to the click event of playerButton
:
playerButton.addEventListener('click', toggleAudio);
We also need to update the icon of playerButton
when the audio ends. To do that, we can use the audio element's event ended and in the listener change the icon back to the play icon:
function audioEnded () {
playerButton.innerHTML = playIcon;
}
audio.onended = audioEnded;
You can try to play the audio by clicking on the play button, and the audio will work!
Add the Timeline Track
Next, we need to add the timeline track, which will allow us to see the progress of the audio, as well as seek through the audio.
To implement the audio timeline track, the easiest approach is to use a range input. Using the range input, we'll first style it with CSS, then bind the functionalities in Javascript.
Add the HTML elements
Inside .controls
add the following input range:
<input type="range" class="timeline" max="100" value="0">
Add the CSS styles
To style a range input, there are two elements to take into account: the thumb, which allows us to change the value of the input, and the track that the thumb resides on.
To style the thumb, the following cross-browser selectors are used:
::-webkit-slider-thumb
::-moz-range-thumb
::-ms-thumb
and the following cross-browser selectors are used to style the track:
::-webkit-slider-runnable-track
::-moz-range-track
::-ms-track
For the simplicity of this tutorial and to avoid repetition, we'll just show the code for -webkit
selectors. You can find the full, cross-browser code in the demo CodePen.
We'll first style the input range itself:
.timeline {
-webkit-appearance: none;
width: calc(100% - var(--player-button-width));
height: .5em;
background-color: #e5e5e5;
border-radius: 5px;
background-size: 0% 100%;
background-image: linear-gradient(#DE5E97, #DE5E97);
background-repeat: no-repeat;
}
Using -webkit-appearance: none;
is necessary to be able to apply the styling.
Using linear-gradient(#DE5E97, #DE5E97);
for background-image
allows us to easily add the progress track of a different color based on the current progress of the audio.
To change the size of the background image, which means the position of the current progress in the audio, we use background-size: 0% 100%;
. The first value is the width. It will be the value we'll update through Javascript to show the progress of the audio.
Next, we'll add the styling of the thumb:
.timeline::-webkit-slider-thumb {
-webkit-appearance: none;
width: 1em;
height: 1em;
border-radius: 50%;
cursor: pointer;
opacity: 0;
transition: all .1s;
background-color: #a94672;
}
.timeline::-webkit-slider-thumb:hover {
background-color: #943f65;
}
.timeline:hover::-webkit-slider-thumb {
opacity: 1;
}
We're just adding some styling to the thumb, and we're hiding it and showing it on hover.
Then, we'll basically hide the track as we'll just use the styling in .timeline
to show the track and the progress of the audio:
.timeline::-webkit-slider-runnable-track {
-webkit-appearance: none;
box-shadow: none;
border: none;
background: transparent;
}
The track will look like this:
Bind the Javascript Functionality
We just need to add the Javascript functionality now. The track should show the progress of the audio, and it should allow changing the progress of the audio by moving the thumb.
First, we'll define the timeline
variable for the element:
const timeline = document.querySelector('.timeline');
Then, we'll add the function that will listen to the timeupdate event. The timeupdate
event is triggered whenever the audio's time changes. So, it's triggered continuously as the audio is playing, and it's triggered when the audio currentTime attribute is updated.
The function will calculate the progress of the audio in percentage using audio's currentTime attribute and audio's duration attribute. Then, will set the backgroundSize
CSS property of the timeline
element based on the calculation:
function changeTimelinePosition () {
const percentagePosition = (100*audio.currentTime) / audio.duration;
timeline.style.backgroundSize = `${percentagePosition}% 100%`;
timeline.value = percentagePosition;
}
audio.ontimeupdate = changeTimelinePosition;
Next, we need to add the function that will handle the change event of the input range button, then change the progress of the audio as well as the backgroundSize
CSS property:
function changeSeek () {
const time = (timeline.value * audio.duration) / 100;
audio.currentTime = time;
}
timeline.addEventListener('change', changeSeek);
You can now play the audio and see how the track shows the progress of the audio. You can also try changing the progress by moving the thumb.
Add the Sound Button
The last thing we'll do is add a sound button. This button will just toggle the sound of the audio, muting and unmuting it.
Add the HTML elements
Add the following HTML elements inside .controls
:
<button class="sound-button">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="#3D3132">
<path fill-rule="evenodd" d="M9.383 3.076A1 1 0 0110 4v12a1 1 0 01-1.707.707L4.586 13H2a1 1 0 01-1-1V8a1 1 0 011-1h2.586l3.707-3.707a1 1 0 011.09-.217zM14.657 2.929a1 1 0 011.414 0A9.972 9.972 0 0119 10a9.972 9.972 0 01-2.929 7.071 1 1 0 01-1.414-1.414A7.971 7.971 0 0017 10c0-2.21-.894-4.208-2.343-5.657a1 1 0 010-1.414zm-2.829 2.828a1 1 0 011.415 0A5.983 5.983 0 0115 10a5.984 5.984 0 01-1.757 4.243 1 1 0 01-1.415-1.415A3.984 3.984 0 0013 10a3.983 3.983 0 00-1.172-2.828 1 1 0 010-1.415z" clip-rule="evenodd" />
</svg>
</button>
This is just a button with an icon.
Add the CSS Styles
Next, we need to add the CSS styles.
First, add 2 new variables inside .audio-player
:
.audio-player {
--player-button-width: 3em;
--sound-button-width: 2em;
--space: .5em;
...
}
The --sound-button-width
will be used for the width of the sound button, and --space
will be used to add space between the track and the button.
Next, change the width
of the .timeline
element and add a margin-right
property as well:
.timeline {
width: calc(100% - (var(--player-button-width) + var(--sound-button-width) + var(--space)));
margin-right: var(--space);
...
}
Finally, add the CSS styling for the sound button:
.sound-button {
background-color: transparent;
border: 0;
width: var(--sound-button-width);
height: var(--sound-button-width);
cursor: pointer;
padding: 0;
}
We'll now have a sound button next to the track:
Bind the Javascript Functionality
Lastly, we just need to bind the functionality of the sound button to the audio element. Clicking the sound button should mute or unmute the sound of the audio.
First, add the following new variable definitions:
const soundButton = document.querySelector('.sound-button'),
soundIcon = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="#3D3132">
<path fill-rule="evenodd" d="M9.383 3.076A1 1 0 0110 4v12a1 1 0 01-1.707.707L4.586 13H2a1 1 0 01-1-1V8a1 1 0 011-1h2.586l3.707-3.707a1 1 0 011.09-.217zM14.657 2.929a1 1 0 011.414 0A9.972 9.972 0 0119 10a9.972 9.972 0 01-2.929 7.071 1 1 0 01-1.414-1.414A7.971 7.971 0 0017 10c0-2.21-.894-4.208-2.343-5.657a1 1 0 010-1.414zm-2.829 2.828a1 1 0 011.415 0A5.983 5.983 0 0115 10a5.984 5.984 0 01-1.757 4.243 1 1 0 01-1.415-1.415A3.984 3.984 0 0013 10a3.983 3.983 0 00-1.172-2.828 1 1 0 010-1.415z" clip-rule="evenodd" />
</svg>`,
muteIcon = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="#3D3132">
<path fill-rule="evenodd" d="M9.383 3.076A1 1 0 0110 4v12a1 1 0 01-1.707.707L4.586 13H2a1 1 0 01-1-1V8a1 1 0 011-1h2.586l3.707-3.707a1 1 0 011.09-.217zM12.293 7.293a1 1 0 011.414 0L15 8.586l1.293-1.293a1 1 0 111.414 1.414L16.414 10l1.293 1.293a1 1 0 01-1.414 1.414L15 11.414l-1.293 1.293a1 1 0 01-1.414-1.414L13.586 10l-1.293-1.293a1 1 0 010-1.414z" clip-rule="evenodd" />
</svg>`;
This will add a soundButton
variable, which will be the sound button element. It will also create two variables soundIcon
and muteIcon
to be used to change the icon of the button based on whether the audio is muted or not.
Next, add the function which will listen to the click event on the sound button:
function toggleSound () {
audio.muted = !audio.muted;
soundButton.innerHTML = audio.muted ? muteIcon : soundIcon;
}
soundButton.addEventListener('click', toggleSound);
Final Demo
This will be the final result of creating the custom audio player:
The player can play, pause, seek, mute and unmute the audio, all while looking great.
Conclusion
Using the pseudo-element selectors, you can do simple design changes to the audio element.
For more complex design changes, it's best to implement a custom audio player, then bind it with Javascript to the audio element to provide the necessary functionalities.
Top comments (2)
Final demo not available.
Thank you for pointing this out! I imported the post using dev’s RSS tool so I didn’t notice that the codepen demos weren’t imported properly. I’ve fixed it now.