aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/js/captions.js4
-rw-r--r--src/js/config/defaults.js3
-rw-r--r--src/js/controls.js50
-rw-r--r--src/js/fullscreen.js3
-rw-r--r--src/js/html5.js10
-rw-r--r--src/js/listeners.js33
-rw-r--r--src/js/plugins/ads.js5
-rw-r--r--src/js/plugins/previewThumbnails.js11
-rw-r--r--src/js/plugins/youtube.js35
-rw-r--r--src/js/plyr.js12
-rw-r--r--src/js/ui.js15
-rw-r--r--src/js/utils/animation.js16
-rw-r--r--src/js/utils/elements.js11
-rw-r--r--src/js/utils/i18n.js4
-rw-r--r--src/js/utils/loadSprite.js1
-rw-r--r--src/js/utils/style.js11
-rw-r--r--src/sass/components/control.scss5
-rw-r--r--src/sass/components/controls.scss1
-rw-r--r--src/sass/lib/mixins.scss3
-rw-r--r--src/sass/plugins/ads.scss2
-rw-r--r--src/sass/plugins/previewThumbnails.scss2
-rw-r--r--src/sass/settings/badges.scss2
-rw-r--r--src/sass/settings/colors.scss18
-rw-r--r--src/sass/settings/controls.scss2
-rw-r--r--src/sass/settings/menus.scss4
-rw-r--r--src/sass/settings/progress.scss4
-rw-r--r--src/sass/settings/sliders.scss2
-rw-r--r--src/sass/settings/tooltips.scss2
28 files changed, 148 insertions, 123 deletions
diff --git a/src/js/captions.js b/src/js/captions.js
index a8bb4d77..f7c24534 100644
--- a/src/js/captions.js
+++ b/src/js/captions.js
@@ -133,7 +133,7 @@ const captions = {
});
// Turn off native caption rendering to avoid double captions
- track.mode = 'hidden';
+ Object.assign(track, { mode: 'hidden' });
// Add event listener for cue changes
on.call(this, track, 'cuechange', () => captions.updateCues.call(this));
@@ -302,7 +302,7 @@ const captions = {
let track;
languages.every(language => {
- track = sorted.find(track => track.language === language);
+ track = sorted.find(t => t.language === language);
return !track; // Break iteration if there is a match
});
diff --git a/src/js/config/defaults.js b/src/js/config/defaults.js
index 808fa0ae..4bb0e8d5 100644
--- a/src/js/config/defaults.js
+++ b/src/js/config/defaults.js
@@ -128,6 +128,7 @@ const defaults = {
// 'fast-forward',
'progress',
'current-time',
+ // 'duration',
'mute',
'volume',
'captions',
@@ -195,7 +196,7 @@ const defaults = {
},
youtube: {
sdk: 'https://www.youtube.com/iframe_api',
- api: 'https://noembed.com/embed?url=https://www.youtube.com/watch?v={0}', // 'https://www.googleapis.com/youtube/v3/videos?id={0}&key={1}&fields=items(snippet(title),fileDetails)&part=snippet',
+ api: 'https://noembed.com/embed?url=https://www.youtube.com/watch?v={0}',
},
googleIMA: {
sdk: 'https://imasdk.googleapis.com/js/sdkloader/ima3.js',
diff --git a/src/js/controls.js b/src/js/controls.js
index d3e20a57..43a92140 100644
--- a/src/js/controls.js
+++ b/src/js/controls.js
@@ -11,7 +11,20 @@ import support from './support';
import { repaint, transitionEndEvent } from './utils/animation';
import { dedupe } from './utils/arrays';
import browser from './utils/browser';
-import { createElement, emptyElement, getAttributesFromSelector, getElement, getElements, hasClass, matches, removeElement, setAttributes, setFocus, toggleClass, toggleHidden } from './utils/elements';
+import {
+ createElement,
+ emptyElement,
+ getAttributesFromSelector,
+ getElement,
+ getElements,
+ hasClass,
+ matches,
+ removeElement,
+ setAttributes,
+ setFocus,
+ toggleClass,
+ toggleHidden,
+} from './utils/elements';
import { off, on } from './utils/events';
import i18n from './utils/i18n';
import is from './utils/is';
@@ -480,15 +493,15 @@ const controls = {
get() {
return menuItem.getAttribute('aria-checked') === 'true';
},
- set(checked) {
+ set(check) {
// Ensure exclusivity
- if (checked) {
+ if (check) {
Array.from(menuItem.parentNode.children)
.filter(node => matches(node, '[role="menuitemradio"]'))
.forEach(node => node.setAttribute('aria-checked', 'false'));
}
- menuItem.setAttribute('aria-checked', checked ? 'true' : 'false');
+ menuItem.setAttribute('aria-checked', check ? 'true' : 'false');
},
});
@@ -596,17 +609,17 @@ const controls = {
let value = 0;
const setProgress = (target, input) => {
- const value = is.number(input) ? input : 0;
+ const val = is.number(input) ? input : 0;
const progress = is.element(target) ? target : this.elements.display.buffer;
// Update value and label
if (is.element(progress)) {
- progress.value = value;
+ progress.value = val;
// Update text label inside
const label = progress.getElementsByTagName('span')[0];
if (is.element(label)) {
- label.childNodes[0].nodeValue = value;
+ label.childNodes[0].nodeValue = val;
}
}
};
@@ -688,14 +701,8 @@ const controls = {
return;
}
- // Calculate percentage
- let percent = 0;
- const clientRect = this.elements.progress.getBoundingClientRect();
const visible = `${this.config.classNames.tooltip}--visible`;
-
- const toggle = toggle => {
- toggleClass(this.elements.display.seekTooltip, visible, toggle);
- };
+ const toggle = show => toggleClass(this.elements.display.seekTooltip, visible, show);
// Hide on touch
if (this.touch) {
@@ -704,6 +711,9 @@ const controls = {
}
// Determine percentage, if already visible
+ let percent = 0;
+ const clientRect = this.elements.progress.getBoundingClientRect();
+
if (is.event(event)) {
percent = (100 / clientRect.width) * (event.pageX - clientRect.left);
} else if (hasClass(this.elements.display.seekTooltip, visible)) {
@@ -1100,7 +1110,7 @@ const controls = {
let target = pane;
if (!is.element(target)) {
- target = Object.values(this.elements.settings.panels).find(pane => !pane.hidden);
+ target = Object.values(this.elements.settings.panels).find(p => !p.hidden);
}
const firstItem = target.querySelector('[role^="menuitem"]');
@@ -1398,7 +1408,7 @@ const controls = {
// Settings button / menu
if (control === 'settings' && !is.empty(this.config.settings)) {
- const control = createElement(
+ const wrapper = createElement(
'div',
extend({}, defaultAttributes, {
class: `${defaultAttributes.class} plyr__menu`.trim(),
@@ -1406,7 +1416,7 @@ const controls = {
}),
);
- control.appendChild(
+ wrapper.appendChild(
createButton.call(this, 'settings', {
'aria-haspopup': true,
'aria-controls': `plyr-settings-${data.id}`,
@@ -1546,11 +1556,11 @@ const controls = {
});
popup.appendChild(inner);
- control.appendChild(popup);
- container.appendChild(control);
+ wrapper.appendChild(popup);
+ container.appendChild(wrapper);
this.elements.settings.popup = popup;
- this.elements.settings.menu = control;
+ this.elements.settings.menu = wrapper;
}
// Picture in picture button
diff --git a/src/js/fullscreen.js b/src/js/fullscreen.js
index c86bf877..d4d15b23 100644
--- a/src/js/fullscreen.js
+++ b/src/js/fullscreen.js
@@ -73,9 +73,6 @@ function toggleFallback(toggle = false) {
.filter(part => part.trim() !== property)
.join(',');
}
-
- // Force a repaint as sometimes Safari doesn't want to fill the screen
- setTimeout(() => repaint(this.target), 100);
}
// Toggle button and fire events
diff --git a/src/js/html5.js b/src/js/html5.js
index 0d9a64ea..b03e9c26 100644
--- a/src/js/html5.js
+++ b/src/js/html5.js
@@ -44,15 +44,17 @@ const html5 = {
const player = this;
- // Set aspect ratio if set
- setAspectRatio.call(player);
+ // Set aspect ratio if fixed
+ if (!is.empty(this.config.ratio)) {
+ setAspectRatio.call(player);
+ }
// Quality
Object.defineProperty(player.media, 'quality', {
get() {
// Get sources
const sources = html5.getSources.call(player);
- const source = sources.find(source => source.getAttribute('src') === player.source);
+ const source = sources.find(s => s.getAttribute('src') === player.source);
// Return size, if match is found
return source && Number(source.getAttribute('size'));
@@ -61,7 +63,7 @@ const html5 = {
// Get sources
const sources = html5.getSources.call(player);
// Get first match for requested size
- const source = sources.find(source => Number(source.getAttribute('size')) === input);
+ const source = sources.find(s => Number(s.getAttribute('size')) === input);
// No matching source found
if (!source) {
diff --git a/src/js/listeners.js b/src/js/listeners.js
index ca527f1e..c5076ff3 100644
--- a/src/js/listeners.js
+++ b/src/js/listeners.js
@@ -147,7 +147,7 @@ class Listeners {
player.loop = !player.loop;
break;
- /* case 73:
+ /* case 73:
this.setLoop('start');
break;
@@ -275,12 +275,12 @@ class Listeners {
elements.container,
'mousemove mouseleave touchstart touchmove enterfullscreen exitfullscreen',
event => {
- const { controls } = elements;
+ const { controls: controlsElement } = elements;
// Remove button states for fullscreen
- if (controls && event.type === 'enterfullscreen') {
- controls.pressed = false;
- controls.hover = false;
+ if (controlsElement && event.type === 'enterfullscreen') {
+ controlsElement.pressed = false;
+ controlsElement.hover = false;
}
// Show, then hide after a timeout unless another control event occurs
@@ -301,14 +301,6 @@ class Listeners {
},
);
- // Force edge to repaint on exit fullscreen
- // TODO: Fix weird bug where Edge doesn't re-draw when exiting fullscreen
- /* if (browser.isEdge) {
- on.call(player, elements.container, 'exitfullscreen', () => {
- setTimeout(() => repaint(elements.container), 100);
- });
- } */
-
// Set a gutter for Vimeo
const setGutter = (ratio, padding, toggle) => {
if (!player.isVimeo) {
@@ -337,15 +329,20 @@ class Listeners {
};
const resized = () => {
- window.clearTimeout(timers.resized);
- timers.resized = window.setTimeout(setPlayerSize, 50);
+ clearTimeout(timers.resized);
+ timers.resized = setTimeout(setPlayerSize, 50);
};
on.call(player, elements.container, 'enterfullscreen exitfullscreen', event => {
const { target, usingNative } = player.fullscreen;
- // Ignore for iOS native
- if (!player.isEmbed || target !== elements.container) {
+ // Ignore events not from target
+ if (target !== elements.container) {
+ return;
+ }
+
+ // If it's not an embed and no ratio specified
+ if (!player.isEmbed && is.empty(player.config.ratio)) {
return;
}
@@ -801,7 +798,7 @@ class Listeners {
// Show controls when they receive focus (e.g., when using keyboard tab key)
this.bind(elements.controls, 'focusin', () => {
- const { config, elements, timers } = player;
+ const { config, timers } = player;
// Skip transition to prevent focus from scrolling the parent element
toggleClass(elements.controls, config.classNames.noTransition, true);
diff --git a/src/js/plugins/ads.js b/src/js/plugins/ads.js
index 2acfaed9..e6fab967 100644
--- a/src/js/plugins/ads.js
+++ b/src/js/plugins/ads.js
@@ -267,7 +267,7 @@ class Ads {
// Advertisement regular events
Object.keys(google.ima.AdEvent.Type).forEach(type => {
- this.manager.addEventListener(google.ima.AdEvent.Type[type], event => this.onAdEvent(event));
+ this.manager.addEventListener(google.ima.AdEvent.Type[type], e => this.onAdEvent(e));
});
// Resolve our adsManager
@@ -310,8 +310,7 @@ class Ads {
// Proxy event
const dispatchEvent = type => {
- const event = `ads${type.replace(/_/g, '').toLowerCase()}`;
- triggerEvent.call(this.player, this.player.media, event);
+ triggerEvent.call(this.player, this.player.media, `ads${type.replace(/_/g, '').toLowerCase()}`);
};
// Bubble the event
diff --git a/src/js/plugins/previewThumbnails.js b/src/js/plugins/previewThumbnails.js
index f03abe69..67367b95 100644
--- a/src/js/plugins/previewThumbnails.js
+++ b/src/js/plugins/previewThumbnails.js
@@ -2,6 +2,7 @@ import { createElement } from '../utils/elements';
import { once } from '../utils/events';
import fetch from '../utils/fetch';
import is from '../utils/is';
+import { extend } from '../utils/objects';
import { formatTime } from '../utils/time';
// Arg: vttDataString example: "WEBVTT\n\n1\n00:00:05.000 --> 00:00:10.000\n1080p-00001.jpg"
@@ -100,6 +101,10 @@ class PreviewThumbnails {
}
this.getThumbnails().then(() => {
+ if (!this.enabled) {
+ return;
+ }
+
// Render DOM elements
this.render();
@@ -425,7 +430,9 @@ class PreviewThumbnails {
if (image.dataset.index !== currentImage.dataset.index && !image.dataset.deleting) {
// Wait 200ms, as the new image can take some time to show on certain browsers (even though it was downloaded before showing). This will prevent flicker, and show some generosity towards slower clients
// First set attribute 'deleting' to prevent multi-handling of this on repeat firing of this function
+ // eslint-disable-next-line no-param-reassign
image.dataset.deleting = true;
+
// This has to be set before the timeout - to prevent issues switching between hover and scrub
const { currentImageContainer } = this;
@@ -632,9 +639,13 @@ class PreviewThumbnails {
// Find difference between height and preview container height
const multiplier = this.thumbContainerHeight / frame.h;
+ // eslint-disable-next-line no-param-reassign
previewImage.style.height = `${Math.floor(previewImage.naturalHeight * multiplier)}px`;
+ // eslint-disable-next-line no-param-reassign
previewImage.style.width = `${Math.floor(previewImage.naturalWidth * multiplier)}px`;
+ // eslint-disable-next-line no-param-reassign
previewImage.style.left = `-${frame.x * multiplier}px`;
+ // eslint-disable-next-line no-param-reassign
previewImage.style.top = `-${frame.y * multiplier}px`;
}
}
diff --git a/src/js/plugins/youtube.js b/src/js/plugins/youtube.js
index a5b1dafd..34c5de7e 100644
--- a/src/js/plugins/youtube.js
+++ b/src/js/plugins/youtube.js
@@ -56,26 +56,23 @@ const youtube = {
if (is.object(window.YT) && is.function(window.YT.Player)) {
youtube.ready.call(this);
} else {
- // Load the API
- loadScript(this.config.urls.youtube.sdk).catch(error => {
- this.debug.warn('YouTube API failed to load', error);
- });
-
- // Setup callback for the API
- // YouTube has it's own system of course...
- window.onYouTubeReadyCallbacks = window.onYouTubeReadyCallbacks || [];
-
- // Add to queue
- window.onYouTubeReadyCallbacks.push(() => {
- youtube.ready.call(this);
- });
+ // Reference current global callback
+ const callback = window.onYouTubeIframeAPIReady;
// Set callback to process queue
window.onYouTubeIframeAPIReady = () => {
- window.onYouTubeReadyCallbacks.forEach(callback => {
+ // Call global callback if set
+ if (is.function(callback)) {
callback();
- });
+ }
+
+ youtube.ready.call(this);
};
+
+ // Load the SDK
+ loadScript(this.config.urls.youtube.sdk).catch(error => {
+ this.debug.warn('YouTube API failed to load', error);
+ });
}
},
@@ -108,7 +105,7 @@ const youtube = {
ready() {
const player = this;
// Ignore already setup (race condition)
- const currentId = player.media.getAttribute('id');
+ const currentId = player.media && player.media.getAttribute('id');
if (!is.empty(currentId) && currentId.startsWith('youtube-')) {
return;
}
@@ -131,16 +128,16 @@ const youtube = {
player.media = replaceElement(container, player.media);
// Id to poster wrapper
- const posterSrc = format => `https://i.ytimg.com/vi/${videoId}/${format}default.jpg`;
+ const posterSrc = s => `https://i.ytimg.com/vi/${videoId}/${s}default.jpg`;
// Check thumbnail images in order of quality, but reject fallback thumbnails (120px wide)
loadImage(posterSrc('maxres'), 121) // Higest quality and unpadded
.catch(() => loadImage(posterSrc('sd'), 121)) // 480p padded 4:3
.catch(() => loadImage(posterSrc('hq'))) // 360p padded 4:3. Always exists
.then(image => ui.setPoster.call(player, image.src))
- .then(posterSrc => {
+ .then(src => {
// If the image is padded, use background-size "cover" instead (like youtube does too with their posters)
- if (!posterSrc.includes('maxres')) {
+ if (!src.includes('maxres')) {
player.elements.poster.style.backgroundSize = 'cover';
}
})
diff --git a/src/js/plyr.js b/src/js/plyr.js
index 5ec5bf2e..84fa87fa 100644
--- a/src/js/plyr.js
+++ b/src/js/plyr.js
@@ -323,27 +323,27 @@ class Plyr {
* Types and provider helpers
*/
get isHTML5() {
- return Boolean(this.provider === providers.html5);
+ return this.provider === providers.html5;
}
get isEmbed() {
- return Boolean(this.isYouTube || this.isVimeo);
+ return this.isYouTube || this.isVimeo;
}
get isYouTube() {
- return Boolean(this.provider === providers.youtube);
+ return this.provider === providers.youtube;
}
get isVimeo() {
- return Boolean(this.provider === providers.vimeo);
+ return this.provider === providers.vimeo;
}
get isVideo() {
- return Boolean(this.type === types.video);
+ return this.type === types.video;
}
get isAudio() {
- return Boolean(this.type === types.audio);
+ return this.type === types.audio;
}
/**
diff --git a/src/js/ui.js b/src/js/ui.js
index 50de7df1..df52eb64 100644
--- a/src/js/ui.js
+++ b/src/js/ui.js
@@ -213,7 +213,7 @@ const ui = {
// Set state
Array.from(this.elements.buttons.play || []).forEach(target => {
- target.pressed = this.playing;
+ Object.assign(target, { pressed: this.playing });
});
// Only update controls on non timeupdate events
@@ -247,15 +247,22 @@ const ui = {
// Toggle controls based on state and `force` argument
toggleControls(force) {
- const { controls } = this.elements;
+ const { controls: controlsElement } = this.elements;
- if (controls && this.config.hideControls) {
+ if (controlsElement && this.config.hideControls) {
// Don't hide controls if a touch-device user recently seeked. (Must be limited to touch devices, or it occasionally prevents desktop controls from hiding.)
const recentTouchSeek = this.touch && this.lastSeekTime + 2000 > Date.now();
// Show controls if force, loading, paused, button interaction, or recent seek, otherwise hide
this.toggleControls(
- Boolean(force || this.loading || this.paused || controls.pressed || controls.hover || recentTouchSeek),
+ Boolean(
+ force ||
+ this.loading ||
+ this.paused ||
+ controlsElement.pressed ||
+ controlsElement.hover ||
+ recentTouchSeek,
+ ),
);
}
},
diff --git a/src/js/utils/animation.js b/src/js/utils/animation.js
index 6b950b61..3f721b5a 100644
--- a/src/js/utils/animation.js
+++ b/src/js/utils/animation.js
@@ -2,7 +2,6 @@
// Animation utils
// ==========================================================================
-import { toggleHidden } from './elements';
import is from './is';
export const transitionEndEvent = (() => {
@@ -21,14 +20,19 @@ export const transitionEndEvent = (() => {
})();
// Force repaint of element
-export function repaint(element) {
+export function repaint(element, delay) {
setTimeout(() => {
try {
- toggleHidden(element, true);
- element.offsetHeight; // eslint-disable-line
- toggleHidden(element, false);
+ // eslint-disable-next-line no-param-reassign
+ element.hidden = true;
+
+ // eslint-disable-next-line no-unused-expressions
+ element.offsetHeight;
+
+ // eslint-disable-next-line no-param-reassign
+ element.hidden = false;
} catch (e) {
// Do nothing
}
- }, 0);
+ }, delay);
}
diff --git a/src/js/utils/elements.js b/src/js/utils/elements.js
index 94744771..4f10938e 100644
--- a/src/js/utils/elements.js
+++ b/src/js/utils/elements.js
@@ -192,11 +192,8 @@ export function toggleHidden(element, hidden) {
hide = !element.hidden;
}
- if (hide) {
- element.setAttribute('hidden', '');
- } else {
- element.removeAttribute('hidden');
- }
+ // eslint-disable-next-line no-param-reassign
+ element.hidden = hide;
}
// Mirror Element.classList.toggle, with IE compatibility for "force" argument
@@ -231,14 +228,14 @@ export function matches(element, selector) {
return Array.from(document.querySelectorAll(selector)).includes(this);
}
- const matches =
+ const method =
prototype.matches ||
prototype.webkitMatchesSelector ||
prototype.mozMatchesSelector ||
prototype.msMatchesSelector ||
match;
- return matches.call(element, selector);
+ return method.call(element, selector);
}
// Find all elements
diff --git a/src/js/utils/i18n.js b/src/js/utils/i18n.js
index 758ed695..5eee5829 100644
--- a/src/js/utils/i18n.js
+++ b/src/js/utils/i18n.js
@@ -36,8 +36,8 @@ const i18n = {
'{title}': config.title,
};
- Object.entries(replace).forEach(([key, value]) => {
- string = replaceAll(string, key, value);
+ Object.entries(replace).forEach(([k, v]) => {
+ string = replaceAll(string, k, v);
});
return string;
diff --git a/src/js/utils/loadSprite.js b/src/js/utils/loadSprite.js
index 092f9986..fe4add00 100644
--- a/src/js/utils/loadSprite.js
+++ b/src/js/utils/loadSprite.js
@@ -18,6 +18,7 @@ export default function loadSprite(url, id) {
const exists = () => document.getElementById(id) !== null;
const update = (container, data) => {
+ // eslint-disable-next-line no-param-reassign
container.innerHTML = data;
// Check again incase of race condition
diff --git a/src/js/utils/style.js b/src/js/utils/style.js
index 6f3069c9..941db8f2 100644
--- a/src/js/utils/style.js
+++ b/src/js/utils/style.js
@@ -27,15 +27,8 @@ export function reduceAspectRatio(ratio) {
}
export function getAspectRatio(input) {
- const parse = ratio => {
- if (!validateRatio(ratio)) {
- return null;
- }
-
- return ratio.split(':').map(Number);
- };
-
- // Provided ratio
+ const parse = ratio => (validateRatio(ratio) ? ratio.split(':').map(Number) : null);
+ // Try provided ratio
let ratio = parse(input);
// Get from config
diff --git a/src/sass/components/control.scss b/src/sass/components/control.scss
index 1c9aab2b..f849d135 100644
--- a/src/sass/components/control.scss
+++ b/src/sass/components/control.scss
@@ -63,9 +63,9 @@ a.plyr__control {
// Video control
.plyr--video .plyr__control {
- svg {
+ /* svg {
filter: drop-shadow(0 1px 1px rgba(#000, 0.15));
- }
+ } */
// Hover and tab focus
&.plyr__tab-focus,
@@ -81,7 +81,6 @@ a.plyr__control {
background: rgba($plyr-video-control-bg-hover, 0.8);
border: 0;
border-radius: 100%;
- box-shadow: 0 1px 1px rgba(#000, 0.15);
color: $plyr-video-control-color;
display: none;
left: 50%;
diff --git a/src/sass/components/controls.scss b/src/sass/components/controls.scss
index 87494957..f4559bba 100644
--- a/src/sass/components/controls.scss
+++ b/src/sass/components/controls.scss
@@ -12,6 +12,7 @@
align-items: center;
display: flex;
justify-content: flex-end;
+ min-width: 0; // Fix for Edge issue where content would overflow
text-align: center;
.plyr__progress__container {
diff --git a/src/sass/lib/mixins.scss b/src/sass/lib/mixins.scss
index 554c66a5..5a1ca753 100644
--- a/src/sass/lib/mixins.scss
+++ b/src/sass/lib/mixins.scss
@@ -62,12 +62,13 @@
.plyr__video-wrapper {
height: 100%;
- width: 100%;
+ position: static;
}
// Vimeo requires some different styling
&.plyr--vimeo .plyr__video-wrapper {
height: 0;
+ position: relative;
top: 50%;
transform: translateY(-50%);
}
diff --git a/src/sass/plugins/ads.scss b/src/sass/plugins/ads.scss
index c5acef75..44ec5351 100644
--- a/src/sass/plugins/ads.scss
+++ b/src/sass/plugins/ads.scss
@@ -23,7 +23,7 @@
// The countdown label
&::after {
- background: rgba($plyr-color-gunmetal, 0.8);
+ background: rgba($plyr-color-gray-9, 0.8);
border-radius: 2px;
bottom: $plyr-control-spacing;
color: #fff;
diff --git a/src/sass/plugins/previewThumbnails.scss b/src/sass/plugins/previewThumbnails.scss
index 02a2f619..b2b272c1 100644
--- a/src/sass/plugins/previewThumbnails.scss
+++ b/src/sass/plugins/previewThumbnails.scss
@@ -7,7 +7,7 @@ $plyr-preview-bg: $plyr-tooltip-bg !default;
$plyr-preview-radius: $plyr-tooltip-radius !default;
$plyr-preview-shadow: $plyr-tooltip-shadow !default;
$plyr-preview-arrow-size: $plyr-tooltip-arrow-size !default;
-$plyr-preview-image-bg: $plyr-color-heather !default;
+$plyr-preview-image-bg: $plyr-color-gray-2 !default;
$plyr-preview-time-font-size: $plyr-font-size-time !default;
$plyr-preview-time-padding: 3px 6px !default;
$plyr-preview-time-bg: rgba(0, 0, 0, 0.55);
diff --git a/src/sass/settings/badges.scss b/src/sass/settings/badges.scss
index 4f98c9a8..5fd0c138 100644
--- a/src/sass/settings/badges.scss
+++ b/src/sass/settings/badges.scss
@@ -2,5 +2,5 @@
// Badges
// ==========================================================================
-$plyr-badge-bg: $plyr-color-fiord !default;
+$plyr-badge-bg: $plyr-color-gray-7 !default;
$plyr-badge-color: #fff !default;
diff --git a/src/sass/settings/colors.scss b/src/sass/settings/colors.scss
index c9ea580c..e3883eef 100644
--- a/src/sass/settings/colors.scss
+++ b/src/sass/settings/colors.scss
@@ -2,8 +2,16 @@
// Colors
// ==========================================================================
-$plyr-color-main: #1aafff !default;
-$plyr-color-gunmetal: #2f343d !default;
-$plyr-color-fiord: #4f5b5f !default;
-$plyr-color-lynch: #6b7d85 !default;
-$plyr-color-heather: #b7c5cd !default;
+$plyr-color-main: hsl(198, 100%, 50%) !default;
+
+// Grayscale
+$plyr-color-gray-9: hsl(210, 15%, 16%);
+$plyr-color-gray-8: lighten($plyr-color-gray-9, 9%);
+$plyr-color-gray-7: lighten($plyr-color-gray-8, 9%);
+$plyr-color-gray-6: lighten($plyr-color-gray-7, 9%);
+$plyr-color-gray-5: lighten($plyr-color-gray-6, 9%);
+$plyr-color-gray-4: lighten($plyr-color-gray-5, 9%);
+$plyr-color-gray-3: lighten($plyr-color-gray-4, 9%);
+$plyr-color-gray-2: lighten($plyr-color-gray-3, 9%);
+$plyr-color-gray-1: lighten($plyr-color-gray-2, 9%);
+$plyr-color-gray-0: lighten($plyr-color-gray-1, 9%);
diff --git a/src/sass/settings/controls.scss b/src/sass/settings/controls.scss
index d6d2c153..da9f4e58 100644
--- a/src/sass/settings/controls.scss
+++ b/src/sass/settings/controls.scss
@@ -13,6 +13,6 @@ $plyr-video-control-color-hover: #fff !default;
$plyr-video-control-bg-hover: $plyr-color-main !default;
$plyr-audio-controls-bg: #fff !default;
-$plyr-audio-control-color: $plyr-color-fiord !default;
+$plyr-audio-control-color: $plyr-color-gray-7 !default;
$plyr-audio-control-color-hover: #fff !default;
$plyr-audio-control-bg-hover: $plyr-color-main !default;
diff --git a/src/sass/settings/menus.scss b/src/sass/settings/menus.scss
index 64df9863..420ebb03 100644
--- a/src/sass/settings/menus.scss
+++ b/src/sass/settings/menus.scss
@@ -3,8 +3,8 @@
// ==========================================================================
$plyr-menu-bg: rgba(#fff, 0.9) !default;
-$plyr-menu-color: $plyr-color-fiord !default;
+$plyr-menu-color: $plyr-color-gray-7 !default;
$plyr-menu-arrow-size: 6px !default;
-$plyr-menu-border-color: $plyr-color-heather !default;
+$plyr-menu-border-color: $plyr-color-gray-2 !default;
$plyr-menu-border-shadow-color: #fff !default;
$plyr-menu-shadow: 0 1px 2px rgba(#000, 0.15) !default;
diff --git a/src/sass/settings/progress.scss b/src/sass/settings/progress.scss
index 074ee3c6..10b6ebb7 100644
--- a/src/sass/settings/progress.scss
+++ b/src/sass/settings/progress.scss
@@ -4,8 +4,8 @@
// Loading
$plyr-progress-loading-size: 25px !default;
-$plyr-progress-loading-bg: rgba($plyr-color-gunmetal, 0.6) !default;
+$plyr-progress-loading-bg: rgba($plyr-color-gray-9, 0.6) !default;
// Buffered
$plyr-video-progress-buffered-bg: rgba(#fff, 0.25) !default;
-$plyr-audio-progress-buffered-bg: rgba($plyr-color-heather, 0.66) !default;
+$plyr-audio-progress-buffered-bg: rgba($plyr-color-gray-2, 0.66) !default;
diff --git a/src/sass/settings/sliders.scss b/src/sass/settings/sliders.scss
index 6ac053b0..c4d239ae 100644
--- a/src/sass/settings/sliders.scss
+++ b/src/sass/settings/sliders.scss
@@ -9,7 +9,7 @@ $plyr-range-thumb-active-shadow-width: 3px !default;
$plyr-range-thumb-height: 13px !default;
$plyr-range-thumb-bg: #fff !default;
$plyr-range-thumb-border: 2px solid transparent !default;
-$plyr-range-thumb-shadow: 0 1px 1px rgba(#000, 0.15), 0 0 0 1px rgba($plyr-color-gunmetal, 0.2) !default;
+$plyr-range-thumb-shadow: 0 1px 1px rgba(#000, 0.15), 0 0 0 1px rgba($plyr-color-gray-9, 0.2) !default;
// Track
$plyr-range-track-height: 5px !default;
diff --git a/src/sass/settings/tooltips.scss b/src/sass/settings/tooltips.scss
index fd304d60..2d298ef8 100644
--- a/src/sass/settings/tooltips.scss
+++ b/src/sass/settings/tooltips.scss
@@ -3,7 +3,7 @@
// ==========================================================================
$plyr-tooltip-bg: rgba(#fff, 0.9) !default;
-$plyr-tooltip-color: $plyr-color-fiord !default;
+$plyr-tooltip-color: $plyr-color-gray-7 !default;
$plyr-tooltip-padding: ($plyr-control-spacing / 2) !default;
$plyr-tooltip-arrow-size: 4px !default;
$plyr-tooltip-radius: 3px !default;