Add skip links for content, player and footer. For #1826

This commit is contained in:
Gabe Kangas 2023-01-21 23:19:17 -08:00
parent b0f88519d0
commit cdaae66e94
No known key found for this signature in database
GPG Key ID: 4345B2060657F330
7 changed files with 47 additions and 5 deletions

View File

@ -11,6 +11,7 @@ import {
isChatAvailableSelector,
clientConfigStateAtom,
fatalErrorStateAtom,
appStateAtom,
} from '../../stores/ClientConfigStore';
import { Content } from '../../ui/Content/Content';
import { Header } from '../../ui/Header/Header';
@ -22,6 +23,7 @@ import { ServerRenderedHydration } from '../../ServerRendered/ServerRenderedHydr
import { Theme } from '../../theme/Theme';
import styles from './Main.module.scss';
import { PushNotificationServiceWorker } from '../../workers/PushNotificationServiceWorker/PushNotificationServiceWorker';
import { AppStateOptions } from '../../stores/application-state';
const lockBodyStyle = `
body {
@ -46,9 +48,11 @@ export const Main: FC = () => {
const { name, title, customStyles } = clientConfig;
const isChatAvailable = useRecoilValue<boolean>(isChatAvailableSelector);
const fatalError = useRecoilValue<DisplayableError>(fatalErrorStateAtom);
const appState = useRecoilValue<AppStateOptions>(appStateAtom);
const layoutRef = useRef<HTMLDivElement>(null);
const { chatDisabled } = clientConfig;
const { videoAvailable } = appState;
useEffect(() => {
setupNoLinkReferrer(layoutRef.current);
@ -137,7 +141,12 @@ export const Main: FC = () => {
<Script strategy="afterInteractive" src="/customjavascript" />
<Layout ref={layoutRef} className={styles.layout}>
<Header name={title || name} chatAvailable={isChatAvailable} chatDisabled={chatDisabled} />
<Header
name={title || name}
chatAvailable={isChatAvailable}
chatDisabled={chatDisabled}
online={videoAvailable}
/>
<Content />
{fatalError && (
<FatalErrorStateModal title={fatalError.title} message={fatalError.message} />

View File

@ -115,7 +115,7 @@ const DesktopContent = ({
return (
<>
<div className={styles.lowerHalf}>
<div className={styles.lowerHalf} id="skip-to-content">
<ContentHeader
name={name}
title={streamTitle}

View File

@ -6,7 +6,7 @@ export type FooterProps = {
};
export const Footer: FC<FooterProps> = ({ version }) => (
<footer className={styles.footer}>
<footer className={styles.footer} id="footer">
<span>
Powered by <a href="https://owncast.online">{version}</a>
</span>

View File

@ -42,3 +42,18 @@
overflow: hidden;
line-height: 1.4;
}
.skipLink {
position: absolute;
left: -10000px;
top: auto;
width: 1px;
height: 1px;
overflow: hidden;
}
.skipLink:focus {
position: static;
width: auto;
height: auto;
}

View File

@ -2,6 +2,7 @@ import { Tag, Tooltip } from 'antd';
import { FC } from 'react';
import cn from 'classnames';
import dynamic from 'next/dynamic';
import Link from 'next/link';
import { OwncastLogo } from '../../common/OwncastLogo/OwncastLogo';
import styles from './Header.module.scss';
@ -18,14 +19,27 @@ export type HeaderComponentProps = {
name: string;
chatAvailable: boolean;
chatDisabled: boolean;
online: boolean;
};
export const Header: FC<HeaderComponentProps> = ({
name = 'Your stream title',
chatAvailable,
chatDisabled,
online,
}) => (
<header className={cn([`${styles.header}`], 'global-header')}>
{online && (
<Link href="#player" className={styles.skipLink}>
Skip to player
</Link>
)}
<Link href="#skip-to-content" className={styles.skipLink}>
Skip to page content
</Link>
<Link href="#footer" className={styles.skipLink}>
Skip to footer
</Link>
<div className={styles.logo}>
<div id="header-logo" className={styles.logoImage}>
<OwncastLogo variant="contrast" />

View File

@ -307,7 +307,7 @@ export const OwncastPlayer: FC<OwncastPlayerProps> = ({
);
return (
<div className={styles.container}>
<div className={styles.container} id="player">
{online && (
<div className={styles.player}>
<VideoJS options={videoJsOptions} onReady={handlePlayerReady} />

View File

@ -6,16 +6,20 @@ import {
currentUserAtom,
visibleChatMessagesSelector,
clientConfigStateAtom,
appStateAtom,
} from '../../../../components/stores/ClientConfigStore';
import Header from '../../../../components/ui/Header/Header';
import { ClientConfig } from '../../../../interfaces/client-config.model';
import { AppStateOptions } from '../../../../components/stores/application-state';
export default function ReadWriteChatEmbed() {
const currentUser = useRecoilValue(currentUserAtom);
const messages = useRecoilValue<ChatMessage[]>(visibleChatMessagesSelector);
const clientConfig = useRecoilValue<ClientConfig>(clientConfigStateAtom);
const appState = useRecoilValue<AppStateOptions>(appStateAtom);
const { name, chatDisabled } = clientConfig;
const { videoAvailable } = appState;
if (!currentUser) {
return null;
@ -26,7 +30,7 @@ export default function ReadWriteChatEmbed() {
return (
<div>
<ClientConfigStore />
<Header name={name} chatAvailable chatDisabled={chatDisabled} />
<Header name={name} chatAvailable chatDisabled={chatDisabled} online={videoAvailable} />
<ChatContainer
messages={messages}
usernameToHighlight={displayName}