Skip to content

Commit

Permalink
Merge pull request #921 from naweidner/tab-navigation
Browse files Browse the repository at this point in the history
Keyboard tab navigation
  • Loading branch information
LukasKalbertodt authored May 10, 2022
2 parents a58ec48 + 69c8a4d commit 9621709
Show file tree
Hide file tree
Showing 9 changed files with 71 additions and 19 deletions.
16 changes: 16 additions & 0 deletions src/style/global-style.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,22 @@ button {
50% { opacity: 0.6 }
to { opacity: 0.85 }
}
/* Remove outline for non-keyboard :focus */
*:focus:not(:focus-visible) {
outline: none !important;
box-shadow: 0 0 0 rgb(255, 255, 255) !important;
}
*:focus-visible {
outline: 5px solid #2e724f !important;
outline-offset: -5px;
}
button:focus-visible {
outline: 5px solid #2e724f !important;
outline-offset: -3px;
}
`;

export default GlobalStyle;
4 changes: 2 additions & 2 deletions src/theme.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ const base = {
...baseButton,
bg: 'error',
color: 'button_fg',
'&:not(:disabled):hover': {
'&:not(:disabled):hover, &:not(:disabled):focus': {
bg: darken('error', 0.03)
}
},
Expand All @@ -91,7 +91,7 @@ const base = {
bg: 'background',
color: 'text',
border: theme => `1px solid ${theme.colors.gray[1]}`,
'&:not(:disabled):hover': {
'&:not(:disabled):hover, &:not(:disabled):focus': {
bg: 'gray.3'
}
}
Expand Down
15 changes: 14 additions & 1 deletion src/ui/header.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,16 @@ export default function Header() {

const Brand = () => {
const location = useLocation();
const { isRecording } = useStudioState();

return (
<Link to={{ pathname: "/", search: location.search }}>
<Link to={{ pathname: "/", search: location.search }}
tabIndex={isRecording ? '-1' : '0'}
sx={{':focus-visible': {
outline: '5px solid #8ec8aa !important',
outlineOffset: '-5px',
}
}}>
<picture sx={{ display: 'block', height: theme => theme.heights.headerHeight }}>
<source
media="(min-width: 920px)"
Expand All @@ -85,13 +92,15 @@ const Brand = () => {
// One element (link) in the navigation.
const NavElement = ({ target, children, icon, ...rest }) => {
const location = useLocation();
const { isRecording } = useStudioState();

return (
<NavLink
to={{
pathname: target,
search: location.search,
}}
tabIndex={isRecording ? '-1' : '0'}
exact
activeStyle={{
backgroundColor: 'black',
Expand All @@ -110,6 +119,10 @@ const NavElement = ({ target, children, icon, ...rest }) => {
'&:hover': {
backgroundColor: 'gray.1',
},
':focus-visible': {
outline: '5px solid #8ec8aa !important',
outlineOffset: '-5px',
},
}}
{...rest}
>
Expand Down
1 change: 1 addition & 0 deletions src/ui/studio/recording/media-devices.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ function MediaDevice({ title, stream, paused }) {
ref={videoRef}
autoPlay
muted
tabIndex={'-1'}
sx={{
outline: 'none',
width: '100%',
Expand Down
6 changes: 4 additions & 2 deletions src/ui/studio/recording/recording-buttons.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export const RecordButton = props => (
large={props.large ? 1 : 0}
disabled={props.disabled || props.countdown}
sx={{
color: '#bd181c',
color: '#bd181c !important',
'svg + svg': { color: '#e22319' },
':disabled': { color: '#aaa' },
':disabled svg + svg': { color: '#bbb' }
Expand Down Expand Up @@ -76,6 +76,8 @@ export const StopButton = props => (
large={!!props.large}
sx={{ color: '#bd181c' }}
>
<FontAwesomeIcon icon={faStopCircle} />
<FontAwesomeIcon icon={faStopCircle}
sx={{color: '#bd181c !important',}}
/>
</Button>
);
2 changes: 1 addition & 1 deletion src/ui/studio/review/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,7 @@ const Preview = forwardRef(function _Preview({ onTimeUpdate, onReady }, ref) {
}
}}
}
preload="auto"
preload="auto" tabIndex={'-1'}
sx={{
width: '100%',
height: '100%',
Expand Down
6 changes: 5 additions & 1 deletion src/ui/studio/save-creation/recording-preview.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ const RecordingPreview = ({ onDownload, recording, title, presenter }) => {
height: '150px',
border: theme => `2px solid ${theme.colors.gray[1]}`,
}}>
<video
<video tabIndex={'-1'}
muted
src={url}
// Without this, some browsers show a black video element instead of the first frame.
Expand Down Expand Up @@ -97,6 +97,10 @@ const RecordingPreview = ({ onDownload, recording, title, presenter }) => {
maxWidth: '215px',
margin: 'auto',
mt: '10px',
'&:focus-visible': {
backgroundColor: '#f5f5f5 !important',
color: 'black !important'
},
}}
target="_blank"
download={downloadName}
Expand Down
35 changes: 25 additions & 10 deletions src/ui/studio/video-setup/prefs.js
Original file line number Diff line number Diff line change
Expand Up @@ -185,9 +185,10 @@ export const StreamSettings = ({ isDesktop, stream }) => {
justifyContent: 'flex-end',
}}>
<div sx={{ textAlign: 'right' }}>
<div
<button
onClick={() => setIsExpanded(old => !old)}
sx={{
border: 'none',
display: 'inline-block',
backgroundColor: 'rgba(0, 0, 0, 0.5)',
color: 'white',
Expand All @@ -203,6 +204,12 @@ export const StreamSettings = ({ isDesktop, stream }) => {
'&:hover > svg': {
transform: isExpanded ? 'none' : 'rotate(45deg)',
},
'&:focus-visible': {
outline: '5px solid #8ec8aa !important',
outlineOffset: '-3px',
backgroundColor: '#286244 !important',
color: 'white !important'
},
}}
>
<FontAwesomeIcon
Expand All @@ -213,7 +220,7 @@ export const StreamSettings = ({ isDesktop, stream }) => {
width: '1em !important',
}}
/>
</div>
</button>
</div>
<div sx={{
height: isExpanded ? (expandedHeight || 'auto') : 0,
Expand All @@ -224,7 +231,7 @@ export const StreamSettings = ({ isDesktop, stream }) => {
fontSize: '18px',
boxShadow: isExpanded ? '0 0 15px rgba(0, 0, 0, 0.3)' : 'none',
}}>
<div sx={{
<div tabIndex={'-1'} sx={{
border: theme => `1px solid ${theme.colors.gray[0]}`,
height: '100%',
overflow: 'auto',
Expand All @@ -237,8 +244,8 @@ export const StreamSettings = ({ isDesktop, stream }) => {
gridGap: '6px 12px',
p: 1,
}} >
{ !isDesktop && <UserSettings {...{ updatePrefs, prefs }} /> }
<UniveralSettings {...{ isDesktop, updatePrefs, prefs, stream, settings }} />
{ !isDesktop && <UserSettings {...{ updatePrefs, prefs, isExpanded }} /> }
<UniveralSettings {...{ isDesktop, updatePrefs, prefs, stream, settings, isExpanded }} />
</div>

<div sx={{
Expand Down Expand Up @@ -286,7 +293,7 @@ const PrefValue = ({ children }) => (
</div>
);

const UniveralSettings = ({ isDesktop, updatePrefs, prefs, stream, settings }) => {
const UniveralSettings = ({ isDesktop, updatePrefs, prefs, stream, settings, isExpanded }) => {
const { t } = useTranslation();

const changeQuality = quality => updatePrefs({ quality });
Expand All @@ -305,6 +312,7 @@ const UniveralSettings = ({ isDesktop, updatePrefs, prefs, stream, settings }) =
<PrefKey>{t('sources-video-quality')}*:</PrefKey>
<PrefValue>
<RadioButton
isExpanded={isExpanded}
id={`quality-auto-${kind}`}
value="auto"
name={`quality-${kind}`}
Expand All @@ -316,6 +324,7 @@ const UniveralSettings = ({ isDesktop, updatePrefs, prefs, stream, settings }) =
qualities.map(q => <Fragment key={`${q}-${kind}`}>
<wbr />
<RadioButton
isExpanded={isExpanded}
id={`quality-${q}-${kind}`}
value={q}
name={`quality-${kind}`}
Expand All @@ -329,7 +338,7 @@ const UniveralSettings = ({ isDesktop, updatePrefs, prefs, stream, settings }) =
</Fragment>;
};

const UserSettings = ({ updatePrefs, prefs }) => {
const UserSettings = ({ updatePrefs, prefs, isExpanded }) => {
const { t } = useTranslation();
const state = useStudioState();

Expand All @@ -355,7 +364,7 @@ const UserSettings = ({ updatePrefs, prefs }) => {
return <Fragment>
<PrefKey>{t('sources-video-device')}:</PrefKey>
<PrefValue>
<select
<select tabIndex={isExpanded ? '0' : '-1'}
sx={{ variant: 'styles.select' }}
value={currentDeviceId}
onChange={e => changeDevice(e.target.value)}
Expand All @@ -371,6 +380,7 @@ const UserSettings = ({ updatePrefs, prefs }) => {
<PrefKey>{t('sources-video-aspect-ratio')}*:</PrefKey>
<PrefValue>
<RadioButton
isExpanded={isExpanded}
id="ar-auto"
value="auto"
name="aspectRatio"
Expand All @@ -382,6 +392,7 @@ const UserSettings = ({ updatePrefs, prefs }) => {
ASPECT_RATIOS.map(ar => <Fragment key={`ar-${ar}`}>
<wbr />
<RadioButton
isExpanded={isExpanded}
id={`ar-${ar}`}
value={ar}
name="aspectRatio"
Expand All @@ -396,7 +407,7 @@ const UserSettings = ({ updatePrefs, prefs }) => {
};

// A styled radio input which looks like a button.
const RadioButton = ({ id, value, checked, name, onChange, label, state }) => {
const RadioButton = ({ id, value, checked, name, onChange, label, state, isExpanded }) => {
const stateColorMap = {
'warn': '#ffe300',
'ok': '#51d18f',
Expand All @@ -422,7 +433,11 @@ const RadioButton = ({ id, value, checked, name, onChange, label, state }) => {
},
}}
/>
<label htmlFor={id}>{ label || value }</label>
<label
tabIndex={isExpanded ? '0' : '-1'}
onKeyDown={e => (e.key === 'Enter' || e.key === ' ' ) && onChange(value)}
htmlFor={id}
>{ label || value }</label>
</Fragment>;
};

Expand Down
5 changes: 3 additions & 2 deletions src/ui/studio/video-setup/preview.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ const PreviewVideo = ({ input }) => {
}

return (
<div sx={{
<div tabIndex={'-1'}
sx={{
width: '100%',
height: '100%',
minHeight: '120px',
Expand All @@ -101,7 +102,7 @@ const PreviewVideo = ({ input }) => {
}

return (
<video
<video tabIndex={'-1'}
ref={videoRef}
autoPlay
muted
Expand Down

0 comments on commit 9621709

Please sign in to comment.