From bc7a6ebdde36a4f092f41196e2f7f9ea5007bc04 Mon Sep 17 00:00:00 2001 From: Sam Potts Date: Thu, 14 Jan 2016 21:20:46 +1100 Subject: Keyboard nav fixes (seek & focus trap in fullscreen), SASS updates --- src/js/plyr.js | 64 +++++++++--- src/less/plyr.less | 9 +- src/sass/plyr.scss | 243 +++++++++++++++++++++++--------------------- src/sprite/icon-restart.svg | 14 ++- 4 files changed, 190 insertions(+), 140 deletions(-) mode change 100644 => 100755 src/sprite/icon-restart.svg (limited to 'src') diff --git a/src/js/plyr.js b/src/js/plyr.js index fac7d555..1953f864 100644 --- a/src/js/plyr.js +++ b/src/js/plyr.js @@ -835,6 +835,44 @@ } } + // Trap focus inside container + function _focusTrap() { + var tabbables = _getElements('input:not([disabled]), button:not([disabled])'), + first = tabbables[0], + last = tabbables[tabbables.length - 1]; + + function _checkFocus(event) { + // If it is TAB + if (event.which === 9 && plyr.isFullscreen) { + // Move focus to first element that can be tabbed if Shift isn't used + if (event.target === last && !event.shiftKey) { + event.preventDefault(); + first.focus(); + } + // Move focus to last element that can be tabbed if Shift is used + else if (event.target === first && event.shiftKey) { + event.preventDefault(); + last.focus(); + } + } + } + + // Bind the handler + _on(plyr.container, 'keydown', _checkFocus); + } + + // Add elements to HTML5 media (source, tracks, etc) + function _insertChildElements(type, attributes) { + if (typeof attributes === 'string') { + _insertElement(type, plyr.media, { src: attributes }); + } + else if (attributes.constructor === Array) { + for (var i = attributes.length - 1; i >= 0; i--) { + _insertElement(type, plyr.media, attributes[i]); + } + } + } + // Insert controls function _injectControls() { // Make a copy of the html @@ -1481,6 +1519,9 @@ // Toggle state _toggleState(plyr.buttons.fullscreen, false); + // Setup focus trap + _focusTrap(); + // Set control hide class hook if (config.fullscreen.hideControls) { _toggleClass(plyr.container, config.classes.fullscreen.hideControls, true); @@ -1642,6 +1683,17 @@ // Set class hook _toggleClass(plyr.container, config.classes.fullscreen.active, plyr.isFullscreen); + // Trap focus + if(plyr.isFullscreen) { + plyr.container.setAttribute('tabindex', '-1'); + } + else { + plyr.container.removeAttribute('tabindex'); + } + + // Trap focus + _focusTrap(plyr.isFullscreen); + // Set button state _toggleState(plyr.buttons.fullscreen, plyr.isFullscreen); @@ -1940,18 +1992,6 @@ _updateProgress(event); } - // Add elements to HTML5 media (source, tracks, etc) - function _insertChildElements(type, attributes) { - if (typeof attributes === 'string') { - _insertElement(type, plyr.media, { src: attributes }); - } - else if (attributes.constructor === Array) { - for (var i = attributes.length - 1; i >= 0; i--) { - _insertElement(type, plyr.media, attributes[i]); - } - } - } - // Add common function to retrieve media source function _source(source) { // If not null or undefined, parse it diff --git a/src/less/plyr.less b/src/less/plyr.less index affa7cef..891b6f9d 100644 --- a/src/less/plyr.less +++ b/src/less/plyr.less @@ -111,7 +111,7 @@ background: @volume-thumb-bg; border: 0; border-radius: 100%; - transition: background .3s ease, transform .2s ease; + transition: background .3s ease; cursor: ew-resize; } .volume-track() { @@ -243,7 +243,7 @@ background: @controls-bg; line-height: 1; text-align: center; - box-shadow: 0 1px 1px rgba(red(@gray-dark), green(@gray-dark), blue(@gray-dark), .2); + box-shadow: 0 1px 1px fade(@gray-dark, 20%); // Layout &--right { @@ -283,7 +283,7 @@ } // Hover and tab focus - &.tab-focus, + &.tab-focus:focus, &:hover { background: @control-bg-hover; color: @control-color-hover; @@ -309,7 +309,6 @@ color: @control-color; font-weight: 600; font-size: @font-size-small; - .font-smoothing(); } // Media duration hidden on small screens @@ -388,7 +387,7 @@ } // Common range styles - input[type='range'].tab-focus { + input[type='range'].tab-focus:focus { .tab-focus(); } diff --git a/src/sass/plyr.scss b/src/sass/plyr.scss index 7cefca3a..8bf481db 100644 --- a/src/sass/plyr.scss +++ b/src/sass/plyr.scss @@ -32,14 +32,16 @@ $control-color-hover: null !default; // Contrast @if lightness($controls-bg) >= 65% { - $control-color: $gray-light; -} @else { - $control-color: $gray-lighter; + $control-color: $gray-light; +} +@else { + $control-color: $gray-lighter; } @if lightness($control-bg-hover) >= 65% { - $control-color-hover: $gray; -} @else { - $control-color-hover: #fff; + $control-color-hover: $gray; +} +@else { + $control-color-hover: #fff; } // Tooltips @@ -48,7 +50,7 @@ $tooltip-border-color: transparentize(@gray-dark, .1) !default; $tooltip-border-width: 1px; $tooltip-shadow: 0 0 5px $tooltip-border-color, 0 0 0 $tooltip-border-width $tooltip-border-color; $tooltip-color: $control-color !default; -$tooltip-padding: $control-spacing !default; +$tooltip-padding: $control-spacing !default; $tooltip-arrow-size: 6px !default; $tooltip-radius: 3px !default; @@ -80,77 +82,61 @@ $bp-captions-large: 768px !default; // When captions jump to the larger // Font smoothing @mixin font-smoothing($mode: on) { - @if ($mode == 'on') { - -moz-osx-font-smoothing: grayscale; - -webkit-font-smoothing: antialiased; - } - @else if ($mode == 'off') { - -moz-osx-font-smoothing: auto; - -webkit-font-smoothing: subpixel-antialiased; - } + @if ($mode == 'on') { + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + } + @else if ($mode == 'off') { + -moz-osx-font-smoothing: auto; + -webkit-font-smoothing: subpixel-antialiased; + } } // Contain floats: nicolasgallagher.com/micro-clearfix-hack/ @mixin clearfix() { zoom: 1; - &:before, + &:before, &:after { content: ''; display: table; } &:after { clear: both; } } // Tab focus styles -@mixin tab-focus() -{ - outline: thin dotted #000; - outline-offset: 0; +@mixin tab-focus() { + outline: thin dotted transparentize($gray-dark, .8); + outline-offset: 3px; } // styling -@mixin volume-thumb() -{ +@mixin volume-thumb() { height: $volume-thumb-height; width: $volume-thumb-width; background: $volume-thumb-bg; border: 0; - border-radius: ($volume-thumb-height / 2); + border-radius: 100%; transition: background .3s ease; cursor: ew-resize; } -@mixin volume-track() -{ +@mixin volume-track() { height: $volume-track-height; background: $volume-track-bg; border: 0; border-radius: ($volume-track-height / 2); } -@mixin seek-thumb() -{ +@mixin seek-thumb() { background: transparent; border: 0; - width: ($control-spacing * 2); + width: ($control-spacing * 4); height: $control-spacing; + transform: translateX(-50%); } -@mixin seek-track() -{ +@mixin seek-track() { background: none; border: 0; } -// Screen reader only -// ------------------------------- -.sr-only { - position: absolute !important; - clip: rect(1px, 1px, 1px, 1px); - padding: 0 !important; - border: 0 !important; - height: 1px !important; - width: 1px !important; - overflow: hidden; -} - // Styles // ------------------------------- -// Base +// Base .plyr { position: relative; max-width: 100%; @@ -161,12 +147,28 @@ $bp-captions-large: 768px !default; // When captions jump to the larger &, *, *::after, - *::before { - box-sizing: border-box; + *::before { + box-sizing: border-box; + } + + // Fix 300ms delay + a, button, input, label { + touch-action: manipulation; + } + + // Screen reader only + &__sr-only { + position: absolute !important; + clip: rect(1px, 1px, 1px, 1px); + padding: 0 !important; + border: 0 !important; + height: 1px !important; + width: 1px !important; + overflow: hidden; } // For video - &-video-wrapper { + &__video-wrapper { position: relative; } video, @@ -177,12 +179,12 @@ $bp-captions-large: 768px !default; // When captions jump to the larger } // For embeds - &-video-embed { + &__video-embed { padding-bottom: 56.25%; /* 16:9 */ height: 0; overflow: hidden; background: #000; - + iframe { position: absolute; top: 0; @@ -201,7 +203,7 @@ $bp-captions-large: 768px !default; // When captions jump to the larger } // Captions - &-captions { + &__captions { display: none; position: absolute; bottom: 0; @@ -226,15 +228,15 @@ $bp-captions-large: 768px !default; // When captions jump to the larger font-size: $font-size-captions-medium; } } - &.captions-active &-captions { + &--captions-active &__captions { display: block; } - &.fullscreen-active &-captions { + &--fullscreen-active &__captions { font-size: $font-size-captions-large; } // Playback controls - &-controls { + &__controls { @include clearfix(); @include font-smoothing(); position: relative; @@ -242,23 +244,23 @@ $bp-captions-large: 768px !default; // When captions jump to the larger background: $controls-bg; line-height: 1; text-align: center; - box-shadow: 0 1px 1px rgba(red($gray-dark), green($gray-dark), blue($gray-dark), .2); + box-shadow: 0 1px 1px transparentize($gray-dark, .2); // Layout - &-right { + &--right { display: block; margin: $control-spacing auto 0; } @media (min-width: $bp-control-split) { - &-left { + &--left { float: left; } - &-right { + &--right { float: right; margin-top: 0; } } - + // Buttons button { display: inline-block; @@ -272,7 +274,7 @@ $bp-captions-large: 768px !default; // When captions jump to the larger cursor: pointer; color: $control-color; transition: background .3s ease, color .3s ease, opacity .3s ease; - + svg { width: 18px; height: 18px; @@ -280,9 +282,9 @@ $bp-captions-large: 768px !default; // When captions jump to the larger fill: currentColor; transition: fill .3s ease; } - + // Hover and tab focus - &.tab-focus, + &.tab-focus:hover, &:hover { background: $control-bg-hover; color: $control-color-hover; @@ -292,27 +294,26 @@ $bp-captions-large: 768px !default; // When captions jump to the larger outline: 0; } } - + // Hide toggle icons by default - .icon-exit-fullscreen, - .icon-muted, - .icon-captions-on { + .icon--exit-fullscreen, + .icon--muted, + .icon--captions-on { display: none; } - + // Time display - .plyr-time { + .plyr__time { display: inline-block; vertical-align: middle; margin-left: $control-spacing; color: $control-color; font-weight: 600; font-size: $font-size-small; - @include font-smoothing(); } // Media duration hidden on small screens - .plyr-time + .plyr-time { + .plyr__time + .plyr__time { display: none; @media (min-width: $bp-control-split) { @@ -328,7 +329,7 @@ $bp-captions-large: 768px !default; // When captions jump to the larger } // Tooltips - &-tooltip { + &__tooltip { position: absolute; z-index: 2; bottom: 100%; @@ -367,8 +368,8 @@ $bp-captions-large: 768px !default; // When captions jump to the larger border-top: $border-arrow-size solid $tooltip-border-color; border-left: $border-arrow-size solid transparent; z-index: 1; - } - // The background triangle + } + // The background triangle &::before { bottom: -$tooltip-arrow-size; border-right: $tooltip-arrow-size solid transparent; @@ -377,15 +378,20 @@ $bp-captions-large: 768px !default; // When captions jump to the larger z-index: 2; } } - button:hover .plyr-tooltip, - button:focus .plyr-tooltip { + button:hover .plyr__tooltip, + button.tab-focus:focus .plyr__tooltip { opacity: 1; transform: translate(-50%, 0) scale(1); } - button:hover .plyr-tooltip { + button:hover .plyr__tooltip { z-index: 3; } + // Common range styles + input[type='range'].tab-focus:focus { + .tab-focus(); + } + // Playback progress // element &-progress { @@ -397,9 +403,9 @@ $bp-captions-large: 768px !default; // When captions jump to the larger height: $control-spacing; background: $progress-bg; - &-buffer[value], - &-played[value], - &-seek[type='range'] { + &--buffer[value], + &--played[value], + &--seek[type='range'] { position: absolute; left: 0; top: 0; @@ -414,8 +420,8 @@ $bp-captions-large: 768px !default; // When captions jump to the larger border: none; background: transparent; } - &-buffer[value], - &-played[value] { + &--buffer[value], + &--played[value] { &::-webkit-progress-bar { background: transparent; } @@ -428,18 +434,18 @@ $bp-captions-large: 768px !default; // When captions jump to the larger background: currentColor; } } - &-played[value] { + &--played[value] { z-index: 2; color: $progress-playing-bg; } - &-buffer[value] { + &--buffer[value] { color: $progress-buffered-bg; } // Seek control // element // Specificity is for bootstrap compatibility - &-seek[type='range'] { + &--seek[type='range'] { z-index: 4; cursor: pointer; outline: 0; @@ -461,7 +467,7 @@ $bp-captions-large: 768px !default; // When captions jump to the larger -moz-appearance: none; @include seek-thumb(); } - + // Microsoft &::-ms-track { color: transparent; @@ -485,47 +491,47 @@ $bp-captions-large: 768px !default; // When captions jump to the larger } // Loading state - &.loading .plyr-progress-buffer { + &--loading .plyr__progress--buffer { animation: progress 1s linear infinite; background-size: $progress-loading-size $progress-loading-size; background-repeat: repeat-x; background-color: $progress-buffered-bg; background-image: linear-gradient( - -45deg, - $progress-loading-bg 25%, - transparent 25%, - transparent 50%, - $progress-loading-bg 50%, + -45deg, + $progress-loading-bg 25%, + transparent 25%, + transparent 50%, + $progress-loading-bg 50%, $progress-loading-bg 75%, - transparent 75%, + transparent 75%, transparent); color: transparent; } // States - &-controls [data-plyr='pause'], - &.playing .plyr-controls [data-plyr='play'] { + &__controls [data-plyr='pause'], + &--playing .plyr__controls [data-plyr='play'] { display: none; } - &.playing .plyr-controls [data-plyr='pause'] { + &--playing .plyr__controls [data-plyr='pause'] { display: inline-block; } // Volume control // element // Specificity is for bootstrap compatibility - &-volume[type='range'] { + &__volume[type='range'] { display: inline-block; vertical-align: middle; -webkit-appearance: none; -moz-appearance: none; width: 100px; margin: 0 $control-spacing 0 0; - padding: 0; + padding: 0; cursor: pointer; background: transparent; border: none; - + // Webkit &::-webkit-slider-runnable-track { @include volume-track(); @@ -543,7 +549,7 @@ $bp-captions-large: 768px !default; // When captions jump to the larger &::-moz-range-thumb { @include volume-thumb(); } - + // Microsoft &::-ms-track { height: $volume-track-height; @@ -578,30 +584,30 @@ $bp-captions-large: 768px !default; // When captions jump to the larger // Hide sound controls on iOS // It's not supported to change volume using JavaScript: // https://developer.apple.com/library/safari/documentation/AudioVideo/Conceptual/Using_HTML5_Audio_Video/Device-SpecificConsiderations/Device-SpecificConsiderations.html - &.ios &-volume, - &.ios [data-plyr='mute'], - &-audio.ios &-controls-right { + &--is-ios &__volume, + &--is-ios [data-plyr='mute'], + &--is-ios.plyr--audio &__controls--right { display: none; } // Center buttons so it looks less odd - &-audio.ios &-controls-left { + &--is-ios.plyr--audio &__controls--left { float: none; } // Audio specific styles // Position the progress within the container - &-audio .plyr-controls { + &--audio .plyr__controls { padding-top: ($control-spacing * 2); } - &-audio .plyr-progress { + &--audio .plyr__progress { bottom: auto; top: 0; background: $off-white; } // Full screen mode - &-fullscreen, - &.fullscreen-active { + &--fullscreen, + &--fullscreen-active { position: fixed; top: 0; left: 0; @@ -615,11 +621,11 @@ $bp-captions-large: 768px !default; // When captions jump to the larger video { height: 100%; } - .plyr-video-wrapper { + .plyr__video-wrapper { height: 100%; width: 100%; } - .plyr-controls { + .plyr__controls { position: absolute; bottom: 0; left: 0; @@ -627,23 +633,24 @@ $bp-captions-large: 768px !default; // When captions jump to the larger } // Hide controls when playing in full screen - &.fullscreen-hide-controls.playing { - .plyr-controls { + &--fullscreen--hide-controls&--fullscreen-active&--playing { + .plyr__controls { transform: translateY(100%) translateY($control-spacing / 2); transition: transform .3s .2s ease; } - &.plyr-hover .plyr-controls { + &.plyr--hover .plyr__controls { transform: translateY(0); } - .plyr-captions { + .plyr__captions { bottom: ($control-spacing / 2); transition: bottom .3s .2s ease; } } // Captions - .plyr-captions, - &.fullscreen-hide-controls.playing.plyr-hover .plyr-captions { + &--fullscreen .plyr__captions, + &--fullscreen-active .plyr__captions, + &--fullscreen--hide-controls&--fullscreen-active&--playing&--hover &__captions { top: auto; bottom: 90px; @@ -654,9 +661,9 @@ $bp-captions-large: 768px !default; // When captions jump to the larger } // Change icons on state change - &.fullscreen-active .icon-exit-fullscreen, - &.muted .plyr-controls .icon-muted, - &.captions-active .plyr-controls .icon-captions-on { + &--fullscreen-active .icon--exit-fullscreen, + &--muted .plyr__controls .icon--muted, + &--captions-active .plyr__controls .icon--captions-on { display: block; & + svg { @@ -669,8 +676,8 @@ $bp-captions-large: 768px !default; // When captions jump to the larger [data-plyr='fullscreen'] { display: none; } - &.captions-enabled [data-plyr='captions'], - &.fullscreen-enabled [data-plyr='fullscreen'] { + &--captions-enabled [data-plyr='captions'], + &--fullscreen-enabled [data-plyr='fullscreen'] { display: inline-block; } } diff --git a/src/sprite/icon-restart.svg b/src/sprite/icon-restart.svg old mode 100644 new mode 100755 index 1e393763..6cf89d8d --- a/src/sprite/icon-restart.svg +++ b/src/sprite/icon-restart.svg @@ -1,5 +1,9 @@ - - - Restart - - \ No newline at end of file + + + + + + -- cgit v1.2.3