I'm styling a native browser element, <audio>
, and to do so, it's necessary to work with native browser pseudo elements, such as:
.audio-message audio::-webkit-media-controls-current-time-display {
display: none;
}
Hiding this element makes the total audio duration element appear with a "/" in front.
I wish to hide this slash, as it makes no sense in the context where the remaining time is hidden, but I cannot use :first-letter
with native element selectors.
My question is, how can we apply something like:
.audio-message audio::-webkit-media-controls-current-time-display::first-letter {
display: none;
}
The example doesn't work, I tried :is()
, using :first-letter
instead of ::
, and also changing to hover and adding important to test if the issue was the selector.
I'd rather a CSS only solution, I know it could be done in JS in a way or other, but there are several audio elements and it would hurt performance.
audio::-webkit-media-controls-current-time-display {
display: none;
}
audio::-webkit-media-controls-current-time-display::first-letter {
/* Doesn't work */
display: none;
}
<audio controls="" src="https://www.w3schools.com/tags/horse.ogg"></audio>
Test the snippet using Chrome-based browser please.
The audio player is displayed differently in every browser. We could only address each of them with cumbersome coding, thus it's a better approach to create a custom audio player, like this for example:
// scripts.js
document.addEventListener('DOMContentLoaded', function() {
var audio = document.getElementById('audio');
var playPauseButton = document.getElementById('play-pause');
var playIcon = document.getElementById('play-icon');
var pauseIcon = document.getElementById('pause-icon');
var volumeControl = document.getElementById('volume');
var muteButton = document.getElementById('mute');
var speakerIcon = document.getElementById('speaker-icon');
var muteIcon = document.getElementById('mute-icon');
var speedOptions = document.querySelectorAll('.speed-option');
var timeline = document.getElementById('timeline');
var currentTimeDisplay = document.getElementById('current-time');
// Play/Pause functionality
playPauseButton.addEventListener('click', function() {
if (audio.paused) {
audio.play();
playIcon.style.display = 'none';
pauseIcon.style.display = 'block';
} else {
audio.pause();
playIcon.style.display = 'block';
pauseIcon.style.display = 'none';
}
});
// Revert to play icon when audio ends
audio.addEventListener('ended', function() {
playIcon.style.display = 'block';
pauseIcon.style.display = 'none';
});
// Volume control functionality
volumeControl.addEventListener('input', function() {
audio.volume = volumeControl.value;
if (audio.volume === 0) {
speakerIcon.style.display = 'none';
muteIcon.style.display = 'block';
} else {
speakerIcon.style.display = 'block';
muteIcon.style.display = 'none';
}
});
// Mute functionality
muteButton.addEventListener('click', function() {
if (audio.muted) {
audio.muted = false;
speakerIcon.style.display = 'block';
muteIcon.style.display = 'none';
} else {
audio.muted = true;
speakerIcon.style.display = 'none';
muteIcon.style.display = 'block';
}
});
// Playback speed control functionality
speedOptions.forEach(function(option) {
option.addEventListener('click', function() {
audio.playbackRate = this.dataset.speed;
});
});
// Update current time display and timeline
audio.addEventListener('timeupdate', function() {
var currentMinutes = Math.floor(audio.currentTime / 60);
var currentSeconds = Math.floor(audio.currentTime % 60);
if (currentSeconds < 10) {
currentSeconds = '0' + currentSeconds;
}
currentTimeDisplay.textContent = currentMinutes + ':' + currentSeconds;
// Update timeline
var value = (audio.currentTime / audio.duration) * 100;
timeline.value = value;
});
// Seek functionality for timeline
timeline.addEventListener('input', function() {
var time = (timeline.value / 100) * audio.duration;
audio.currentTime = time;
});
});
/* styles.css */
.audio-player {
display: flex;
align-items: center;
background: #f3f3f3;
border-radius: 100px;
padding: 5px;
gap: 10px;
width: 100%;
max-width: 290px;
margin: 0 auto;
}
button {
padding: 10px 10px;
background-color: transparent;
border: none;
border-bottom-right-radius: 20px;
border-top-right-radius: 20px;
font-size: 20px;
line-height: 10px;
z-index: 37;
position: relative;
cursor: pointer;
}
#play-pause {
border-radius: 50px;
}
#play-pause:hover {
background-color: #e0e0e0;
}
input[type="range"] {
width: 100px;
}
#timeline {
width: 80px;
}
.volume-control {
display: flex;
align-items: center;
position: relative;
}
.volume-control input[type="range"] {
position: relative;
top: -2px;
right: 50px;
width: 47px;
}
.wrap {
width: 0;
overflow: hidden;
position: absolute;
float: right;
}
.volume-control:hover .wrap {
overflow: visible;
}
#play-icon,
#pause-icon,
#speaker-icon,
#mute-icon {
width: 24px;
height: 24px;
}
.dropdown {
position: relative;
display: inline-block;
}
.dropdown-content {
display: none;
position: absolute;
background-color: #f9f9f9;
min-width: 50px;
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
z-index: 1;
}
.dropdown-content button {
color: black;
padding: 8px 12px;
text-decoration: none;
display: block;
background: none;
border: none;
width: 100%;
text-align: left;
font-size: 14px;
}
.dropdown-content button:hover {
background-color: #f1f1f1;
}
.dropdown:hover .dropdown-content {
display: block;
}
.wrap::before {
background-color: #e0e0e0;
content: "";
margin: 0 auto;
position: absolute;
right: -45px;
width: 100px;
height: 43px;
border-radius: 50px;
top: -17px;
border: 5px solid #f3f3f3;
}
input[type="range"] {
-webkit-appearance: none;
width: 100%;
height: 5px;
border-radius: 5px;
background: #595959;
outline: none;
opacity: 1;
transition: opacity .2s;
}
input[type="range"]:hover {
opacity: 1;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 11px;
height: 11px;
border-radius: 50%;
background: #000;
cursor: pointer;
box-shadow: 0 0 3px rgba(0, 0, 0, 0.1);
}
input[type="range"]::-moz-range-thumb {
width: 11px;
height: 11px;
border-radius: 50%;
background: #000;
cursor: pointer;
box-shadow: 0 0 3px rgba(0, 0, 0, 0.1);
border: 0;
}
input[type="range"]::-ms-thumb {
width: 11px;
height: 11px;
border-radius: 50%;
background: #000;
cursor: pointer;
box-shadow: 0 0 3px rgba(0, 0, 0, 0.1);
}
/* For IE */
input[type="range"]::-ms-track {
width: 100%;
height: 8px;
border-color: transparent;
color: transparent;
background: transparent;
}
input[type="range"]::-ms-fill-lower {
background: #f0f0f0;
border-radius: 10px;
}
input[type="range"]::-ms-fill-upper {
background: #f0f0f0;
border-radius: 10px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Custom Audio Player</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="audio-player">
<audio id="audio" src="https://www.w3schools.com/tags/horse.ogg"></audio>
<button id="play-pause">
<svg id="play-icon" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8 5v14l11-7L8 5z" fill="currentColor"/>
</svg>
<svg id="pause-icon" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" style="display: none;">
<path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z" fill="currentColor"/>
</svg>
</button>
<span id="current-time">0:00</span>
<input type="range" id="timeline" value="0" max="100">
<div class="volume-control">
<button id="mute">
<svg id="speaker-icon" width="24px" height="24px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16 9C16.5 9.5 17 10.5 17 12C17 13.5 16.5 14.5 16 15M19 6C20.5 7.5 21 10 21 12C21 14 20.5 16.5 19 18M13 3L7 8H5C3.89543 8 3 8.89543 3 10V14C3 15.1046 3.89543 16 5 16H7L13 21V3Z" stroke="#000000" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
<svg id="mute-icon" width="24px" height="24px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" style="display: none;">
<path d="M10.6 5L13 3V8M3 3L21 21M13 18V21L7 16H5C3.89543 16 3 15.1046 3 14V10C3 9.63571 3.09739 9.29417 3.26756 9" stroke="#000000" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</button>
<span class="wrap"><input type="range" id="volume" min="0" max="1" step="0.01" value="1"></span>
</div>
<div class="dropdown">
<button id="speed-menu">⋮</button>
<div class="dropdown-content">
<button class="speed-option" data-speed="0.5">0.5x</button>
<button class="speed-option" data-speed="1">1x</button>
<button class="speed-option" data-speed="1.5">1.5x</button>
<button class="speed-option" data-speed="2">2x</button>
</div>
</div>
</div>
<script src="scripts.js"></script>
</body>
</html>
EDIT:
Here's a pure CSS approach (workaround):
audio::-webkit-media-controls-current-time-display {
display: none;
}
.wrap {
position: relative;
}
/*essential part for the cover: */
.wrap::before {
content: '';
background: #f3f3f3;
width: 10px;
height: 15px;
position: absolute;
top: 20px;
left: 42px;
z-index: 1;
}
/*to hide the cover in Firefox: */
@-moz-document url-prefix() {
.wrap::before {
content: none;
}
}
<div class="wrap"><audio controls="" src="https://www.w3schools.com/tags/horse.ogg"></audio>
<div>
This covers the "/" with the matching background color of the background it stands on.