summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArmand Philippot <git@armandphilippot.com>2022-02-11 17:56:27 +0100
committerArmand Philippot <git@armandphilippot.com>2022-02-11 17:56:27 +0100
commit8647197a05490e2c10106a021cf6760bdabb5b2a (patch)
tree5fb56fad1c0f585ac063a8f6390e68586e3e6084
parentc69c107de84aa3b2cdbf0ed087d0314f22d30b18 (diff)
chore: improve accessibility
-rw-r--r--src/components/FooterNav/FooterNav.tsx8
-rw-r--r--src/components/MainNav/MainNav.tsx8
-rw-r--r--src/components/PostsList/PostsList.tsx6
-rw-r--r--src/components/SearchForm/SearchForm.tsx6
-rw-r--r--src/components/Sidebar/Sidebar.tsx10
-rw-r--r--src/components/Widgets/ToC/ToC.tsx15
m---------src/content0
-rw-r--r--src/i18n/en.json56
-rw-r--r--src/i18n/fr.json56
-rw-r--r--src/pages/article/[slug].tsx16
-rw-r--r--src/pages/cv.tsx16
-rw-r--r--src/pages/projet/[slug].tsx18
-rw-r--r--src/pages/sujet/[slug].tsx16
-rw-r--r--src/pages/thematique/[slug].tsx16
14 files changed, 230 insertions, 17 deletions
diff --git a/src/components/FooterNav/FooterNav.tsx b/src/components/FooterNav/FooterNav.tsx
index 918fed7..f1fd0b7 100644
--- a/src/components/FooterNav/FooterNav.tsx
+++ b/src/components/FooterNav/FooterNav.tsx
@@ -29,7 +29,13 @@ const FooterNav = () => {
return (
<div className={styles.wrapper}>
- <nav className={styles.nav}>
+ <nav
+ className={styles.nav}
+ aria-label={intl.formatMessage({
+ defaultMessage: 'Footer',
+ description: 'FooterNav: aria-label',
+ })}
+ >
<ul className={styles.list}>{navItems}</ul>
</nav>
</div>
diff --git a/src/components/MainNav/MainNav.tsx b/src/components/MainNav/MainNav.tsx
index e996e89..c7789ba 100644
--- a/src/components/MainNav/MainNav.tsx
+++ b/src/components/MainNav/MainNav.tsx
@@ -151,7 +151,13 @@ const MainNav = ({
})}
</span>
</label>
- <nav className={styles.nav}>
+ <nav
+ className={styles.nav}
+ aria-label={intl.formatMessage({
+ defaultMessage: 'Primary',
+ description: 'MainNav: aria-label',
+ })}
+ >
<ul className={styles.list}>{navItems}</ul>
</nav>
</div>
diff --git a/src/components/PostsList/PostsList.tsx b/src/components/PostsList/PostsList.tsx
index 16deee3..b57630e 100644
--- a/src/components/PostsList/PostsList.tsx
+++ b/src/components/PostsList/PostsList.tsx
@@ -50,7 +50,11 @@ const PostsList = (
<li className={styles.item}>
<PostPreview post={post} titleLevel={titleLevel} />
</li>
- {isLastPost && <span ref={ref} tabIndex={-1} />}
+ {isLastPost && (
+ <li className={styles.item}>
+ <span ref={ref} tabIndex={-1} />
+ </li>
+ )}
</Fragment>
);
})}
diff --git a/src/components/SearchForm/SearchForm.tsx b/src/components/SearchForm/SearchForm.tsx
index 38ae60d..da6f25c 100644
--- a/src/components/SearchForm/SearchForm.tsx
+++ b/src/components/SearchForm/SearchForm.tsx
@@ -35,6 +35,12 @@ const SearchForm = ({ isOpened }: { isOpened: boolean }) => {
})}
</div>
<Form submitHandler={launchSearch} modifier="search">
+ <label htmlFor="search-query" className="screen-reader-text">
+ {intl.formatMessage({
+ defaultMessage: 'Keywords:',
+ description: 'SearchForm: search field label',
+ })}
+ </label>
<Input
ref={inputRef}
id="search-query"
diff --git a/src/components/Sidebar/Sidebar.tsx b/src/components/Sidebar/Sidebar.tsx
index f319f9e..9e2079d 100644
--- a/src/components/Sidebar/Sidebar.tsx
+++ b/src/components/Sidebar/Sidebar.tsx
@@ -6,10 +6,12 @@ type SidebarPosition = 'left' | 'right';
const Sidebar = ({
children,
position,
+ ariaLabel,
title,
}: {
children: ReactNode;
position: SidebarPosition;
+ ariaLabel?: string;
title?: string;
}) => {
const childrenWithProps = Children.map(children, (child) => {
@@ -22,9 +24,13 @@ const Sidebar = ({
const positionClass = `wrapper--${position}`;
return (
- <aside className={`${styles.wrapper} ${styles[positionClass]}`}>
+ <aside
+ className={`${styles.wrapper} ${styles[positionClass]}`}
+ aria-label={ariaLabel}
+ aria-labelledby={title ? `${position}-sidebar-title` : undefined}
+ >
<div className={styles.body}>
- {title && <h2>{title}</h2>}
+ {title && <h2 id={`${position}-sidebar-title`}>{title}</h2>}
{childrenWithProps}
</div>
</aside>
diff --git a/src/components/Widgets/ToC/ToC.tsx b/src/components/Widgets/ToC/ToC.tsx
index 8a2d493..f3f783c 100644
--- a/src/components/Widgets/ToC/ToC.tsx
+++ b/src/components/Widgets/ToC/ToC.tsx
@@ -1,7 +1,7 @@
import { ExpandableWidget, OrderedList } from '@components/WidgetParts';
import { Heading } from '@ts/types/app';
import useHeadingsTree from '@utils/hooks/useHeadingsTree';
-import { useIntl } from 'react-intl';
+import { FormattedMessage, useIntl } from 'react-intl';
const ToC = () => {
const intl = useIntl();
@@ -15,7 +15,18 @@ const ToC = () => {
return headings.map((heading) => {
return (
<li key={heading.id}>
- <a href={`#${heading.id}`}>{heading.title}</a>
+ <a href={`#${heading.id}`}>
+ <FormattedMessage
+ defaultMessage="<a11y>Jump to </a11y>{title}"
+ description="ToC: link"
+ values={{
+ title: heading.title,
+ a11y: (chunks: string) => (
+ <span className="screen-reader-text">{chunks}</span>
+ ),
+ }}
+ />
+ </a>
{heading.children.length > 0 && (
<OrderedList items={getItems(heading.children)} />
)}
diff --git a/src/content b/src/content
-Subproject 94917e7d3d431dadf3d3920bf5aa31fd5dcdddf
+Subproject 4ef65066368e293376a9b39476010a843e8e38e
diff --git a/src/i18n/en.json b/src/i18n/en.json
index 015e87c..4f01ec1 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -67,6 +67,10 @@
"defaultMessage": "Legal notice - {websiteName}",
"description": "LegalNoticePage: SEO - Page title"
},
+ "6dXfvr": {
+ "defaultMessage": "Table of Contents",
+ "description": "ProjectPage: ToC sidebar aria-label"
+ },
"6ibqFS": {
"defaultMessage": "Name",
"description": "ContactForm: name field label"
@@ -83,6 +87,10 @@
"defaultMessage": "Please fill the form to contact me.",
"description": "ContactPage: page introduction"
},
+ "9nhYRA": {
+ "defaultMessage": "Table of Contents",
+ "description": "ArticlePage: ToC sidebar aria-label"
+ },
"A4LTGq": {
"defaultMessage": "Discover search results for {query}",
"description": "SearchPage: meta description with query"
@@ -155,6 +163,18 @@
"defaultMessage": "Reading time:",
"description": "Article meta"
},
+ "GgIWnN": {
+ "defaultMessage": "<a11y>Jump to </a11y>{title}",
+ "description": "ToC: link"
+ },
+ "H7C5Bk": {
+ "defaultMessage": "Primary",
+ "description": "MainNav: aria-label"
+ },
+ "HTdaZj": {
+ "defaultMessage": "Footer",
+ "description": "FooterNav: aria-label"
+ },
"HriY57": {
"defaultMessage": "Thematics",
"description": "BlogPage: thematics list widget title"
@@ -175,6 +195,10 @@
"defaultMessage": "Comment",
"description": "CommentForm: Comment field label"
},
+ "JeYOeA": {
+ "defaultMessage": "Sidebar",
+ "description": "ArticlePage: right sidebar aria-label"
+ },
"KERk7L": {
"defaultMessage": "Filter by:",
"description": "BlogPage: sidebar title"
@@ -255,6 +279,10 @@
"defaultMessage": "Failed to load.",
"description": "ThematicsList: failed to load text"
},
+ "QHOm5t": {
+ "defaultMessage": "Sidebar",
+ "description": "CVPage: right sidebar aria-label"
+ },
"Qh2CwH": {
"defaultMessage": "Find me elsewhere",
"description": "ContactPage: social media widget title"
@@ -351,6 +379,14 @@
"defaultMessage": "Read more articles about:",
"description": "PostFooter: read more posts about given subjects"
},
+ "YvMPuD": {
+ "defaultMessage": "Keywords:",
+ "description": "SearchForm: search field label"
+ },
+ "YwvYfw": {
+ "defaultMessage": "Table of Contents",
+ "description": "ThematicPage: ToC sidebar aria-label"
+ },
"Z1eSIz": {
"defaultMessage": "Open {type}",
"description": "ButtonToolbar: Open button"
@@ -435,6 +471,10 @@
"defaultMessage": "{count, plural, =0 {Technologies:} one {Technology:} other {Technologies:}}",
"description": "ProjectSummary: technologies list label"
},
+ "eu3beS": {
+ "defaultMessage": "Sidebar",
+ "description": "TopicPage: right sidebar aria-label"
+ },
"fGnfqp": {
"defaultMessage": "Published on:",
"description": "PostMeta: publication date label"
@@ -443,10 +483,18 @@
"defaultMessage": "Failed to load.",
"description": "SearchPage: failed to load text"
},
+ "g4DckL": {
+ "defaultMessage": "Table of Contents",
+ "description": "CVPage: ToC sidebar aria-label"
+ },
"gQKeF+": {
"defaultMessage": "Thanks. Your message was successfully sent. I will answer it as soon as possible.",
"description": "ContactForm: success message"
},
+ "hHrNd0": {
+ "defaultMessage": "Sidebar",
+ "description": "ProjectPage: right sidebar aria-label"
+ },
"hKagVG": {
"defaultMessage": "License:",
"description": "ProjectSummary: license label"
@@ -491,6 +539,10 @@
"defaultMessage": "Email",
"description": "Sharing: Email"
},
+ "lsDB5G": {
+ "defaultMessage": "Table of Contents",
+ "description": "TopicPage: ToC sidebar aria-label"
+ },
"mC21ht": {
"defaultMessage": "Number of articles loaded out of the total available.",
"description": "PaginationCursor: loaded articles count aria-label"
@@ -555,6 +607,10 @@
"defaultMessage": "Published on:",
"description": "Comment: publication date label"
},
+ "syLgY9": {
+ "defaultMessage": "Sidebar",
+ "description": "ThematicPage: right sidebar aria-label"
+ },
"tMuNTy": {
"defaultMessage": "{websiteName} is a front-end developer located in France. He codes and he writes mostly about web development and open-source.",
"description": "HomePage: SEO - Meta description"
diff --git a/src/i18n/fr.json b/src/i18n/fr.json
index b20a241..812475b 100644
--- a/src/i18n/fr.json
+++ b/src/i18n/fr.json
@@ -67,6 +67,10 @@
"defaultMessage": "Mentions légales - {websiteName}",
"description": "LegalNoticePage: SEO - Page title"
},
+ "6dXfvr": {
+ "defaultMessage": "Table des matières",
+ "description": "ProjectPage: ToC sidebar aria-label"
+ },
"6ibqFS": {
"defaultMessage": "Nom",
"description": "ContactForm: name field label"
@@ -83,6 +87,10 @@
"defaultMessage": "Veuillez remplir le formulaire pour me contacter.",
"description": "ContactPage: page introduction"
},
+ "9nhYRA": {
+ "defaultMessage": "Table des matières",
+ "description": "ArticlePage: ToC sidebar aria-label"
+ },
"A4LTGq": {
"defaultMessage": "Découvrez les résultats de recherche pour {query}",
"description": "SearchPage: meta description with query"
@@ -155,6 +163,18 @@
"defaultMessage": "Temps de lecture :",
"description": "Article meta"
},
+ "GgIWnN": {
+ "defaultMessage": "<a11y>Atteindre </a11y>{title}",
+ "description": "ToC: link"
+ },
+ "H7C5Bk": {
+ "defaultMessage": "Principal",
+ "description": "MainNav: aria-label"
+ },
+ "HTdaZj": {
+ "defaultMessage": "Pied de page",
+ "description": "FooterNav: aria-label"
+ },
"HriY57": {
"defaultMessage": "Thématiques",
"description": "BlogPage: thematics list widget title"
@@ -175,6 +195,10 @@
"defaultMessage": "Commentaire",
"description": "CommentForm: Comment field label"
},
+ "JeYOeA": {
+ "defaultMessage": "Barre latérale",
+ "description": "ArticlePage: right sidebar aria-label"
+ },
"KERk7L": {
"defaultMessage": "Filtrer par :",
"description": "BlogPage: sidebar title"
@@ -255,6 +279,10 @@
"defaultMessage": "Échec du chargement.",
"description": "ThematicsList: failed to load text"
},
+ "QHOm5t": {
+ "defaultMessage": "Barre latérale",
+ "description": "CVPage: right sidebar aria-label"
+ },
"Qh2CwH": {
"defaultMessage": "Retrouvez-moi ailleurs",
"description": "ContactPage: social media widget title"
@@ -351,6 +379,14 @@
"defaultMessage": "Lire plus d'articles à propos de :",
"description": "PostFooter: read more posts about given subjects"
},
+ "YvMPuD": {
+ "defaultMessage": "Mots-clés :",
+ "description": "SearchForm: search field label"
+ },
+ "YwvYfw": {
+ "defaultMessage": "Table des matières",
+ "description": "ThematicPage: ToC sidebar aria-label"
+ },
"Z1eSIz": {
"defaultMessage": "Ouvrez {type}",
"description": "ButtonToolbar: Open button"
@@ -435,6 +471,10 @@
"defaultMessage": "{count, plural, =0 {Technologies :} one {Technologie :} other {Technologies :}}",
"description": "ProjectSummary: technologies list label"
},
+ "eu3beS": {
+ "defaultMessage": "Barre latérale",
+ "description": "TopicPage: right sidebar aria-label"
+ },
"fGnfqp": {
"defaultMessage": "Publié le :",
"description": "PostMeta: publication date label"
@@ -443,10 +483,18 @@
"defaultMessage": "Échec du chargement.",
"description": "SearchPage: failed to load text"
},
+ "g4DckL": {
+ "defaultMessage": "Table des matières",
+ "description": "CVPage: ToC sidebar aria-label"
+ },
"gQKeF+": {
"defaultMessage": "Merci. Votre message a bien été envoyé. J'y répondrai dès que possible.",
"description": "ContactForm: success message"
},
+ "hHrNd0": {
+ "defaultMessage": "Barre latérale",
+ "description": "ProjectPage: right sidebar aria-label"
+ },
"hKagVG": {
"defaultMessage": "Licence :",
"description": "ProjectSummary: license label"
@@ -491,6 +539,10 @@
"defaultMessage": "Email",
"description": "Sharing: Email"
},
+ "lsDB5G": {
+ "defaultMessage": "Table des matières",
+ "description": "TopicPage: ToC sidebar aria-label"
+ },
"mC21ht": {
"defaultMessage": "Nombre d'articles chargés sur le total disponible.",
"description": "PaginationCursor: loaded articles count aria-label"
@@ -555,6 +607,10 @@
"defaultMessage": "Publié le :",
"description": "Comment: publication date label"
},
+ "syLgY9": {
+ "defaultMessage": "Barre latérale",
+ "description": "ThematicPage: right sidebar aria-label"
+ },
"tMuNTy": {
"defaultMessage": "{websiteName} est intégrateur web / développeur front-end en France. Il code et il écrit essentiellement à propos de développement web et du libre.",
"description": "HomePage: SEO - Meta description"
diff --git a/src/pages/article/[slug].tsx b/src/pages/article/[slug].tsx
index faf2271..689f123 100644
--- a/src/pages/article/[slug].tsx
+++ b/src/pages/article/[slug].tsx
@@ -175,7 +175,13 @@ const SingleArticle: NextPageWithLayout<ArticleProps> = ({ post }) => {
data-prismjs-color-scheme-light={lightTheme}
>
<PostHeader intro={intro} meta={meta} title={title} />
- <Sidebar position="left">
+ <Sidebar
+ position="left"
+ ariaLabel={intl.formatMessage({
+ defaultMessage: 'Table of Contents',
+ description: 'ArticlePage: ToC sidebar aria-label',
+ })}
+ >
<ToC />
</Sidebar>
<div
@@ -183,7 +189,13 @@ const SingleArticle: NextPageWithLayout<ArticleProps> = ({ post }) => {
dangerouslySetInnerHTML={{ __html: content }}
></div>
<PostFooter topics={topics} />
- <Sidebar position="right">
+ <Sidebar
+ position="right"
+ ariaLabel={intl.formatMessage({
+ defaultMessage: 'Sidebar',
+ description: 'ArticlePage: right sidebar aria-label',
+ })}
+ >
<Sharing title={title} excerpt={intro} />
</Sidebar>
<section id="comments" className={styles.comments}>
diff --git a/src/pages/cv.tsx b/src/pages/cv.tsx
index 311d0ce..e77c586 100644
--- a/src/pages/cv.tsx
+++ b/src/pages/cv.tsx
@@ -110,13 +110,25 @@ const CV: NextPageWithLayout = () => {
className={`${styles.article} ${styles['article--no-comments']}`}
>
<PostHeader intro={intro} meta={pageMeta} title={meta.title} />
- <Sidebar position="left">
+ <Sidebar
+ position="left"
+ ariaLabel={intl.formatMessage({
+ defaultMessage: 'Table of Contents',
+ description: 'CVPage: ToC sidebar aria-label',
+ })}
+ >
<ToC />
</Sidebar>
<div className={styles.body}>
<CVContent />
</div>
- <Sidebar position="right">
+ <Sidebar
+ position="right"
+ ariaLabel={intl.formatMessage({
+ defaultMessage: 'Sidebar',
+ description: 'CVPage: right sidebar aria-label',
+ })}
+ >
<CVPreview
title={intl.formatMessage({
defaultMessage: 'Others formats',
diff --git a/src/pages/projet/[slug].tsx b/src/pages/projet/[slug].tsx
index f96da0e..f72063a 100644
--- a/src/pages/projet/[slug].tsx
+++ b/src/pages/projet/[slug].tsx
@@ -22,6 +22,7 @@ import Head from 'next/head';
import { useRouter } from 'next/router';
import { ParsedUrlQuery } from 'querystring';
import { ComponentType } from 'react';
+import { useIntl } from 'react-intl';
import { Article, Graph, WebPage } from 'schema-dts';
const Project: NextPageWithLayout<ProjectProps> = ({
@@ -29,6 +30,7 @@ const Project: NextPageWithLayout<ProjectProps> = ({
}: {
project: ProjectData;
}) => {
+ const intl = useIntl();
const router = useRouter();
const projectUrl = `${settings.url}${router.asPath}`;
const { id, intro, meta, title, seo } = project;
@@ -107,14 +109,26 @@ const Project: NextPageWithLayout<ProjectProps> = ({
className={`${styles.article} ${styles['article--no-comments']}`}
>
<PostHeader title={title} intro={intro} meta={{ dates }} />
- <Sidebar position="left">
+ <Sidebar
+ position="left"
+ ariaLabel={intl.formatMessage({
+ defaultMessage: 'Table of Contents',
+ description: 'ProjectPage: ToC sidebar aria-label',
+ })}
+ >
<ToC />
</Sidebar>
<div className={styles.body}>
<ProjectSummary id={id} title={title} meta={meta} />
<ProjectContent components={components} />
</div>
- <Sidebar position="right">
+ <Sidebar
+ position="right"
+ ariaLabel={intl.formatMessage({
+ defaultMessage: 'Sidebar',
+ description: 'ProjectPage: right sidebar aria-label',
+ })}
+ >
<Sharing title={title} excerpt={intro} />
</Sidebar>
</article>
diff --git a/src/pages/sujet/[slug].tsx b/src/pages/sujet/[slug].tsx
index ca7d7cd..910c02c 100644
--- a/src/pages/sujet/[slug].tsx
+++ b/src/pages/sujet/[slug].tsx
@@ -126,7 +126,13 @@ const Topic: NextPageWithLayout<TopicProps> = ({ topic }) => {
meta={meta}
title={topic.title}
/>
- <Sidebar position="left">
+ <Sidebar
+ position="left"
+ ariaLabel={intl.formatMessage({
+ defaultMessage: 'Table of Contents',
+ description: 'TopicPage: ToC sidebar aria-label',
+ })}
+ >
<ToC />
</Sidebar>
<div className={styles.body}>
@@ -146,7 +152,13 @@ const Topic: NextPageWithLayout<TopicProps> = ({ topic }) => {
</section>
)}
</div>
- <Sidebar position="right">
+ <Sidebar
+ position="right"
+ ariaLabel={intl.formatMessage({
+ defaultMessage: 'Sidebar',
+ description: 'TopicPage: right sidebar aria-label',
+ })}
+ >
<RelatedThematics thematics={relatedThematics.current} />
<TopicsList
title={intl.formatMessage({
diff --git a/src/pages/thematique/[slug].tsx b/src/pages/thematique/[slug].tsx
index df7ff1a..166e0bb 100644
--- a/src/pages/thematique/[slug].tsx
+++ b/src/pages/thematique/[slug].tsx
@@ -116,7 +116,13 @@ const Thematic: NextPageWithLayout<ThematicProps> = ({ thematic }) => {
className={`${styles.article} ${styles['article--no-comments']}`}
>
<PostHeader intro={thematic.intro} meta={meta} title={thematic.title} />
- <Sidebar position="left">
+ <Sidebar
+ position="left"
+ ariaLabel={intl.formatMessage({
+ defaultMessage: 'Table of Contents',
+ description: 'ThematicPage: ToC sidebar aria-label',
+ })}
+ >
<ToC />
</Sidebar>
<div className={styles.body}>
@@ -136,7 +142,13 @@ const Thematic: NextPageWithLayout<ThematicProps> = ({ thematic }) => {
</section>
)}
</div>
- <Sidebar position="right">
+ <Sidebar
+ position="right"
+ ariaLabel={intl.formatMessage({
+ defaultMessage: 'Sidebar',
+ description: 'ThematicPage: right sidebar aria-label',
+ })}
+ >
<RelatedTopics topics={relatedTopics.current} />
<ThematicsList
title={intl.formatMessage({