Skip to content

Commit

Permalink
feat(project): make all accessible by keyboard
Browse files Browse the repository at this point in the history
  • Loading branch information
royschut committed May 26, 2021
1 parent 8fab1dd commit 14c10af
Show file tree
Hide file tree
Showing 26 changed files with 102 additions and 34 deletions.
4 changes: 4 additions & 0 deletions src/components/Button/Button.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,16 @@

@media (hover: hover) and (pointer: fine) {
&:hover:not(.active),
&:focus:not(.active),
&:active {
z-index: 1;
background-color: rgba(variables.$black, 0.8);
border-color: var(--highlight-color, white);
transform: scale(1.1);
opacity: 1;
}
&:focus.active {
transform: scale(1.1);
}
}
}
5 changes: 4 additions & 1 deletion src/components/Card/Card.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@
cursor: pointer;
transition: transform 0.2s ease-out, -webkit-transform 0.2s ease-out;

// @media (hover: hover) and (pointer: fine) {
&:focus,
&:hover {
z-index: 10;
z-index: 1;
outline: none;
transform: scale(1.05);

& .poster {
Expand Down
14 changes: 12 additions & 2 deletions src/components/Card/Card.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { memo } from 'react';
import React, { KeyboardEvent, memo } from 'react';
import classNames from 'classnames';

import { formatDurationTag } from '../../utils/formatting';
Expand Down Expand Up @@ -39,7 +39,17 @@ function Card({
};

return (
<div className={cardClassName} onClick={onClick} onMouseEnter={onHover} role="button" aria-label={`Play ${title}`}>
<div
className={cardClassName}
onClick={onClick}
onMouseEnter={onHover}
tabIndex={disabled ? -1 : 0}
onKeyDown={(event: KeyboardEvent) =>
(event.key === 'Enter' || event.key === ' ') && !disabled && onClick && onClick()
}
role="button"
aria-label={`Play ${title}`}
>
<div className={posterClassNames} style={{ backgroundImage: posterSource ? `url(${posterSource})` : '' }}>
<div className={styles.meta}>
<div className={styles.title}>{featured ? title : ''}</div>
Expand Down
4 changes: 3 additions & 1 deletion src/components/Dropdown/Dropdown.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ $select-focus: theme.$primary-color;
background-image: linear-gradient(to top, #ddd, #eee 33%);
cursor: not-allowed;
}
&:hover {
&:hover,
&:focus {
outline: none;
border-color: $select-focus;
}

Expand Down
4 changes: 2 additions & 2 deletions src/components/Dropdown/Dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ type Props = {

const Dropdown: React.FC<Props> = ({ name, value, defaultLabel, options, onClick, onChange, optionsStyle }: Props) => {
return (
<div className={styles.dropdown}>
<select className={styles.select} name={name} value={value} onClick={onClick} onChange={onChange}>
<div className={styles.dropdown} tabIndex={0}>
<select className={styles.select} name={name} value={value} onClick={onClick} onChange={onChange} tabIndex={-1}>
<option className={classNames(styles.option, optionsStyle)} value="">
{defaultLabel}
</option>
Expand Down
5 changes: 5 additions & 0 deletions src/components/Filter/__snapshots__/Filter.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ exports[`<Filter> renders Filter 1`] = `
aria-label="close menu"
class="iconButton"
role="button"
tabindex="0"
>
<svg
aria-hidden="true"
Expand Down Expand Up @@ -43,27 +44,31 @@ exports[`<Filter> renders Filter 1`] = `
>
<div
class="menuButton"
tabindex="0"
>
<span>
x
</span>
</div>
<div
class="menuButton"
tabindex="0"
>
<span>
y
</span>
</div>
<div
class="menuButton"
tabindex="0"
>
<span>
z
</span>
</div>
<div
class="menuButton"
tabindex="0"
>
<span>
bb
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ exports[`<FilterModal> renders and matches snapshot 1`] = `
aria-label="close menu"
class="iconButton"
role="button"
tabindex="0"
>
<svg
aria-hidden="true"
Expand Down
2 changes: 1 addition & 1 deletion src/components/Header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import classNames from 'classnames';
import ButtonLink from '../ButtonLink/ButtonLink';
import Logo from '../Logo/Logo';
import Menu from '../../icons/Menu';
import IconButton from '../../components/IconButton/IconButton';

import styles from './Header.module.scss';
import IconButton from '../../components/IconButton/IconButton';

type TypeHeader = 'static' | 'fixed';

Expand Down
1 change: 1 addition & 0 deletions src/components/Header/__snapshots__/Header.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ exports[`<Header /> renders header 1`] = `
aria-label="open menu"
class="iconButton"
role="button"
tabindex="0"
>
<svg
aria-hidden="true"
Expand Down
1 change: 1 addition & 0 deletions src/components/IconButton/IconButton.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
cursor: pointer;
opacity: 0.7;
transition: transform 0.1s ease;
outline-color: var(--highlight-color, white);

&:hover {
transform: scale(1.1);
Expand Down
14 changes: 12 additions & 2 deletions src/components/IconButton/IconButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,21 @@ type Props = {
onClick?: () => void;
children: JSX.Element;
'aria-label': string;
tabIndex?: number;
};

const IconButton: React.FC<Props> = ({ children, onClick, 'aria-label': ariaLabel }: Props) => {
const IconButton: React.FC<Props> = ({ children, onClick, 'aria-label': ariaLabel, tabIndex = 0 }: Props) => {
return (
<div className={styles.iconButton} onClick={onClick} aria-label={ariaLabel} role="button">
<div
className={styles.iconButton}
onClick={onClick}
aria-label={ariaLabel}
role="button"
tabIndex={tabIndex}
onKeyDown={(event: React.KeyboardEvent) =>
(event.key === 'Enter' || event.key === ' ') && tabIndex >= 0 && onClick && onClick()
}
>
{children}
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ exports[`<IconButton> renders and matches snapshot 1`] = `
aria-label="Icon button"
class="iconButton"
role="button"
tabindex="0"
>
<svg
aria-hidden="true"
Expand Down
9 changes: 7 additions & 2 deletions src/components/Layout/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import Sidebar from '../Sidebar/Sidebar';
import DynamicBlur from '../DynamicBlur/DynamicBlur';
import { ConfigContext } from '../../providers/ConfigProvider';
import { UIStateContext } from '../../providers/uiStateProvider';
import MenuButton from '../../components/MenuButton/MenuButton';

import styles from './Layout.module.scss';
import MenuButton from '../../components/MenuButton/MenuButton';

type LayoutProps = {
children?: ReactNode;
Expand All @@ -34,7 +34,12 @@ const Layout: FC<LayoutProps> = ({ children }) => {
isOpen={sideBarOpen}
onClose={() => setSideBarOpen(false)}
playlistMenuItems={menu.map((item) => (
<MenuButton key={item.playlistId} label={item.label} to={`/p/${item.playlistId}`} />
<MenuButton
key={item.playlistId}
label={item.label}
to={`/p/${item.playlistId}`}
tabIndex={sideBarOpen ? 0 : -1}
/>
))}
/>
{children}
Expand Down
4 changes: 4 additions & 0 deletions src/components/Layout/__snapshots__/Layout.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ exports[`<Layout /> renders layout 1`] = `
aria-label="open menu"
class="iconButton"
role="button"
tabindex="0"
>
<svg
aria-hidden="true"
Expand Down Expand Up @@ -76,6 +77,7 @@ exports[`<Layout /> renders layout 1`] = `
aria-label="close menu"
class="iconButton"
role="button"
tabindex="-1"
>
<svg
aria-hidden="true"
Expand Down Expand Up @@ -103,6 +105,7 @@ exports[`<Layout /> renders layout 1`] = `
aria-current="page"
class="menuButton active"
href="/"
tabindex="-1"
>
<span>
Home
Expand All @@ -114,6 +117,7 @@ exports[`<Layout /> renders layout 1`] = `
<a
class="menuButton"
href="/u"
tabindex="-1"
>
<span>
Settings
Expand Down
1 change: 1 addition & 0 deletions src/components/MenuButton/MenuButton.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
cursor: pointer;
opacity: 0.7;
transition: background 0.1s ease;
outline-color: var(--highlight-color, white);

@media (hover: hover) and (pointer: fine) {
&:hover,
Expand Down
7 changes: 4 additions & 3 deletions src/components/MenuButton/MenuButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,21 @@ type Props = {
label?: string;
to?: string;
onClick?: () => void;
tabIndex?: number;
active?: boolean;
};

const MenuButton: React.FC<Props> = ({ label, to, onClick, active = false }: Props) => {
const MenuButton: React.FC<Props> = ({ label, to, onClick, tabIndex = 0, active = false }: Props) => {
if (to) {
return (
<NavLink className={styles.menuButton} activeClassName={styles.active} to={to} exact>
<NavLink className={styles.menuButton} activeClassName={styles.active} to={to} tabIndex={tabIndex} exact>
<span className={styles.label}>{label}</span>
</NavLink>
);
}

return (
<div className={classNames(styles.menuButton, { [styles.active]: active })} onClick={onClick}>
<div className={classNames(styles.menuButton, { [styles.active]: active })} onClick={onClick} tabIndex={tabIndex}>
<span className={styles.label}>{label}</span>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ exports[`<MenuButton> renders and matches snapshot 1`] = `
<div>
<div
class="menuButton"
tabindex="0"
>
<span>
Label
Expand Down
4 changes: 4 additions & 0 deletions src/components/Root/__snapshots__/Root.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ exports[`<Root /> renders and matches snapshot 1`] = `
aria-label="open menu"
class="iconButton"
role="button"
tabindex="0"
>
<svg
aria-hidden="true"
Expand Down Expand Up @@ -75,6 +76,7 @@ exports[`<Root /> renders and matches snapshot 1`] = `
aria-label="close menu"
class="iconButton"
role="button"
tabindex="-1"
>
<svg
aria-hidden="true"
Expand All @@ -101,6 +103,7 @@ exports[`<Root /> renders and matches snapshot 1`] = `
<a
class="menuButton"
href="/"
tabindex="-1"
>
<span>
Home
Expand All @@ -112,6 +115,7 @@ exports[`<Root /> renders and matches snapshot 1`] = `
<a
class="menuButton"
href="/u"
tabindex="-1"
>
<span>
Settings
Expand Down
1 change: 1 addition & 0 deletions src/components/Shelf/Shelf.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

.arrow {
cursor: pointer;
outline-color: var(--highlight-color, white);
transition: transform 0.2s ease-out, -webkit-transform 0.2s ease-out;
> svg {
width: 30px;
Expand Down
25 changes: 15 additions & 10 deletions src/components/Shelf/Shelf.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ const Shelf: React.FC<ShelfProps> = ({
const isLargeScreen = breakpoint >= Breakpoint.md;
const imageSourceWidth = (featured ? 640 : 320) * (window.devicePixelRatio > 1 || isLargeScreen ? 2 : 1);

const handleSlide = (doSlide: () => void): void => {
setDidSlideBefore(true);
doSlide();
};

if (!playlist) return null;

return (
Expand All @@ -62,30 +67,30 @@ const Shelf: React.FC<ShelfProps> = ({
showControls={!matchMedia('(hover: none)').matches}
transitionTime={loading ? '0s' : '0.3s'}
spacing={12}
renderLeftControl={(handleClick) => (
renderLeftControl={(doSlide) => (
<div
className={didSlideBefore ? styles.arrow : styles.arrowDisabled}
role="button"
tabIndex={didSlideBefore ? 0 : -1}
aria-label="Slide left"
onClick={() => {
setDidSlideBefore(true);
handleClick();
}}
onKeyDown={(event: React.KeyboardEvent) =>
(event.key === 'Enter' || event.key === ' ') && handleSlide(doSlide)
}
onClick={() => handleSlide(doSlide)}
>
<ArrowLeft />
</div>
)}
renderRightControl={(handleClick) => (
renderRightControl={(doSlide) => (
<div
className={styles.arrow}
role="button"
tabIndex={0}
aria-label="Slide right"
onClick={() => {
setDidSlideBefore(true);
handleClick();
}}
onKeyDown={(event: React.KeyboardEvent) =>
(event.key === 'Enter' || event.key === ' ') && handleSlide(doSlide)
}
onClick={() => handleSlide(doSlide)}
>
<ArrowRight />
</div>
Expand Down
Loading

0 comments on commit 14c10af

Please sign in to comment.