diff options
| author | lsces <lester@lsces.co.uk> | 2026-04-02 10:19:52 +0100 |
|---|---|---|
| committer | lsces <lester@lsces.co.uk> | 2026-04-02 10:19:52 +0100 |
| commit | 4c70be06f0adb962dc67b77f88742694f61a4fa6 (patch) | |
| tree | eb4f74c4de422e044423560a3c4df75d7eafe0ac /javascript/videojs/src/css | |
| parent | 38f17bdc406c3a2606583b3f0ff8efe570282d57 (diff) | |
| download | util-4c70be06f0adb962dc67b77f88742694f61a4fa6.tar.gz util-4c70be06f0adb962dc67b77f88742694f61a4fa6.tar.bz2 util-4c70be06f0adb962dc67b77f88742694f61a4fa6.zip | |
video.js library
Diffstat (limited to 'javascript/videojs/src/css')
44 files changed, 2168 insertions, 0 deletions
diff --git a/javascript/videojs/src/css/_icons.scss b/javascript/videojs/src/css/_icons.scss new file mode 100644 index 0000000..fc0f974 --- /dev/null +++ b/javascript/videojs/src/css/_icons.scss @@ -0,0 +1,29 @@ +// CSS styles for SVG icons used throughout video.js. +// +// The goal is to replace all icons from the font family pulled from videojs/font entirely. +// This project currently uses fonts. We want to replace this with SVGs from +// images/icons.svg. This will ensure consitency between versions, as well as simplified +// and straight-forward customization. + +// Default styling for all SVG icons +.vjs-svg-icon { + display: inline-block; + background-repeat: no-repeat; + background-position: center; + + fill: currentColor; + height: 1.8em; + width: 1.8em; + + // Overwrite any font content + &:before { + content: none !important; + } +} + +// SVG shadow on hover and focus +.vjs-svg-icon:hover, +.vjs-control:focus .vjs-svg-icon { + -webkit-filter: drop-shadow(0 0 0.25em #fff); + filter: drop-shadow(0 0 0.25em #fff); +} diff --git a/javascript/videojs/src/css/_print.scss b/javascript/videojs/src/css/_print.scss new file mode 100644 index 0000000..7a10c4e --- /dev/null +++ b/javascript/videojs/src/css/_print.scss @@ -0,0 +1,5 @@ +@media print { + .video-js > *:not(.vjs-tech):not(.vjs-poster) { + visibility:hidden; + } +} diff --git a/javascript/videojs/src/css/_private-variables.scss b/javascript/videojs/src/css/_private-variables.scss new file mode 100644 index 0000000..46d7e55 --- /dev/null +++ b/javascript/videojs/src/css/_private-variables.scss @@ -0,0 +1,22 @@ +@use "sass:color"; + +// Text, icons, hover states +$primary-foreground-color: #fff !default; + +// Control backgrounds (control bar, big play, menus) +$primary-background-color: #2B333F !default; +$primary-background-transparency: 0.7 !default; + +// Hover states, slider backgrounds +$secondary-background-color: color.adjust($primary-background-color, $lightness: 33%, $space: hsl) !default; +$secondary-background-transparency: 0.5 !default; + +// Avoiding helvetica: issue #376 +$text-font-family: Arial, Helvetica, sans-serif !default; + +// Using the '--' naming for component-specific styles +$big-play-button--border-size: 0.06666em !default; +$big-play-button--width: 3em !default; +$big-play-button--line-height: 1.5em !default; +$big-play-button--height: $big-play-button--line-height + ($big-play-button--border-size * 2) !default; +$big-play-button--transparency: 0.8 !default; diff --git a/javascript/videojs/src/css/_utilities.scss b/javascript/videojs/src/css/_utilities.scss new file mode 100644 index 0000000..7dd1cce --- /dev/null +++ b/javascript/videojs/src/css/_utilities.scss @@ -0,0 +1,81 @@ +@import "utilities/linear-gradient"; + +@mixin background-color-with-alpha($color, $alpha) { + background-color: $color; + background-color: rgba($color, $alpha); +} + +@mixin transform($transform) { + transform: $transform; +} + +@mixin transition($string: $transition--default) { + transition: $string; +} + +@mixin hide-visually { + border: 0; + clip: rect(0 0 0 0); + height: 1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; +} + +@mixin border-radius($radius) { + border-radius: $radius; +} + +@mixin animation($string: spin 1s infinite linear) { + animation: $string; +} + +@mixin display-flex($alignment: '', $justification: '') { + display: flex; + + @if $alignment != '' { + align-items: $alignment; + } + + @if $justification != '' { + justify-content: $justification; + } +} + +@mixin flex($value) { + flex: $value; +} + +// https://developer.mozilla.org/en-US/docs/Web/CSS/user-select +// https://stackoverflow.com/questions/826782/how-to-disable-text-selection-highlighting-using-css (version: January, 2017) +@mixin user-select($string: none) { + /* iOS Safari */ + -webkit-touch-callout: $string; + /* Safari, and Chrome 53 */ + -webkit-user-select: $string; + /* Non-prefixed version, currently supported by Chrome and Opera */ + user-select: $string; +} + +// https://developer.mozilla.org/en-US/docs/Web/CSS/box-shadow +@mixin box-shadow ($string: 0 0 1em rgba(0, 0, 0, 0.25)) { + box-shadow: $string; +} + +@mixin order($value) { + order: $value; +} + +%fill-parent { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; +} + +%icon-default { + @extend %fill-parent; + text-align: center; +} diff --git a/javascript/videojs/src/css/_variables.scss b/javascript/videojs/src/css/_variables.scss new file mode 100644 index 0000000..0ed69c2 --- /dev/null +++ b/javascript/videojs/src/css/_variables.scss @@ -0,0 +1 @@ +$icon-font-path: 'font' !default; diff --git a/javascript/videojs/src/css/components/_adaptive.scss b/javascript/videojs/src/css/components/_adaptive.scss new file mode 100644 index 0000000..8977438 --- /dev/null +++ b/javascript/videojs/src/css/components/_adaptive.scss @@ -0,0 +1,71 @@ +// When the player is "medium" and higher, display everything by default. +// +// When the player is "small", display only: +// - Play button +// - Volume Mute button +// - Progress bar +// - Track buttons +// - Native PiP button +// - Fullscreen button +// +// When the player is "x-small", display only: +// - Play button +// - Volume Mute button +// - Spacer +// - Track buttons +// - Native PiP button +// - Fullscreen button +// +// When the player is "tiny", display only: +// - Play button +// - Volume Mute button +// - Track buttons +// - Native PiP button +// - Fullscreen Button +// +.video-js { + + &.vjs-layout-small, + &.vjs-layout-x-small, + &.vjs-layout-tiny { + .vjs-current-time, + .vjs-time-divider, + .vjs-duration, + .vjs-remaining-time, + .vjs-playback-rate, + .vjs-volume-control { + display: none; + } + + // Reset the size of the volume panel to the default so we don't see a big + // empty space to the right of the mute button. + .vjs-volume-panel.vjs-volume-panel-horizontal { + &:hover, + &:active, + &.vjs-slider-active, + &.vjs-hover { + width: auto; + width: initial; + } + } + } + + // At x-small and tiny, the progress control is too narrow to be useful. + &.vjs-layout-x-small, + &.vjs-layout-tiny { + + .vjs-progress-control { + display: none; + } + } + + // At x-small, the buttons alone leave a large gap on the right. Fill it with + // the spacer element. + &.vjs-layout-x-small { + + .vjs-custom-control-spacer { + @include flex(auto); + display: block; + } + } +} diff --git a/javascript/videojs/src/css/components/_audio.scss b/javascript/videojs/src/css/components/_audio.scss new file mode 100644 index 0000000..3f7d703 --- /dev/null +++ b/javascript/videojs/src/css/components/_audio.scss @@ -0,0 +1,19 @@ +.video-js .vjs-audio-button .vjs-icon-placeholder { + @extend .vjs-icon-audio; +} + +.video-js .vjs-audio-button + .vjs-menu .vjs-descriptions-menu-item .vjs-menu-item-text .vjs-icon-placeholder, +.video-js .vjs-audio-button + .vjs-menu .vjs-main-desc-menu-item .vjs-menu-item-text .vjs-icon-placeholder { + vertical-align: middle; + display: inline-block; + margin-bottom: -0.1em; +} + +// Mark a main-desc-menu-item (main + description) or description item with a trailing Audio Description icon +.video-js .vjs-audio-button + .vjs-menu .vjs-descriptions-menu-item .vjs-menu-item-text .vjs-icon-placeholder:before, +.video-js .vjs-audio-button + .vjs-menu .vjs-main-desc-menu-item .vjs-menu-item-text .vjs-icon-placeholder:before { + font-family: VideoJS; + content: " \f12e"; + font-size: 1.5em; + line-height: inherit; +} diff --git a/javascript/videojs/src/css/components/_big-play.scss b/javascript/videojs/src/css/components/_big-play.scss new file mode 100644 index 0000000..7af937a --- /dev/null +++ b/javascript/videojs/src/css/components/_big-play.scss @@ -0,0 +1,62 @@ +@use "sass:math"; + +.video-js .vjs-big-play-button { + font-size: 3em; + line-height: $big-play-button--line-height; + height: $big-play-button--height; + width: $big-play-button--width; // Firefox bug: For some reason without width the icon wouldn't show up. Switched to using width and removed padding. + display: block; + position: absolute; + top: 50%; + left: 50%; + padding: 0; + margin-top: -(math.div($big-play-button--height, 2)); + margin-left: -(math.div($big-play-button--width, 2)); + cursor: pointer; + opacity: 1; + border: $big-play-button--border-size solid $primary-foreground-color; + + // Need a slightly gray bg so it can be seen on black backgrounds + @include background-color-with-alpha($primary-background-color, $primary-background-transparency); + @include border-radius(0.3em); + @include transition(all 0.4s); + + // Since the big play button doesn't inherit from vjs-control, we need to specify a bit more than + // other buttons for the icon. + & .vjs-icon-placeholder:before { + @extend .vjs-icon-play; + + @extend %icon-default; + } +} + +.vjs-big-play-button .vjs-svg-icon { + width: 1em; + height: 1em; + position: absolute; + top: 50%; + left: 50%; + line-height: 1; + transform: translate(-50%, -50%); +} + +.video-js:hover .vjs-big-play-button, +.video-js .vjs-big-play-button:focus { + border-color: $primary-foreground-color; + + @include background-color-with-alpha($secondary-background-color, $secondary-background-transparency); + @include transition(all 0s); +} + +// Hide if controls are disabled, the video is playing, or native controls are used. +.vjs-controls-disabled .vjs-big-play-button, +.vjs-has-started .vjs-big-play-button, +.vjs-using-native-controls .vjs-big-play-button, +.vjs-error .vjs-big-play-button { + display: none; +} + +// Show big play button if video is paused and .vjs-show-big-play-button-on-pause is set on video element +.vjs-has-started.vjs-paused.vjs-show-big-play-button-on-pause:not(.vjs-seeking, .vjs-scrubbing, .vjs-error) .vjs-big-play-button { + display: block; +} diff --git a/javascript/videojs/src/css/components/_button.scss b/javascript/videojs/src/css/components/_button.scss new file mode 100644 index 0000000..e14a921 --- /dev/null +++ b/javascript/videojs/src/css/components/_button.scss @@ -0,0 +1,27 @@ +.video-js button { + background: none; + border: none; + color: inherit; + display: inline-block; + + font-size: inherit; // IE in general. WTF. + line-height: inherit; + text-transform: none; + text-decoration: none; + transition: none; + + // Chrome < 83 + -webkit-appearance: none; + appearance: none; +} + +// Replacement for focus in case spatial navigation is enabled +.video-js.vjs-spatial-navigation-enabled .vjs-button:focus { + outline: 0.0625em solid rgba($primary-foreground-color, 1); + box-shadow: none; +} + +.vjs-control .vjs-button { + width: 100%; + height: 100%; +} diff --git a/javascript/videojs/src/css/components/_captions-settings.scss b/javascript/videojs/src/css/components/_captions-settings.scss new file mode 100644 index 0000000..29c7bfd --- /dev/null +++ b/javascript/videojs/src/css/components/_captions-settings.scss @@ -0,0 +1,121 @@ +.vjs-modal-dialog.vjs-text-track-settings { + background-color: $primary-background-color; + background-color: rgba($primary-background-color, 0.75); + color: $primary-foreground-color; + height: 70%; + + // When Spatial Navigation is enabled + .vjs-spatial-navigation-enabled & { + height: 80%; + } +} + +// Hide if an error occurs +.vjs-error .vjs-text-track-settings { + display: none; +} + +// Layout divs +.vjs-text-track-settings .vjs-modal-dialog-content { + display: table; +} + +.vjs-text-track-settings .vjs-track-settings-colors, +.vjs-text-track-settings .vjs-track-settings-font, +.vjs-text-track-settings .vjs-track-settings-controls { + display: table-cell; +} + +.vjs-text-track-settings .vjs-track-settings-controls { + text-align: right; + vertical-align: bottom; +} + +// code that will only run if CSS Grid is supported by the browser +@supports (display: grid) { + .vjs-text-track-settings .vjs-modal-dialog-content { + display: grid; + grid-template-columns: 1fr 1fr; + grid-template-rows: 1fr; + // Flex and Grid for Firefox, IE, and Edge remove the bottom padding/margin in a container as size decreases + // so we add bottom padding/margin to the last item in the grid instead of here + // see https://stackoverflow.com/a/23754080 + padding: 20px 24px 0px 24px; + } + + // see the comment for padding above + .vjs-track-settings-controls .vjs-default-button { + margin-bottom: 20px; + } + + .vjs-text-track-settings .vjs-track-settings-controls { + // make this take up both columns + grid-column: 1 / -1; + } + + // 1 column for small players + .vjs-layout-small .vjs-text-track-settings .vjs-modal-dialog-content , + .vjs-layout-x-small .vjs-text-track-settings .vjs-modal-dialog-content, + .vjs-layout-tiny .vjs-text-track-settings .vjs-modal-dialog-content { + grid-template-columns: 1fr; + } + +} + +// Form elements +.vjs-text-track-settings select { + font-size: inherit; +} + +.vjs-track-setting > select { + margin-right: 1em; + margin-bottom: 0.5em; +} + +.vjs-text-track-settings fieldset { + margin: 10px; + border: none; +} + +.vjs-text-track-settings fieldset span { + display: inline-block; + padding: 0 .6em .8em; +} + +// style the second select for text colors +.vjs-text-track-settings fieldset span > select { + max-width: 7.3em; +} + +.vjs-text-track-settings legend { + color: $primary-foreground-color; + font-weight: bold; + font-size: 1.2em; +} + +.vjs-text-track-settings .vjs-label { + margin: 0 .5em .5em 0; +} + +.vjs-track-settings-controls button:focus, +.vjs-track-settings-controls button:active { + outline-style: solid; + outline-width: medium; + background-image: linear-gradient(0deg, $primary-foreground-color 88%, $secondary-background-color 100%); +} + +.vjs-track-settings-controls button:hover { + color: rgba(#2B333F, 0.75); +} + +.vjs-track-settings-controls button { + background-color: $primary-foreground-color; + background-image: linear-gradient(-180deg, $primary-foreground-color 88%, $secondary-background-color 100%); + color: #2B333F; + cursor: pointer; + border-radius: 2px; +} + +.vjs-track-settings-controls .vjs-default-button { + margin-right: 1em; +} diff --git a/javascript/videojs/src/css/components/_captions.scss b/javascript/videojs/src/css/components/_captions.scss new file mode 100644 index 0000000..8cffef9 --- /dev/null +++ b/javascript/videojs/src/css/components/_captions.scss @@ -0,0 +1,7 @@ +.video-js .vjs-captions-button .vjs-icon-placeholder { + @extend .vjs-icon-captions; +} + +.video-js.vjs-audio-only-mode .vjs-captions-button { + display: none; +} diff --git a/javascript/videojs/src/css/components/_chapters.scss b/javascript/videojs/src/css/components/_chapters.scss new file mode 100644 index 0000000..7eab3e8 --- /dev/null +++ b/javascript/videojs/src/css/components/_chapters.scss @@ -0,0 +1,7 @@ +.video-js .vjs-chapters-button .vjs-icon-placeholder { + @extend .vjs-icon-chapters; +} + +.vjs-chapters-button .vjs-menu ul { + width: 24em; +} diff --git a/javascript/videojs/src/css/components/_close-button.scss b/javascript/videojs/src/css/components/_close-button.scss new file mode 100644 index 0000000..07193e7 --- /dev/null +++ b/javascript/videojs/src/css/components/_close-button.scss @@ -0,0 +1,12 @@ +.video-js .vjs-control.vjs-close-button { + cursor: pointer; + height: 3em; + position: absolute; + right: 0; + top: 0.5em; + z-index: 2; + + & .vjs-icon-placeholder { + @extend .vjs-icon-cancel; + } +} diff --git a/javascript/videojs/src/css/components/_control-bar.scss b/javascript/videojs/src/css/components/_control-bar.scss new file mode 100644 index 0000000..3679539 --- /dev/null +++ b/javascript/videojs/src/css/components/_control-bar.scss @@ -0,0 +1,63 @@ +.video-js .vjs-control-bar { + display: none; + width: 100%; + position: absolute; + bottom: 0; + left: 0; + right: 0; + height: 3.0em; + + @include background-color-with-alpha($primary-background-color, $primary-background-transparency); +} + +.video-js.vjs-spatial-navigation-enabled .vjs-control-bar { + gap: 1px; +} + +// Locks the display only if: +// - controls are not disabled +// - native controls are not used +// - there is no error +.video-js:not(.vjs-controls-disabled, .vjs-using-native-controls, .vjs-error) .vjs-control-bar.vjs-lock-showing { + display: flex !important; +} + +// Video has started playing or we are in audioOnlyMode +.vjs-has-started .vjs-control-bar, +.vjs-audio-only-mode .vjs-control-bar { + @include display-flex; + visibility: visible; + opacity: 1; + + $trans: visibility 0.1s, opacity 0.1s; // Var needed because of comma + @include transition($trans); +} + +// Video has started playing AND user is inactive +.vjs-has-started.vjs-user-inactive.vjs-playing .vjs-control-bar { + // Remain visible for screen reader and keyboard users + visibility: visible; + opacity: 0; + // prevent a click/tap from interacting with vjs-lock-showing menu's + // or other controls while we are inactive/hidden + pointer-events: none; + + $trans: visibility 1.0s, opacity 1.0s; + @include transition($trans); + +} + +.vjs-controls-disabled .vjs-control-bar, +.vjs-using-native-controls .vjs-control-bar, +.vjs-error .vjs-control-bar { + // !important is ok in this context. + display: none !important; +} + +// Don't hide the control bar if it's audio or in audioOnlyMode +.vjs-audio.vjs-has-started.vjs-user-inactive.vjs-playing .vjs-control-bar, +.vjs-audio-only-mode.vjs-has-started.vjs-user-inactive.vjs-playing .vjs-control-bar { + opacity: 1; + visibility: visible; + pointer-events: auto; +} diff --git a/javascript/videojs/src/css/components/_control-spacer.scss b/javascript/videojs/src/css/components/_control-spacer.scss new file mode 100644 index 0000000..010751b --- /dev/null +++ b/javascript/videojs/src/css/components/_control-spacer.scss @@ -0,0 +1,3 @@ +.video-js .vjs-custom-control-spacer { + display: none; +} diff --git a/javascript/videojs/src/css/components/_control.scss b/javascript/videojs/src/css/components/_control.scss new file mode 100644 index 0000000..6dd7b6b --- /dev/null +++ b/javascript/videojs/src/css/components/_control.scss @@ -0,0 +1,45 @@ +// vjs-control might be better named vjs-button now. +// It's used on both real buttons (play button) +// and div buttons (menu buttons) +.video-js .vjs-control { + position: relative; + text-align: center; + margin: 0; + padding: 0; + height: 100%; + width: 4em; + @include flex(none); +} + +.video-js .vjs-control.vjs-visible-text { + width: auto; + padding-left: 1em; + padding-right: 1em; +} + +.vjs-button > .vjs-icon-placeholder:before { + font-size: 1.8em; + line-height: 1.67; + + @extend %icon-default; +} + +.vjs-button > .vjs-icon-placeholder { + display: block; +} + +.vjs-button > .vjs-svg-icon { + display: inline-block; +} + +// Replacement for focus outline +.video-js .vjs-control:focus:before, +.video-js .vjs-control:hover:before, +.video-js .vjs-control:focus { + text-shadow: 0em 0em 1em rgba($primary-foreground-color, 1); +} + +// Hide control text visually, but have it available for screenreaders +.video-js *:not(.vjs-visible-text) > .vjs-control-text { + @include hide-visually; +} diff --git a/javascript/videojs/src/css/components/_descriptions.scss b/javascript/videojs/src/css/components/_descriptions.scss new file mode 100644 index 0000000..9cf8d95 --- /dev/null +++ b/javascript/videojs/src/css/components/_descriptions.scss @@ -0,0 +1,7 @@ +.video-js .vjs-descriptions-button .vjs-icon-placeholder { + @extend .vjs-icon-audio-description; +} + +.video-js.vjs-audio-only-mode .vjs-descriptions-button { + display: none; +} diff --git a/javascript/videojs/src/css/components/_error.scss b/javascript/videojs/src/css/components/_error.scss new file mode 100644 index 0000000..118e262 --- /dev/null +++ b/javascript/videojs/src/css/components/_error.scss @@ -0,0 +1,4 @@ +.vjs-error .vjs-error-display .vjs-modal-dialog-content { + font-size: 1.4em; + text-align: center; +} diff --git a/javascript/videojs/src/css/components/_fullscreen.scss b/javascript/videojs/src/css/components/_fullscreen.scss new file mode 100644 index 0000000..f682341 --- /dev/null +++ b/javascript/videojs/src/css/components/_fullscreen.scss @@ -0,0 +1,18 @@ +.video-js .vjs-fullscreen-control { + cursor: pointer; + @include flex(none); + + & .vjs-icon-placeholder { + @extend .vjs-icon-fullscreen-enter; + } +} + +.video-js.vjs-audio-only-mode .vjs-fullscreen-control, +.vjs-pip-window .vjs-fullscreen-control { + display: none; +} + +// Switch to the exit icon when the player is in fullscreen +.video-js.vjs-fullscreen .vjs-fullscreen-control .vjs-icon-placeholder { + @extend .vjs-icon-fullscreen-exit; +} diff --git a/javascript/videojs/src/css/components/_layout.scss b/javascript/videojs/src/css/components/_layout.scss new file mode 100644 index 0000000..fdf9d25 --- /dev/null +++ b/javascript/videojs/src/css/components/_layout.scss @@ -0,0 +1,211 @@ +@use "sass:math"; + +.video-js { + display: inline-block; + // Make video.js videos align top when next to video elements + vertical-align: top; + box-sizing: border-box; + + color: $primary-foreground-color; + background-color: #000; + position: relative; + padding: 0; + // Start with 10px for base font size so other dimensions can be em based and + // easily calculable. + font-size: 10px; + line-height: 1; + + // Provide some basic defaults for fonts + font-weight: normal; + font-style: normal; + // Avoiding helvetica: issue #376 + font-family: $text-font-family; + + // reset word-break inside the player div + word-break: initial; + + // Fix for Firefox 9 fullscreen (only if it is enabled). Not needed when + // checking fullScreenEnabled. + &:-moz-full-screen { position: absolute; } + + &:-webkit-full-screen { + width: 100% !important; + height: 100% !important; + } +} + +.video-js[tabindex="-1"] { + outline: none; +} + +// All elements inherit border-box sizing +.video-js *, +.video-js *:before, +.video-js *:after { + box-sizing: inherit; +} + +// List style reset +.video-js ul { + font-family: inherit; + font-size: inherit; + line-height: inherit; + list-style-position: outside; + + // Important to specify each + margin-left: 0; + margin-right: 0; + margin-top: 0; + margin-bottom: 0; +} + +// Fill the width of the containing element and use padding to create the +// desired aspect ratio. Default to 16x9 unless another ratio is given. +@mixin apply-aspect-ratio($width, $height) { + padding-top: 100% * math.div($height, $width); +} + +// Not including a default AR in vjs-fluid because it would override +// the user set AR injected into the header. +.video-js.vjs-fluid, +.video-js.vjs-16-9, +.video-js.vjs-4-3, +.video-js.vjs-9-16, +.video-js.vjs-1-1 { + width: 100%; + max-width: 100%; +} + +.video-js.vjs-fluid:not(.vjs-audio-only-mode), +.video-js.vjs-16-9:not(.vjs-audio-only-mode), +.video-js.vjs-4-3:not(.vjs-audio-only-mode), +.video-js.vjs-9-16:not(.vjs-audio-only-mode), +.video-js.vjs-1-1:not(.vjs-audio-only-mode) { + height: 0; +} + +.video-js.vjs-16-9:not(.vjs-audio-only-mode) { + @include apply-aspect-ratio(16, 9); +} + +.video-js.vjs-4-3:not(.vjs-audio-only-mode) { + @include apply-aspect-ratio(4, 3); +} + +.video-js.vjs-9-16:not(.vjs-audio-only-mode) { + @include apply-aspect-ratio(9, 16); +} + +.video-js.vjs-1-1:not(.vjs-audio-only-mode) { + @include apply-aspect-ratio(1, 1); +} + +.video-js.vjs-fill:not(.vjs-audio-only-mode) { + width: 100%; + height: 100%; +} + +// Playback technology elements expand to the width/height of the containing div +// <video> or <object> +.video-js .vjs-tech { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; +} + +.video-js.vjs-audio-only-mode .vjs-tech { + display: none; +} + +// Fullscreen and Document Picture-in-Picture Styles +body.vjs-full-window, +body.vjs-pip-window { + padding: 0; + margin: 0; + height: 100%; +} +.vjs-full-window .video-js.vjs-fullscreen, +body.vjs-pip-window .video-js { + position: fixed; + overflow: hidden; + z-index: 1000; + left: 0; + top: 0; + bottom: 0; + right: 0; +} +.video-js.vjs-fullscreen:not(.vjs-ios-native-fs), +body.vjs-pip-window .video-js { + width: 100% !important; + height: 100% !important; + // Undo any aspect ratio padding for fluid layouts + padding-top: 0 !important; + // Older Safari (<= 15.6) needs display: block in fullscreen. + display: block; +} + +.video-js.vjs-fullscreen.vjs-user-inactive { + cursor: none; +} + +.vjs-pip-container .vjs-pip-text { + position: absolute; + bottom: 10%; + font-size: 2em; + background-color: rgba(0, 0, 0, .7); + padding: .5em; + text-align: center; + width: 100% +} + +.vjs-layout-tiny.vjs-pip-container .vjs-pip-text, +.vjs-layout-x-small.vjs-pip-container .vjs-pip-text, +.vjs-layout-small.vjs-pip-container .vjs-pip-text { + bottom: 0; + font-size: 1.4em; +} + + +// Hide disabled or unsupported controls. +.vjs-hidden { display: none !important; } + +.vjs-disabled { + opacity: 0.5; + cursor: default; +} + +// Visually hidden offscreen, but accessible to screen readers. +.video-js .vjs-offscreen { + height: 1px; + left: -9999px; + position: absolute; + top: 0; + width: 1px; +} + +.vjs-lock-showing { + display: block !important; + opacity: 1 !important; + visibility: visible !important; +} + +// This optional paragraph inside the video tag can provide a message to users +// about what's required to play video when JavaScript is disabled +.vjs-no-js { + padding: 20px; + color: #fff; + background-color: #000; + font-size: 18px; + font-family: $text-font-family; + text-align: center; + width: 300px; + height: 150px; + margin: 0px auto; +} + +.vjs-no-js a, +.vjs-no-js a:visited { + color: #66A8CC; +} diff --git a/javascript/videojs/src/css/components/_live.scss b/javascript/videojs/src/css/components/_live.scss new file mode 100644 index 0000000..a0a5d46 --- /dev/null +++ b/javascript/videojs/src/css/components/_live.scss @@ -0,0 +1,66 @@ +// css for the old live ui, assumes that the progress bar is hidden +.video-js .vjs-live-control { + @include display-flex(flex-start); + @include flex(auto); + font-size: 1em; + line-height: 3em; +} + +// hide the LiveDisplay when not live or when +// the new liveui is in use +.video-js:not(.vjs-live) .vjs-live-control, +.video-js.vjs-liveui .vjs-live-control { + display: none; +} + +// css for the new live ui below +.video-js .vjs-seek-to-live-control { + align-items: center; + cursor: pointer; + @include flex(none); + display: inline-flex; + height: 100%; + padding-left: 0.5em; + padding-right: 0.5em; + font-size: 1em; + line-height: 3em; + width: auto; + min-width: 4em; +} + +// hide the SeekToLive button when not live and +// when the liveui is not in use +.video-js.vjs-live:not(.vjs-liveui) .vjs-seek-to-live-control, +.video-js:not(.vjs-live) .vjs-seek-to-live-control { + display: none; +} + +// only show as a pointer when we will seek to live edge +.vjs-seek-to-live-control.vjs-control.vjs-at-live-edge { + cursor: auto; +} + +.vjs-seek-to-live-control .vjs-icon-placeholder { + margin-right: 0.5em; + @extend .vjs-icon-circle; + color: #888; +} + +.vjs-svg-icons-enabled .vjs-seek-to-live-control { + line-height: 0; +} + +.vjs-seek-to-live-control .vjs-svg-icon { + width: 1em; + height: 1em; + pointer-events: none; + fill: #888888; +} + +// make the live circle red when at the live edge +.vjs-seek-to-live-control.vjs-control.vjs-at-live-edge .vjs-icon-placeholder { + color: red; +} +.vjs-seek-to-live-control.vjs-control.vjs-at-live-edge .vjs-svg-icon { + fill: red; +} diff --git a/javascript/videojs/src/css/components/_loading.scss b/javascript/videojs/src/css/components/_loading.scss new file mode 100644 index 0000000..e9b82bf --- /dev/null +++ b/javascript/videojs/src/css/components/_loading.scss @@ -0,0 +1,100 @@ +.vjs-loading-spinner { + display: none; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + opacity: 0.85; + + // Need to fix centered page layouts + text-align: left; + + border: .6em solid rgba($primary-background-color, $primary-background-transparency); + // border: 6px solid rgba(43, 51, 63, 0.5); + + box-sizing: border-box; + background-clip: padding-box; + width: 5em; + height: 5em; + border-radius: 50%; + visibility: hidden; +} + +.vjs-seeking .vjs-loading-spinner, +.vjs-waiting .vjs-loading-spinner { + display: flex; + justify-content: center; + align-items: center; + + // add a delay before actual show the spinner + animation: vjs-spinner-show 0s linear 0.3s forwards; +} + +// Hide if an error occurs +.vjs-error .vjs-loading-spinner { + display: none; +} + +.vjs-loading-spinner:before, +.vjs-loading-spinner:after { + content: ""; + position: absolute; + box-sizing: inherit; + width: inherit; + height: inherit; + border-radius: inherit; + // Keep 100% opacity so they don't show through each other + opacity: 1; + border: inherit; + border-color: transparent; + border-top-color: white; +} + +// only animate when showing because it can be processor heavy +.vjs-seeking .vjs-loading-spinner:before, +.vjs-seeking .vjs-loading-spinner:after, +.vjs-waiting .vjs-loading-spinner:before, +.vjs-waiting .vjs-loading-spinner:after { + animation: vjs-spinner-spin 1.1s cubic-bezier(0.6, 0.2, 0, 0.8) infinite, vjs-spinner-fade 1.1s linear infinite; +} + +.vjs-seeking .vjs-loading-spinner:before, +.vjs-waiting .vjs-loading-spinner:before { + border-top-color: rgb(255,255,255); +} + +.vjs-seeking .vjs-loading-spinner:after, +.vjs-waiting .vjs-loading-spinner:after { + border-top-color: rgb(255,255,255); + animation-delay: 0.44s; +} + +@keyframes vjs-spinner-show { + to { + visibility: visible; + } +} + +@keyframes vjs-spinner-spin { + 100% { + transform: rotate(360deg); + } +} + +@keyframes vjs-spinner-fade { + 0% { + border-top-color: $secondary-background-color; + } + 20% { + border-top-color: $secondary-background-color; + } + 35% { + border-top-color: white; + } + 60% { + border-top-color: $secondary-background-color; + } + 100% { + border-top-color: $secondary-background-color; + } +} diff --git a/javascript/videojs/src/css/components/_modal-dialog.scss b/javascript/videojs/src/css/components/_modal-dialog.scss new file mode 100644 index 0000000..0c88c5b --- /dev/null +++ b/javascript/videojs/src/css/components/_modal-dialog.scss @@ -0,0 +1,21 @@ +.video-js .vjs-modal-dialog { + @extend %fill-parent; + @include linear-gradient(180deg, rgba(0, 0, 0, 0.8), rgba(255, 255, 255, 0)); + + // This allows scrolling of content if need be. + overflow: auto; +} + +// Reset box-sizing inside the modal dialog. +.video-js .vjs-modal-dialog > * { + box-sizing: border-box; +} + +.vjs-modal-dialog .vjs-modal-dialog-content { + @extend %fill-parent; + + font-size: 1.2em; // 12px + line-height: 1.5; // 18px + padding: 20px 24px; + z-index: 1; +} diff --git a/javascript/videojs/src/css/components/_picture-in-picture.scss b/javascript/videojs/src/css/components/_picture-in-picture.scss new file mode 100644 index 0000000..5992407 --- /dev/null +++ b/javascript/videojs/src/css/components/_picture-in-picture.scss @@ -0,0 +1,18 @@ +.video-js .vjs-picture-in-picture-control { + cursor: pointer; + @include flex(none); + + & .vjs-icon-placeholder { + @extend .vjs-icon-picture-in-picture-enter; + } +} + +.video-js.vjs-audio-only-mode .vjs-picture-in-picture-control, +.vjs-pip-window .vjs-picture-in-picture-control { + display: none; +} + +// Switch to the exit icon when the player is in Picture-in-Picture +.video-js.vjs-picture-in-picture .vjs-picture-in-picture-control .vjs-icon-placeholder { + @extend .vjs-icon-picture-in-picture-exit; +} diff --git a/javascript/videojs/src/css/components/_play-pause.scss b/javascript/videojs/src/css/components/_play-pause.scss new file mode 100644 index 0000000..b9615fd --- /dev/null +++ b/javascript/videojs/src/css/components/_play-pause.scss @@ -0,0 +1,13 @@ +.video-js .vjs-play-control { + cursor: pointer; +} +.video-js .vjs-play-control .vjs-icon-placeholder { + @include flex(none); + @extend .vjs-icon-play; +} +.video-js .vjs-play-control.vjs-playing .vjs-icon-placeholder { + @extend .vjs-icon-pause; +} +.video-js .vjs-play-control.vjs-ended .vjs-icon-placeholder { + @extend .vjs-icon-replay; +} diff --git a/javascript/videojs/src/css/components/_playback-rate.scss b/javascript/videojs/src/css/components/_playback-rate.scss new file mode 100644 index 0000000..e13a517 --- /dev/null +++ b/javascript/videojs/src/css/components/_playback-rate.scss @@ -0,0 +1,21 @@ +// TODO: I feel like this should be a generic menu. Research later. +.vjs-playback-rate > .vjs-menu-button, +.vjs-playback-rate .vjs-playback-rate-value { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; +} + +.vjs-playback-rate .vjs-playback-rate-value { + pointer-events: none; + font-size: 1.5em; + line-height: 2; + text-align: center; +} + +.vjs-playback-rate .vjs-menu { + width: 4em; + left: 0em; +} diff --git a/javascript/videojs/src/css/components/_poster.scss b/javascript/videojs/src/css/components/_poster.scss new file mode 100644 index 0000000..d1f1d22 --- /dev/null +++ b/javascript/videojs/src/css/components/_poster.scss @@ -0,0 +1,32 @@ +.vjs-poster { + display: inline-block; + vertical-align: middle; + cursor: pointer; + margin: 0; + padding: 0; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + height: 100%; +} + +// Hide the poster after the video has started playing and when native controls are used +.vjs-has-started .vjs-poster, +.vjs-using-native-controls .vjs-poster { + display: none; +} + +// Don't hide the poster if we're playing audio or when audio-poster-mode is true +.vjs-audio.vjs-has-started .vjs-poster, +.vjs-has-started.vjs-audio-poster-mode .vjs-poster, +.vjs-pip-container.vjs-has-started .vjs-poster { + display: block; +} + +.vjs-poster img { + width: 100%; + height: 100%; + object-fit: contain; +} diff --git a/javascript/videojs/src/css/components/_progress.scss b/javascript/videojs/src/css/components/_progress.scss new file mode 100644 index 0000000..5334c65 --- /dev/null +++ b/javascript/videojs/src/css/components/_progress.scss @@ -0,0 +1,192 @@ +// .vjs-progress-control / ProgressControl +// +// This is the container for all progress bar-related components/elements. +.video-js .vjs-progress-control { + cursor: pointer; + @include flex(auto); + @include display-flex(center); + min-width: 4em; + touch-action: none; +} + +.video-js .vjs-progress-control.disabled { + cursor: default; +} + +.vjs-live .vjs-progress-control { + display: none; +} + +.vjs-liveui .vjs-progress-control { + @include display-flex(center); +} + +// .vjs-progress-holder / SeekBar +// +// Box containing play and load progress bars. It also acts as seek scrubber. +.video-js .vjs-progress-holder { + @include flex(auto); + @include transition(all 0.2s); + height: 0.3em; +} + +.video-js .vjs-progress-control .vjs-progress-holder { + + // This is one of the rare cases where we are using a pixel dimension. The + // reason is that the progress holder font-size changes on hover. With the + // default em-based margins, this means it gets narrower and causes issues + // with mouseover behaviors/math. + margin: 0 10px; +} + +// This increases the size of the progress holder so there is an increased +// hit area for clicks/touches. +.video-js .vjs-progress-control:hover .vjs-progress-holder, +.video-js.vjs-scrubbing.vjs-touch-enabled .vjs-progress-control .vjs-progress-holder { + font-size: 1.666666666666666666em; +} + +.video-js .vjs-progress-control:hover .vjs-progress-holder.disabled { + font-size: 1em; +} + +// .vjs-play-progress / PlayProgressBar and .vjs-load-progress / LoadProgressBar +// +// These are bars that appear within the progress control to communicate the +// amount of media that has played back and the amount of media that has +// loaded, respectively. +.video-js .vjs-progress-holder .vjs-play-progress, +.video-js .vjs-progress-holder .vjs-load-progress, +.video-js .vjs-progress-holder .vjs-load-progress div { + position: absolute; + display: block; + height: 100%; + margin: 0; + padding: 0; + // updated by javascript during playback + width: 0; +} + +.video-js .vjs-play-progress { + background-color: $primary-foreground-color; + @extend .vjs-icon-circle; + + // Progress handle + &:before { + font-size: 0.9em; + position: absolute; + right: -0.5em; + line-height: .35em; + z-index: 1; + } +} + +// Remove content from play-progress when using SVGs. +.vjs-svg-icons-enabled .vjs-play-progress { + &:before { + content: none !important; + } +} + +.vjs-play-progress .vjs-svg-icon { + position: absolute; + top: -0.35em; + right: -0.4em; + width: 0.9em; + height: 0.9em; + pointer-events: none; + line-height: 0.15em; + z-index: 1; +} + +.video-js .vjs-load-progress { + background: rgba($secondary-background-color, $secondary-background-transparency); +} + +// There are child elements of the load progress bar that represent the +// specific time ranges that have been buffered. +.video-js .vjs-load-progress div { + background: rgba($secondary-background-color, 0.75); +} + +// .vjs-time-tooltip +// +// These elements are displayed above the progress bar. +// +// By default, they are hidden and only shown when hovering over the progress +// control. +.video-js .vjs-time-tooltip { + @include background-color-with-alpha(#fff, 0.8); + @include border-radius(0.3em); + color: #000; + + // By floating the tooltips to the right, their right edge becomes aligned + // with the right edge of their parent element. However, in order to have them + // centered, they must be pulled further to the right via positioning (e.g. + // `right: -10px;`. This part is left to JavaScript. + float: right; + font-family: $text-font-family; + + // The font-size should translate to a consistent 10px for time tooltips in + // all states. This is tricky because the .vjs-progress-holder element + // changes its font-size when the .vjs-progress-control is hovered. + font-size: 1em; + padding: 6px 8px 8px 8px; + pointer-events: none; + position: absolute; + top: -3.4em; + visibility: hidden; + z-index: 1; +} + +.video-js .vjs-progress-holder:focus .vjs-time-tooltip { + display: none; +} + +.video-js .vjs-progress-control:hover .vjs-time-tooltip, +.video-js .vjs-progress-control:hover .vjs-progress-holder:focus .vjs-time-tooltip, +.video-js.vjs-scrubbing.vjs-touch-enabled .vjs-progress-control .vjs-time-tooltip { + display: block; + + // Ensure that we maintain a font-size of ~10px. + font-size: 0.6em; + visibility: visible; +} + +.video-js .vjs-progress-control.disabled:hover .vjs-time-tooltip { + font-size: 1em; +} + +// .vjs-mouse-display / MouseTimeDisplay +// +// This element tracks the mouse position along the progress control and +// includes a tooltip, which displays the time at that point in the media. +.video-js .vjs-progress-control .vjs-mouse-display { + display: none; + position: absolute; + width: 1px; + height: 100%; + background-color: #000; + z-index: 1; +} + +.video-js .vjs-progress-control:hover .vjs-mouse-display { + display: block; +} + +.video-js.vjs-scrubbing.vjs-touch-enabled .vjs-progress-control .vjs-mouse-display { + display: block; +} + +.video-js.vjs-user-inactive .vjs-progress-control .vjs-mouse-display, +.video-js.vjs-touch-enabled:not(.vjs-scrubbing) .vjs-progress-control .vjs-mouse-display { + visibility: hidden; + opacity: 0; + $trans: visibility 1.0s, opacity 1.0s; + @include transition($trans); +} + +.vjs-mouse-display .vjs-time-tooltip { + color: #fff; + @include background-color-with-alpha(#000, 0.8); +} diff --git a/javascript/videojs/src/css/components/_skip-buttons.scss b/javascript/videojs/src/css/components/_skip-buttons.scss new file mode 100644 index 0000000..135e895 --- /dev/null +++ b/javascript/videojs/src/css/components/_skip-buttons.scss @@ -0,0 +1,40 @@ +.video-js .vjs-skip-forward-5 { + cursor: pointer; + & .vjs-icon-placeholder { + @extend .vjs-icon-forward-5; + } +} + +.video-js .vjs-skip-forward-10 { + cursor: pointer; + & .vjs-icon-placeholder { + @extend .vjs-icon-forward-10; + } +} +.video-js .vjs-skip-forward-30 { + cursor: pointer; + & .vjs-icon-placeholder { + @extend .vjs-icon-forward-30; + } +} + +.video-js .vjs-skip-backward-5 { + cursor: pointer; + & .vjs-icon-placeholder { + @extend .vjs-icon-replay-5; + } +} + +.video-js .vjs-skip-backward-10 { + cursor: pointer; + & .vjs-icon-placeholder { + @extend .vjs-icon-replay-10; + } +} + +.video-js .vjs-skip-backward-30 { + cursor: pointer; + & .vjs-icon-placeholder { + @extend .vjs-icon-replay-30; + } +} diff --git a/javascript/videojs/src/css/components/_slider.scss b/javascript/videojs/src/css/components/_slider.scss new file mode 100644 index 0000000..2843fd3 --- /dev/null +++ b/javascript/videojs/src/css/components/_slider.scss @@ -0,0 +1,25 @@ +.video-js .vjs-slider { + position: relative; + cursor: pointer; + padding: 0; + margin: 0 0.45em 0 0.45em; + + @include user-select(none); + + @include background-color-with-alpha($secondary-background-color, $secondary-background-transparency); + } + +.video-js .vjs-slider.disabled { + cursor: default; +} + +.video-js .vjs-slider:focus { + text-shadow: 0em 0em 1em rgba($primary-foreground-color, 1); + + @include box-shadow(0 0 1em $primary-foreground-color); +} + +// Replacement for focus in case spatial navigation is enabled +.video-js.vjs-spatial-navigation-enabled .vjs-slider:focus { + outline: 0.0625em solid rgba($primary-foreground-color, 1); +} diff --git a/javascript/videojs/src/css/components/_subs-caps.scss b/javascript/videojs/src/css/components/_subs-caps.scss new file mode 100644 index 0000000..e22a59d --- /dev/null +++ b/javascript/videojs/src/css/components/_subs-caps.scss @@ -0,0 +1,36 @@ +// North America uses 'CC' icon +.video-js:lang(en) .vjs-subs-caps-button .vjs-icon-placeholder, +.video-js:lang(fr-CA) .vjs-subs-caps-button .vjs-icon-placeholder { + @extend .vjs-icon-captions; +} + +// ROW uses 'subtitles' +// Double selector because @extend puts these rules above the captions icon +.video-js .vjs-subs-caps-button .vjs-icon-placeholder, +.video-js.video-js:lang(en-GB) .vjs-subs-caps-button .vjs-icon-placeholder, +.video-js.video-js:lang(en-IE) .vjs-subs-caps-button .vjs-icon-placeholder, +.video-js.video-js:lang(en-AU) .vjs-subs-caps-button .vjs-icon-placeholder, +.video-js.video-js:lang(en-NZ) .vjs-subs-caps-button .vjs-icon-placeholder { + @extend .vjs-icon-subtitles; +} + +.vjs-subs-caps-button + .vjs-menu .vjs-captions-menu-item .vjs-svg-icon { + width: 1.5em; + height: 1.5em; +} + +.video-js .vjs-subs-caps-button + .vjs-menu .vjs-captions-menu-item .vjs-menu-item-text .vjs-icon-placeholder { + vertical-align: middle; + display: inline-block; + margin-bottom: -0.1em; +} +.video-js .vjs-subs-caps-button + .vjs-menu .vjs-captions-menu-item .vjs-menu-item-text .vjs-icon-placeholder:before { + font-family: VideoJS; + content: "\f10c"; + font-size: 1.5em; + line-height: inherit; +} + +.video-js.vjs-audio-only-mode .vjs-subs-caps-button { + display: none; +} diff --git a/javascript/videojs/src/css/components/_subtitles.scss b/javascript/videojs/src/css/components/_subtitles.scss new file mode 100644 index 0000000..fb5da83 --- /dev/null +++ b/javascript/videojs/src/css/components/_subtitles.scss @@ -0,0 +1,3 @@ +.video-js .vjs-subtitles-button .vjs-icon-placeholder { + @extend .vjs-icon-subtitles; +} diff --git a/javascript/videojs/src/css/components/_text-track.scss b/javascript/videojs/src/css/components/_text-track.scss new file mode 100644 index 0000000..5b58f3c --- /dev/null +++ b/javascript/videojs/src/css/components/_text-track.scss @@ -0,0 +1,57 @@ +// Emulated tracks +.vjs-text-track-display { + position: absolute; + bottom: 3em; + left: 0; + right: 0; + top: 0; + pointer-events: none; +} + +// Hide if an error occurs +.vjs-error .vjs-text-track-display { + display: none; +} + +// Move captions down when controls aren't being shown +.video-js.vjs-controls-disabled .vjs-text-track-display, +.video-js.vjs-user-inactive.vjs-playing .vjs-text-track-display { + bottom: 1em; +} + +// Individual tracks +.video-js .vjs-text-track { + font-size: 1.4em; + text-align: center; + margin-bottom: 0.1em; +} + +.vjs-subtitles { color: #fff; } // Subtitles are white +.vjs-captions { color: #fc6; } // Captions are yellow +.vjs-tt-cue { display: block; } + +// Native tracks +video::-webkit-media-text-track-display { + @include transform(translateY(-3em)); +} + +// Move captions down when controls aren't being shown +.video-js.vjs-controls-disabled video::-webkit-media-text-track-display, +.video-js.vjs-user-inactive.vjs-playing video::-webkit-media-text-track-display { + @include transform(translateY(-1.5em)); +} + +// force cues to be center aligned +.video-js.vjs-force-center-align-cues .vjs-text-track-cue { + text-align: center !important; + width: 80% !important; +} + +@supports not (inset: 10px) { + .video-js .vjs-text-track-display > div { + top: 0; + right: 0; + bottom: 0; + left: 0; + } +} diff --git a/javascript/videojs/src/css/components/_time.scss b/javascript/videojs/src/css/components/_time.scss new file mode 100644 index 0000000..cec3411 --- /dev/null +++ b/javascript/videojs/src/css/components/_time.scss @@ -0,0 +1,25 @@ +.video-js .vjs-time-control { + @include flex(none); + font-size: 1em; + line-height: 3em; + min-width: 2em; + width: auto; + padding-left: 1em; + padding-right: 1em; +} + +.vjs-live .vjs-time-control, +.vjs-live .vjs-time-divider, +.video-js .vjs-current-time, +.video-js .vjs-duration { + display: none; +} + +.vjs-time-divider { + display: none; + line-height: 3em; +} + +.vjs-normalise-time-controls:not(.vjs-live) .vjs-time-control { + display: flex; +} diff --git a/javascript/videojs/src/css/components/_title-bar.scss b/javascript/videojs/src/css/components/_title-bar.scss new file mode 100644 index 0000000..19b5b6b --- /dev/null +++ b/javascript/videojs/src/css/components/_title-bar.scss @@ -0,0 +1,46 @@ +.vjs-title-bar { + + // At a base inherited font-size of 10px, the title bar overall height should + // be 96px with the area of text occupying the first 48px and the rest being + // padding. This leaves plenty of room for the gradient to fade to + // transparent while maintaining an WCAG AA-compliant contrast ratio (tested + // using the TPGi Color Contrast Analyzer application) even on top of a solid + // white background. + @include linear-gradient( + 180deg, + rgba(0, 0, 0, 0.9) 0%, + rgba(0, 0, 0, 0.7) 60%, + rgba(0, 0, 0, 0) 100% + ); + font-size: 1.2em; // 12px + line-height: 1.5; // 18px + @include transition(opacity 0.1s); + padding: 0.666em 1.333em 4em; // 8px 16px 48px + pointer-events: none; + position: absolute; + top: 0; + width: 100%; +} + +// Hide if an error occurs +.vjs-error .vjs-title-bar { + display: none; +} + +.vjs-title-bar-title, +.vjs-title-bar-description { + margin: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.vjs-title-bar-title { + font-weight: bold; + margin-bottom: 0.333em; // 4px +} + +.vjs-playing.vjs-user-inactive .vjs-title-bar { + opacity: 0; + @include transition(opacity 1s); +} diff --git a/javascript/videojs/src/css/components/_transient-button.scss b/javascript/videojs/src/css/components/_transient-button.scss new file mode 100644 index 0000000..ddc866f --- /dev/null +++ b/javascript/videojs/src/css/components/_transient-button.scss @@ -0,0 +1,48 @@ +.video-js .vjs-transient-button { + position: absolute; + height: 3em; + display: flex; + align-items: center; + justify-content: center; + background-color: rgba(50, 50, 50, 0.5); + cursor: pointer; + opacity: 1; + transition: opacity 1s; +} + +.video-js:not(.vjs-has-started) .vjs-transient-button { + display: none; +} + +.video-js.not-hover .vjs-transient-button:not(.force-display), +.video-js.vjs-user-inactive .vjs-transient-button:not(.force-display) { + opacity: 0; +} + +.video-js .vjs-transient-button span { + padding: 0 0.5em; +} + +.video-js .vjs-transient-button.vjs-left { + left: 1em; +} + +.video-js .vjs-transient-button.vjs-right { + right: 1em; +} + +.video-js .vjs-transient-button.vjs-top { + top: 1em; +} + +.video-js .vjs-transient-button.vjs-near-top { + top: 4em; +} + +.video-js .vjs-transient-button.vjs-bottom { + bottom: 4em; +} + +.video-js .vjs-transient-button:hover { + background-color: rgba(50, 50, 50, 0.9); +} diff --git a/javascript/videojs/src/css/components/_volume.scss b/javascript/videojs/src/css/components/_volume.scss new file mode 100644 index 0000000..f355a93 --- /dev/null +++ b/javascript/videojs/src/css/components/_volume.scss @@ -0,0 +1,268 @@ +.video-js .vjs-mute-control { + cursor: pointer; + @include flex(none); + + & .vjs-icon-placeholder { + @extend .vjs-icon-volume-high; + } +} + +.video-js .vjs-mute-control.vjs-vol-0 .vjs-icon-placeholder { + @extend .vjs-icon-volume-mute; +} +.video-js .vjs-mute-control.vjs-vol-1 .vjs-icon-placeholder { + @extend .vjs-icon-volume-low; +} +.video-js .vjs-mute-control.vjs-vol-2 .vjs-icon-placeholder { + @extend .vjs-icon-volume-mid; +} + +.video-js .vjs-volume-control { + cursor: pointer; + margin-right: 1em; + @include display-flex; +} +.video-js .vjs-volume-control.vjs-volume-horizontal { + width: 5em; +} + +.video-js .vjs-volume-panel .vjs-volume-control { + visibility: visible; + opacity: 0; + width: 1px; + height: 1px; + margin-left: -1px; +} + +.video-js .vjs-volume-panel { + @include transition(width 1s); + + &.vjs-hover .vjs-volume-control, + &:active .vjs-volume-control, + &:focus .vjs-volume-control, + & .vjs-volume-control:active, + &.vjs-hover .vjs-mute-control ~ .vjs-volume-control, + & .vjs-volume-control.vjs-slider-active { + visibility: visible; + opacity: 1; + position: relative; + $transition-property: visibility 0.1s, opacity 0.1s, height 0.1s, width 0.1s, left 0s, top 0s; + @include transition($transition-property); + + &.vjs-volume-horizontal { + width: 5em; + height: 3em; + margin-right: 0; + } + + &.vjs-volume-vertical { + left: -3.5em; + @include transition(left 0s); + } + } + + &.vjs-volume-panel-horizontal { + &.vjs-hover, + &:active, + &.vjs-slider-active { + width: 10em; + + @include transition(width 0.1s); + } + &.vjs-mute-toggle-only { + width: 4em; + } + } +} + +.video-js .vjs-volume-panel .vjs-volume-control.vjs-volume-vertical { + height: 8em; + width: 3em; + left: -3000em; + + $transition-property: visibility 1s, opacity 1s, height 1s 1s, width 1s 1s, left 1s 1s, top 1s 1s; + @include transition($transition-property) +} + +.video-js .vjs-volume-panel .vjs-volume-control.vjs-volume-horizontal { + $transition-property: visibility 1s, opacity 1s, height 1s 1s, width 1s, left 1s 1s, top 1s 1s; + @include transition($transition-property) +} + +.video-js .vjs-volume-panel { + @include display-flex; +} + +.video-js .vjs-volume-bar { + margin: 1.35em 0.45em; +} + +.vjs-volume-bar.vjs-slider-horizontal { + width: 5em; + height: 0.3em; +} + +.vjs-volume-bar.vjs-slider-vertical { + width: 0.3em; + height: 5em; + margin: 1.35em auto; +} + +.video-js .vjs-volume-level { + position: absolute; + bottom: 0; + left: 0; + + background-color: $primary-foreground-color; + + @extend .vjs-icon-circle; + + // Volume handle + &:before { + position: absolute; + font-size: 0.9em; // Doing this to match the handle on play progress. + z-index: 1; + } +} + +.vjs-slider-vertical .vjs-volume-level { + width: 0.3em; + + // Volume handle + &:before { + top: -0.5em; + left: -0.3em; + z-index: 1; + } +} +// Remove content from volume-level when using SVGs. +.vjs-svg-icons-enabled .vjs-volume-level { + &:before { + content: none; + } +} + +.vjs-volume-level .vjs-svg-icon { + position: absolute; + width: 0.9em; + height: 0.9em; + pointer-events: none; + z-index: 1; +} + +.vjs-slider-horizontal .vjs-volume-level { + height: 0.3em; + + // Volume handle + &:before { + line-height: .35em; + right: -0.5em; + } +} + +// Update placement of circle icon when using SVG icons +.vjs-slider-horizontal .vjs-volume-level .vjs-svg-icon { + right: -0.3em; + transform: translateY(-50%); +} +.vjs-slider-vertical .vjs-volume-level .vjs-svg-icon { + top: -0.55em; + transform: translateX(-50%); +} + +.video-js .vjs-volume-panel.vjs-volume-panel-vertical { + width: 4em; +} + +// Assumes volume starts at 1.0. +.vjs-volume-bar.vjs-slider-vertical .vjs-volume-level { + height: 100%; +} + +.vjs-volume-bar.vjs-slider-horizontal .vjs-volume-level { + width: 100%; +} + +.video-js .vjs-volume-vertical { + width: 3em; + height: 8em; + bottom: 8em; + + @include background-color-with-alpha($primary-background-color, $primary-background-transparency); +} + +.video-js .vjs-volume-horizontal .vjs-menu { + left: -2em; +} + +// .vjs-volume-tooltip +// +// These elements are displayed above the volume bar. +// +// By default, they are hidden and only shown when hovering over the volume +// control. +.video-js .vjs-volume-tooltip { + @include background-color-with-alpha(#fff, 0.8); + @include border-radius(0.3em); + color: #000; + float: right; + font-family: $text-font-family; + font-size: 1em; + padding: 6px 8px 8px 8px; + pointer-events: none; + position: absolute; + top: -3.4em; + visibility: hidden; + z-index: 1; +} + +.video-js .vjs-volume-control:hover .vjs-volume-tooltip, +.video-js .vjs-volume-control:hover .vjs-progress-holder:focus .vjs-volume-tooltip { + display: block; + font-size: 1em; + visibility: visible; +} + +.video-js .vjs-volume-vertical:hover .vjs-volume-tooltip, +.video-js .vjs-volume-vertical:hover .vjs-progress-holder:focus .vjs-volume-tooltip { + left: 1em; + top: -12px; +} + +.video-js .vjs-volume-control.disabled:hover .vjs-volume-tooltip { + font-size: 1em; +} + +// .vjs-mouse-display / MouseVolumeLevelDisplay +// +// This element tracks the mouse position along the volume control and +// includes a tooltip, which displays the volume level. +.video-js .vjs-volume-control .vjs-mouse-display { + display: none; + position: absolute; + width: 100%; + height: 1px; + background-color: #000; + z-index: 1; +} + +.video-js .vjs-volume-horizontal .vjs-mouse-display { + width: 1px; + height: 100%; +} + +.video-js .vjs-volume-control:hover .vjs-mouse-display { + display: block; +} + +.video-js.vjs-user-inactive .vjs-volume-control .vjs-mouse-display { + visibility: hidden; + opacity: 0; + $trans: visibility 1.0s, opacity 1.0s; + @include transition($trans); +} + +.vjs-mouse-display .vjs-volume-tooltip { + color: #fff; + @include background-color-with-alpha(#000, 0.8); +} diff --git a/javascript/videojs/src/css/components/menu/_menu-inline.scss b/javascript/videojs/src/css/components/menu/_menu-inline.scss new file mode 100644 index 0000000..4ed1f5c --- /dev/null +++ b/javascript/videojs/src/css/components/menu/_menu-inline.scss @@ -0,0 +1,48 @@ +.video-js .vjs-menu-button-inline { + @include transition(all 0.4s); + overflow: hidden; +} + +.video-js .vjs-menu-button-inline:before { + // Icon pseudoelement has a different base font size (1.8em), so we need to + // account for that in the width. 4em (standard button width) divided by 1.8 + // to get the same button width as normal. + width: 2.222222222em; +} + +// Hover state +.video-js .vjs-menu-button-inline:hover, +.video-js .vjs-menu-button-inline:focus, +.video-js .vjs-menu-button-inline.vjs-slider-active { + // This width is currently specific to the inline volume bar. + width: 12em; +} + +.vjs-menu-button-inline .vjs-menu { + opacity: 0; + height: 100%; + width: auto; + + position: absolute; + left: 4em; + top: 0; + + padding: 0; + margin: 0; + + @include transition(all 0.4s); +} + +.vjs-menu-button-inline:hover .vjs-menu, +.vjs-menu-button-inline:focus .vjs-menu, +.vjs-menu-button-inline.vjs-slider-active .vjs-menu { + display: block; + opacity: 1; +} + +.vjs-menu-button-inline .vjs-menu-content { + width: auto; + height: 100%; + margin: 0; + overflow: hidden; +} diff --git a/javascript/videojs/src/css/components/menu/_menu-popup.scss b/javascript/videojs/src/css/components/menu/_menu-popup.scss new file mode 100644 index 0000000..550c761 --- /dev/null +++ b/javascript/videojs/src/css/components/menu/_menu-popup.scss @@ -0,0 +1,49 @@ +.vjs-menu-button-popup .vjs-menu { + display: none; + position: absolute; + bottom: 0; + width: 10em; + left: -3em; // (Width of vjs-menu - width of button) / 2 + height: 0em; + margin-bottom: 1.5em; + border-top-color: rgba($primary-background-color, $primary-background-transparency); // Same as ul background +} + +.vjs-pip-window .vjs-menu-button-popup .vjs-menu { + left: unset; + right: 1em; // Extra offset for last menu button in pip window, as fullscreen button not present +} + +// Button Pop-up Menu +.vjs-menu-button-popup .vjs-menu .vjs-menu-content { + @include background-color-with-alpha($primary-background-color, $primary-background-transparency); + + position: absolute; + width: 100%; + bottom: 1.5em; // Same bottom as vjs-menu border-top + max-height: 15em; +} + +.vjs-layout-tiny .vjs-menu-button-popup .vjs-menu .vjs-menu-content, +.vjs-layout-x-small .vjs-menu-button-popup .vjs-menu .vjs-menu-content { + max-height: 5em; +} + +.vjs-layout-small .vjs-menu-button-popup .vjs-menu .vjs-menu-content { + max-height: 10em; +} + +.vjs-layout-medium .vjs-menu-button-popup .vjs-menu .vjs-menu-content { + max-height: 14em; +} + +.vjs-layout-large .vjs-menu-button-popup .vjs-menu .vjs-menu-content, +.vjs-layout-x-large .vjs-menu-button-popup .vjs-menu .vjs-menu-content, +.vjs-layout-huge .vjs-menu-button-popup .vjs-menu .vjs-menu-content { + max-height: 25em; +} + +.vjs-workinghover .vjs-menu-button-popup.vjs-hover .vjs-menu, +.vjs-menu-button-popup .vjs-menu.vjs-lock-showing { + display: block; +} diff --git a/javascript/videojs/src/css/components/menu/_menu.scss b/javascript/videojs/src/css/components/menu/_menu.scss new file mode 100644 index 0000000..be53c3a --- /dev/null +++ b/javascript/videojs/src/css/components/menu/_menu.scss @@ -0,0 +1,80 @@ +.vjs-menu-button { + cursor: pointer; +} + +// Change cursor back to default if the menu button is disabled +.vjs-menu-button.vjs-disabled { + cursor: default; +} + +// prevent menus from opening while disabled +.vjs-workinghover .vjs-menu-button.vjs-disabled:hover .vjs-menu { + display: none; +} + +.vjs-menu .vjs-menu-content { + display: block; + padding: 0; + margin: 0; + font-family: $text-font-family; + + // This allows scrolling of content if need be. + overflow: auto; +} + +// Reset box-sizing inside the menu. +.vjs-menu .vjs-menu-content > * { + box-sizing: border-box; +} + +// prevent menus from opening while scrubbing +.vjs-scrubbing .vjs-control.vjs-menu-button:hover .vjs-menu { + display: none; +} + +.vjs-menu li { + display: flex; + justify-content: center; + list-style: none; + margin: 0; + padding: 0.2em 0; + line-height: 1.4em; + font-size: 1.2em; + text-align: center; + text-transform: lowercase; +} + +.vjs-menu li.vjs-menu-item:focus, +.vjs-menu li.vjs-menu-item:hover, +.js-focus-visible .vjs-menu li.vjs-menu-item:hover { + @include background-color-with-alpha($secondary-background-color, $secondary-background-transparency); +} + +.vjs-menu li.vjs-selected, +.vjs-menu li.vjs-selected:focus, +.vjs-menu li.vjs-selected:hover, +.js-focus-visible .vjs-menu li.vjs-selected:hover { + background-color: $primary-foreground-color; + color: $primary-background-color; + + // Change the SVG color when an item is selected + .vjs-svg-icon { + fill: #000000; + } +} + +.video-js .vjs-menu *:not(.vjs-selected):focus:not(:focus-visible), +.js-focus-visible .vjs-menu *:not(.vjs-selected):focus:not(.focus-visible) { + background: none; +} + +.vjs-menu li.vjs-menu-title { + text-align: center; + text-transform: uppercase; + font-size: 1em; + line-height: 2em; + padding: 0; + margin: 0 0 0.3em 0; + font-weight: bold; + cursor: default; +} diff --git a/javascript/videojs/src/css/utilities/_linear-gradient.scss b/javascript/videojs/src/css/utilities/_linear-gradient.scss new file mode 100644 index 0000000..5adc2eb --- /dev/null +++ b/javascript/videojs/src/css/utilities/_linear-gradient.scss @@ -0,0 +1,93 @@ +@use "sass:math"; + +// These functions and mixins taken from: +// +// "Building a linear-gradient Mixin in Sass" by Hugo Giraudel +// http://www.sitepoint.com/building-linear-gradient-mixin-sass/ +// http://sassmeister.com/gist/b58f6e2cc3160007c880 +// + +/// Convert angle +/// @author Chris Eppstein +/// @param {Number} $value - Value to convert +/// @param {String} $unit - Unit to convert to +/// @return {Number} Converted angle +@function convert-angle($value, $unit) { + $convertable-units: deg grad turn rad; + $conversion-factors: 1 math.div(10grad, 9deg) math.div(1turn, 360deg) math.div(3.1415926rad, 180deg); + @if index($convertable-units, unit($value)) and index($convertable-units, $unit) { + @return math.div($value, nth($conversion-factors, index($convertable-units, unit($value)))) + * nth($conversion-factors, index($convertable-units, $unit)); + } + + @warn "Cannot convert `#{unit($value)}` to `#{$unit}`."; +} + +/// Test if `$value` is an angle +/// @param {*} $value - Value to test +/// @return {Bool} +@function is-direction($value) { + $is-direction: index(( + 'to top', + 'to top right', + 'to right top', + 'to right', + 'to bottom right', + 'to right bottom', + 'to bottom', + 'to bottom left', + 'to left bottom', + 'to left', + 'to left top', + 'to top left' + ), $value); + $is-angle: type-of($value) == 'number' and index('deg' 'grad' 'turn' 'rad', unit($value)); + + @return $is-direction or $is-angle; +} + +/// Convert a direction to legacy syntax +/// @param {Keyword | Angle} $value - Value to convert +/// @require {function} is-direction +/// @require {function} convert-angle +@function legacy-direction($value) { + @if is-direction($value) == false { + @warn "Cannot convert `#{$value}` to legacy syntax because it doesn't seem to be an angle or a direction"; + } + + $conversion-map: ( + 'to top' : 'bottom', + 'to top right' : 'bottom left', + 'to right top' : 'left bottom', + 'to right' : 'left', + 'to bottom right' : 'top left', + 'to right bottom' : 'left top', + 'to bottom' : 'top', + 'to bottom left' : 'top right', + 'to left bottom' : 'right top', + 'to left' : 'right', + 'to left top' : 'right bottom', + 'to top left' : 'bottom right' + ); + + @if map-has-key($conversion-map, $value) { + @return map-get($conversion-map, $value); + } + + @return 90deg - convert-angle($value, 'deg'); +} + +/// Mixin printing a linear-gradient +/// as well as a plain color fallback +/// @access public +/// @param {String | List | Angle} $direction - Linear gradient direction +/// @param {Arglist} $color-stops - List of color-stops composing the gradient +@mixin linear-gradient($direction, $color-stops...) { + @if is-direction($direction) == false { + $color-stops: ($direction, $color-stops); + $direction: 180deg; + } + + background: nth(nth($color-stops, 1), 1); + background: linear-gradient($direction, $color-stops); +} diff --git a/javascript/videojs/src/css/video-js.scss b/javascript/videojs/src/css/video-js.scss new file mode 100644 index 0000000..fcb8388 --- /dev/null +++ b/javascript/videojs/src/css/video-js.scss @@ -0,0 +1,68 @@ +@import "icons"; +@import "variables"; +@import "private-variables"; +@import "utilities"; + +@import "videojs-font/scss/icons"; + +@import "components/layout"; +@import "components/big-play"; +@import "components/button"; +@import "components/close-button"; +@import "components/modal-dialog"; + +@import "components/menu/menu"; +@import "components/menu/menu-popup"; +@import "components/menu/menu-inline"; + +@import "components/control-bar"; +@import "components/control"; +@import "components/control-spacer"; + +@import "components/progress"; +@import "components/slider"; + +@import "components/volume"; + +@import "components/poster"; +@import "components/live"; +@import "components/time"; +@import "components/play-pause"; +@import "components/text-track"; +@import "components/picture-in-picture"; +@import "components/fullscreen"; +@import "components/playback-rate"; +@import "components/error"; +@import "components/loading"; +@import "components/captions"; +@import "components/chapters"; +@import "components/descriptions"; +@import "components/subtitles"; +@import "components/subs-caps"; +@import "components/audio"; +@import "components/adaptive"; +@import "components/captions-settings"; +@import "components/title-bar"; +@import "components/skip-buttons"; +@import "components/transient-button"; + +@import "print"; + +.vjs-resize-manager { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + border: none; + z-index: -1000; +} + +// The rule is needed for :focus-visible polyfill +.js-focus-visible .video-js *:focus:not(.focus-visible) { + outline: none; +} + +.video-js *:focus:not(:focus-visible) { + outline: none; +} diff --git a/javascript/videojs/src/css/vjs-cdn.scss b/javascript/videojs/src/css/vjs-cdn.scss new file mode 100644 index 0000000..abf75b6 --- /dev/null +++ b/javascript/videojs/src/css/vjs-cdn.scss @@ -0,0 +1,3 @@ +$icon-font-path: '//vjs.zencdn.net/font/1.5.1'; + +@import 'video-js'; diff --git a/javascript/videojs/src/css/vjs.scss b/javascript/videojs/src/css/vjs.scss new file mode 100644 index 0000000..d80517f --- /dev/null +++ b/javascript/videojs/src/css/vjs.scss @@ -0,0 +1 @@ +@import "video-js"; |
