Showing preview only (215K chars total). Download the full file or copy to clipboard to get everything.
Repository: AccessibleForAll/AccessibleWebDev
Branch: main
Commit: d5a42c7725f3
Files: 96
Total size: 192.0 KB
Directory structure:
gitextract_u4kywwvh/
├── .eslintignore
├── .eslintrc.json
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ └── feature_request.md
│ ├── PULL_REQUEST_TEMPLATE.md
│ └── workflows/
│ ├── appreciation.yml
│ └── axe.yaml
├── .gitignore
├── .gitpod.yml
├── .husky/
│ └── pre-commit
├── .prettierignore
├── .prettierrc.json
├── CODE_OF_CONDUCT.md
├── CODING_STANDARDS.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── components/
│ ├── CodeBlock/
│ │ ├── CodeBlock.module.css
│ │ └── CodeBlock.tsx
│ ├── ContentTemplates/
│ │ ├── AlertsTemplate.tsx
│ │ ├── AnimationsTemplate.tsx
│ │ ├── AudioTemplate.tsx
│ │ ├── BreadcrumbsTemplate.module.css
│ │ ├── BreadcrumbsTemplate.tsx
│ │ ├── ButtonsTemplate.module.css
│ │ ├── ButtonsTemplate.tsx
│ │ ├── CaptchasTemplate.tsx
│ │ ├── ChartsTemplate.tsx
│ │ ├── FormsTemplate.tsx
│ │ ├── HeadingsTemplate.tsx
│ │ ├── IconsTemplate.tsx
│ │ ├── ImagesTemplate.tsx
│ │ ├── LinksTemplate.module.css
│ │ ├── LinksTemplate.tsx
│ │ ├── ListsTemplate.tsx
│ │ ├── MenusTemplate.tsx
│ │ ├── ModalsTemplate.tsx
│ │ ├── NavigationTemplate.tsx
│ │ ├── PaginationTemplate.tsx
│ │ ├── TablesTemplate.tsx
│ │ └── VideoTemplate.tsx
│ ├── CopyCodeBlock/
│ │ ├── CopyCodeBlock.module.css
│ │ └── CopyCodeBlock.tsx
│ ├── Footer/
│ │ ├── Footer.module.css
│ │ └── Footer.tsx
│ ├── Header/
│ │ ├── Header.module.css
│ │ └── Header.tsx
│ ├── Layout/
│ │ ├── Layout.module.css
│ │ └── Layout.tsx
│ ├── MaintainerCard/
│ │ ├── MaintainerCard.module.css
│ │ └── MaintainerCard.tsx
│ ├── Nav/
│ │ ├── NavItem.tsx
│ │ ├── NavPrimary.module.css
│ │ ├── NavPrimary.tsx
│ │ └── NavPrimaryMobile.tsx
│ ├── NavPage/
│ │ ├── NavPage.module.css
│ │ └── NavPage.tsx
│ ├── PageUpdated/
│ │ ├── PageUpdated.module.css
│ │ └── PageUpdated.tsx
│ ├── SkipLink/
│ │ ├── SkipLink.module.css
│ │ └── SkipLink.tsx
│ ├── TemplateSection/
│ │ ├── TemplateSection.module.css
│ │ └── TemplateSection.tsx
│ ├── ThemeSwitcher/
│ │ ├── ThemeSwitcher.module.css
│ │ ├── ThemeSwitcher.tsx
│ │ └── themes.ts
│ ├── TipOfTheDay/
│ │ ├── TipOfTheDay.module.css
│ │ └── TipOfTheDay.tsx
│ └── WorkInProgress/
│ ├── WorkInProgress.module.css
│ └── WorkInProgress.tsx
├── customHooks/
│ └── useOnClickOutside.ts
├── data/
│ ├── maintainers.ts
│ ├── pageNavigationLists.ts
│ ├── pages.ts
│ └── tipsOfTheDay.ts
├── docs-style-guide.md
├── next-env.d.ts
├── next-i18next.config.js
├── next.config.js
├── package.json
├── pages/
│ ├── 404.tsx
│ ├── [content].tsx
│ ├── _app.tsx
│ ├── about.tsx
│ ├── api/
│ │ └── hello.ts
│ ├── docs/
│ │ └── Style Guide.md
│ └── index.tsx
├── public/
│ ├── locales/
│ │ ├── en/
│ │ │ ├── common.json
│ │ │ └── homepage.json
│ │ └── sv/
│ │ ├── common.json
│ │ └── homepage.json
│ └── site.webmanifest
├── styles/
│ ├── about.module.css
│ └── globals.css
├── tsconfig.json
└── utils.ts
================================================
FILE CONTENTS
================================================
================================================
FILE: .eslintignore
================================================
================================================
FILE: .eslintrc.json
================================================
{
"extends": [
"next/core-web-vitals",
"prettier",
"plugin:@typescript-eslint/recommended",
"plugin:jsx-a11y/strict"
],
"rules": {
"comma-spacing": ["error", { "before": false, "after": true }],
"react/no-unescaped-entities": 0,
"react/prop-types": "off",
// suppress errors for missing 'import React' in files
"react/react-in-jsx-scope": "off",
// allow jsx syntax in js files (for next.js project)
"react/jsx-filename-extension": [
1,
{ "extensions": [".js", ".jsx", ".ts", ".tsx"] }
] //should add ".ts" if typescript project
},
"overrides": [
{
"files": ["*.ts", "*.tsx"],
"rules": {
"react/prop-types": "off",
"@typescript-eslint/naming-convention": [
"warn",
{
"selector": "default",
"format": ["camelCase"],
"leadingUnderscore": "forbid",
"trailingUnderscore": "forbid"
},
{
"selector": "objectLiteralProperty",
"modifiers": ["requiresQuotes"],
"format": null
},
{
"selector": "objectLiteralProperty",
"format": ["strictCamelCase", "StrictPascalCase"]
},
{
"selector": "typeLike",
"format": ["StrictPascalCase", "PascalCase"]
},
{
"selector": "interface",
"format": ["PascalCase"]
},
{
"selector": "function",
// Exception for FunctionComponents
"format": ["strictCamelCase", "StrictPascalCase"]
},
{
"selector": "parameter",
"format": ["strictCamelCase", "StrictPascalCase"]
},
{
"selector": "variable",
"format": ["camelCase", "UPPER_CASE"]
},
{
"selector": "variable",
"modifiers": ["exported"],
// Exception for components wrapped in HOC's and for React's Context
"format": ["strictCamelCase", "StrictPascalCase", "UPPER_CASE"]
},
{
"selector": "enumMember",
"format": ["StrictPascalCase"]
}
]
}
}
]
}
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
## Describe your changes
## Screenshots - If Any (Optional)
## Link to issue
<!-- Example: Closes #31 -->
Closes #
## Checklist before requesting a review
- [ ] I have performed a self-review of my code.
- [ ] Followed the repository's [Contributing Guidelines](/CONTRIBUTING.md).
- [ ] I ran the app and tested it locally to verify that it works as expected.
- [ ] I have checked my code with an automatic accessibility tool such as Axe Dev Tools or Wave
and it shows no errors.
## Additional Information (Optional)
Any additional information that you want to give us.
================================================
FILE: .github/workflows/appreciation.yml
================================================
name: 'Thank Contributors'
on:
issues:
types: [opened]
pull_request_target:
types: [opened]
jobs:
welcome-new-contributor:
runs-on: ubuntu-latest
steps:
- name: 'Greet the contributor'
uses: garg3133/welcome-new-contributors@v1.2
with:
token: ${{ secrets.GITHUB_TOKEN }}
issue-message: 'Hello @contributor_name, thanks for raising an issue in this project. The maintainers of this project are volunteers so please be understanding if it takes time before you get a response. We still appreciate your help with raising issues!'
pr-message: 'Hello @contributor_name, thanks for raising a pull request in this project. The maintainers of this project are volunteers so please be understanding if it takes time before you get a response. We still appreciate your help with creating pull requests!'
================================================
FILE: .github/workflows/axe.yaml
================================================
name: axe
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
axe:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Use Node.js 16.x
uses: actions/setup-node@v1
with:
node-version: 16.x
- run: npm ci
- run: npm run dev & npx wait-on http://localhost:3000
- name: Install browser drivers
run: npx browser-driver-manager install chrome
- name: Run axe
run: |
npm install -g @axe-core/cli
axe http://localhost:3000 --exit
================================================
FILE: .gitignore
================================================
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
.yarn
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# local env files
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
# VS code
.vscode
# IDEA
.idea
================================================
FILE: .gitpod.yml
================================================
# List the start up tasks. Learn more https://www.gitpod.io/docs/config-start-tasks/
tasks:
- name: npm
init: npm install
command: npm run dev
# List the ports to expose. Learn more https://www.gitpod.io/docs/config-ports/
ports:
- port: 3000
onOpen: open-browser
github:
prebuilds:
master: true
branches: true
pullRequests: true
pullRequestsFromForks: true
addCheck: true
addComment: false
addBadge: true
================================================
FILE: .husky/pre-commit
================================================
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx lint-staged
================================================
FILE: .prettierignore
================================================
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
*.md
================================================
FILE: .prettierrc.json
================================================
{
"trailingComma": "es5",
"useTabs": true,
"tabWidth": 2,
"semi": false,
"singleQuote": false,
"bracketSpacing": true,
"bracketSameLine": true,
"endOfLine": "lf",
"singleAttributePerLine": false
}
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
emma.l.dawson@gmail.com.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.
================================================
FILE: CODING_STANDARDS.md
================================================
# Coding Standards
## Naming
Choosing good names is critical to creating code that is easy to use and easy to understand. You
should always take the time to think about whether you have chosen the right name for something.
### English
All code, names, comments, etc. must be in English.
### Use one name for one thing
Do not reuse names. Do not use names that can mean multiple things. Always use the same name for the
same thing.
### Descriptive
Names must be descriptive for the working or usage of the class, method or variable.
### Context
A name should make sense within its context and should not have unnecessary information for that
context. For example a variable that holds the name of a user can be named `name` within a `User`
context. However if you need to hold the name of a user in another place, `userName` might be a
better name. Adding `user` within a `User` context (`user.userName`) is redundant and should be
avoided.
### Interfaces
Interfaces should use the `I` as prefix. Like `IMyInterface`
### Functions
Prefer using a verb as a name to indicate it will do something. Like `render`, `open` or `getData`.
### Variables, Properties, etc.
All non-functions should have a noun as a name, not a verb.
### Booleans
Should start with `is`, `has`, `will` or `should`. Like `isValid` or `hasValues`.
### Always Affirmative
Avoid negations. Prefer `isShown` over `isHidden` or
`isEnabled` over `isDisabled`. Do not use names like `notEditable`.
## Casing
### Classes, Interfaces, Types, Enums and Generics
**PascalCase** Every individual word start with an upper case character, no underscores, no dashes.
### Functions, Properties, Arguments and Variables
**camelCase** Starts with a lower case character, every following individual word start with an
upper case character, no underscores, no dashes.
### Globally used constants
**SNAKE_UPPER_CASE** Only use upper case characters, individual words must be separated with an underscore.
## File names
### Page files, data files and hook files
**camelCase** Starts with a lower case character, every following individual word start with an
upper case character, no underscores, no dashes.
### Component files
**PascalCase** Every individual word start with an upper case character, no underscores, no dashes.
## Coding
### Functions
Deconstruct props in the function parameters rather than inside the function body
```ts
interface IProps {
propOne: string
propTwo: string
}
const Component = ({ propOne, propTwo }: IProps) => { ... }
// rather than
const Component = (props: IProps) => {
const { propOne, propTwo } = props
...
}
```
Named export rather than default export
```ts
export const Component = () => { ... }
// rather than
const Component = () => { ... }
export default Component
```
### CSS
When in need to use a colour, use the defined variable from the `globals.ccs` file rather than adding hex codes.
```css
.class {
color: var(--text);
}
/* rather than */
.class {
color: #272727;
}
```
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to Accessible Web Dev
## Languages
This project is built with Next.JS, Typescript and CSS modules.
## Pre-requisites (these need to be installed on your machine)
[Node.js](https://nodejs.org/en/)
## Available packages
Icons - [React Icons Documentation]( https://react-icons.github.io/react-icons)
Code Snippets - [React Syntax Highlighter Documentation](https://github.com/react-syntax-highlighter/react-syntax-highlighter)
Translations - [next-i18next Documentation](https://next.i18next.com/)
## How to contribute
1. Look through [Pre-existing issues](https://github.com/AccessibleForAll/AccessibleWebDev/issues) or [Raise a new issue](https://github.com/AccessibleForAll/AccessibleWebDev/issues/new/choose) and ask to be assigned. Pull requests made without a corresponding issue will likely be closed.
2. Please check the [coding standards](https://github.com/AccessibleForAll/AccessibleWebDev/blob/main/CODING_STANDARDS.md) page before start contributing.
3. [Fork](https://github.com/AccessibleForAll/AccessibleWebDev/fork) the project
4. Clone the project:
```bash
git clone https://github.com/<your-github-username>/AccessibleWebDev
```
5. Navigate to the project directory:
```bash
cd AccessibleWebDev
```
6. Set the upstream repository:
```bash
git remote add upstream https://github.com/AccessibleForAll/AccessibleWebDev.git
```
7. Install dependencies:
```bash
npm install
```
8. Create a new branch:
```bash
git checkout -b <YourBranchName>
```
9. To run the whole project locally:
```bash
npm run dev
```
10. Make your changes
11. Stage your changes:
```bash
git add <NameOfFileChanged>
```
12. Commit your changes and provide a meaningful commit message:
Unsure how to write a meaningful commit message? Check out this article about [How to Write a Good Git Commit Message](https://blog.ossph.org/how-to-write-a-good-git-commit-message/#:~:text=To%20set%20up%20a%20Git,t%20meet%20the%20specified%20format.)
```bash
git commit -m "<Your commit message here>"
```
13. Push your commits to your local repository
```bash
git push origin <YourBranchName>
```
14. Create a [pull request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request)
15. Wait for maintainers to review your pull request and suggest any changes
**Congratulations!** You made a contribution to the Accessible For All Community!
## Questions?
If at any time you need help with contributing, please reach out to the [maintainers](https://github.com/AccessibleForAll/Support/blob/main/README.md#our-maintainers)
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2022 Accessible For All
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# [Accessible Web Dev](https://accessibleweb.dev/) 🌐
Welcome to **Accessible Web Dev**, a resource designed to help web developers learn the fundamentals of accessibility in a practical, easy-to-understand way. This site is an **entry point** into the world of accessibility, complementing current standards by breaking down complex concepts. While it doesn't replace existing standards like the [WCAG standards](https://www.w3.org/WAI/standards-guidelines/wcag/), it offers a beginner-friendly pathway into accessibility for developers of all experience levels.

---
## 🚀 Features
- **Beginner-Friendly Content**: Step-by-step guides to start your accessibility journey.
- **Practical Examples**: Real-world implementations of accessibility best practices.
- **Resources & Tools**: Discover accessibility checkers, ARIA guides, and more.
- **Community-Driven**: Contributions from developers worldwide to keep the content relevant and up-to-date.
---
## 🤝 How to Contribute
We welcome and encourage contributions! There are multiple ways to help improve this project:
1. **Report Bugs:**
If you encounter a bug, please raise a [Bug Report](https://github.com/AccessibleForAll/AccessibleWebDev/issues/new?assignees=&labels=&template=bug_report.md&title=).
2. **Request New Features:**
Have ideas for new features? Submit an [Issue](https://github.com/AccessibleForAll/AccessibleWebDev/issues/new?assignees=&labels=&template=feature_request.md&title=).
3. **Contribute Code, Documentation, or Design:**
We are always looking to improve! Please follow our [Contributing Guidelines](https://github.com/AccessibleForAll/AccessibleWebDev/blob/main/CONTRIBUTING.md) to submit code, translations, or design updates.
4. **Spread the Word:**
Share the project with your peers to help promote accessibility across the web.
---
## 📄 License
Accessible Web Dev is licensed under the **MIT License**. See the full [License](https://github.com/AccessibleForAll/AccessibleWebDev/blob/main/LICENSE) for more details.
================================================
FILE: components/CodeBlock/CodeBlock.module.css
================================================
.CodeBlockContainer {
position: relative;
width: 90vw;
max-width: 850px;
border: 1px solid var(--primaryLt);
border-radius: 8px;
background-color: var(--primaryLt);
padding: 40px 8px 2px 8px;
margin: 16px 0;
}
.CodeBlock {
overflow-x: auto;
}
.codeLanguage {
background-color: var(--dark);
padding: 8px;
color: var(--white);
position: absolute;
top: 10px;
left: 8px;
right: 8px;
border-bottom: 1px solid var(--primaryLt);
}
@media only screen and (min-width: 800px) {
.CodeBlockContainer {
width: 61vw;
}
}
@media only screen and (min-width: 1000px) {
.CodeBlockContainer {
width: 70vw;
}
}
================================================
FILE: components/CodeBlock/CodeBlock.tsx
================================================
import { useEffect, useRef, useState } from "react"
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"
import { nightOwl } from "react-syntax-highlighter/dist/cjs/styles/prism"
import CopyCodeBlock from "../CopyCodeBlock/CopyCodeBlock"
import styles from "./CodeBlock.module.css"
type TCodeLanguage =
| "html"
| "css"
| "javascript"
| "jsx"
| "typescript"
| "tsx"
interface ICodeBlockProps {
codeSnippet: string
languageType: TCodeLanguage
}
export const CodeBlock = ({ codeSnippet, languageType }: ICodeBlockProps) => {
const [scrollableRegion, setScrollableRegion] = useState(false)
const ref = useRef<HTMLPreElement>(null)
const PreWithRef = (preProps: React.HTMLAttributes<HTMLPreElement>) => (
<pre {...preProps} ref={ref} />
)
useEffect(() => {
if (ref.current) {
const element = ref.current
setScrollableRegion(element.clientWidth < element.scrollWidth)
}
}, [])
return (
<div className={styles.CodeBlockContainer}>
<div className={styles.codeLanguage}>{languageType}</div>
<CopyCodeBlock code={codeSnippet} />
<SyntaxHighlighter
language={languageType}
style={nightOwl}
className={styles.CodeBlock}
PreTag={PreWithRef}
tabIndex={scrollableRegion ? 0 : -1}>
{codeSnippet}
</SyntaxHighlighter>
</div>
)
}
================================================
FILE: components/ContentTemplates/AlertsTemplate.tsx
================================================
import { WorkInProgress } from "../WorkInProgress/WorkInProgress"
export const AlertsTemplate = () => {
return <WorkInProgress />
}
================================================
FILE: components/ContentTemplates/AnimationsTemplate.tsx
================================================
import { WorkInProgress } from "../WorkInProgress/WorkInProgress"
export const AnimationsTemplate = () => {
return <WorkInProgress />
}
================================================
FILE: components/ContentTemplates/AudioTemplate.tsx
================================================
import { CodeBlock } from "../CodeBlock/CodeBlock"
import { NavPage } from "../NavPage/NavPage"
import { PageUpdated } from "../PageUpdated/PageUpdated"
import { TemplateSection } from "../TemplateSection/TemplateSection"
import { audioPageNavigation } from "../../data/pageNavigationLists"
export const AudioTemplate = () => {
return (
<>
<NavPage pageNavigation={audioPageNavigation} />
<TemplateSection sectionName="introduction" title="Introduction">
<p>
Any form of audio on a website, whether prerecorded or live, audio
only or as part of a video or animation, should be provided in an
alternative form so that it can be perceived by people that can't
access the audio. Such alternatives include transcripts, captions and
sign language.
</p>
</TemplateSection>
<TemplateSection sectionName={"transcripts"} title={"Transcripts"}>
<p>
An audio transcript is a text version of the audio in a prerecorded
audio or video file. It should be located close to the audio or video
file so that it is easy to find for anyone that wishes to read it. As
a transcript can be long, it is adequate to provide a link to the
transcript in close proximity to the audio or video file.
</p>
<p>
Transcripts should identify who is speaking and include any relevant
background sounds. Full verbatim transcripts include every sound, even
ums and ahs, that occured in the original audio. Clean verbatim
transcripts remove these extra sounds whilst leaving the rest of the
audio as is. Nothing is paraphrased or summarised. They should include
information about who is speaking and time stamps can also be useful,
although not required, especially if the transcript is for a video.
</p>
<p>
An added benefit of transcripts is that they can be more easily
translated into other languages. This makes the audio content
available to a wider range of people.
</p>
</TemplateSection>
<TemplateSection sectionName={"captions"} title={"Captions"}>
<p>
Captions are a text version of audio displayed alongside video inside
the media player. They should include all forms of audio such as
dialogue, background music, sound effects and anything else relevant
to the person watching. If the video is prerecorded the captions
should be synced with the audio. If the video is live there can
sometimes be a short delay as the captions are being added in real
time by professional real-time captioners. Most online video services
offer the ability to upload caption files alongside the video. It is
important to choose a video upload service with these capabilities.
</p>
<p>
There are two types of captions: Open Captions and Closed Captions.
</p>
<h3>Open Captions</h3>
<p>
Open captions are captions that are burned into the video track. They
are always visible when the video is playing and there is no way to
adjust the font size or style of the captions. They do however provide
a consistent experience for everyone.
</p>
<h3>Closed Captions</h3>
<p>
Closed captions are provided on a separate audio track which means
they can be switched on and off by the user. Often the user will also
have some control over the size and style of the captions so they can
be adjusted to suit their personal needs. However, closed caption
support may differ between different media players. The closed
captions should be easy to access. Ideally, the button to turn them on
and off is at the same level as the play/pause button in the media
player.
</p>
<h3>Auto-generated captions</h3>
<p>
When using captions within video-conferencing tools or for live events
then auto-generated captions can provide some help. They are generally
said to be around 80% accurate. However, this still means that 20% of
information is lost. Auto-generated captions also become less accurate
for people with accents and often are only available for English.
</p>
<p>
Auto-generated captions are better than nothing but they are not
sufficient to be WCAG compliant. If you have the possibility to
provide real time captioning, or even sign language, for live events
then that should be the preference. Auto-generated captions should
also be edited if a recording of the live event is being uploaded to a
video-sharing platform afterwards.
</p>
</TemplateSection>
<TemplateSection
sectionName="howToAdd"
title="How To Add Closed Captions To Your Audio?">
<p>
Whether you are adding an introductory video about yourself on
websites or embedding an audio clip of your favorite song to a game
you're building, it's important to add closed captions to them. This
will ensure that users who need captions have an easier viewing
experience.
</p>
<p>
If using the video element within HTML you can specify a captions
track by linking to a vtt file and specifying that the kind is
captions. Multiple caption tracks in different languages can be added
if necessary.
</p>
<CodeBlock
languageType="html"
codeSnippet={`<video controls width="200">
<source src="video.mp4" type="video/mp4" />
<track
default
kind="captions"
src="https://youtube.com/captions.vtt"
srclang="en"
/>
</video>
`}
/>
<p>
Video upload services such as YouTube and Vimeo allow you to upload
captions in various file formats including VTT, WebVTT and SRT.
</p>
</TemplateSection>
<TemplateSection sectionName={"signLanguage"} title={"Sign Language"}>
<p>
There are over 300 different sign languages used around the world. For
many deaf and hard of hearing people it is their primary method of
communication and may be easier to understand or be less cognitively
demanding than reading captions or transcripts.
</p>
<p>
Sign language is more often used for live events as information can be
conveyed more quickly than using live captions. Important information
on a website could also be conveyed by embedding videos of the
information being signed.
</p>
</TemplateSection>
<TemplateSection
sectionName={"accessibleMediaPlayers"}
title={"Accessible Media Players"}>
<p>
The audio and video media players included in the HTML specification
are very basic and will require some work to make them accessible.
Many video upload/sharing services exist where work is well underway
to provide an accessible experience for users so it is often
beneficial to use such a service instead and embed the media player
into your site. However, it's always important to check any third
party tool you choose to use to make sure it is accessible.
</p>
<p>
The key things to check when looking for a media player are that it is
keyboard accessible and doesn't trap keyboard focus. It should also
have the ability to add captions and the captions should be easy to
find and operate with assistive technology.
</p>
</TemplateSection>
<TemplateSection sectionName={"WCAGCriteria"} title={"WCAG Criteria"}>
<ul className="list">
<li>
<a
href="https://www.w3.org/TR/WCAG22/#audio-only-and-video-only-prerecorded"
className="blockLink">
1.2.1 Audio-only and Video-only (prerecorded)
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG22/#captions-prerecorded"
className="blockLink">
1.2.2 Captions (prerecorded)
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG22/#audio-description-or-media-alternative-prerecorded"
className="blockLink">
1.2.3 Audio Description or Media Alternative (prerecorded)
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG22/#captions-live"
className="blockLink">
1.2.4 Captions (live)
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG22/#audio-description-prerecorded"
className="blockLink">
1.2.5 Audio Description (prerecorded)
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG22/#sign-language-prerecorded"
className="blockLink">
1.2.6 Sign Language (prerecorded)
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG22/#media-alternative-prerecorded"
className="blockLink">
1.2.8 Media Alternative (prerecorded)
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG22/#audio-only-live"
className="blockLink">
1.2.9 Audio-only (live)
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG22/#sensory-characteristics"
className="blockLink">
1.3.3 Sensory characteristics
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG22/#audio-control"
className="blockLink">
1.4.2 Audio Control
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG22/#low-or-no-background-audio"
className="blockLink">
1.4.7 Low or No Background Audio
</a>
</li>
</ul>
</TemplateSection>
<TemplateSection sectionName={"resources"} title={"Resources"}>
<ul className="list">
<li>
<a href="https://www.w3.org/WAI/media/av/" className="blockLink">
WAI Making Audio and Video Media Accessible
</a>
</li>
</ul>
</TemplateSection>
<PageUpdated date="7th October 2023" />
</>
)
}
================================================
FILE: components/ContentTemplates/BreadcrumbsTemplate.module.css
================================================
.breadcrumbsExample {
background-color: var(--highlight);
}
.breadcrumbsExample ol {
list-style: none;
margin: 0;
padding: 1rem;
display: flex;
gap: 0.5rem;
}
.breadcrumbsExample ol li {
display: flex;
gap: 0.5rem;
}
.breadcrumbsExample p {
margin: 0;
}
================================================
FILE: components/ContentTemplates/BreadcrumbsTemplate.tsx
================================================
import { breadcrumbsPageNavigation } from "../../data/pageNavigationLists"
import { CodeBlock } from "../CodeBlock/CodeBlock"
import { NavPage } from "../NavPage/NavPage"
import { PageUpdated } from "../PageUpdated/PageUpdated"
import { TemplateSection } from "../TemplateSection/TemplateSection"
import styles from "./BreadcrumbsTemplate.module.css"
export const BreadcrumbsTemplate = () => {
return (
<>
<NavPage pageNavigation={breadcrumbsPageNavigation} />
<TemplateSection sectionName="introduction" title="Introduction">
<p>
Breadcrumbs are a type of navigation, designed to make it easy to see
where you are within the structure of a website and easily navigate to
different areas. The name refers to the story of Hansel and Gretel who
left a trail of breadcrumbs in the hope to find their way back out of
the forest. They are most useful when content is nested several layers
deep.
</p>
<p>
Breadcrumbs are most often found at the top of a page and should be
placed just before the main content. There is no semantic HTML element
for breadcrumbs but it can be made from a combination of semantic HTML
elements plus a small amount of ARIA.
</p>
<nav
aria-label="Breadcrumbs (example only)"
className={styles.breadcrumbsExample}>
<ol>
<li>
<a href="#">Website root</a>
<p aria-hidden="true">{">"}</p>
</li>
<li>
<a href="#">Website level 1</a>
<p aria-hidden="true">{">"}</p>
</li>
<li>
<a href="#" aria-current="page">
Website level 2
</a>
</li>
</ol>
</nav>
</TemplateSection>
<TemplateSection sectionName="htmlStructure" title="HTML Structure">
<p>
Although there is not one HTML element that can be used to make
breadcrumbs, there are a few elements available that, when used
together, offer the semantics needed to make accessible breadcrumbs.
</p>
<p>What is needed:</p>
<ul className="list">
<li>
A nav element will let users know that this is a navigation
landmark. Landmarks make things easier to find, especially for
screen reader users.
</li>
<li>
An ordered list will let users know that the order of the items is
important.
</li>
<li>
Links within the list will let users know that they can click to
navigate to that area of the website.
</li>
<li>
Since the last element in a breadcrumb trail should represent the
current page, making it a link is optional.
</li>
</ul>
</TemplateSection>
<TemplateSection
sectionName="aria"
title="Add Extra Information with ARIA">
<p>
ARIA is used to add extra semantic information where HTML is not
enough. In this case, there is probably another navigation present on
the page. Therefore we will need to distinguish the breadcrumb
navigation from any other navigation with an aria-label.
</p>
<p>
If the last link in the breadcrumb trail is the current page, we can
also use aria-current on that link to specify that it is the currently
active page. If the last element is not a link then aria-current is
optional.
</p>
<p>Aria-hidden can be used to hide any dividers between links.</p>
</TemplateSection>
<TemplateSection
sectionName="breadcrumbsExample"
title="Breadcrumbs Example">
<CodeBlock
languageType="html"
codeSnippet={`<nav aria-label="Breadcrumbs" class="breadcrumbs">
<ol>
<li>
<a href="#">Website root</a>
<p aria-hidden="true">></p>
</li>
<li>
<a href="#">Website level 1</a>
<p aria-hidden="true">></p>
</li>
<li>
<a href="#" aria-current="page">Website level 2</a>
</li>
</ol>
</nav>`}
/>
</TemplateSection>
<TemplateSection
sectionName="breadcrumbStyling"
title="Styling Breadcrumbs">
<p>
Using an ordered list element within breadcrumbs causes each list item
to be numbered. This styling can be removed with CSS and flexbox can
be used to make list items sit side by side. CSS can also be used to
create dividers between each link, or the divider can be added within
the HTML and hidden with aria-hidden (as in the previous example).
</p>
<CodeBlock
codeSnippet={`.breadcrumbs ol {
list-style: none;
margin: 0;
padding: 1rem;
display: flex;
gap: 0.5rem;
}`}
languageType="css"
/>
<p>
Be aware that removing list styles from lists can cause the screen
reader VoiceOver on Mac to stop announcing items as a list. If this
happens, add role="list" to the ol element.
</p>
</TemplateSection>
<TemplateSection sectionName="WCAGCriteria" title="WCAG Criteria">
<ul className="list">
<li>
<a
href="https://www.w3.org/TR/WCAG21/#info-and-relationships"
className="blockLink">
1.3.1 Info and Relationships
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG21/#identify-purpose"
className="blockLink">
1.3.6 Identify Purpose
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG21/#keyboard"
className="blockLink">
2.1.1 Keyboard
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG21/#focus-order"
className="blockLink">
2.4.3 Focus Order
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG21/#link-purpose-in-context"
className="blockLink">
2.4.4 Link Purpose
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG21/#multiple-ways"
className="blockLink">
2.4.5 Multiple Ways
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG21/#focus-visible"
className="blockLink">
2.4.7 Focus Visible
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG21/#location"
className="blockLink">
2.4.8 Location
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG21/#target-size"
className="blockLink">
2.5.5 Target Size
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG21/#on-focus"
className="blockLink">
3.2.1 On Focus
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG21/#consistent-navigation"
className="blockLink">
3.2.3 Consistent Navigation
</a>
</li>
</ul>
</TemplateSection>
<TemplateSection sectionName="otherResources" title="Other Resources">
<ul className="list">
<li>
<a
href="https://www.w3.org/WAI/ARIA/apg/patterns/breadcrumb/examples/breadcrumb/"
className="blockLink">
ARIA Authoring Practices Guide Breadcrumb Example
</a>
</li>
<li>
<a
href="https://design-system.service.gov.uk/components/breadcrumbs/"
className="blockLink">
Gov.uk Design System - Breadcrumbs
</a>
</li>
</ul>
</TemplateSection>
<PageUpdated date="10th April 2023" />
</>
)
}
================================================
FILE: components/ContentTemplates/ButtonsTemplate.module.css
================================================
.exampleButton {
padding: 0.6rem 1.2rem;
cursor: pointer;
background-color: var(--primaryLt);
color: var(--text);
font-size: 0.9rem;
font-family: inherit;
border: 1px solid var(--dark);
border-radius: 5px;
display: inline-block;
margin: 0.5rem 0;
}
.exampleButton span {
display: flex;
align-items: center;
gap: 0.5rem;
color: inherit;
}
.exampleButton:hover, .exampleButton:focus {
background-color: var(--primary);
color: var(--invertText)
}
.exampleHover {
padding: 0.6rem 1.2rem;
display: flex;
align-items: center;
gap: 0.5rem;
cursor: pointer;
background-color: var(--primary);
color:var(--invertText);
font-size: 0.9rem;
font-family: inherit;
border: 1px solid var(--dark);
border-radius: 5px;
display: inline-block;
margin: 0.5rem 0;
}
.exampleFocus {
padding: 0.6rem 1.2rem;
display: flex;
align-items: center;
gap: 0.5rem;
cursor: pointer;
background-color: var(--primary);
color:var(--invertText);
font-size: 0.9rem;
font-family: inherit;
border: 1px solid var(--dark);
border-radius: 5px;
display: inline-block;
outline: 2px transparent solid;
box-shadow: 0 0 0 2px #000, 0 0 0 4px #fff;
margin: 0.5rem 0;
}
.exampleActive {
position: relative;
top: 2px;
left: 1px;
padding: 0.6rem 1.2rem;
display: flex;
align-items: center;
gap: 0.5rem;
cursor: pointer;
background-color: var(--primary);
color:var(--invertText);
font-size: 0.9rem;
font-family: inherit;
border: 2px inset var(--primaryLt);
border-radius: 5px;
display: inline-block;
margin: 0.5rem 0;
}
.exampleDisabled {
padding: 0.6rem 1.2rem;
display: flex;
align-items: center;
gap: 0.5rem;
cursor: not-allowed;
background-color: var(--primaryLt);
font-size: 0.9rem;
font-family: inherit;
border: 1px solid var(--dark);
border-radius: 5px;
display: inline-block;
margin: 0.5rem 0;
}
================================================
FILE: components/ContentTemplates/ButtonsTemplate.tsx
================================================
import { TemplateSection } from "../TemplateSection/TemplateSection"
import { CodeBlock } from "../CodeBlock/CodeBlock"
import { FaSave } from "react-icons/fa"
import { NavPage } from "../NavPage/NavPage"
import { buttonPageNavigation } from "../../data/pageNavigationLists"
import { PageUpdated } from "../PageUpdated/PageUpdated"
import styles from "./ButtonsTemplate.module.css"
export const ButtonsTemplate = () => {
const handleTextButton = () => {
alert("This is a Text button")
}
const handleAddToBasketButton = () => {
alert("This is an example Add to Basket button")
}
const handleTextAndIconButton = () => {
alert("This is an example Text and Icon button")
}
return (
<>
<NavPage pageNavigation={buttonPageNavigation} />
<TemplateSection sectionName="introduction" title="Introduction">
<p>
Buttons are interactive elements that perform an action when pressed.
That action could be something like submitting a form or opening a
menu.
</p>
<p>
Buttons have their own semantic HTML element which comes with lots of
inbuilt functionality. Using an HTML button means it can be reached by
the tab key and activated with both a mouse and a keyboard
automatically just by adding an onclick event. Using the semantic
button element will save you lots of time.
</p>
<CodeBlock
codeSnippet={`<button type="submit" onclick="handleClick()"></button>`}
languageType={"html"}
/>
<p>
If another element is used to create a button instead, such as a div,
then functionality must be added to make the div behave exactly like a
button. Adding the onclick event will not automatically make the
element keyboard interactive. It also needs to:
</p>
<ul className="list">
<li>Activate when a user presses the enter key</li>
<li>Activate when a user presses the space key</li>
<li>Be included in the tab sequence</li>
<li>Be given the explicit role of button</li>
</ul>
</TemplateSection>
<TemplateSection sectionName="textButtons" title="Text Buttons">
<p>A text button is a button which has only text inside it.</p>
<button className={styles.exampleButton} onClick={handleTextButton}>
Text here
</button>
<CodeBlock
codeSnippet={`<button type="button" onclick="handleClick()">Text here</button>`}
languageType={"html"}
/>
<p>
It gets its accessible name from the text between the opening and
closing button tags. If your text clearly explains the button's
purpose then this is an accessible button. However, if you have
several text buttons with the same text you may need to do more to
make them accessible.
</p>
<h3>Buttons with identical text</h3>
<p>
Let's say we have multiple products on a page, each with an "Add to
basket" button. How do we help screen reader users and speech
dictation users differentiate between the different buttons?
</p>
<ul>
<li>
Product 1{" "}
<button
className={styles.exampleButton}
onClick={handleAddToBasketButton}>
Add to basket
</button>
</li>
<li>
Product 2{" "}
<button
className={styles.exampleButton}
onClick={handleAddToBasketButton}>
Add to basket
</button>
</li>
<li>
Product 3{" "}
<button
className={styles.exampleButton}
onClick={handleAddToBasketButton}>
Add to basket
</button>
</li>
<li>
Product 4{" "}
<button
className={styles.exampleButton}
onClick={handleAddToBasketButton}>
Add to basket
</button>
</li>
<li>
Product 5{" "}
<button
className={styles.exampleButton}
onClick={handleAddToBasketButton}>
Add to basket
</button>
</li>
</ul>
<p>We could use CSS to visibly hide some extra text:</p>
<CodeBlock
codeSnippet={`.visibly-hidden {
clip: rect(0 0 0 0);
clip-path: inset(50%);
height: 1px;
overflow: hidden;
position: absolute;
white-space: nowrap;
width: 1px;
}`}
languageType={"css"}
/>
<CodeBlock
codeSnippet={`<button type="button" onclick="handleClick()">Add to basket
<span class="visibly-hidden">Product 1</span>
</button>`}
languageType={"html"}
/>
<p>
This would then read out "Add to basket product 1" to screen reader
users.
</p>
<p>
However, it's important to be careful of word order when using this
technique. If we had instead inserted the product name in the middle
of the sentence, for example: "Add product 1 to basket" this can cause
problems for users of speech input software.
</p>
<p>We could also use an aria-label.</p>
<CodeBlock
codeSnippet={`<button type="button" onclick="handleClick()" aria-label="Add to basket product 1">Add to basket</button>`}
languageType={"html"}
/>
<p>
Again, it is important that the start of the aria-label matches with
the visible text on the button to help users of speech input software
be able to activate the button.{" "}
</p>
</TemplateSection>
<TemplateSection
sectionName="textAndIconButtons"
title="Text and Icon Buttons">
<p>
Some buttons have both text and icons inside. The icon can help
complement the text and aid understanding. To prevent the accessible
name from repetition, eg "Button, save icon, save", the icon should be
given an empty(null) alt attribute or the state aria-hidden="true"
</p>
<button
className={styles.exampleButton}
type="button"
onClick={handleTextAndIconButton}>
<span>
<FaSave size="1rem" aria-hidden="true" /> Save
</span>
</button>
<CodeBlock
codeSnippet={`<button type="button" onclick="handleClick()">
<img src="icon-url.png" alt="" />
Text
</button>`}
languageType={"html"}
/>
<CodeBlock
codeSnippet={`<button type="button" onclick="handleClick()">
<img src="icon-url.png" aria-hidden="true" />
Text
</button>`}
languageType={"html"}
/>
<p>
An empty alt attribute has the most wide-spread support, so that
should be your go to choice when using icons. However, some icon
libraries don't allow an alt attribute to be passed and that's where
aria-hidden can be useful.
</p>
</TemplateSection>
<TemplateSection sectionName="iconOnlyButtons" title="Icon-only buttons">
<p>
Some buttons use only an icon to convey their function. In this case
it's important to choose icons that are widely recognised and
understood, otherwise it can be difficult to know their function. The
icons should also have a suitable label. This can be done with the alt
attribute if using images or the aria-label attribute.
</p>
<button
className={styles.exampleButton}
type="button"
onClick={handleTextAndIconButton}>
<FaSave size="1rem" aria-label="Save" />
</button>
<CodeBlock
codeSnippet={`<button type="button" onclick="handleClick()">
<img src="icon-url.png" alt="Save" />
</button>`}
languageType={"html"}
/>
<CodeBlock
codeSnippet={`<button type="button" onclick="handleClick()">
<i class="fa-solid fa-floppy-disk" aria-label="Save"></i>
</button>`}
languageType={"html"}
/>
<p>
Make sure that the label you give the icon matches the function of the
button, not necessarily describing the actual icon.
</p>
<p>
<strong>Do this:</strong>
</p>
<CodeBlock
codeSnippet={`<button type="button" onclick="handleClick()">
<i class="fa-solid fa-magnifying-glass" aria-label="Search"></i>
</button>`}
languageType={"html"}
/>
<p>
<strong>Don't do this:</strong>
</p>
<CodeBlock
codeSnippet={`<button type="button" onclick="handleClick()">
<i class="fa-solid fa-magnifying-glass" aria-label="Magnifying glass"></i>
</button>`}
languageType={"html"}
/>
</TemplateSection>
<TemplateSection sectionName="buttonStates" title="Button States">
<p>
Buttons can be used to communicate different states. A toggle button
can communicate both an on and off state. Buttons can also have
default, hover, focus, active and disabled states.
</p>
<h3>Default, hover, focus and active states</h3>
<p>These states can be handled with CSS.</p>
<ul>
<li>
<strong>Default state: </strong>
The normal state of a button when it's not being interacted with.
Any text or icon should have a contrast ratio of 4.5:1 with the
button background color. The button background color should have a
3:1 contrast ratio with the background.
<div>
<button
className={styles.exampleButton}
type="button"
onClick={handleTextButton}>
Default
</button>
</div>
</li>
<li>
<strong>Hover state: </strong>
How the button looks when hovered over with a mouse cursor. This
state is not visible on all devices, most notably touch screens.
It's not a requirement to have a hover state but if used then normal
color contrast ratios apply.
<div>
<button
className={styles.exampleHover}
type="button"
onClick={handleTextButton}>
Hover
</button>
</div>
</li>
<li>
<strong>Focus state: </strong>A button in focus should have a focus
indicator which makes it easy to see where focus is on the screen.
This is often a thicker border or outline around the button and
should have a contrast ratio of at least 3:1 with both the button
background color and the background behind the button. If the
button's colors change from default they should still meet the
required color contrast ratio between text and background.
<div>
<button
className={styles.exampleFocus}
type="button"
onClick={handleTextButton}>
Focus
</button>
</div>
</li>
<li>
<strong>Active state: </strong>A button is active in the moment it
is pressed. It will often look like it's been pressed.
<div>
<button
className={styles.exampleActive}
type="button"
onClick={handleTextButton}>
Active
</button>
</div>
</li>
</ul>
<h3>Disabled state</h3>
<p>
A disabled button is a button that is not focusable or clickable. It
has been given the disabled attribute. The browser styling often greys
out the text making the contrast lower than 3:1 which makes disabled
buttons difficult to see for some people. Often they are also not
discoverable by users of assistive technology. Therefore, it's best to
avoid disabled buttons and add in logic that provides error messages
instead if the button is pressed.
</p>
<button
className={styles.exampleDisabled}
type="button"
disabled
onClick={handleTextButton}>
Disabled button
</button>
<CodeBlock
codeSnippet={`<button type="button" onclick="handleClick()" disabled>Disabled button</button>`}
languageType={"html"}
/>
<h3>Toggle buttons - pressed and unpressed states</h3>
<p>
You might have a button that turns something on or off, or only has
two states. One example of this might be a light/dark mode.
</p>
<p>
In this case you need to add the extra information by using the aria
attribute aria-pressed which can either be true or false. This can be
toggled with the use of JavaScript. This lets screen reader users know
the state of the button.The button may also dynamically change the
text/icon to visually convey the state too.
</p>
<CodeBlock
codeSnippet={`<button type="button" onclick="handleClick()" aria-pressed="true">Toggle button</button>`}
languageType={"html"}
/>
<h3>Menus and popups - expanded and collapsed state</h3>
<p>
If a button is used to open a menu or popup then it should also
communicate to screen readers whether the popup is shown or not. This
is done with the aria-expanded attribute which can take true or false
values. If the popup is shown then aria-expanded="true". If the popup
is not shown then aria-expanded="false" and the screen reader will
announce that the popup is collapsed. The state of aria-expanded can
be controlled with JavaScript.
</p>
<CodeBlock
codeSnippet={`<button type="button" onclick="handleClick()" aria-expanded="true">Button with popup</button>`}
languageType={"html"}
/>
</TemplateSection>
<TemplateSection sectionName="buttonOrLink" title="Button or Link?">
<p>
How do you know when it's best to use a button and when it's best to
use a link?
</p>
<p>
Don't let CSS dictate which element you use. The styling can always be
changed. A simple rule of thumb is to think about what the element
should do. Buttons are for actions and links are to take you to new
places.
</p>
<p>
Is it performing an action like opening a modal, a menu or some other
popup? Is it applying some styling to a page like a bold or italic
button in a text editor? Is it allowing you to save something or
search for something? Then you should probably use a button.
</p>
<p>
Is it taking you to another page or another area of the page? Then it
should probably be a link.
</p>
</TemplateSection>
<TemplateSection
sectionName="touchTargetMinimum"
title="Touch Target Minimum">
<p>
When it comes to creating accessible buttons for your websites and
applications, it's important that they are easy to activate. To
achieve this goal, WCAG suggests that buttons have a minimum target
size of at least 24 by 24 CSS pixels. In doing so, users, especially
those who suffer from mobility impairments like hand tremors or are
amputees, have an easier time clicking on them.
</p>
</TemplateSection>
<TemplateSection sectionName="WCAGCriteria" title="WCAG Criteria">
<ul className="list">
<li>
<a
href="https://www.w3.org/TR/WCAG21/#non-text-content"
className="blockLink">
1.1.1 Non-text content
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG21/#info-and-relationships"
className="blockLink">
1.3.1 Info and Relationships
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG21/#identify-purpose"
className="blockLink">
1.3.6 Identify Purpose
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG21/#contrast-minimum"
className="blockLink">
1.4.3 Contrast (minimum)
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG21/#non-text-contrast"
className="blockLink">
1.4.11 Non-text Contrast
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG21/#content-on-hover-or-focus"
className="blockLink">
1.4.13 Content on Hover or Focus
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG21/#keyboard"
className="blockLink">
2.1.1 Keyboard
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG21/#focus-visible"
className="blockLink">
2.4.7 Focus Visible
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG21/#label-in-name"
className="blockLink">
2.5.3 Label in Name
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG22/#target-size-enhanced"
className="blockLink">
2.5.5 Target Size (Enhanced)
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG22/#target-size-minimum"
className="blockLink">
2.5.8 Target Size (Minimum)
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG21/#on-focus"
className="blockLink">
3.2.1 On Focus
</a>
</li>
</ul>
</TemplateSection>
<TemplateSection sectionName="otherResources" title="Other Resources">
<ul className="list">
<li>
<a
href="https://design-system.service.gov.uk/components/button/"
className="blockLink">
Gov.uk Design System Button
</a>
</li>
<li>
<a
href="https://yatil.net/blog/buttons-vs-links"
className="blockLink">
Buttons vs. Links by Eric Eggert
</a>
</li>
</ul>
</TemplateSection>
<PageUpdated date="18th February 2023" />
</>
)
}
================================================
FILE: components/ContentTemplates/CaptchasTemplate.tsx
================================================
import { captchasPageNavigation } from "../../data/pageNavigationLists"
import { NavPage } from "../NavPage/NavPage"
import { PageUpdated } from "../PageUpdated/PageUpdated"
import { TemplateSection } from "../TemplateSection/TemplateSection"
export const CaptchasTemplate = () => {
return (
<>
<NavPage pageNavigation={captchasPageNavigation} />
<TemplateSection sectionName="introduction" title="Introduction">
<p>
CAPTCHA stands for Completely Automated Public Turing test to tell
Computers and Humans Apart. CAPTCHAs are tests to prove you are human
and not a bot, such as picking out objects from a set of images,
listening to a garbled audio or trying to pick out letters and numbers
from distorted text.
</p>
<p>
CAPTCHAs are inherently not accessible by design. They rely on human
senses and cognition which means that people with certain disabilities
are unable to complete them.
</p>
</TemplateSection>
<TemplateSection
sectionName={"captchaIssues"}
title={"What's the issue with CAPTCHAs?"}>
<p>
The easiest way to explain the issues with CAPTCHAs is with examples.
</p>
<h3>Visual</h3>
<p>
Any CAPTCHA relying on sight, such as picking all the traffic light
images, or writing the letters in a distorted text, are not accessible
to people who are blind, are deaf-blind, have low vision or have a
reading disability such as dyslexia.
</p>
<h3>Audio</h3>
<p>
Any CAPTCHA relying on audio, such as picking out a word in the middle
of a distorted soundtrack, are not accessible to people who are deaf,
hard of hearing or those who have audio processing disorders.
</p>
<h3>Maths challenge</h3>
<p>
Any CAPTCHA that relies on doing calculations, even those considered
simple such as 1 + 2, are not accessible to some people with cognitive
disabilities, learning disabilities or those who have anxiety.
</p>
<h3>Alignment challenges</h3>
<p>
Any CAPTCHA relying on aligning two images are not accessible to
people with vision disabilities or motor disabilities.
</p>
<h3>Click to prove you're not a robot</h3>
<p>
Even having a checkbox CAPTCHA can be difficult for some people
because if they can't click it, sometimes another more complicated
CAPTCHA is triggered instead.
</p>
</TemplateSection>
<TemplateSection
sectionName={"accessibleCAPTCHA"}
title={"Accessible CAPTCHA"}>
<p>
The best way to make CAPTCHA accessible is to remove it. Unless you
have a large problem with spam then CAPTCHA is probably not needed.
You can try using things like honeypots for form submission. These are
hidden form fields that only bots can find. Or consider two factor
authentication for creating accounts and logging into services.
</p>
<p>
If you must use CAPTCHA then you must provide multiple ways for people
to attempt the CAPTCHA. This means that if people can't fill in a
visual CAPTCHA due to a disability, they can attempt an audio CAPTCHA
instead.
</p>
<p>
Even having two types of CAPTCHA does not make your site accessible
for everyone. If using CAPTCHA is the only way to get to a certain
part of a website or perform a specific action, then you also need to
provide a way for people who can't get past the CAPTCHAs to get help.
</p>
</TemplateSection>
<TemplateSection sectionName="WCAGCriteria" title="WCAG Criteria">
<ul className="list">
<li>
<a
href="https://www.w3.org/TR/WCAG21/#non-text-content"
className="blockLink">
1.1.1 Non-text content
</a>
</li>
</ul>
</TemplateSection>
<TemplateSection sectionName={"otherResources"} title={"Other Resources"}>
<ul>
<li>
<a href="https://www.w3.org/TR/turingtest/" className="blockLink">
The inaccessibility of CAPTCHAS
</a>
</li>
</ul>
</TemplateSection>
<PageUpdated date="28th June 2024" />
</>
)
}
================================================
FILE: components/ContentTemplates/ChartsTemplate.tsx
================================================
import { WorkInProgress } from "../WorkInProgress/WorkInProgress"
export const ChartsTemplate = () => {
return <WorkInProgress />
}
================================================
FILE: components/ContentTemplates/FormsTemplate.tsx
================================================
import { formsPageNavigation } from "../../data/pageNavigationLists"
import { CodeBlock } from "../CodeBlock/CodeBlock"
import { NavPage } from "../NavPage/NavPage"
import { PageUpdated } from "../PageUpdated/PageUpdated"
import { TemplateSection } from "../TemplateSection/TemplateSection"
export const FormsTemplate = () => {
return (
<>
<NavPage pageNavigation={formsPageNavigation} />
<TemplateSection sectionName="introduction" title="Introduction">
<p>
The web is filled with all types of digital forms. Digital forms are
used to collect user information in an organized way. Whether
inputting personal health information, completing a simple quiz, or
placing an order for a local restaurant, forms are there to capture
data.
</p>
<p>
Examples of digital form elements include radio dial buttons, editable
text fields, drop-down select elements, and text areas. When a form is
not accessible, barriers are created that prevent the accurate input
of data. Users may encounter difficulty using a keyboard to move
through the forms, or they may miss information needed to complete
data input accurately.
</p>
<p>
Additionally, assistive technology such as screen readers may not be
able to read the forms properly. It is imperative that users are able
to interact with, understand, and complete forms on web pages with
accuracy and without frustration.
</p>
</TemplateSection>
<TemplateSection
sectionName="whatToConsider"
title="What Should We Consider When Building Accessible Forms?">
<p>
There are some key considerations when it comes to building forms.
</p>
<ul className="list">
<li>
Forms should be simple, understandable and have clear labels and
instructions on how to input data.
</li>
<li>
Forms must be keyboard operable. Users should be able to use the tab
key to navigate through all form controls.
</li>
<li>
Forms must be programmatically labelled to match the form control.
</li>
<li>
Forms should provide validation and error notifications that do not
rely on just one sensory characteristic to alert users.
</li>
<li>
Any time limits are communicated up front, and users are able to
extend time limits if necessary.
</li>
</ul>
</TemplateSection>
<TemplateSection
sectionName="creatingForms"
title="Creating Simple, Understandable Forms">
<p>
Users should be able to understand what the form is asking for and how
to enter the data into these fields. A form should be demarcated with
the <form> element. Inside the form element, there should be
clearly identified sections and inputs with labels that ask for
information in the required format.
</p>
<p>
Provide simple instructions for filling out the form, including any
required formatting guidelines. For example, if putting in a phone
number or security number, the visual label before the text field
should be:{" "}
</p>
<p>Phone number (XXX-XXX-XXXX): [text field]</p>
<p>
This is better than just having “Phone number” as the visual label, as
it explicitly lets the user know the expected number format without
having to guess.
</p>
<p>
Additionally, forms should have good visual contrast between the text
and background, as well as a clear delineation of the field input area
and background area. This will make it easier for users to visually
navigate, especially those with low vision or visual perceptual
difficulties.
</p>
</TemplateSection>
<TemplateSection
sectionName="labelsAndInputControls"
title="Form Labels and Input Controls">
<p>
Inputs are created using the <input> element. This element takes
a type which specifies to the user what kind of data should be
entered. Use the appropriate <input> type for each field, such
as “text”, “email”, or “number”.
</p>
<p>
Placeholder text can be helpful for providing examples or additional
information, but it should not be used as a substitute for a semantic
label. This is because it disappears when the input is in focus, which
causes an extra load on memory.
</p>
<p>
Let’s revisit our phone number example. the example shows a simple
code example of a form with an area to place your phone number.
</p>
<CodeBlock
codeSnippet={`<form>
<label for="phone">Phone Number (XXX-XXX-XXXX):</label>
<input type="tel" id="phone" name="phone">
</form>
`}
languageType={"html"}
/>
<p>
This code will work and create a form with the text “Phone number:”
and an editable text field next to it to place a number. In the edit
field, there will be the text “enter phone number” as an additional
visual cue thanks to the placeholder attribute (remember that screen
readers do not read placeholder text, so important information should
be placed elsewhere).
</p>
<p>
With the code in this format, the description “Phone number” is
*visually* determined, and it is *programmatically* determined.
Meaning the text “Phone Number” is attached meaningfully to the input
area.
</p>
<p>
To make sure a label and input are programmatically linked as well as
visually linked we need to use attributes. The <label> element
should have a “for” attribute and the <input> element should
have an “id” attribute. The values of the for attribute and id
attribute must match to connect the label to the input. In this
example, the label represents both the visual description users see on
the page and the programmatic description that is read by a screen
reader.
</p>
<p>
When the label area is selected by a user, the focus of the browser
goes to the input field. This makes the tap/ click area larger for the
user, which is especially helpful for those with dexterity and
cognitive impairments. It also makes explicitly clear that these two
areas belong together.
</p>
</TemplateSection>
<TemplateSection
sectionName="keyboardAccessibleForms"
title="Keyboard Accessible Forms">
<p>
All forms should be navigable by using the keyboard only. Form
controls are natively keyboard accessible when using the <form>
element. Users should be able to press the tab or arrow buttons and
move through all form elements and links. The form elements should be
activated when the arrow or tab buttons are used (or given focus),
then activated using the enter or spacebar key.
</p>
<p>
When using JavaScript to create interactions and changes of state, the
native keyboard accessibility of a form may become inoperable. Because
there are interactions you can create that are only accessible by
mouse, extra care should be taken to ensure they can also be accessed
using the keyboard.
</p>
<p>
Avoid using tables to style forms, instead use CSS. Placing a form in
a <table> tag may cause navigation issues for assistive
technology and those using a keyboard to move around the page.
Manually testing your forms with a keyboard is imperative for catching
errors caused by JavaScript and general layout issues.
</p>
</TemplateSection>
<TemplateSection
sectionName="formErrors"
title="Error Validation and Changes in State">
<p>
It is essential that users know when they have placed the wrong
information in the form. Error messages and alerts should be clear,
easy to understand, and given in multiple ways.
</p>
<p>
This alert can be in the form of a popup box (navigable by keyboard)
or an alert next to the form control. The alert should be perceivable
by everyone. Therefore, alerts should not be limited to a sound, or
text, or be conveyed by changing the element to a red color. The alert
should also be readable by a screen reader.
</p>
{
// INFO: move this section!
}
<p>
Additionally, users should know when an input field is required. This
can be done using the “required” attribute. An example using the Phone
number form control would go as follows:
</p>
<CodeBlock
codeSnippet={`<form>
<label for="phone">Phone Number (XXX-XXX-XXXX):</label>
<input type="tel" id="phone" name="phone" required>
</form>
`}
languageType={"html"}
/>
<p>
What would be a good way to convey an error if someone entered their
phone number in an incorrect format? Preferably, the error message
would appear close to the input, specifying what the error is in as
much detail as possible. An icon next to the text would help
differentiate it from other text. The error should also be added to a
<div> element with role=alert in order to be read out to a
screen reader user.
</p>
</TemplateSection>
<TemplateSection sectionName="WCAGCriteria" title="WCAG Criteria">
<ul className="list">
<li>
<a href="https://www.w3.org/TR/WCAG21/#info-and-relationships">
1.3.1 Info and Relationships
</a>
</li>
<li>
<a href="https://www.w3.org/TR/WCAG21/#sensory-characteristics">
1.3.3 Sensory Characteristics
</a>
</li>
<li>
<a href="https://www.w3.org/TR/WCAG21/#identify-input-purpose">
1.3.5 Identify Input Purpose
</a>
</li>
<li>
<a href="https://www.w3.org/TR/WCAG21/#use-of-color">
1.4.1 Use of Color
</a>
</li>
<li>
<a href="https://www.w3.org/TR/WCAG21/#keyboard">2.1.1 Keyboard</a>
</li>
<li>
<a href="https://www.w3.org/TR/WCAG21/#timing-adjustable">
2.2.1 Timing Adjustable
</a>
</li>
<li>
<a href="https://www.w3.org/TR/WCAG21/#timeouts">2.2.6 Timeouts</a>
</li>
<li>
<a href="https://www.w3.org/TR/WCAG21/#headings-and-labels">
2.4.6 Headings and Labels
</a>
</li>
<li>
<a href="https://www.w3.org/TR/WCAG21/#focus-visible">
2.4.7 Focus Visible
</a>
</li>
<li>
<a href="https://www.w3.org/TR/WCAG21/#label-in-name">
2.5.3 Label in Name
</a>
</li>
<li>
<a href="https://www.w3.org/TR/WCAG21/#on-input">3.3.2 On Input</a>
</li>
<li>
<a href="https://www.w3.org/TR/WCAG21/#error-identification">
3.3.1 Error Identification
</a>
</li>
<li>
<a href="https://www.w3.org/TR/WCAG21/#labels-or-instructions">
3.3.2 Labels or Instructions
</a>
</li>
<li>
<a href="https://www.w3.org/TR/WCAG21/#error-suggestion">
3.3.3 Error Suggestion
</a>
</li>
<li>
<a href="https://www.w3.org/TR/WCAG21/#error-prevention-legal-financial-data">
3.3.4 Error Prevention
</a>
</li>
<li>
<a href="https://www.w3.org/TR/WCAG21/#name-role-value">
4.1.2 Name, Role, Value
</a>
</li>
</ul>
</TemplateSection>
<PageUpdated date="25th July 2023" />
</>
)
}
================================================
FILE: components/ContentTemplates/HeadingsTemplate.tsx
================================================
import Image from "next/legacy/image"
import { NavPage } from "../NavPage/NavPage"
import { CodeBlock } from "../CodeBlock/CodeBlock"
import { headingsPageNavigation } from "../../data/pageNavigationLists"
import { TemplateSection } from "../TemplateSection/TemplateSection"
import { PageUpdated } from "../PageUpdated/PageUpdated"
export const HeadingsTemplate = () => {
return (
<>
<NavPage pageNavigation={headingsPageNavigation} />
<article>
<TemplateSection sectionName="intro" title="Introduction">
<p>
HTML headings are tags that are used to hold titles and subheadings
on a web page. They range from H1, which holds the overall title of
the page, to H2 - H6, which hold subheadings. The importance of the
heading decreases as the number increases.
</p>
<p>
A good way to think about headings is to imagine a table of
contents. The headings make up the important parts of your content
that you would want users to find quickly.
</p>
<div className="imageContainer">
<Image
src="/images/headingsTemplate/contents.png"
alt="screenshot of a table of contents for article about 20 coffee chats with developers, displaying headings for each section"
width={400}
height={400}
layout="intrinsic"
/>
</div>
</TemplateSection>
<TemplateSection
sectionName="whyHeadings"
title="Why are HTML Headings Important?">
<p>Headings are important for a number of reasons.</p>
<ul className="list">
<li>
They give order and structure to the web page, making it easier
for users to understand the layout and content of the page.
</li>
<li>
They allow the browser to index and structure the page visually,
which helps users find what they are looking for.
</li>
<li>
They allow screen reader users to skip to specific sections of the
page, so they can find the information they need quickly.
</li>
</ul>
</TemplateSection>
<TemplateSection
sectionName="includedInHeadings"
title="What Should Be Included in a Heading?">
<p>
When using headings, it is important to include descriptive titles
and subheadings that accurately describe the content on the page.
This will help users to find what they are looking for more easily.
Headings should also be concise and to the point. They should
clearly summarize the content that follows.
</p>
</TemplateSection>
<TemplateSection
sectionName="goodHeadings"
title="How to Write a Good Heading">
<p>
Writing a good heading is all about being descriptive, and
remembering how to nest heading sections within one another in
sequential order.
</p>
<p>Below is a code snippet example with semantic section headings.</p>
<CodeBlock
languageType="html"
codeSnippet={`<h1> Yoga for Developers </h1>
<h2> What is Yoga? </h2>
<h3> The History of Yoga </h3>
<h4> Yoga's Origin </h4>
<h4> Yoga in Modern Times </h4>
<h2> The Benefit of Yoga </h2>
<h3> Strength Conditioning </h3>
<h4> Power Yoga </h4>
<h3> Increasing Flexibility</h3>
<h4> Gentle Stretches </h4>
<h5> Stretches for the hands </h5>
<h6> Prayer Pose </h6>
<h6> Nerve Gliding Exercises </h6>`}
/>
</TemplateSection>
<TemplateSection
sectionName="avoidHeadings"
title="What Should We Avoid When Using Headings?">
<ul className="list">
<li>
Avoid using more than one H1 on a page, as this can confuse the
structure and hierarchy of the page. Instead, the H1 should be
used for the most important topic on the page.
</li>
<li>
Headings should not be chosen for style purposes - use CSS to
style sections instead.
</li>
<li>
Headings should not be placed in non sequential order to ensure
that the structure of the page makes sense.
</li>
</ul>
</TemplateSection>
<TemplateSection sectionName="WCAGCriteria" title="WCAG Criteria">
<ul className="list">
<li>
<a
href="https://www.w3.org/TR/WCAG21/#section-headings"
className="blockLink">
2.4.10 Section Headings
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/UNDERSTANDING-WCAG20/navigation-mechanisms-descriptive.html"
className="blockLink">
2.4.6 Headings and Labels
</a>
</li>
</ul>
</TemplateSection>
<TemplateSection
sectionName="QuizQuestions"
title="Test Your Knowledge">
<ol className="list">
<li>How many H1s should we have on our webpage?</li>
<li>Why is heading order important?</li>
<li>
Why is it important to have accurate and descriptive headings?
</li>
<li>
How does non-semantic headings affect users of screen readers?
</li>
<li>What should we use to change the style of our headings?</li>
<li>
If you wanted to add another subsection under an h2 heading that
is directly related to the h2 subject, what heading level would
you use?
</li>
<li>
If you wanted to add a new subsection after an h2 that is not
related to the current subsection of h2, which heading level would
you use to make a new section?
</li>
</ol>
</TemplateSection>
</article>
<PageUpdated date="8th April 2023" />
</>
)
}
================================================
FILE: components/ContentTemplates/IconsTemplate.tsx
================================================
import Link from "next/link"
import { CodeBlock } from "../CodeBlock/CodeBlock"
import { NavPage } from "../NavPage/NavPage"
import { PageUpdated } from "../PageUpdated/PageUpdated"
import { TemplateSection } from "../TemplateSection/TemplateSection"
import { IPageNavigationItem } from "../../data/pageNavigationLists"
export const iconsPageNavigation: IPageNavigationItem[] = [
{ linkName: "Introduction", href: "#introduction" },
{ linkName: "Decorative Icons", href: "#decorativeIcons" },
{ linkName: "Informative Icons", href: "#informativeIcons" },
{ linkName: "Text Alternatives", href: "#textAlternatives" },
{ linkName: "Icon Links", href: "#iconLinks" },
{ linkName: "Icon Buttons", href: "#iconButtons" },
{ linkName: "Use of Colour", href: "#useOfColour" },
{ linkName: "Target Size", href: "#targetSize" },
{ linkName: "WCAG Criteria", href: "#WCAGCriteria" },
// { linkName: "Other Resources", href: "#otherResources" },
]
export const IconsTemplate = () => {
return (
<>
<NavPage pageNavigation={iconsPageNavigation} />
<article>
<TemplateSection sectionName="introduction" title="Introduction">
<p>
Icons are small stylized graphical images that are often used on
webpages to complement text. They are also often used alone instead
of text in order to save space. They are commonly included in
buttons and links.
</p>
<p>
Icons should be added to a page using the inbuilt HTML img element
or as an svg. This way, alternative text can be added to the icon if
needed.
</p>
</TemplateSection>
<TemplateSection sectionName="decorativeIcons" title="Decorative Icons">
<p>
An icon is decorative if it doesn't add any information to a page.
This is often the case when icons are used to complement text,
especially as part of links and buttons. If a SAVE button included
both the text "Save" and an icon of a floppy disk the icon is
decorative. It should have an empty alternative text to avoid the
word "Save" being announced twice by screen readers.
</p>
</TemplateSection>
<TemplateSection
sectionName="informativeIcons"
title="Informative and Functional Icons">
<p>
If an icon is informative or functional it needs to have alternative
text. Examples could include an envelope icon next to an email
address, a magnifying glass used as a search button or a cog to
stand for settings. In each case, the alternative text should
describe the function, for example "search" rather than "magnifying
glass".
</p>
</TemplateSection>
<TemplateSection
sectionName="textAlternatives"
title="Text Alternatives">
<p>
The text alternative for an icon depends upon its purpose. Is the
icon purely decorative? Or does it serve a functional role like an
icon pointing to the homepage?
</p>
<p>
In HTML the text alternative is added to the img element which takes
an alt attribute. The alt attribute should be present on every img
element even if the icon is decorative. If the icon is an svg it
must be given the role of img and a title to make it accessible.
</p>
<p>
The alternative text can be read out by screen readers or converted
into braille for refreshable braille displays. Without it, screen
reader users may hear the whole image url instead, which can often
be a string of incomprehensible letters and numbers.
</p>
<p>
An icon may need different alternative text depending on where it is
placed on a webpage. For example, an icon button of a magnifying
glass may serve as a search button in one place and as a zoom button
in another.
</p>
<CodeBlock
codeSnippet={`<img src="url" alt="The text alternative goes here" />`}
languageType="html"
/>
<CodeBlock
codeSnippet={`<svg role="img" height="210" width="400">
<title>The text alternative goes here</title>
<path d="M120 10 L55 200 L265 180 Z" />
</svg>`}
languageType="html"
/>
</TemplateSection>
<TemplateSection sectionName="iconLinks" title="Icon Links">
<p>
You can turn an icon into a clickable link that takes you to another
webpage. Since icon links don't have a visible link text, you need
to use a different method to give the link an accessible name. These
methods can include adding an aria-label, using a visibly-hidden
class or adding an alt attribute to the image wrapped inside the
link. See the <Link href={"/links"}>Links Page</Link> for detail of
how to use these techniques.
</p>
<p>
The important thing to remember when using icons as links is to
describe the link destination rather than the content of the image.
</p>
<CodeBlock
codeSnippet={`<a href="https://google.com" aria-label="Google">
<FaGoogle />
</a>
<a href="https://google.com">
<FaGoogle />
<span class="visibly-hidden">Google</span>
</a>
<a href="https://google.com">
<img src="url" alt="Google" />
</a>`}
languageType={"html"}
/>
</TemplateSection>
<TemplateSection sectionName="iconButtons" title="Icon Buttons">
<p>
Some buttons use only an icon to convey their function. In this case
it's important to choose icons that are widely recognised and
understood, otherwise it can be difficult to know their function.
The icons should also have a suitable label. This can be done with
the alt attribute or the aria-label attribute on the button.
</p>
<CodeBlock
codeSnippet={`<button type="button" onclick="handleClick()">
<img src="icon-url.png" alt="Save" />
</button>`}
languageType={"html"}
/>
<CodeBlock
codeSnippet={`<button type="button" onclick="handleClick()">
<i class="fa-solid fa-floppy-disk" aria-label="Save"></i>
</button>`}
languageType={"html"}
/>
</TemplateSection>
<TemplateSection sectionName={"useOfColour"} title={"Use of Colour"}>
<p>
Icons are often used to communicate status. The colour of the icon
should not be the only way to do this. Instead, use different icons
or complement the icon with text. For example, if you have an icon
to indicate online status, avoid green for online and red for
offline as people with red-green colour vision deficiencies will
have difficulty telling the two colours apart.
</p>
</TemplateSection>
<TemplateSection sectionName={"targetSize"} title={"Target Size"}>
<p>
Icons are often quite small and if used as icon buttons they can be
difficult to press. Therefore, they should have a minimum target
size to make sure they can be activated even for users with
dexterity limitations.
</p>
<p>
The minimum target size should be at least 24 x 24 CSS pixels unless
the interactive element has enough space so that it does not
intersect another element, it has an equivalent larger control
elsewhere on the same page, it is inline, or the presentation is
considered essential.
</p>
<p>
Ideally, the target size is even larger than 24 CSS pixels, making
it easier to press. On mobile devices, 44 x 44 CSS pixels is the
recommended size. This allows for the reduced accuracy of touch.
</p>
</TemplateSection>
<TemplateSection sectionName="WCAGCriteria" title="WCAG Criteria">
<ul className="list">
<li>
<a
href="https://www.w3.org/TR/WCAG21/#non-text-content"
className="blockLink">
1.1.1 Non-text content
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG21/#info-and-relationships"
className="blockLink">
1.3.1 Info and Relationships
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG21/#identify-purpose"
className="blockLink">
1.3.6 Identify Purpose
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG21/#use-of-color"
className="blockLink">
1.4.1 Use of Color
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG21/#non-text-contrast"
className="blockLink">
1.4.11 Non-text Contrast
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG22/#link-purpose-in-context"
className="blockLink">
2.4.4 Link Purpose (in Context)
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG22/#link-purpose-link-only"
className="blockLink">
2.4.9 Link Purpose (Link Only)
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG21/#label-in-name"
className="blockLink">
2.5.3 Label in Name
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG22/#target-size-enhanced"
className="blockLink">
2.5.5 Target Size (Enhanced)
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG22/#target-size-minimum"
className="blockLink">
2.5.8 Target Size (Minimum)
</a>
</li>
</ul>
</TemplateSection>
{/* <TemplateSection sectionName="otherResources" title="Other Resources">
<ul className="list">
<li>
<a
href="https://www.w3.org/WAI/tutorials/images/"
className="blockLink">
W3C Images Tutorial
</a>
</li>
</ul>
</TemplateSection> */}
</article>
<PageUpdated date="8th July 2024" />
</>
)
}
================================================
FILE: components/ContentTemplates/ImagesTemplate.tsx
================================================
import Image from "next/legacy/image"
import { NavPage } from "../NavPage/NavPage"
import { CodeBlock } from "../CodeBlock/CodeBlock"
import { imagePageNavigation } from "../../data/pageNavigationLists"
import { TemplateSection } from "../TemplateSection/TemplateSection"
import { PageUpdated } from "../PageUpdated/PageUpdated"
export const ImagesTemplate = () => {
return (
<>
<NavPage pageNavigation={imagePageNavigation} />
<article>
<TemplateSection sectionName="introduction" title="Introduction">
<p>
A picture is worth more than a thousand words, as the old adage
goes. Images help bring texts to life, they complement complex
explanations and often lead to greater understanding and better web
experiences. For many people they help make things easier to
understand. However, not everyone can see images and therefore a
text alternative is needed so that everyone has access to the
information conveyed in the image.
</p>
</TemplateSection>
<TemplateSection
sectionName="textAlternatives"
title="Text Alternatives">
<p>
The text alternative for an image depends upon its purpose. Is the
image purely decorative? Does it help to describe or explain the
text? Or does it serve a functional role like an icon pointing to
the homepage?
</p>
<p>
In HTML the text alternative is added to the image element which
takes an alt attribute. The alt attribute should be present on every
img element. If the image is an svg it must be given the role of
image and a title to make it accessible.
</p>
<p>
The alternative text can be read out by screen readers or converted
into braille for refreshable braille displays. Without it, screen
reader users may hear the whole image url instead, which can often
be a string of incomprehensible letters and numbers.
</p>
<CodeBlock
codeSnippet={`<img src="url" alt="The text alternative goes here" />`}
languageType="html"
/>
<CodeBlock
codeSnippet={`<svg role="img" height="210" width="400">
<title>The text alternative goes here</title>
<path d="M120 10 L55 200 L265 180 Z" />
</svg>`}
languageType="html"
/>
</TemplateSection>
<TemplateSection
sectionName="decorativeImages"
title="Decorative Images">
<p>
Decorative images usually add no extra information to a page and a
person’s understanding is not hindered if the images are not
visible. Background patterns and shapes are often decorative. Icons
are also sometimes decorative if they are combined with a link and
the link text describes the icon. Decorative images should have an
empty (sometimes also called null) alt attribute.
</p>
<div className="imageContainer">
<Image
src="/images/imagesTemplate/oldPaperTexture.jpg"
alt="Old brown paper texture"
width={300}
height={200}
layout="intrinsic"
/>
<Image
src="/images/imagesTemplate/hexagonsPattern.jpg"
alt="Blue tiled hexagons"
width={300}
height={200}
layout="intrinsic"
/>
</div>
<CodeBlock
codeSnippet={`<img src="/oldPaperTexture.jpg" alt="" />
<img src="/hexagonsPattern.jpg" alt="" />`}
languageType="html"
/>
</TemplateSection>
<TemplateSection
sectionName="informativeImages"
title="Informative Images">
<p>
Informative images compliment written information by illustrating
concepts and examples visually. An image of a cake alongside a
recipe, a diagram visualising an instruction and a telephone icon
preceding a phone number are all examples of informative images.
These should have a short and succinct text alternative.
</p>
<div className="imageContainer">
<Image
src="/images/imagesTemplate/cupcakes.jpg"
alt="Cupcakes with pink icing and small sugar heart decorations"
width={300}
height={200}
layout="intrinsic"
/>
</div>
<CodeBlock
codeSnippet={`<img src="/cupcakes.jpg" alt="Cupcakes with pink icing and small sugar heart decorations" />`}
languageType="html"
/>
<h3>Complex Informative Images</h3>
<p>
Some informative images such as graphs can convey a lot of complex
information. In such cases the text alternative needs to convey the
same information which can be difficult to do in a short and
succinct alt attribute. In this case the text alternative is split
into two parts. The alt attribute should summarise the image and
also explain where a longer form text explanation can be found. The
longer text can be found on the same page or as a link to a separate
page.
</p>
<p>
The longer text should convey the same complex information as the
image. In the case of graphs the data should be explained along with
any trends or relationships. Avoid using colour as the only way of
explaining the information as not everyone perceives colour in the
same way.
</p>
<div className="imageContainer">
<Image
src="/images/imagesTemplate/drill-instruction.png"
alt="Drawn instructions for how to use a handheld drill"
width={200}
height={175}
layout="intrinsic"
/>
</div>
<CodeBlock
codeSnippet={`<img src="drill-instructions.png" alt="How to use a handheld drill. Further instructions below." />`}
languageType="html"
/>
</TemplateSection>
<TemplateSection
sectionName="functionalImages"
title="Functional Images">
<p>
If images are used as part of a link or button they are usually
there to perform a function. Examples include using a magnifying
glass to signify a search field, a floppy disk icon as a save button
or a logo as a link back to the home page. The text alternative
should match the action performed by the link or button.
</p>
<div className="imageContainer">
<Image
src="/images/imagesTemplate/searchBtn.png"
alt="Magnifying glass used as search button example"
width={150}
height={150}
layout="intrinsic"
/>
<Image
src="/images/imagesTemplate/playBtn.png"
alt="Triangle used as play button example"
width={150}
height={150}
layout="intrinsic"
/>
</div>
<CodeBlock
codeSnippet={`<img src="searchBtn.png" alt="Search" />
<img src="playBtn.png" alt="Play" />`}
languageType="html"
/>
</TemplateSection>
<TemplateSection
sectionName="goodAltText"
title="How to write good alt text">
<p>
Writing good text alternatives is not always easy and there are many
differing opinions about how to do it well. The text alternative
should get across all the important information. A good rule of
thumb is to imagine you are describing an image to someone over the
phone. Which details would you tell them about? Which details would
you leave out? The more complex the image, the more detailed your
alternative text needs to be.
</p>
<p>Let's take a look at the following image:</p>
<div className="imageContainer">
<Image
src="/images/imagesTemplate/vw-beetle-car.jpg"
alt="VW Beetle Car in pale green in front of a pitched roof house painted in the same colour"
width={600}
height={400}
layout="intrinsic"
/>
</div>
<p>
The focus of this image would depend on the context of where it's
used. Is the focus the car or the house? Let's look at a couple of
alternatives.
</p>
<p>
<strong>Alternative 1: The house is for sale</strong>
</p>
<p>
The main focus should be on describing the house. The car is
probably not important as that won't be included in the house sale.
Let's try it.
</p>
<p>
<strong>Basic: </strong>House with pitched roof and brown front
door.
</p>
<p>
<strong>More detailed: </strong>Pale green, one storey house with
pitched roof, large windows and steps up to a brown front door.
Surrounded by a white picket fence.
</p>
<p>
<strong>Too detailed? </strong>Pale green, one storey house of
wooden construction. White painted trim surrounds large windows with
3 panels on either side of a stained wood front door. The house is
surrounded by a small garden and white picket fence with steps
leading from the road to the porch. There is both on-road parking
out the front and a driveway to the left of the house. The garden is
well-established with palms, various trees, bushes and vines.
</p>
<p>
<strong>Alternative 2: A car magazine</strong>
</p>
<p>
This time the car is the main focus and the house needs less
description. However, the house adds to the aesthetic and emotion of
the image.
</p>
<p>
<strong>Basic: </strong>Vintage car parked on a street.
</p>
<p>
<strong>More detailed: </strong>Soft, pale green vintage VW Beetle
parked in front of a house painted in the same pale green.
</p>
<p>
<strong>Too detailed? </strong>Soft, pale green vintage VW Beetle in
excellent condition with shiny silver trim and new tyres. The car is
parked on a quiet street in front of a one storey house painted in
the same color in a sleepy suburb.
</p>
</TemplateSection>
<TemplateSection sectionName="imagesOfText" title="Images of Text">
<p>
Wherever possible, images of text should be avoided as the text
can’t be enlarged in the same way as text in HTML. However, if you
must have an image of text then the alt text should match the text
in the image.
</p>
<div className="imageContainer">
<Image
src="/images/imagesTemplate/make-the-day-great.jpg"
alt="Make the day great sign"
width={300}
height={200}
layout="intrinsic"
/>
</div>
<CodeBlock
codeSnippet={`<img src="make-the-day-great.jpg" alt="Make the day great sign" />`}
languageType="html"
/>
</TemplateSection>
<TemplateSection
sectionName="imagesAndColour"
title="Images and Colour">
<p>
Colour should not be the only way to represent meaning in an image
because not everyone perceives colour in the same way. If colour is
used in graphs, charts and diagrams then there should also be
another way to tell the colours apart, such as different patterns or
symbols.
</p>
{
// TODO: Add an example here of a pie chart or bar chart
}
</TemplateSection>
<TemplateSection sectionName="imageMaps" title="Image Maps">
<p>
An image map is a larger image with clickable hotspots, each of
which open a different section. An example could be a floor plan of
a local building which opens up an enlarged map of each room when
clicked. The hotspots are made with the area element and each area
element should have its own alt attribute. The overall image should
also have an alt attribute.
</p>
<p>
Let's take a look at the image below. The image itself has an alt
attribute which describes the whole image. Each area is clickable
and leads to a Wikipedia article. Each area also has an alt
attribute describing just that section of the image.
</p>
<map name="primary">
<area
shape="circle"
coords="190,130,70"
href="https://en.wikipedia.org/wiki/Doughnut"
target="_blank"
alt="Doughnut"
/>
<area
shape="circle"
coords="380,250,100"
href="https://en.wikipedia.org/wiki/Coffee"
target="_blank"
alt="Coffee"
/>
</map>
<Image
useMap="#primary"
src="/images/imagesTemplate/donut-coffee.png"
width={600}
height={400}
alt="Illustration of a pink doughnut with sprinkles next to a cup of black coffee"
layout="intrinsic"
/>
<CodeBlock
codeSnippet={`<map name="primary">
<area
shape="circle"
coords="190,130,70"
href="url"
alt="Doughnut"
/>
<area
shape="circle"
coords="380,250,100"
href="url"
alt="Coffee"
/>
</map>
<img
usemap="#primary"
src="donut-coffee.png"
alt="Illustration of a pink doughnut with sprinkles next to a cup of black coffee"
/>`}
languageType="html"
/>
</TemplateSection>
{/* TODO: <TemplateSection sectionName="checklist" title="Images Checklist">
<p>Add some points here</p>
</TemplateSection> */}
<TemplateSection sectionName="WCAGCriteria" title="WCAG Criteria">
<ul className="list">
<li>
<a
href="https://www.w3.org/TR/WCAG21/#non-text-content"
className="blockLink">
1.1.1 Non-text content
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG21/#use-of-color"
className="blockLink">
1.4.1 Use of Color
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG21/#images-of-text"
className="blockLink">
1.4.5 Images of Text
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG21/#images-of-text-no-exception"
className="blockLink">
1.4.9 Images of Text (No Exception)
</a>
</li>
</ul>
</TemplateSection>
<TemplateSection sectionName="otherResources" title="Other Resources">
<ul className="list">
<li>
<a
href="https://www.w3.org/WAI/tutorials/images/"
className="blockLink">
W3C Images Tutorial
</a>
</li>
<li>
<a
href="https://www.w3.org/WAI/tutorials/images/decision-tree/"
className="blockLink">
W3C An alt decision tree
</a>
</li>
<li>
<a
href="https://jakearchibald.com/2021/great-alt-text/"
className="blockLink">
Writing great alt text: Emotion matters
</a>
</li>
<li>
<a
href="https://tink.uk/thoughts-on-skin-tone-and-text-descriptions.md-notes-on-synthetic-speech/"
className="blockLink">
Thoughts on skin tone and text descriptions
</a>
</li>
<li>
<a
href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/map"
className="blockLink">
MDN: The Image Map element
</a>
</li>
</ul>
</TemplateSection>
</article>
<PageUpdated date="26th November 2022" />
</>
)
}
================================================
FILE: components/ContentTemplates/LinksTemplate.module.css
================================================
.srOnly{
border: 0;
clip: rect(0,0,0,0);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
}
================================================
FILE: components/ContentTemplates/LinksTemplate.tsx
================================================
import { TemplateSection } from "../TemplateSection/TemplateSection"
import { CodeBlock } from "../CodeBlock/CodeBlock"
import { NavPage } from "../NavPage/NavPage"
import { linkPageNavigation } from "../../data/pageNavigationLists"
import { PageUpdated } from "../PageUpdated/PageUpdated"
export const LinksTemplate = () => {
return (
<>
<NavPage pageNavigation={linkPageNavigation} />
<TemplateSection sectionName="introduction" title="Introduction">
<p>
Links, also known as hyperlinks, are a fundamental element in HTML.
They connect different pages, or parts of pages, to one another.
</p>
<p>
In HTML, links are created using the anchor element
<strong>{`${" <a>"}`}</strong>. This element allows you to make text
or images clickable, and has inbuilt interactivity allowing users to
activate the link with a mouse click, a touch device or by pressing
Enter on a keyboard. It is also automatically reachable with the tab
key.
</p>
<p>
The accessible name for links created using the anchor element comes
from the content between the opening and closing tag. If the content
between the opening and closing tags is not text, or the text is very
generic the accessible name should be added in another way, for
example with ARIA.
</p>
<p>
An important attribute for accessibility is the href attribute. This
specifies the destination of the link. If the href is not present then
the link is not accessible.
</p>
</TemplateSection>
<TemplateSection
sectionName="accessibleLinkNames"
title="Accessible Link Names">
<div>
<h3>Descriptive Link Text</h3>
<p>
Link text is the text between the opening and closing anchor tag
that describes a hyperlink on a webpage. It's important to have
descriptive link text to provide clear context about where the link
leads and helps users understand the purpose of the link. This is
helpful for people with cognitive disabilities, screen reader users
and voice input users.
</p>
<CodeBlock
codeSnippet={`<a href="https://accessibleweb.dev">Visit Accessible Web Dev</a>
<!--The descriptive name in this case is "Visit Accessible Web Dev" -->`}
languageType={"html"}
/>
</div>
<div>
<h3>Non-descriptive Link Text</h3>
<p>
If possible, avoid using vague, non-descriptive phrases as link text
such as "Click Here" or "Read More". These phrases are difficult to
understand without surrounding context and can pose problems for
screen reader users and voice input users. It is also common to see
these phrases repeated multiple times on a page, for example on an
e-commerce site listing several product cards, making it more
difficult to differentiate between the different link destinations.
</p>
<p>
If you must use non-descriptive text, you can make it more
accessible by using methods to overwrite the visible text. Two such
methods are using aria-label or hiding additional text with CSS.
</p>
<h4>Aria-label</h4>
<p>
The first method is to use an aria-label. This will overwrite the
visible text and be read out to screen reader users instead. It's
important to keep in mind that the aria-label should start with the
same text as shown in the visible text so that the link still works
for voice input users. If the visible link says "Read more" the
aria-label might be "Read more about accessible buttons"
</p>
<p>
An aria-label can also be used to give an image or icon link an
accessible name when there is no visible link text.
</p>
<CodeBlock
codeSnippet={`<a href="https://accessibleweb.dev/buttons" aria-label="Read more about accessible buttons">Read more</a>`}
languageType={"html"}
/>
<p>
Some downsides to aria-label are that it doesn't always get
translated by in-browser translation tools. It also completely
overwrites the visible text which can be problematic if the two do
not match.
</p>
<h4>Hiding elements with CSS</h4>
<p>
Another solution is to use CSS to hide some extra text. This can be
done by adding a visibly hidden <strong>{`${"<span>"}`}</strong>{" "}
element within the <strong>{`${"<a>"}`}</strong> element to provide
descriptive text that gets read out to screen readers but is not
visible to sighted users.
</p>
<CodeBlock
codeSnippet={`<a href="https://accessibleweb.dev/buttons">Read more
<span class="visibly-hidden">about accessible buttons</span>
</a>`}
languageType={"html"}
/>
<CodeBlock
codeSnippet={`.visibly-hidden {
border: 0;
clip: rect(0,0,0,0);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
}
/* You may also see this class called screen-reader-only or sr-only in
other places on the web */`}
languageType={"css"}
/>
</div>
</TemplateSection>
<TemplateSection sectionName="linkStates" title="Link State and Style">
<p>
To improve the accessibility of your website, it's crucial to make
links easy to distinguish from non-interactive elements on a page.
</p>
<p>Here's how to achieve this:</p>
<ul className="list">
<li>
<strong>Underline links by default: </strong>
Links should be underlined by default. This provides a clear visual
indicator that a piece of text or an element is clickable. It's a
universal convention that helps all users understand what's
clickable.
</li>
<li>
<strong>Focus state: </strong>A focused link should have a focus
indicator which makes it easy to see where focus is on the screen.
This is often a thicker border or outline around the link and should
have a contrast ratio of at least 3:1 with the background.
</li>
<li>
<strong>Ensure sufficient color contrast: </strong>
Make sure the color of your links contrasts well with the background
color or surrounding text color. This ensures that people with
visual impairments or color vision deficiencies can easily
distinguish the links from the surrounding text. The contrast ratio
should be at least 4.5:1 for WCAG level AA compliance. Color should
not be the only way to distinguish links from surrounding text.
</li>
<li>
<strong>Don't rely on hover state to convey links: </strong>
Hover is not available on touch devices or for people navigating the
web with keyboard, screen readers or other input devices. Therefore
hover states only, such as underline or color change on hover,
should not be relied upon to convey that something is a link.
</li>
</ul>
</TemplateSection>
<TemplateSection sectionName="linkImage" title="Image and Icon Links">
<p>
You can turn an image or icon into a clickable link that takes you to
another webpage. Since image and icon links don't have a visible link
text, you need to use a different method to give the link an
accessible name. These methods can include adding an aria-label, using
a visibly-hidden class or adding an alt attribute to the image text
wrapped inside the link.
</p>
<p>
The important thing to remember when using images and icons as links
is to describe the link destination rather than the content of the
image.
</p>
<CodeBlock
codeSnippet={`<a href="https://google.com" aria-label="Google">
<FaGoogle />
</a>
<a href="https://google.com">
<FaGoogle />
<span class="visibly-hidden">Google</span>
</a>
<a href="https://google.com">
<img src="url" alt="Google" />
</a>`}
languageType={"html"}
/>
</TemplateSection>
<TemplateSection
sectionName="touchTargetMinimum"
title="Touch Target Minimum">
<p>
When it comes to creating accessible links, it's important that they
are easy to activate. To do this, WCAG recommends that links that are
not within text have a target size of at least 24 x 24 CSS pixels.
Doing this makes it easier for users who have motor dexterity issues
to click them.
</p>
</TemplateSection>
<TemplateSection sectionName="WCAGCriteria" title="WCAG Criteria">
<ul className="list">
<li>
<a
href="https://www.w3.org/TR/WCAG21/#non-text-content"
className="blockLink">
1.1.1 Non-text content
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG21/#info-and-relationships"
className="blockLink">
1.3.1 Info and Relationships
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG21/#identify-purpose"
className="blockLink">
1.3.6 Identify Purpose
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG21/#contrast-minimum"
className="blockLink">
1.4.3 Contrast (minimum)
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG21/#non-text-contrast"
className="blockLink">
1.4.11 Non-text Contrast
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG21/#content-on-hover-or-focus"
className="blockLink">
1.4.13 Content on Hover or Focus
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG21/#keyboard"
className="blockLink">
2.1.1 Keyboard
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG22/#link-purpose-in-context"
className="blockLink">
2.4.4 Link Purpose (in Context)
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG21/#focus-visible"
className="blockLink">
2.4.7 Focus Visible
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG22/#link-purpose-link-only"
className="blockLink">
2.4.9 Link Purpose (Link Only)
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG22/#focus-not-obscured-minimum"
className="blockLink">
2.4.11 Focus Not Obscured (Minimum)
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG22/#focus-not-obscured-enhanced"
className="blockLink">
2.4.12 Focus Not Obscured (Enhanced)
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG22/#focus-appearance"
className="blockLink">
2.4.13 Focus Appearance
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG21/#label-in-name"
className="blockLink">
2.5.3 Label in Name
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG22/#target-size-enhanced"
className="blockLink">
2.5.5 Target Size (Enhanced)
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG22/#target-size-minimum"
className="blockLink">
2.5.8 Target Size (Minimum)
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG21/#on-focus"
className="blockLink">
3.2.1 On Focus
</a>
</li>
</ul>
</TemplateSection>
<TemplateSection sectionName="otherResources" title="Other Resources">
<ul className="list">
<li>
<a
href="https://www.tpgi.com/well-color-us-surprised-this-sc-can-be-a-tricky-customer/"
className="blockLink">
Well Color Us Surprised - This SC Can Be a Tricky Customer
</a>
</li>
<li>
<a
href="https://webaim.org/blog/wcag-2-0-and-link-colors/"
className="blockLink">
WCAG 2.0 and link colors
</a>
</li>
</ul>
</TemplateSection>
<PageUpdated date="5th January 2024" />
</>
)
}
================================================
FILE: components/ContentTemplates/ListsTemplate.tsx
================================================
import { WorkInProgress } from "../WorkInProgress/WorkInProgress"
export const ListsTemplate = () => {
return <WorkInProgress />
}
================================================
FILE: components/ContentTemplates/MenusTemplate.tsx
================================================
import { WorkInProgress } from "../WorkInProgress/WorkInProgress"
export const MenusTemplate = () => {
return <WorkInProgress />
}
================================================
FILE: components/ContentTemplates/ModalsTemplate.tsx
================================================
import { TemplateSection } from "../TemplateSection/TemplateSection"
import { CodeBlock } from "../CodeBlock/CodeBlock"
import { NavPage } from "../NavPage/NavPage"
import { modalPageNavigation } from "../../data/pageNavigationLists"
import { PageUpdated } from "../PageUpdated/PageUpdated"
export const ModalsTemplate = () => {
return (
<>
<NavPage pageNavigation={modalPageNavigation} />
<TemplateSection sectionName="introduction" title="Introduction">
<p>
Modals are elements of a website or application that are appear on top
of the main window. They are usually activated by the click of a
button or some event. The main window may still visible underneath
although you should not be able to interact with it when the modal is
open. Modals go by many names, including modal window, dialog, popup
and even lightbox. On this page we will use the term modal throughout
to describe this pattern.
</p>
<p>
Before 2023, there was no semantic HTML element that could be used to
create a modal. That means developers combined HTML elements to create
their own modals. Many different patterns for modals exist out in the
wild but unfortunately many are not accessible.
</p>
<p>
In 2023, the dialog element was added to the HTML specification in an
attempt to minimise the need for developers to build their own modals.
On this page you will find examples of two accessible modals, one with
the dialog element and one without.
</p>
</TemplateSection>
<TemplateSection
sectionName="howAModalShouldWork"
title="How a modal should work">
<ul className="list">
<li>
Things behind the modal should become inactive when the modal is
open.
</li>
<li>
The focus should move to an element within the modal when it opens.
</li>
<li>
The focus should move back to the original activating element when
the modal closes.
</li>
<li>
The focus should be trapped within the modal when open - it should
not be possible to tab to something outside of the modal when it is
open.
</li>
<li>
It should be possible to close the modal by pressing the Escape key.
</li>
<li>
The modal should not be part of the tab order when it is closed.
</li>
</ul>
</TemplateSection>
<TemplateSection sectionName="usingDialog" title={"Using <dialog>"}>
<p>
The HTML <dialog> element has full support since Chrome 37, Edge
79, Firefox 98 and Safari 15.4. If you need to support browsers older
than this then using a different method is suggested.
</p>
<h3>HTML</h3>
<p>
The <dialog> element will handle focus if using a keyboard to
navigate without the need for additional JavaScript. On opening the
modal, focus will be placed on the first interactive item. If no
interactive item exists then the focus is placed on the modal itself.
All content behind the modal is made inert. Using the Escape key
closes the modal. However, clicking on the modal backdrop does not
automatically close the modal.
</p>
<CodeBlock
codeSnippet={`<button id="openDialogBtn">Open dialog</button>
<dialog id="dialog" aria-labelledby="dialog_heading">
<h2 id="dialog_heading">This is a heading</h2>
<p>This is some placeholder text within the dialog.</p>
<button type="button" id="cancelBtn">Cancel</button>
</dialog>`}
languageType={"html"}
/>
<h3>CSS</h3>
<p>
The modal comes with some basic built in styles. The backdrop can be
customised using the ::backdrop pseudo-element. Dialog styles can be
overwritten by styling the dialog element directly.
</p>
<CodeBlock
codeSnippet={`dialog::backdrop {
background: rgb(0 0 0 / 70%);
}
dialog {
border: 1px solid #000;
box-shadow: 0 19px 38px rgb(0 0 0 / 12%), 0 15px 12px rgb(0 0 0 / 22%);
}`}
languageType={"css"}
/>
<h3>JavaScript</h3>
<p>
Using the showModal() method will open the dialog element in a modal
window (on top of the other content) and apply an implicit state of
aria-modal="true". The modal can be closed with the close() method. No
extra JavaScript is needed to handle keyboard focus, making background
elements inert or closing the modal with the Escape key.
</p>
<CodeBlock
codeSnippet={`const openDialogBtn = document.getElementById("openDialogBtn");
const dialog = document.getElementById("dialog");
const cancelBtn = document.getElementById("cancelBtn");
openDialogBtn.addEventListener("click", () => dialog.showModal());
cancelBtn.addEventListener("click", () => dialog.close());`}
languageType={"javascript"}
/>
</TemplateSection>
<TemplateSection sectionName="customModals" title="Custom Modals">
<p>
If you need to support browsers older than those listed above, then
you may need to build a custom modal component. In this case you will
need to add accessible information yourself.
</p>
<h3>HTML</h3>
<p>
In this case, the div used to create the modal is given a
role="dialog" to indicate that this is a modal. It is labelled with
aria-labelledby so that it announces the heading when the modal
appears. It is also given a tabindex="-1" so that focus can be
manually handled with JavaScript.
</p>
<CodeBlock
codeSnippet={`<div id="wrapper">
<button type="button" id="openModal">Open Modal</button>
</div>
<div class="modalBackground hidden"></div>
<div role="dialog" id="modal" aria-labelledby="modal_heading" class="hidden" tabindex="-1">
<h2 id="modal_heading">This is a heading</h2>
<p>This is some placeholder text within the modal.</p>
<button type="button" id="cancelBtn">Cancel</button>
</div>`}
languageType={"html"}
/>
<h3>CSS</h3>
<p>
For this modal we can use the [role="dialog] attribute to add the
styling. All of the positioning needs to be added as none of it is
inbuilt, unlike the dialog element.
</p>
<CodeBlock
codeSnippet={`.hidden {
display: none;
}
[role="dialog"] {
position: absolute;
padding: 15px;
border: 1px solid #000;
background-color: #fff;
min-width: 300px;
top: 2rem;
left: 50vw;
transform: translateX(-50%);
overflow: hidden;
box-shadow: 0 19px 38px rgb(0 0 0 / 12%), 0 15px 12px rgb(0 0 0 / 22%);
}
.modalBackground {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: rgb(0 0 0 / 70%);
}
`}
languageType={"css"}
/>
<h3>JavaScript</h3>
<p>
In a custom modal you need to handle focus and inert manually. When
the modal is opened, focus moves from the button to something inside
the modal. In this case it moves to the modal itself, but it could
also move to the heading or the first interactive element. The page
content is wrapped in a div with id="wrapper" so can be selected. Once
the modal is open the inert attribute is applied to the main content
so that focus is trapped within the modal.
</p>
<p>
When the modal is closed, the inert attribute needs to be removed from
the main content. This must happen before you set focus back to the
opening button. If not, then the button won't be found. An event
listener also needs adding for the Escape key so that it also runs the
closeModal function. In this custom component it is also possible to
add an event to the modal backdrop to close the modal on click.
</p>
<CodeBlock
codeSnippet={`const wrapper = document.getElementById("wrapper")
const openModalBtn = document.getElementById("openModal");
const cancelBtn = document.getElementById("cancelBtn");
const modal = document.getElementById("modal");
const modalBackground = document.querySelector(".modalBackground");
const openModal = () => {
modal.classList.remove("hidden");
modalBackground.classList.remove("hidden");
modal.focus();
wrapper.setAttribute("inert", "");
}
const closeModal = () => {
modal.classList.add("hidden");
modalBackground.classList.add("hidden");
wrapper.removeAttribute("inert")
openModalBtn.focus()
}
openModalBtn.addEventListener("click", openModal)
cancelBtn.addEventListener("click", closeModal)
modalBackground.addEventListener("click", closeModal)
window.addEventListener("keyup", (e) => {
if (e.key === "Escape" && !modal.classList.contains("hidden")) {
closeModal()
}
})`}
languageType={"javascript"}
/>
</TemplateSection>
<TemplateSection sectionName="WCAGCriteria" title="WCAG Criteria">
<ul className="list">
<li>
<a
href="https://www.w3.org/TR/WCAG21/#keyboard"
className="blockLink">
2.1.1 Keyboard
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG21/#no-keyboard-trap"
className="blockLink">
2.1.2 No Keyboard Trap
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG21/#name-role-value"
className="blockLink">
4.1.2 Name, role, value
</a>
</li>
</ul>
</TemplateSection>
<TemplateSection sectionName="otherResources" title="Other Resources">
<ul className="list">
<li>
<a
href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog"
className="blockLink">
MDN web docs - The Dialog element
</a>
</li>
<li>
<a
href="https://adrianroselli.com/2020/10/dialog-focus-in-screen-readers.html"
className="blockLink">
Dialog Focus in Screen Readers by Adrian Roselli
</a>
</li>
<li>
<a
href="https://www.scottohara.me/blog/2023/01/26/use-the-dialog-element.html"
className="blockLink">
Use the dialog element (reasonably) by Scott O'Hara
</a>
</li>
</ul>
</TemplateSection>
<PageUpdated date="23rd June 2023" />
</>
)
}
================================================
FILE: components/ContentTemplates/NavigationTemplate.tsx
================================================
import { navigationPageNavigation } from "../../data/pageNavigationLists"
import { CodeBlock } from "../CodeBlock/CodeBlock"
import { NavPage } from "../NavPage/NavPage"
import { PageUpdated } from "../PageUpdated/PageUpdated"
import { TemplateSection } from "../TemplateSection/TemplateSection"
// TODO: Add information about using aria-current
// TODO: Add working examples to complement the code examples
export const NavigationTemplate = () => {
return (
<>
<NavPage pageNavigation={navigationPageNavigation} />
<TemplateSection sectionName="introduction" title="Introduction">
<p>
A website navigation is a section of the page aimed at helping you
find the main sections of a website. HTML has a semantic <nav>
element which should be used around all navigations on the page. This
element is a landmark element and can be used by screen reader users
to more easily find their way around the page.
</p>
</TemplateSection>
<TemplateSection sectionName="simpleNav" title="A Simple Navigation">
<p>
In its simplest form a navigation is a list of links to other pages
within a larger website or to other regions of the same page. A nav
element wraps an unordered list of links in order to communicate how
long the navigation is.
</p>
<CodeBlock
codeSnippet={`<nav>
<ul>
<li>
<a href"/">Link 1</a>
</li>
<li>
<a href"/">Link 2</a>
</li>
<li>
<a href"/">Link 3</a>
</li>
</ul>
</nav>`}
languageType={"html"}
/>
</TemplateSection>
<TemplateSection sectionName={"hamburgerMenus"} title={"Hamburger Menus"}>
<p>
Often on mobile there is not space for a full navigation and a common
pattern is to use a hamburger menu. This is usually a button with an
icon that looks like three layers of a hamburger and represents the
place to find the navigation. Pressing the button opens the navigation
and often the icon will change to a cross to signify a close button.
</p>
<p>
To make a hamburger menu accessible it must be reachable and operable
by keyboard only. If a button element is used then this will happen
automatically. It should have a clear label stating the function of
the button and it should also announce whether it is opened or closed
to screen reader users. For this, aria-label and aria-expanded are
useful and JavaScript will be needed to toggle the navigation open and
closed.
</p>
{
// TODO: Fix this code block!
}
<CodeBlock
codeSnippet={`<button id="hamburgerMenu" class="navButton" aria-label="Navigation menu" aria-expanded="false" aria-controls="primaryNav">
Menu
</button>
<nav id="primaryNav" class="hidden">
<ul class="primaryNavList">
<li>
<a href="#">Item 1</a>
</li>
<li>
<a href="#">Item 2</a>
</li>
<li>
<a href="#">Item 3</a>
</li>
</ul>
</nav>`}
languageType={"html"}
/>
<CodeBlock
codeSnippet={`.navButton {
position: relative;
}
.primaryNavList {
position: absolute;
list-style: none;
margin: 0;
padding: 0;
border: 1px solid black;
width: 10rem;
z-index: 1;
}
.primaryNavList li {
padding: 1rem;
}
.hidden {
display: none;
}`}
languageType={"css"}
/>
<CodeBlock
codeSnippet={`const navButton = document.getElementById("hamburgerMenu");
const disclosure = document.getElementById("primaryNav");
const listItems = disclosure.querySelectorAll("li a");
function openNavigation() {
navButton.setAttribute("aria-expanded", "true");
disclosure.classList.remove("hidden");
}
function closeNavigation() {
navButton.setAttribute("aria-expanded", "false");
disclosure.classList.add("hidden");
}
function toggleNavigation() {
const open = navButton.getAttribute("aria-expanded");
open === "false" ? openNavigation() : closeNavigation();
}
// This function closes an open disclosure if a user tabs away from the last anchor element in the list. It is reliant on the ul container having a class to check whether the relatedTarget is within the disclosure. If not, it will close.
function handleBlur() {
const navList = event.currentTarget.closest(".primaryNavList");
if (!event.relatedTarget || !navList.contains(event.relatedTarget)) {
closeNavigation();
}
}
navButton.addEventListener("click", toggleNavigation);
// add event to the last item in the nav list to trigger the disclosure to close if the user tabs out of the disclosure
listItems[listItems.length - 1].addEventListener("blur", handleBlur);
// Close the disclosure if a user presses the escape key
window.addEventListener("keyup", (e) => {
if (e.key === "Escape") {
navButton.focus();
closeNavigation();
}
});`}
languageType={"javascript"}
/>
</TemplateSection>
<TemplateSection sectionName={"megaMenus"} title={"Mega Menus"}>
<p>
Mega menus are more complex navigations with nested links to different
sections and pages. Examples can often be found on popular clothing
chain websites.
</p>
<p>
The top level of a mega menu is usually broken up into broad
categories. These categories can be made with buttons that trigger a
popup to open when pressed. The list of navigation links is then
shown.
</p>
<p>
In the example below a very simple "Mega Menu" is shown with only two
sections. These can be extended as necessary.
</p>
<CodeBlock
codeSnippet={`<nav>
<ul class="nav-list">
<li class="nav-group">
<button id="navItem1" class="navItem" aria-expanded="false" aria-controls="disclosure1">Nav 1 ▼</button>
<ul class="disclosure hidden" id="disclosure1">
<li>
<a href="#">Dropdown 1 - item 1</a>
</li>
<li>
<a href="#">Dropdown 1 - item 2</a>
</li>
<li>
<a href="#">Dropdown 1 - item 3</a>
</li>
</ul>
</li>
<li class="nav-group">
<button id="navItem2" class="navItem" aria-expanded="false" aria-controls="disclosure2">Nav list 2 ▼</button>
<ul class="disclosure hidden" id="disclosure2">
<li>
<a href="#">Dropdown 2 - item 1</a>
</li>
<li>
<a href="#">Dropdown 2 - item 2</a>
</li>
</ul>
</li>
</ul>
</nav>`}
languageType={"html"}
/>
<CodeBlock
codeSnippet={`.disclosure {
position: absolute;
border: 1px solid black;
width: 12rem;
padding: 0.5rem 1rem;
z-index: 1;
}
.disclosure li {
margin: 0.5rem 0;
}
.hidden {
display: none;
}`}
languageType={"css"}
/>
<CodeBlock
codeSnippet={`const navButtons = document.querySelectorAll(".navItem");
const disclosures = document.querySelectorAll(".disclosure");
function openNavigation(button) {
button.setAttribute("aria-expanded", "true");
// The ul is a direct sibling to the button
const disclosure = button.nextElementSibling;
disclosure.classList.remove("hidden");
}
function closeNavigation(button) {
button.setAttribute("aria-expanded", "false");
const disclosure = button.nextElementSibling;
disclosure.classList.add("hidden");
}
function toggleNavigation(index) {
// First we close any open dropdowns not related to the current button in focus by looping over all nav buttons
navButtons.forEach((button, buttonIndex) => {
if (buttonIndex != index) {
closeNavigation(button);
}
});
const currentButton = event.target;
const open = currentButton.getAttribute("aria-expanded");
open === "false"
? openNavigation(currentButton)
: closeNavigation(currentButton);
}
// This function closes an open disclosure if a user tabs away from the last anchor element in the list. It is reliant on the top-level list item of the top level ul having a class to find the group containing button + disclosure it controls
function handleBlur(button) {
const navList = event.currentTarget.closest(".nav-group");
if (!event.relatedTarget || !navList.contains(event.relatedTarget)) {
closeNavigation(button);
}
}
// Adds the toggle event to every top level button
navButtons.forEach((button, index) => {
button.addEventListener("click", () => toggleNavigation(index));
});
// This adds the handleBlur event to the last anchor element in each disclosure
disclosures.forEach((disclosure) => {
const listItems = disclosure.querySelectorAll("li a");
listItems[listItems.length - 1].addEventListener("blur", (event) => {
handleBlur(disclosure.previousElementSibling);
});
});
// This adds a global event listener to close any open disclosures when the escape key is pressed
window.addEventListener("keyup", (e) => {
if (e.key === "Escape") {
const navButtonsArr = Array.from(navButtons);
const currentOpenButtonIndex = navButtonsArr.findIndex(
(button) => button.getAttribute("aria-expanded") === "true"
);
// If there is an open disclosure, close it and send focus back to the button that controls it.
if (currentOpenButtonIndex >= 0) {
const currentOpenButton = navButtons[currentOpenButtonIndex];
currentOpenButton.focus();
closeNavigation(currentOpenButton);
}
}
});`}
languageType={"javascript"}
/>
</TemplateSection>
<TemplateSection sectionName={"linksOrder"} title={"Order of Links"}>
<p>
To help users find their way around easily, it's important that the
relative order of links remains the same across all pages within a web
site.
</p>
</TemplateSection>
<TemplateSection
sectionName={"multipleNavs"}
title={"Multiple Navigations Per Page"}>
<p>
It is common to have more than one navigation per page, for example a
primary navigation that takes people between different pages of the
website and a secondary navigation that takes people to different
sections within a page.
</p>
<p>
When there are multiple navigations using the nav element it is
important to differentiate them for screen reader users by labelling
them. Use aria-label or aria-labelledby to let screen reader users
know the different functions of the navigation elements.
</p>
<CodeBlock
codeSnippet={`<nav aria-label="Site">
...
</nav>
`}
languageType={"html"}
/>
<CodeBlock
codeSnippet={`<nav aria-label="Within page">
...
</nav>`}
languageType={"html"}
/>
</TemplateSection>
<TemplateSection sectionName="WCAGCriteria" title="WCAG Criteria">
<ul className="list">
<li>
<a
href="https://www.w3.org/TR/WCAG21/#info-and-relationships"
className="blockLink">
1.3.1 Info and Relationships
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG21/#reflow"
className="blockLink">
1.4.10 Reflow
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG21/#content-on-hover-or-focus"
className="blockLink">
1.4.13 Content on Hover and Focus
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG21/#keyboard"
className="blockLink">
2.1.1 Keyboard
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG21/#link-purpose-in-context"
className="blockLink">
2.4.4 Link Purpose (in Context)
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG21/#focus-visible"
className="blockLink">
2.4.7 Focus visible
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG21/#location"
className="blockLink">
2.4.8 Location
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG21/#consistent-navigation"
className="blockLink">
3.2.3 Consistent Navigation
</a>
</li>
<li>
<a
href="https://www.w3.org/TR/WCAG21/#name-role-value"
className="blockLink">
4.1.2 Name, Role, Value
</a>
</li>
</ul>
</TemplateSection>
{/* <TemplateSection sectionName="otherResources" title="Other Resources">
<ul className="list">
<li>
<a href="#" className="blockLink">
###
</a>
</li>
</ul>
</TemplateSection> */}
<PageUpdated date="27th August 2023" />
</>
)
}
================================================
FILE: components/ContentTemplates/PaginationTemplate.tsx
================================================
import { WorkInProgress } from "../WorkInProgress/WorkInProgress"
export const PaginationTemplate = () => {
return <WorkInProgress />
}
================================================
FILE: components/ContentTemplates/TablesTemplate.tsx
================================================
import { WorkInProgress } from "../WorkInProgress/WorkInProgress"
export const TablesTemplate = () => {
return <WorkInProgress />
}
================================================
FILE: components/ContentTemplates/VideoTemplate.tsx
================================================
import { WorkInProgress } from "../WorkInProgress/WorkInProgress"
export const VideoTemplate = () => {
return <WorkInProgress />
}
================================================
FILE: components/CopyCodeBlock/CopyCodeBlock.module.css
================================================
.codeCopyBtn {
color: var(--text);
background-color: var(--primaryLt);
padding: 4px 8px;
position: absolute;
display: flex;
align-items: center;
gap: 0.25rem;
border: none;
border-radius: var(--borderRadius);
right: 14px;
top: 18px;
font-size: 0.8rem;
cursor: pointer;
}
================================================
FILE: components/CopyCodeBlock/CopyCodeBlock.tsx
================================================
import { useState } from "react"
import { FaCopy, FaCheckSquare } from "react-icons/fa"
import styles from "./CopyCodeBlock.module.css"
interface CodeSnippet {
code: string
}
const CopyCodeBlock = ({ code }: CodeSnippet) => {
const [copyOk, setCopyOk] = useState(false)
const handleClick = () => {
navigator.clipboard.writeText(code)
setCopyOk(true)
setTimeout(() => {
setCopyOk(false)
}, 3000)
}
return (
<button
className={styles.codeCopyBtn}
onClick={handleClick}
aria-label="Copy code snippet to clipboard">
{copyOk ? (
<>
<FaCheckSquare />
Copied
</>
) : (
<>
<FaCopy />
Copy
</>
)}
</button>
)
}
export default CopyCodeBlock
================================================
FILE: components/Footer/Footer.module.css
================================================
.footer {
background-color: var(--bg);
padding: 16px;
color: var(--text);
margin-top: 2rem;
border-top: 1px solid var(--primaryLt);
}
.footerList {
padding: 0;
margin: 0;
list-style: none;
line-height: 1.6;
}
.footerList li {
margin: 14px 0 14px 0;
}
.footerLink, .footerLink:visited {
text-decoration: none;
font-size: 0.9rem;
}
.footerLink:hover {
text-decoration: underline;
}
.footerBottom {
display: flex;
justify-content: space-between;
align-items: center;
margin: 0rem;
padding: 16px 0 0 0;
}
.footerCopyrightText {
margin: 0;
font-size: 0.9rem;
}
.footerLogos {
color: var(--text)
}
@media only screen and (min-width: 55rem) {
.footerList {
display: flex;
flex-direction: row;
justify-content: space-between;
}
.footerList li {
margin: 0;
}
}
================================================
FILE: components/Footer/Footer.tsx
================================================
import Link from "next/link"
import styles from "./Footer.module.css"
import { BsGithub, BsBoxArrowUpRight } from "react-icons/bs"
export const Footer = () => {
return (
<footer className={styles.footer}>
<div className={styles.footerTop}>
<ul className={styles.footerList}>
<li>
<Link href="/about" className={styles.footerLink}>
About the maintainers
</Link>
</li>
<li>
<a
href="https://github.com/AccessibleForAll/AccessibleWebDev/blob/main/CONTRIBUTING.md"
className={styles.footerLink}>
Become a Contributor
</a>
</li>
<li>
<a
href="https://github.com/sponsors/EmmaDawsonDev"
className={styles.footerLink}>
Support Us
</a>
</li>
<li>
<a
href="mailto:emma.l.dawson@gmail.com"
className={styles.footerLink}>
Contact Us
</a>
</li>
<li>
<Link href="/" className={styles.footerLink}>
Accessibility Statement - coming soon
</Link>
</li>
</ul>
</div>
<div className={styles.footerBottom}>
<p className={styles.footerCopyrightText}>
© Accessible Web Dev by <strong>Accessible For All</strong>. Making
accessibility accessible for everyone!
</p>
<a href="https://github.com/AccessibleForAll">
<BsGithub
className={styles.footerLogos}
size="1.5rem"
aria-label="Github"
/>
</a>
</div>
</footer>
)
}
================================================
FILE: components/Header/Header.module.css
================================================
.topBanner {
background-color: var(--bg);
color: var(--text);
height: var(--headerHeight);
width: 100%;
padding: 0 16px;
display: flex;
justify-content: space-between;
align-items: center;
position: fixed;
top: 0;
border-bottom: 1px solid var(--primaryLt);
z-index: 1;
}
.topBanner a {
text-decoration: none;
font-weight: bold;
}
.buttonsContainer {
display: flex;
gap: 0.5rem;
align-items: center;
position: relative;
}
.topBanner button {
cursor: pointer;
}
.hamburgerBtn {
display: grid;
place-items: center;
border: none;
padding: 0;
background-color: transparent;
color: var(--text);
}
@media (min-width: 800px) {
.topBanner .hamburgerBtn {
display: none;
}
}
================================================
FILE: components/Header/Header.tsx
================================================
import Link from "next/link"
import { IoMenuSharp, IoCloseSharp } from "react-icons/io5"
import { ThemeSwitcher } from "../ThemeSwitcher/ThemeSwitcher"
import styles from "./Header.module.css"
export interface IHeaderProps {
handleNavClick: () => void
showNavMobile: boolean
}
export const Header = ({ handleNavClick, showNavMobile }: IHeaderProps) => {
return (
<>
<header>
<div className={styles.topBanner}>
<Link href="/">AWD</Link>
<div className={styles.buttonsContainer}>
<ThemeSwitcher />
<button
className={styles.hamburgerBtn}
onClick={handleNavClick}
aria-label="Navigation menu"
aria-expanded={showNavMobile}>
{!showNavMobile && (
<IoMenuSharp size="1.5rem" aria-hidden="true" />
)}
{showNavMobile && (
<IoCloseSharp size="1.5rem" aria-hidden="true" />
)}
</button>
</div>
</div>
</header>
</>
)
}
================================================
FILE: components/Layout/Layout.module.css
================================================
.layoutContainer {
display: flex;
min-height: calc(100vh - var(--headerHeight));
margin-top: 3rem;
}
.layoutContainer main {
padding: 16px;
max-width: 1200px;
margin: 0 auto;
}
.columnContainer {
display: flex;
flex-direction: column;
justify-content: space-between;
}
@media (min-width: 800px) {
.columnContainer {
flex-grow: 1;
margin-left: 15rem;
}
}
================================================
FILE: components/Layout/Layout.tsx
================================================
import { useState } from "react"
import { Footer } from "../Footer/Footer"
import { Header } from "../Header/Header"
import { NavPrimary } from "../Nav/NavPrimary"
import { NavPrimaryMobile } from "../Nav/NavPrimaryMobile"
import { SkipLink } from "../SkipLink/SkipLink"
import styles from "./Layout.module.css"
interface ILayoutProps {
activeNavLink: string
children: JSX.Element
}
export const Layout = ({ activeNavLink, children }: ILayoutProps) => {
const [showNavMobile, setShowNavMobile] = useState<boolean>(false)
const handleNavClick = () => {
setShowNavMobile((prevState) => !prevState)
}
return (
<>
<SkipLink />
<Header handleNavClick={handleNavClick} showNavMobile={showNavMobile} />
<div className={styles.layoutContainer}>
<NavPrimary activeNavLink={activeNavLink} />
{showNavMobile && (
<NavPrimaryMobile
activeNavLink={activeNavLink}
handleNavClick={handleNavClick}
/>
)}
<div className={styles.columnContainer}>
<main id="main">{children}</main>
<Footer />
</div>
</div>
</>
)
}
================================================
FILE: components/MaintainerCard/MaintainerCard.module.css
================================================
.maintainerContainer {
align-items: center;
border: 0.15rem solid var(--primaryLt);
border-radius: 0.5rem;
display: flex;
flex-direction: column;
margin: 4.5rem 0 0 0;
padding: 1rem;
width: 20rem;
}
.maintainerImage {
background-color: var(--primaryLt);
border-radius: 50%;
height: 8rem;
margin-top: -5rem;
position: absolute;
width: 8rem;
}
.maintainerFullName {
font-size: 2rem;
margin-bottom: 0;
margin-top: 3.5rem;
}
.maintainerDescription {
font-size: 1rem;
margin-top: 0.5rem;
align-self: center;
}
.linkButton {
background-color: var(--primaryLt);
border-radius: 5px;
cursor: pointer;
font-size: 1rem;
margin-top: 1rem;
padding: 0.5rem 1rem;
text-decoration: none;
}
================================================
FILE: components/MaintainerCard/MaintainerCard.tsx
================================================
/* eslint-disable @next/next/no-img-element */
import styles from "./MaintainerCard.module.css"
import { IMaintainer } from "../../data/maintainers"
export interface IMaintainerCardProps {
maintainer: IMaintainer
}
export const MaintainerCard = ({ maintainer }: IMaintainerCardProps) => {
const { image, fullName, description, githubLink } = maintainer
return (
<section className={styles.maintainerContainer}>
{image ? (
<img className={styles.maintainerImage} src={image} alt={fullName} />
) : (
<p className={styles.maintainerImage}>{image}</p>
)}
<p className={styles.maintainerFullName}>{fullName}</p>
<p className={styles.maintainerDescription}>{description}</p>
<a className={styles.linkButton} href={githubLink}>
{`${fullName}'s Github`}
</a>
</section>
)
}
================================================
FILE: components/Nav/NavItem.tsx
================================================
import Link from "next/link"
import styles from "./NavPrimary.module.css"
import { IPage } from "../../data/pages"
export interface INavItemProps {
page: IPage
activeNavLink: string
handleNavClick?: () => void
}
export const NavItem = ({
page,
activeNavLink,
handleNavClick,
}: INavItemProps) => {
const isLinkActive = activeNavLink === page.href
return (
<li className={`${styles.navListItem} ${isLinkActive ? "active" : ""}`}>
<Link
href={page.href}
onClick={handleNavClick}
aria-current={isLinkActive ? "page" : false}>
{page.name}
</Link>
</li>
)
}
================================================
FILE: components/Nav/NavPrimary.module.css
================================================
.navPrimary {
display: none;
}
.navPrimaryMobile {
position: fixed;
left: 0;
right: 0;
top: var(--headerHeight);
bottom: 0;
background-color: var(--bg);
z-index: 1;
overflow: scroll;
}
.navList {
margin: 8px 0 0 0;
padding: 0;
}
.navListItem {
list-style: none;
margin: 2px;
margin-left: 16px;
}
.navListItem a {
text-decoration: none;
padding: 8px;
display: inline-block;
border-radius: 5px;
}
.navListItem a:hover {
background-color: var(--primaryLt);
color: var(--text);
}
.navList a:focus {
background-color: var(--primary);
color: var(--invertText);
}
@media (min-width: 800px) {
.navPrimary {
width: 15rem;
display: block;
position: fixed;
overflow-y: auto;
height: calc(100vh - var(--headerHeight));
top: var(--headerHeight);
left: 0;
border-right: 1px solid var(--primaryLt);
}
.navPrimaryMobile {
display: none;
}
}
================================================
FILE: components/Nav/NavPrimary.tsx
================================================
import { NavItem } from "./NavItem"
import { pages } from "../../data/pages"
import styles from "./NavPrimary.module.css"
export interface INavProps {
activeNavLink: string
}
export const NavPrimary = ({ activeNavLink }: INavProps) => {
return (
<nav aria-label="Primary" className={styles.navPrimary}>
<ul className={styles.navList}>
{pages.map((page, index) =>(
<NavItem
key={page.name + index}
page={page}
activeNavLink={activeNavLink}
/>
))}
</ul>
</nav>
)
}
================================================
FILE: components/Nav/NavPrimaryMobile.tsx
================================================
import { NavItem } from "./NavItem"
import { pages } from "../../data/pages"
import styles from "./NavPrimary.module.css"
export interface INavProps {
activeNavLink: string
handleNavClick: () => void
}
export const NavPrimaryMobile = ({
activeNavLink,
handleNavClick,
}: INavProps) => {
return (
<nav aria-label="Primary" className={styles.navPrimaryMobile}>
<ul className={styles.navList}>
{pages.map((page, index) => (
<NavItem
key={page.name + index}
page={page}
activeNavLink={activeNavLink}
handleNavClick={handleNavClick}
/>
))}
</ul>
</nav>
)
}
================================================
FILE: components/NavPage/NavPage.module.css
================================================
.pageNav {
background-color: var(--highlight);
padding: 8px 16px;
}
.pageNavTitle {
font-weight: bold;
font-size: 1.1rem;
cursor: pointer;
}
.pageNavList {
list-style: none;
margin: 8px 0;
padding: 0;
line-height: 1.6;
}
.pageNavList li {
margin: 14px 0;
}
================================================
FILE: components/NavPage/NavPage.tsx
================================================
import styles from "./NavPage.module.css"
import { IPageNavigationItem } from "../../data/pageNavigationLists"
interface INavPageProps {
pageNavigation: IPageNavigationItem[]
}
export const NavPage = ({ pageNavigation }: INavPageProps) => {
return (
<details className={styles.pageNav}>
<summary className={styles.pageNavTitle}>On This Page</summary>
<nav aria-label="On This Page">
<ul className={styles.pageNavList}>
{pageNavigation.map((item, index) => (
<li key={index}>
<a href={item.href}>{item.linkName}</a>
</li>
))}
</ul>
</nav>
</details>
)
}
================================================
FILE: components/PageUpdated/PageUpdated.module.css
================================================
.pageLastUpdated {
margin-top: 5rem;
}
@media (min-width: 800px) {
.pageLastUpdated {
text-align: right;
}
}
================================================
FILE: components/PageUpdated/PageUpdated.tsx
================================================
import styles from "./PageUpdated.module.css"
interface IPageUpdatedProps {
date: string
}
export const PageUpdated = ({ date }: IPageUpdatedProps) => (
<p className={styles.pageLastUpdated}>
<strong>Page last updated: </strong>
{date}
</p>
)
================================================
FILE: components/SkipLink/SkipLink.module.css
================================================
.skipToMainContent {
display: flex;
justify-content: center;
}
.skipToMainContent a {
position: absolute;
color: var(--text);
background-color: var(--bg);
text-decoration: none;
padding: 0.3rem;
border: 1px solid var(--primary);
z-index: 100;
transform: translateY(-500px);
transition: all .4s ease-in-out;
}
.skipToMainContent a:focus {
transform: translateY(8px);
}
================================================
FILE: components/SkipLink/SkipLink.tsx
================================================
import styles from "./SkipLink.module.css"
export const SkipLink = () => (
<div className={styles.skipToMainContent}>
<a href="#main">Skip to main content</a>
</div>
)
================================================
FILE: components/TemplateSection/TemplateSection.module.css
================================================
.infoSection {
margin-top: 2rem;
width: 100%;
max-width: 52rem;
}
================================================
FILE: components/TemplateSection/TemplateSection.tsx
================================================
import { ReactNode } from "react"
import styles from "./TemplateSection.module.css"
interface ITemplateSectionProps {
sectionName: string
title: string
children: ReactNode
}
export const TemplateSection = ({
sectionName,
title,
children,
}: ITemplateSectionProps) => {
return (
<section aria-labelledby={sectionName} className={styles.infoSection}>
<h2 id={sectionName}>{title}</h2>
{children}
</section>
)
}
================================================
FILE: components/ThemeSwitcher/ThemeSwitcher.module.css
================================================
.themeBtn {
display: flex;
align-items: center;
gap: 0.5rem;
border: 1px solid var(--primaryLt);
padding: 0.2rem 0.5rem;
border-radius: var(--borderRadius);
background-color: transparent;
cursor: pointer;
color: var(--text);
font-size: .8rem;
}
.themeSwitcher {
position: absolute;
top: 8px;
right: 32px;
width: 10em;
background-color: var(--bg);
padding: 0.5rem;
border: 1px solid var(--primaryLt);
border-radius: var(--borderRadius);
list-style: none;
}
.themeSwitcher li {
margin-bottom: 0.25rem;
}
.themeSwitcherBtn {
background-color: none;
color: var(--text);
display: flex;
gap: 0.5rem;
align-items: center;
cursor: pointer;
padding: 0.5rem;
width: 100%;
border: none;
background-color: var(--bg);
font-size: 0.8rem;
}
.themeSwitcher li.activeBtn {
border-left: 3px solid var(--primary);
}
.themeSwitcherBtn:hover {
background-color: var(--primaryLt);
font-weight: bold;
}
.themeSwitcherBtn:focus {
background-color: var(--primaryLt);
font-weight: bold;
}
@media (min-width: 800px) {
.themeSwitcher {
top: 8px;
right: 0;
}
}
================================================
FILE: components/ThemeSwitcher/ThemeSwitcher.tsx
================================================
import { useState, useRef, useEffect, FocusEvent } from "react"
import { BsCircleHalf } from "react-icons/bs"
import { useTheme } from "next-themes"
import useOnClickOutside from "../../customHooks/useOnClickOutside"
import styles from "./ThemeSwitcher.module.css"
import { themes } from "./themes"
export const ThemeSwitcher = () => {
const [showThemeSwitcher, setShowThemeSwitcher] = useState<boolean>(false)
const { theme, setTheme } = useTheme()
const buttonRef = useRef<HTMLButtonElement>(null)
const divRef = useRef<HTMLDivElement>(null)
const handleThemeSwitcher = () => {
setShowThemeSwitcher((prevState) => !prevState)
}
const handleChangeTheme = (theme: string) => {
setTheme(theme)
handleThemeSwitcher()
buttonRef.current?.focus()
}
const handleThemeSwitcherKB = (e: { key: string }) => {
if (e.key === "Esc" || e.key === "Escape") {
handleThemeSwitcher()
buttonRef.current?.focus()
}
}
const handleBlur = (e: FocusEvent) => {
if (!e.currentTarget.contains(e.relatedTarget) && showThemeSwitcher) {
setShowThemeSwitcher(false)
}
}
const handleClickOutside = () => {
setShowThemeSwitcher(false)
}
useOnClickOutside(divRef, handleClickOutside)
useEffect(() => {
document.addEventListener("keyup", handleThemeSwitcherKB)
return () => document.removeEventListener("keyup", handleThemeSwitcherKB)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
return (
<div ref={divRef}>
<button
className={styles.themeBtn}
type="button"
id="themeSwitcherBtn"
aria-controls="themeSwitcherMenu"
onClick={handleThemeSwitcher}
aria-expanded={showThemeSwitcher}
ref={buttonRef}>
<BsCircleHalf
className={styles.themeIcon}
size="1rem"
aria-hidden="true"
/>
Theme
</button>
{showThemeSwitcher && (
<ul
className={styles.themeSwitcher}
id="themeSwitcherMenu"
aria-labelledby="themeSwitcherBtn"
onBlur={handleBlur}>
{themes.map(({ label, value, Icon }) => (
<li key={label} className={theme === value ? styles.activeBtn : ""}>
<button
className={styles.themeSwitcherBtn}
onClick={() => handleChangeTheme(value)}
aria-pressed={theme === value}>
<Icon
size="0.7rem"
aria-hidden="true"
className={styles.themeIcon}
/>
{label}
</button>
</li>
))}
</ul>
)}
</div>
)
}
================================================
FILE: components/ThemeSwitcher/themes.ts
================================================
import { BsCircleHalf, BsFillSunFill, BsFillMoonFill } from "react-icons/bs"
export const themes = [
{ label: "Device settings", value: "system", Icon: BsCircleHalf },
{ label: "Light mode", value: "light", Icon: BsFillSunFill },
{ label: "Dark mode", value: "dark", Icon: BsFillMoonFill },
]
================================================
FILE: components/TipOfTheDay/TipOfTheDay.module.css
================================================
.card {
background-color: var(--bg);
border: 1px solid var(--primaryLt);
padding: 1rem;
border-radius: 12px;
color: var(--text);
max-width: 100%;
}
================================================
FILE: components/TipOfTheDay/TipOfTheDay.tsx
================================================
import { useEffect, useState } from "react"
import styles from "./TipOfTheDay.module.css"
import { Tips } from "../../data/tipsOfTheDay"
interface ITip {
title: string
description: string
tipLink: string
tipLinkLabel: string
}
export const TipOfTheDay = () => {
const [randomIndex, setRandomIndex] = useState<number>(0)
const [loading, setLoading] = useState<boolean>(true)
const tipIndex: ITip = Tips[randomIndex]
useEffect(() => {
const randomNumber = Math.floor(Math.random() * Tips.length)
setRandomIndex(randomNumber)
setLoading(false)
}, [])
const createTipOfTheDay = () => {
return (
<>
<h2>Tip of the day: {tipIndex.title}</h2>
<p>{tipIndex.description}</p>
<a href={tipIndex.tipLink}>{tipIndex.tipLinkLabel}</a>
</>
)
}
const tip = loading ? null : createTipOfTheDay()
return <div className={styles.card}>{tip}</div>
}
================================================
FILE: components/WorkInProgress/WorkInProgress.module.css
================================================
.WIPContainer {
background-color: var(--highlight);
padding: 32px;
}
================================================
FILE: components/WorkInProgress/WorkInProgress.tsx
================================================
import styles from "./WorkInProgress.module.css"
export const WorkInProgress = () => {
return (
<section className={styles.WIPContainer}>
<h2>This section is a work in progress</h2>
<p>Please come back at a later date.</p>
<a href="https://github.com/AccessibleForAll/AccessibleWebDev/discussions">
Let us know what information you're looking for.
</a>
</section>
)
}
================================================
FILE: customHooks/useOnClickOutside.ts
================================================
import { RefObject } from "react"
import { useEventListener } from "usehooks-ts"
type Handler = (event: MouseEvent) => void
function useOnClickOutside<T extends HTMLElement = HTMLElement>(
ref: RefObject<T>,
handler: Handler,
mouseEvent: "mousedown" | "mouseup" = "mousedown"
): void {
useEventListener(mouseEvent, (event) => {
const el = ref?.current
// Do nothing if clicking ref's element or descendent elements
if (!el || el.contains(event.target as Node)) {
return
}
handler(event)
})
}
export default useOnClickOutside
================================================
FILE: data/maintainers.ts
================================================
export interface IMaintainer {
image: string
fullName: string
description: string
githubLink: string
}
export const currentMaintainers: IMaintainer[] = [
{
image: "https://avatars.githubusercontent.com/u/57045550?v=4&s=150",
fullName: "Emma Dawson",
description:
"Emma is a full stack developer from Stockholm with a passion for accessibility and open source. She wants to make web accessibility easy to learn for everyone.",
githubLink: "https://github.com/EmmaDawsonDev",
},
{
image: "https://avatars.githubusercontent.com/u/1682188?v=4&s=150",
fullName: "Cristian Toffanin",
description:
"Cristian is a full stack developer based in the Netherlands. He's always curious and always learning. He's currently learning about accessibility (Emma's mentee).",
githubLink: "https://github.com/ctoffanin",
},
]
================================================
FILE: data/pageNavigationLists.ts
================================================
export interface IPageNavigationItem {
linkName: string
href: string
}
export const audioPageNavigation: IPageNavigationItem[] = [
{ linkName: "Introduction", href: "#introduction" },
{ linkName: "Transcripts", href: "#transcripts" },
{ linkName: "Captions", href: "#captions" },
{ linkName: "How To Add Closed Captions To Your Video?", href: "#howToAdd" },
{ linkName: "Sign Language", href: "#signLanguage" },
{ linkName: "Accessible media players", href: "#accessibleMediaPlayers" },
{ linkName: "WCAG Criteria", href: "#WCAGCriteria" },
{ linkName: "Other Resources", href: "#otherResources" },
]
export const breadcrumbsPageNavigation: IPageNavigationItem[] = [
{ linkName: "Introduction", href: "#introduction" },
{ linkName: "HTML Structure", href: "#htmlStructure" },
{ linkName: "Add Extra Information with ARIA", href: "#aria" },
{ linkName: "Breadcrumbs Example", href: "#breadcrumbsExample" },
{ linkName: "Styling Breadcrumbs", href: "#breadcrumbStyling" },
{ linkName: "WCAG Criteria", href: "#WCAGCriteria" },
{ linkName: "Other Resources", href: "#otherResources" },
]
export const buttonPageNavigation: IPageNavigationItem[] = [
{ linkName: "Introduction", href: "#introduction" },
{ linkName: "Text Buttons", href: "#textButtons" },
{ linkName: "Text and Icon Buttons", href: "#textAndIconButtons" },
{ linkName: "Icon-only Buttons", href: "#iconOnlyButtons" },
{ linkName: "Button States", href: "#buttonStates" },
{ linkName: "Button or Link?", href: "#buttonOrLink" },
{ linkName: "Touch Target Minimum", href: "#touchTargetMinimum" },
{ linkName: "WCAG Criteria", href: "#WCAGCriteria" },
{ linkName: "Other Resources", href: "#otherResources" },
]
export const captchasPageNavigation: IPageNavigationItem[] = [
{
linkName: "Introduction",
href: "#introduction",
},
{ linkName: "What's the issue with CAPTCHAs?", href: "#captchaIssues" },
{ linkName: "Accessible CAPTCHA", href: "#accessibleCAPTCHA" },
{ linkName: "WCAG Criteria", href: "#WCAGCriteria" },
{ linkName: "Other Resources", href: "#otherResources" },
]
export const imagePageNavigation: IPageNavigationItem[] = [
{ linkName: "Introduction", href: "#introduction" },
{ linkName: "Text Alternatives", href: "#textAlternatives" },
{ linkName: "Decorative Images", href: "#decorativeImages" },
{ linkName: "Informative Images", href: "#informativeImages" },
{ linkName: "Functional Images", href: "#functionalImages" },
{ linkName: "How to write good alt text", href: "#goodAltText" },
{ linkName: "Images of Text", href: "#imagesOfText" },
{ linkName: "Images and Colour", href: "#imagesAndColour" },
{ linkName: "Image Maps", href: "#imageMaps" },
// { linkName: "Image Checklist", href: "#checklist" },
{ linkName: "WCAG Criteria", href: "#WCAGCriteria" },
{ linkName: "Other Resources", href: "#otherResources" },
]
export const headingsPageNavigation: IPageNavigationItem[] = [
{ linkName: "Introduction", href: "#intro" },
{ linkName: "Why are HTML Headings Important?", href: "#whyHeadings" },
{
linkName: "What Should Be Included in a Heading?",
href: "#includedInHeadings",
},
{ linkName: "How to Write a Good Heading", href: "#goodHeadings" },
{
linkName: "What Should We Avoid When Using Headings?",
href: "#avoidHeadings",
},
{ linkName: "WCAG Criteria", href: "#WCAGCriteria" },
{ linkName: "Test Your Knowledge", href: "#QuizQuestions" },
]
export const formsPageNavigation: IPageNavigationItem[] = [
{ linkName: "Introduction", href: "#introduction" },
{
linkName: "What Should We Consider When Building Accessible Forms?",
href: "#whatToConsider",
},
{
linkName: "Creating Simple, Understandable Forms",
href: "#creatingForms",
},
{
linkName: "Keyboard Accessible Forms",
href: "#keyboardAccessibleForms",
},
{ linkName: "Error Validation and Changes in State", href: "#formErrors" },
{ linkName: "WCAG Criteria", href: "#WCAGCriteria" },
]
export const linkPageNavigation: IPageNavigationItem[] = [
{ linkName: "Introduction", href: "#introduction" },
{ linkName: "Accessible Link Names", href: "#accessibleLinkNames" },
{ linkName: "Link State and Style", href: "#linkStates" },
{ linkName: "Image and Icon Links", href: "#linkImage" },
{ linkName: "Touch Target Minimum", href: "#touchTargetMinimum" },
{ linkName: "WCAG Criteria", href: "#WCAGCriteria" },
{ linkName: "Other Resources", href: "#otherResources" },
]
export const modalPageNavigation: IPageNavigationItem[] = [
{ linkName: "Introduction", href: "#introduction" },
{ linkName: "How a modal should work", href: "#howAModalShouldWork" },
{ linkName: "Using <dialog>", href: "#usingDialog" },
{ linkName: "Custom Modals", href: "#customModals" },
{ linkName: "WCAG Criteria", href: "#WCAGCriteria" },
{ linkName: "Other Resources", href: "#otherResources" },
]
export const navigationPageNavigation: IPageNavigationItem[] = [
{ linkName: "Introduction", href: "#introduction" },
{ linkName: "A Simple Navigation", href: "#simpleNav" },
{ linkName: "Hamburger Menus", href: "#hamburgerMenus" },
{ linkName: "Mega Menus", href: "#megaMenus" },
{ linkName: "Order of Links", href: "#linksOrder" },
{ linkName: "Multiple Navigations Per Page", href: "#multipleNavs" },
{ linkName: "WCAG Criteria", href: "#WCAGCriteria" },
//{ linkName: "Other Resources", href: "#otherResources" },
]
================================================
FILE: data/pages.ts
================================================
export interface IPage {
name: string
href: string
content?: string
}
export const pages: IPage[] = [
{ name: "Home", href: "/" },
// { name: "Alerts", href: "/alerts", content: "alerts" },
// {
// name: "Animations",
// href: "/animations",
// content: "animations",
// },
{ name: "Audio", href: "/audio", content: "audio" },
{ name: "Breadcrumbs", href: "/breadcrumbs", content: "breadcrumbs" },
{ name: "Buttons", href: "/buttons", content: "buttons" },
{
name: "CAPTCHAs",
href: "/captchas",
content: "captchas",
},
// {
// name: "Charts",
// href: "/charts",
// content: "charts",
// },
{ name: "Forms", href: "/forms", content: "forms" },
{ name: "Headings", href: "/headings", content: "headings" },
{ name: "Icons", href: "/icons", content: "icons" },
{ name: "Images", href: "/images", content: "images" },
{ name: "Links", href: "/links", content: "links" },
// { name: "Lists", href: "/lists", content: "lists" },
// { name: "Menus", href: "/menus", content: "menus" },
{ name: "Modals", href: "/modals", content: "modals" },
{ name: "Navigation", href: "/navigation", content: "navigation" },
// { name: "Pagination", href: "/pagination", content: "pagination" },
// { name: "Tables", href: "/tables", content: "tables" },
// { name: "Video", href: "/video", content: "video" },
]
================================================
FILE: data/tipsOfTheDay.ts
================================================
export interface ITips {
title: string
description: string
tipLink: string
tipLinkLabel: string
}
export const Tips: ITips[] = [
{
title: "Test with Assistive Technology",
description:
"Test your website using assistive technologies such as screen readers and keyboard-only navigation to identify and fix accessibility issues.",
tipLink: "https://www.w3.org/WAI/test-evaluate/preliminary/",
tipLinkLabel: "Read more about assistive technology...",
},
{
title: "Make Forms Accessible",
description:
"Ensure that all form elements have labels, are navigable via keyboard, and provide clear instructions for users filling out the form.",
tipLink: "https://www.w3.org/TR/WCAG21/#forms",
tipLinkLabel: "Read more about forms...",
},
{
title: "Provide Descriptive Links",
description:
"Use clear and descriptive link text that conveys the purpose and destination of the link, as screen readers often provide a list of links and users need to understand where each link will take them.",
tipLink:
"https://www.w3.org/WAI/tutorials/forms/labels/#descriptive-link-text",
tipLinkLabel: "Read more about descriptive technologies...",
},
{
title: "Use Sufficient Color Contrast",
description:
"Ensure that there is enough contrast between foreground and background colors to make text readable for users with low vision.",
tipLink: "https://www.w3.org/TR/WCAG21/#contrast-minimum",
tipLinkLabel: "Read more about color contrast...",
},
{
title: "Ensure Keyboard Accessibility",
description:
"Make sure all interactive elements on your website can be accessed and used via keyboard alone, as some users may not be able to use a mouse or other pointing device.",
tipLink: "https://www.w3.org/TR/WCAG21/#keyboard-accessible",
tipLinkLabel: "Read more about keyboard accessibility...",
},
{
title: "Use Semantic HTML",
description:
"Use appropriate HTML tags to help convey the meaning and structure of your content, which makes it easier for screen readers and other assistive technologies to navigate.",
tipLink: "https://webaim.org/techniques/semanticstructure/",
tipLinkLabel: "Read more about semantic HTML...",
},
{
title: "Use Alt Text for Images",
description:
"Include descriptive text in the alt attribute of image tags to help screen readers and visually impaired users understand the content of the image.",
tipLink: "https://www.w3.org/WAI/tutorials/images/",
tipLinkLabel: "Read more about alt text...",
},
]
================================================
FILE: docs-style-guide.md
================================================
# About this guide 👋
Welcome to our simple and straightforward style guide! We created this guide for our community of tech and non-tech experts to ensure we communicate effectively and inclusively. This guide is divided into four sections:
- 🌎 [Inclusive Language](inclusive-language)
- 📝 [Grammar & Mechanics](grammar-and-mechanics)
- 🤔 [Clarity](clarity)
- ♿ [Accessibility](accessibility)
## Inclusive Language 🌎
Inclusive language is an essential aspect of communication. It promotes equality, respects diversity, and avoids offense. Here are some best practices to follow:
- Use gender-neutral words for common terms
- ❌ **Not this:** A maintainer should try to be kind yet constructive in **his** feedback.
- ✅ **Use this:** A maintainer should try to be kind yet constructive in **their** feedback.
- Avoid using terms that have colonialistic or racist connotations.
- ❌ **Not this:** Contributors should push their pull requests to the **master** branch.
- ✅ **Use this:** Contributors should push their pull requests to the **main** branch.
## Grammar & Mechanics 📝
Grammar and mechanics help ensure that your writing is clear, concise, and easy to understand. Here are some best practices to follow:
### Commas
- Use commas when describing lists.
- ❌ **Not this:** You can also add your **timeline testimonials and upcoming events** that you are participating in.
- ✅ **Use this:** You can also add your **timeline, testimonials, and upcoming events** that you are participating in.
- Use commas in introductory elements.
- ❌ **Not this:** **If you do not have one yet** you can create one for free with an email address and password.
- ✅ **Use this:** **If you do not have one yet,** you can create one for free with an email address and password.
- Use commas before the seven, so-called, coordinating conjunctions (and, but, so, for, nor, yet, or) to separate two independent clauses.
- ❌ **Not this:** There are 4 ways you can add your profile **but** for this Quickstart, we will use the GitHub UI.
- ✅ **Use this:** There are 4 ways you can add your profile, **but** for this Quickstart, we will use the GitHub UI.
### Capitalisation
- Only capitalize terms that describe a product, its feature, the first letter of a sentence, or a person’s name.
- ❌ **Not this:** There are 4 ways you can add your profile, but for this **quickstart** we will use the **github ui**.
- ✅ **Use this:** There are 4 ways you can add your profile, but for this Quickstart, we will use the **GitHub UI**.
## Clarity 🤔
Clarity in writing helps ensure that your audience understands your message and intentions. Here are some best practices to follow:
### Active vs. Passive
Using the active voice in writing makes your message more direct and engaging.
- ❌ **Not this:** This repo is **maintained** by a team of people.
- ✅ **Use this:** A team of people **maintains** this repo.
### Jargon
Using technical and coding jargon can alienate non-technical readers.
- ❌ **Not this:** Before submitting your code review, it is always helpful to add comments such as **LGTM!** in the textbox.
- ✅ **Use this:** Before submitting your code review, it is always helpful to add comments such as **Great job!** in the textbox.
## Accessibility ♿
### Bullet Points
- Avoid describing things only by their color or position.
- ❌ **Not this:** see **the** **image** above.
- ✅ **Use this:** See **Image 2.2**.
### Emojis
- Avoid using emojis as bullet points or numbered lists.
- ❌ **Not this:** 1️⃣ Fork the repository
- 2️⃣ Visit the Accessibleforall repository
- ✅ **Use this:** **1.** Fork the repository
- **2️.** Visit the Accessibleforall repository
- Avoid using emojis in the middle of a sentence.
- ❌ **Not this:** There are 4️⃣ ways you can add your profile, but for this Quickstart we will use the GitHub UI.
- ✅ **Use this:** There are **4** ways you can fix your profile, but for this tutorial, we will use the GitHub UI.
- Use them sparingly at the end of a sentence.
- ❌ **Not this:** Join the conversation on our Discord community! 😀 😄 🎉
- ✅ **Use this:** Join the conversation on our Discord community! 😀
### Headings
- Use descriptive titles for headings.
- ❌ **Not this:** `<h1> Yoga</h1>`
- ✅ **Use this:** `<h1>Yoga for Developers</h1>`
- Place headings in sequential order.
- ❌ **Not this:** `<h1>Yoga for Developers</h1>`
`<h3>What is Yoga?</h3>`
`<h2>The History of Yoga</h2>`
- ✅ **Use this:** `<h1>Yoga for Developers</h1>`
`<h2>What is Yoga?</h2>`
`<h3>The History of Yoga</h3>`
### Images
- Use descriptive alt text for images.
- ❌ **Not this:** ``
- ✅ **Use this:** ``
Have some ideas for improving the guide? Raise an issue on our [GitHub page](https://github.com/AccessibleForAll/AccessibleWebDev/issues/new/choose). We would love to receive your feedback!
================================================
FILE: next-env.d.ts
================================================
/// <reference types="next" />
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
================================================
FILE: next-i18next.config.js
================================================
module.exports = {
i18n: {
locales: ["en", "sv"],
defaultLocale: "en",
},
}
================================================
FILE: next.config.js
================================================
/** @type {import('next').NextConfig} */
const { i18n } = require("./next-i18next.config")
const nextConfig = {
reactStrictMode: true,
swcMinify: true,
i18n,
}
module.exports = nextConfig
================================================
FILE: package.json
================================================
{
"name": "accessible-web-dev",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"prepare": "husky install"
},
"dependencies": {
"next": "^13.5.6",
"next-i18next": "^12.1.0",
"next-themes": "^0.2.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-icons": "^4.4.0",
"react-syntax-highlighter": "^15.5.0",
"usehooks-ts": "^2.7.2"
},
"devDependencies": {
"@babel/core": "^7.19.3",
"@types/node": "18.0.5",
"@types/react": "18.0.15",
"@types/react-dom": "18.0.6",
"@types/react-syntax-highlighter": "^15.5.6",
"@typescript-eslint/eslint-plugin": "^5.44.0",
"babel-loader": "^8.2.5",
"eslint": "8.20.0",
"eslint-config-next": "^13.5.6",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-jsx-a11y": "^6.10.2",
"husky": "^8.0.0",
"lint-staged": "^13.0.3",
"prettier": "^2.7.1",
"typescript": "4.7.4"
},
"lint-staged": {
"**/*.{js,jsx,ts,tsx}": [
"eslint --fix",
"prettier --write"
]
}
}
================================================
FILE: pages/404.tsx
================================================
/* eslint-disable @next/next/no-img-element */
import notFoundImage from "../public/404.png"
import Image from "next/legacy/image"
import Head from "next/head"
import { Layout } from "../components/Layout/Layout"
import { useTranslation } from "next-i18next"
import { serverSideTranslations } from "next-i18next/serverSideTranslations"
import { GetStaticProps } from "next"
const PageNotFound = () => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { t } = useTranslation("common")
return (
<>
<Head>
<title>{`Page Not Found - Accessible Web Dev`}</title>
<meta name="description" content="404 - Page Not Found" />
</Head>
<Layout activeNavLink={""}>
<>
<h1>404 - Not Found</h1>
<div className="center">
<Image
src={notFoundImage.src}
alt="Page not found"
width={800}
height={600}
/>
</div>
</>
</Layout>
</>
)
}
export const getStaticProps: GetStaticProps = async (context) => {
const locale: string = context?.locale || ""
return {
props: {
...(await serverSideTranslations(locale, ["common"])),
// Will be passed to the page component as props
},
}
}
export default PageNotFound
================================================
FILE: pages/[content].tsx
================================================
import type { GetStaticPaths, GetStaticProps, NextPage } from "next"
import { Layout } from "../components/Layout/Layout"
import Head from "next/head"
import { capitalizeRoute } from "../utils"
import { pages } from "../data/pages"
import { useTranslation } from "next-i18next"
import { serverSideTranslations } from "next-i18next/serverSideTranslations"
// Components
import { AlertsTemplate } from "../components/ContentTemplates/AlertsTemplate"
import { AnimationsTemplate } from "../components/ContentTemplates/AnimationsTemplate"
import { AudioTemplate } from "../components/ContentTemplates/AudioTemplate"
import { BreadcrumbsTemplate } from "../components/ContentTemplates/BreadcrumbsTemplate"
import { ButtonsTemplate } from "../components/ContentTemplates/ButtonsTemplate"
import { CaptchasTemplate } from "../components/ContentTemplates/CaptchasTemplate"
import { ChartsTemplate } from "../components/ContentTemplates/ChartsTemplate"
import { FormsTemplate } from "../components/ContentTemplates/FormsTemplate"
import { HeadingsTemplate } from "../components/ContentTemplates/HeadingsTemplate"
import { IconsTemplate } from "../components/ContentTemplates/IconsTemplate"
import { ImagesTemplate } from "../components/ContentTemplates/ImagesTemplate"
import { LinksTemplate } from "../components/ContentTemplates/LinksTemplate"
import { ListsTemplate } from "../components/ContentTemplates/ListsTemplate"
import { MenusTemplate } from "../components/ContentTemplates/MenusTemplate"
import { ModalsTemplate } from "../components/ContentTemplates/ModalsTemplate"
import { NavigationTemplate } from "../components/ContentTemplates/NavigationTemplate"
import { TablesTemplate } from "../components/ContentTemplates/TablesTemplate"
import { VideoTemplate } from "../components/ContentTemplates/VideoTemplate"
import { PaginationTemplate } from "../components/ContentTemplates/PaginationTemplate"
interface IProps {
page: string
}
const ContentPage: NextPage<IProps> = ({ page }) => {
const title = capitalizeRoute(page)
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { t } = useTranslation("common")
return (
<>
<Head>
<title>{`${title} - Accessible Web Dev`}</title>
<meta
name="description"
content={`Learn how to make ${title} accessible`}
/>
<meta property="og:title" content={`${title} - Accessible Web Dev`} />
<meta
property="og:description"
content={`Learn how to make ${title} accessible`}
/>
<meta property="og:url" content={`https://accessibleweb.dev/${page}`} />
<meta
property="twitter:url"
content={`https://accessibleweb.dev/${page}`}
/>
<meta name="twitter:title" content={`${title} - Accessible Web Dev`} />
<meta
name="twitter:description"
content={`Learn how to make ${title} accessible`}
/>
</Head>
<Layout activeNavLink={`/${page}`}>
<>
<h1>{`Accessible ${title}`}</h1>
{page === "alerts" && <AlertsTemplate />}
{page === "animations" && <AnimationsTemplate />}
{page === "audio" && <AudioTemplate />}
{page === "breadcrumbs" && <BreadcrumbsTemplate />}
{page === "buttons" && <ButtonsTemplate />}
{page === "captchas" && <CaptchasTemplate />}
{page === "charts" && <ChartsTemplate />}
{page === "forms" && <FormsTemplate />}
{page === "headings" && <HeadingsTemplate />}
{page === "icons" && <IconsTemplate />}
{page === "images" && <ImagesTemplate />}
{page === "links" && <LinksTemplate />}
{page === "lists" && <ListsTemplate />}
{page === "menus" && <MenusTemplate />}
{page === "modals" && <ModalsTemplate />}
{page === "navigation" && <NavigationTemplate />}
{page === "pagination" && <PaginationTemplate />}
{page === "tables" && <TablesTemplate />}
{page === "video" && <VideoTemplate />}
</>
</Layout>
</>
)
}
export const getStaticPaths: GetStaticPaths = async (context) => {
const locales = context?.locales || []
const paths = pages.filter((page) => page.content)
const pathsWithLocales = paths.flatMap((path) => {
return locales.map((locale) => {
return {
params: {
content: path.content,
},
locale: locale,
}
})
})
return {
paths: pathsWithLocales,
fallback: false,
}
}
export const getStaticProps: GetStaticProps = async (context) => {
const content = context.params?.content || ""
const locale: string = context?.locale || ""
return {
props: {
...(await serverSideTranslations(locale, ["common"])),
page: content,
},
}
}
export default ContentPage
================================================
FILE: pages/_app.tsx
================================================
import "../styles/globals.css"
import type { AppProps } from "next/app"
import Head from "next/head"
import { appWithTranslation } from "next-i18next"
import { ThemeProvider } from "next-themes"
function MyApp({ Component, pageProps }: AppProps) {
return (
<>
<Head>
<meta name="viewport" content="initial-scale=1.0, width=device-width" />
<link rel="icon" href="/favicon.ico" />
<link
rel="apple-touch-icon"
sizes="180x180"
href="/apple-touch-icon.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="/favicon-16x16.png"
/>
<link rel="manifest" href="/site.webmanifest" />
<meta property="og:type" content="website" />
<meta
property="og:image"
content="https://accessibleweb.dev/images/websiteScreenshot.png"
/>
<meta name="twitter:card" content="summary_large_image" />
<meta property="twitter:domain" content="accessibleweb.dev" />
<meta
name="twitter:image"
content="https://accessibleweb.dev/images/websiteScreenshot.png"
/>
</Head>
<ThemeProvider attribute="class">
<Component {...pageProps} />
</ThemeProvider>
</>
)
}
export default appWithTranslation(MyApp)
================================================
FILE: pages/about.tsx
================================================
import type { GetStaticProps, NextPage } from "next"
import { Layout } from "../components/Layout/Layout"
import Head from "next/head"
import { useTranslation } from "next-i18next"
import { serverSideTranslations } from "next-i18next/serverSideTranslations"
import { MaintainerCard } from "../components/MaintainerCard/MaintainerCard"
import { currentMaintainers, IMaintainer } from "../data/maintainers"
import styles from "../styles/about.module.css"
interface IAboutProps {
currentMaintainerData: IMaintainer[]
}
const About: NextPage<IAboutProps> = ({ currentMaintainerData }) => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { t } = useTranslation("common")
return (
<>
<Head>
<title>{`About - Accessible Web Dev`}</title>
<meta
name="description"
content="About the Accessible Web Dev site & team"
/>
<meta property="og:title" content="Accessible Web Dev - About" />
<meta property="og:description" content="About Accessible Web Dev" />
<meta property="og:url" content="https://accessibleweb.dev/about" />
<meta
property="twitter:url"
content="https://accessibleweb.dev/about"
/>
<meta name="twitter:title" content="Accessible Web Dev - About" />
<meta name="twitter:description" content="About Accessible Web Dev" />
</Head>
<Layout activeNavLink="">
<div>
<h1>About Us</h1>
<section>
<h2>Current Maintainers</h2>
<div className={styles.aboutRow}>
{currentMaintainerData.map((maintainer, index) => (
<MaintainerCard key={index} maintainer={maintainer} />
))}
</div>
</section>
</div>
</Layout>
</>
)
}
export const getStaticProps: GetStaticProps = async (context) => {
const locale: string = context?.locale || ""
const currentMaintainerData = currentMaintainers
return {
props: {
...(await serverSideTranslations(locale, ["common"])),
// Will be passed to the page component as props
currentMaintainerData: currentMaintainerData,
},
}
}
export default About
================================================
FILE: pages/api/hello.ts
================================================
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type { NextApiRequest, NextApiResponse } from "next"
type Data = {
name: string
}
export default function handler(
req: NextApiRequest,
res: NextApiResponse<Data>
) {
res.status(200).json({ name: "John Doe" })
}
================================================
FILE: pages/docs/Style Guide.md
================================================
# About this guide 👋
Welcome to our simple and straightforward style guide! We created this guide for our community of tech and non-tech experts to ensure we communicate effectively and inclusively. This guide is divided into four sections:
- 🌎 [Inclusive Language](inclusive-language)
- 📝 [Grammar & Mechanics](grammar-and-mechanics)
- 🤔 [Clarity](clarity)
- ♿ [Accessibility](accessibility)
## Inclusive Language 🌎
Inclusive language is an essential aspect of communication. It promotes equality, respects diversity, and avoids offense. Here are some best practices to follow:
- Use gender-neutral words for common terms
- ❌ **Not this:** A maintainer should try to be kind yet constructive in **his** feedback.
- ✅ **Use this:** A maintainer should try to be kind yet constructive in **their** feedback.
- Avoid using terms that have colonialistic or racist connotations.
- ❌ **Not this:** Contributors should push their pull requests to the **master** branch.
- ✅ **Use this:** Contributors should push their pull requests to the **main** branch.
## Grammar & Mechanics 📝
Grammar and mechanics help ensure that your writing is clear, concise, and easy to understand. Here are some best practices to follow:
### Commas
- Use commas when describing lists.
- ❌ **Not this:** You can also add your **timeline testimonials and upcoming events** that you are participating in.
- ✅ **Use this:** You can also add your **timeline, testimonials, and upcoming events** that you are participating in.
- Use commas in introductory elements.
- ❌ **Not this:** **If you do not have one yet** you can create one for free with an email address and password.
- ✅ **Use this:** If you do not have one yet, you can create one for free with an email address and password.
- Use commas before the seven, so-called, coordinating conjunctions (and, but, so, for, nor, yet, or) to separate two independent clauses.
- ❌ **Not this:** There are 4 ways you can add your profile **but** for this Quickstart, we will use the GitHub UI.
- ✅ **Use this:** There are 4 ways you can add your profile, **but** for this Quickstart, we will use the GitHub UI.
### Capitalisation
- Only capitalize terms that describe a product, its feature, the first letter of a sentence, or a person’s name.
- ❌ **Not this:** There are 4 ways you can add your profile but****for this **quickstart**we will use the **github ui**.
- ✅ **Use this:** There are 4 ways you can add your profile, **but** for this Quickstart, we will use the **GitHub UI**.
## Clarity 🤔
Clarity in writing helps ensure that your audience understands your message and intentions. Here are some best practices to follow:
### Active vs. Passive
Using the active voice in writing makes your message more direct and engaging.
- ❌ **Not this:** Gitpod **was** an open-source application that provides a prebuilt development environment in your browser - powered by VS Code.
- ✅ **Use this:** Gitpod **is** an open-source application that provides a prebuilt development envir
gitextract_u4kywwvh/ ├── .eslintignore ├── .eslintrc.json ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.md │ │ └── feature_request.md │ ├── PULL_REQUEST_TEMPLATE.md │ └── workflows/ │ ├── appreciation.yml │ └── axe.yaml ├── .gitignore ├── .gitpod.yml ├── .husky/ │ └── pre-commit ├── .prettierignore ├── .prettierrc.json ├── CODE_OF_CONDUCT.md ├── CODING_STANDARDS.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── components/ │ ├── CodeBlock/ │ │ ├── CodeBlock.module.css │ │ └── CodeBlock.tsx │ ├── ContentTemplates/ │ │ ├── AlertsTemplate.tsx │ │ ├── AnimationsTemplate.tsx │ │ ├── AudioTemplate.tsx │ │ ├── BreadcrumbsTemplate.module.css │ │ ├── BreadcrumbsTemplate.tsx │ │ ├── ButtonsTemplate.module.css │ │ ├── ButtonsTemplate.tsx │ │ ├── CaptchasTemplate.tsx │ │ ├── ChartsTemplate.tsx │ │ ├── FormsTemplate.tsx │ │ ├── HeadingsTemplate.tsx │ │ ├── IconsTemplate.tsx │ │ ├── ImagesTemplate.tsx │ │ ├── LinksTemplate.module.css │ │ ├── LinksTemplate.tsx │ │ ├── ListsTemplate.tsx │ │ ├── MenusTemplate.tsx │ │ ├── ModalsTemplate.tsx │ │ ├── NavigationTemplate.tsx │ │ ├── PaginationTemplate.tsx │ │ ├── TablesTemplate.tsx │ │ └── VideoTemplate.tsx │ ├── CopyCodeBlock/ │ │ ├── CopyCodeBlock.module.css │ │ └── CopyCodeBlock.tsx │ ├── Footer/ │ │ ├── Footer.module.css │ │ └── Footer.tsx │ ├── Header/ │ │ ├── Header.module.css │ │ └── Header.tsx │ ├── Layout/ │ │ ├── Layout.module.css │ │ └── Layout.tsx │ ├── MaintainerCard/ │ │ ├── MaintainerCard.module.css │ │ └── MaintainerCard.tsx │ ├── Nav/ │ │ ├── NavItem.tsx │ │ ├── NavPrimary.module.css │ │ ├── NavPrimary.tsx │ │ └── NavPrimaryMobile.tsx │ ├── NavPage/ │ │ ├── NavPage.module.css │ │ └── NavPage.tsx │ ├── PageUpdated/ │ │ ├── PageUpdated.module.css │ │ └── PageUpdated.tsx │ ├── SkipLink/ │ │ ├── SkipLink.module.css │ │ └── SkipLink.tsx │ ├── TemplateSection/ │ │ ├── TemplateSection.module.css │ │ └── TemplateSection.tsx │ ├── ThemeSwitcher/ │ │ ├── ThemeSwitcher.module.css │ │ ├── ThemeSwitcher.tsx │ │ └── themes.ts │ ├── TipOfTheDay/ │ │ ├── TipOfTheDay.module.css │ │ └── TipOfTheDay.tsx │ └── WorkInProgress/ │ ├── WorkInProgress.module.css │ └── WorkInProgress.tsx ├── customHooks/ │ └── useOnClickOutside.ts ├── data/ │ ├── maintainers.ts │ ├── pageNavigationLists.ts │ ├── pages.ts │ └── tipsOfTheDay.ts ├── docs-style-guide.md ├── next-env.d.ts ├── next-i18next.config.js ├── next.config.js ├── package.json ├── pages/ │ ├── 404.tsx │ ├── [content].tsx │ ├── _app.tsx │ ├── about.tsx │ ├── api/ │ │ └── hello.ts │ ├── docs/ │ │ └── Style Guide.md │ └── index.tsx ├── public/ │ ├── locales/ │ │ ├── en/ │ │ │ ├── common.json │ │ │ └── homepage.json │ │ └── sv/ │ │ ├── common.json │ │ └── homepage.json │ └── site.webmanifest ├── styles/ │ ├── about.module.css │ └── globals.css ├── tsconfig.json └── utils.ts
SYMBOL INDEX (24 symbols across 21 files)
FILE: components/CodeBlock/CodeBlock.tsx
type TCodeLanguage (line 7) | type TCodeLanguage =
type ICodeBlockProps (line 14) | interface ICodeBlockProps {
FILE: components/CopyCodeBlock/CopyCodeBlock.tsx
type CodeSnippet (line 5) | interface CodeSnippet {
FILE: components/Header/Header.tsx
type IHeaderProps (line 7) | interface IHeaderProps {
FILE: components/Layout/Layout.tsx
type ILayoutProps (line 9) | interface ILayoutProps {
FILE: components/MaintainerCard/MaintainerCard.tsx
type IMaintainerCardProps (line 5) | interface IMaintainerCardProps {
FILE: components/Nav/NavItem.tsx
type INavItemProps (line 5) | interface INavItemProps {
FILE: components/Nav/NavPrimary.tsx
type INavProps (line 5) | interface INavProps {
FILE: components/Nav/NavPrimaryMobile.tsx
type INavProps (line 5) | interface INavProps {
FILE: components/NavPage/NavPage.tsx
type INavPageProps (line 4) | interface INavPageProps {
FILE: components/PageUpdated/PageUpdated.tsx
type IPageUpdatedProps (line 2) | interface IPageUpdatedProps {
FILE: components/TemplateSection/TemplateSection.tsx
type ITemplateSectionProps (line 4) | interface ITemplateSectionProps {
FILE: components/TipOfTheDay/TipOfTheDay.tsx
type ITip (line 5) | interface ITip {
FILE: customHooks/useOnClickOutside.ts
type Handler (line 4) | type Handler = (event: MouseEvent) => void
function useOnClickOutside (line 6) | function useOnClickOutside<T extends HTMLElement = HTMLElement>(
FILE: data/maintainers.ts
type IMaintainer (line 1) | interface IMaintainer {
FILE: data/pageNavigationLists.ts
type IPageNavigationItem (line 1) | interface IPageNavigationItem {
FILE: data/pages.ts
type IPage (line 1) | interface IPage {
FILE: data/tipsOfTheDay.ts
type ITips (line 1) | interface ITips {
FILE: pages/[content].tsx
type IProps (line 31) | interface IProps {
FILE: pages/_app.tsx
function MyApp (line 7) | function MyApp({ Component, pageProps }: AppProps) {
FILE: pages/about.tsx
type IAboutProps (line 10) | interface IAboutProps {
FILE: pages/api/hello.ts
type Data (line 4) | type Data = {
function handler (line 8) | function handler(
Condensed preview — 96 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (230K chars).
[
{
"path": ".eslintignore",
"chars": 0,
"preview": ""
},
{
"path": ".eslintrc.json",
"chars": 1935,
"preview": "{\n\t\"extends\": [\n\t\t\"next/core-web-vitals\",\n\t\t\"prettier\",\n\t\t\"plugin:@typescript-eslint/recommended\",\n\t\t\"plugin:jsx-a11y/st"
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report.md",
"chars": 834,
"preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Describe the b"
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request.md",
"chars": 595,
"preview": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Is your fea"
},
{
"path": ".github/PULL_REQUEST_TEMPLATE.md",
"chars": 581,
"preview": "## Describe your changes\n\n## Screenshots - If Any (Optional)\n\n## Link to issue\n<!-- Example: Closes #31 -->\nCloses #\n\n##"
},
{
"path": ".github/workflows/appreciation.yml",
"chars": 870,
"preview": "name: 'Thank Contributors'\n\non:\n issues:\n types: [opened]\n pull_request_target:\n types: [opened]\n\njobs:\n welcom"
},
{
"path": ".github/workflows/axe.yaml",
"chars": 568,
"preview": "name: axe\non:\n push:\n branches: [main]\n pull_request:\n branches: [main]\njobs:\n axe:\n runs-on: ubuntu-latest\n"
},
{
"path": ".gitignore",
"chars": 410,
"preview": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pn"
},
{
"path": ".gitpod.yml",
"chars": 452,
"preview": "# List the start up tasks. Learn more https://www.gitpod.io/docs/config-start-tasks/\ntasks:\n - name: npm\n init: npm "
},
{
"path": ".husky/pre-commit",
"chars": 69,
"preview": "#!/usr/bin/env sh\n. \"$(dirname -- \"$0\")/_/husky.sh\"\n\nnpx lint-staged\n"
},
{
"path": ".prettierignore",
"chars": 227,
"preview": "# dependencies\n/node_modules\n/.pnp\n.pnp.js\n\n# testing\n/coverage\n\n# production\n/build\n\n# misc\n.DS_Store\n.env.local\n.env.d"
},
{
"path": ".prettierrc.json",
"chars": 206,
"preview": "{\n\t\"trailingComma\": \"es5\",\n\t\"useTabs\": true,\n\t\"tabWidth\": 2,\n\t\"semi\": false,\n\t\"singleQuote\": false,\n\t\"bracketSpacing\": t"
},
{
"path": "CODE_OF_CONDUCT.md",
"chars": 5225,
"preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participa"
},
{
"path": "CODING_STANDARDS.md",
"chars": 3030,
"preview": "# Coding Standards\n\n## Naming\n\nChoosing good names is critical to creating code that is easy to use and easy to understa"
},
{
"path": "CONTRIBUTING.md",
"chars": 2750,
"preview": "# Contributing to Accessible Web Dev\n\n## Languages\n\nThis project is built with Next.JS, Typescript and CSS modules.\n\n## "
},
{
"path": "LICENSE",
"chars": 1075,
"preview": "MIT License\n\nCopyright (c) 2022 Accessible For All\n\nPermission is hereby granted, free of charge, to any person obtainin"
},
{
"path": "README.md",
"chars": 2214,
"preview": "\n# [Accessible Web Dev](https://accessibleweb.dev/) 🌐 \n\nWelcome to **Accessible Web Dev**, a resource designed to help "
},
{
"path": "components/CodeBlock/CodeBlock.module.css",
"chars": 623,
"preview": ".CodeBlockContainer {\n\tposition: relative;\n\twidth: 90vw;\n\tmax-width: 850px;\n\tborder: 1px solid var(--primaryLt);\n\tborder"
},
{
"path": "components/CodeBlock/CodeBlock.tsx",
"chars": 1303,
"preview": "import { useEffect, useRef, useState } from \"react\"\nimport { Prism as SyntaxHighlighter } from \"react-syntax-highlighter"
},
{
"path": "components/ContentTemplates/AlertsTemplate.tsx",
"chars": 134,
"preview": "import { WorkInProgress } from \"../WorkInProgress/WorkInProgress\"\n\nexport const AlertsTemplate = () => {\n\treturn <WorkIn"
},
{
"path": "components/ContentTemplates/AnimationsTemplate.tsx",
"chars": 138,
"preview": "import { WorkInProgress } from \"../WorkInProgress/WorkInProgress\"\n\nexport const AnimationsTemplate = () => {\n\treturn <Wo"
},
{
"path": "components/ContentTemplates/AudioTemplate.tsx",
"chars": 9743,
"preview": "import { CodeBlock } from \"../CodeBlock/CodeBlock\"\nimport { NavPage } from \"../NavPage/NavPage\"\nimport { PageUpdated } f"
},
{
"path": "components/ContentTemplates/BreadcrumbsTemplate.module.css",
"chars": 272,
"preview": ".breadcrumbsExample {\n background-color: var(--highlight);\n}\n\n.breadcrumbsExample ol {\n\tlist-style: none;\n\tmargin: 0;\n\t"
},
{
"path": "components/ContentTemplates/BreadcrumbsTemplate.tsx",
"chars": 7167,
"preview": "import { breadcrumbsPageNavigation } from \"../../data/pageNavigationLists\"\nimport { CodeBlock } from \"../CodeBlock/CodeB"
},
{
"path": "components/ContentTemplates/ButtonsTemplate.module.css",
"chars": 1846,
"preview": ".exampleButton {\n\tpadding: 0.6rem 1.2rem;\t\n\tcursor: pointer;\n\tbackground-color: var(--primaryLt);\n color: var(--text);\n"
},
{
"path": "components/ContentTemplates/ButtonsTemplate.tsx",
"chars": 16802,
"preview": "import { TemplateSection } from \"../TemplateSection/TemplateSection\"\nimport { CodeBlock } from \"../CodeBlock/CodeBlock\"\n"
},
{
"path": "components/ContentTemplates/CaptchasTemplate.tsx",
"chars": 4100,
"preview": "import { captchasPageNavigation } from \"../../data/pageNavigationLists\"\nimport { NavPage } from \"../NavPage/NavPage\"\nimp"
},
{
"path": "components/ContentTemplates/ChartsTemplate.tsx",
"chars": 134,
"preview": "import { WorkInProgress } from \"../WorkInProgress/WorkInProgress\"\n\nexport const ChartsTemplate = () => {\n\treturn <WorkIn"
},
{
"path": "components/ContentTemplates/FormsTemplate.tsx",
"chars": 11325,
"preview": "import { formsPageNavigation } from \"../../data/pageNavigationLists\"\nimport { CodeBlock } from \"../CodeBlock/CodeBlock\"\n"
},
{
"path": "components/ContentTemplates/HeadingsTemplate.tsx",
"chars": 5540,
"preview": "import Image from \"next/legacy/image\"\nimport { NavPage } from \"../NavPage/NavPage\"\nimport { CodeBlock } from \"../CodeBlo"
},
{
"path": "components/ContentTemplates/IconsTemplate.tsx",
"chars": 9724,
"preview": "import Link from \"next/link\"\n\nimport { CodeBlock } from \"../CodeBlock/CodeBlock\"\nimport { NavPage } from \"../NavPage/Nav"
},
{
"path": "components/ContentTemplates/ImagesTemplate.tsx",
"chars": 14873,
"preview": "import Image from \"next/legacy/image\"\nimport { NavPage } from \"../NavPage/NavPage\"\nimport { CodeBlock } from \"../CodeBlo"
},
{
"path": "components/ContentTemplates/LinksTemplate.module.css",
"chars": 160,
"preview": ".srOnly{\n\tborder: 0;\n clip: rect(0,0,0,0);\n height: 1px;\n margin: -1px;\n overflow: hidden;\n padding: 0;\n "
},
{
"path": "components/ContentTemplates/LinksTemplate.tsx",
"chars": 11944,
"preview": "import { TemplateSection } from \"../TemplateSection/TemplateSection\"\nimport { CodeBlock } from \"../CodeBlock/CodeBlock\"\n"
},
{
"path": "components/ContentTemplates/ListsTemplate.tsx",
"chars": 133,
"preview": "import { WorkInProgress } from \"../WorkInProgress/WorkInProgress\"\n\nexport const ListsTemplate = () => {\n\treturn <WorkInP"
},
{
"path": "components/ContentTemplates/MenusTemplate.tsx",
"chars": 133,
"preview": "import { WorkInProgress } from \"../WorkInProgress/WorkInProgress\"\n\nexport const MenusTemplate = () => {\n\treturn <WorkInP"
},
{
"path": "components/ContentTemplates/ModalsTemplate.tsx",
"chars": 10012,
"preview": "import { TemplateSection } from \"../TemplateSection/TemplateSection\"\nimport { CodeBlock } from \"../CodeBlock/CodeBlock\"\n"
},
{
"path": "components/ContentTemplates/NavigationTemplate.tsx",
"chars": 12298,
"preview": "import { navigationPageNavigation } from \"../../data/pageNavigationLists\"\nimport { CodeBlock } from \"../CodeBlock/CodeBl"
},
{
"path": "components/ContentTemplates/PaginationTemplate.tsx",
"chars": 138,
"preview": "import { WorkInProgress } from \"../WorkInProgress/WorkInProgress\"\n\nexport const PaginationTemplate = () => {\n\treturn <Wo"
},
{
"path": "components/ContentTemplates/TablesTemplate.tsx",
"chars": 134,
"preview": "import { WorkInProgress } from \"../WorkInProgress/WorkInProgress\"\n\nexport const TablesTemplate = () => {\n\treturn <WorkIn"
},
{
"path": "components/ContentTemplates/VideoTemplate.tsx",
"chars": 133,
"preview": "import { WorkInProgress } from \"../WorkInProgress/WorkInProgress\"\n\nexport const VideoTemplate = () => {\n\treturn <WorkInP"
},
{
"path": "components/CopyCodeBlock/CopyCodeBlock.module.css",
"chars": 284,
"preview": ".codeCopyBtn {\n\tcolor: var(--text);\n\tbackground-color: var(--primaryLt);\n\tpadding: 4px 8px;\n\tposition: absolute;\n\tdispla"
},
{
"path": "components/CopyCodeBlock/CopyCodeBlock.tsx",
"chars": 712,
"preview": "import { useState } from \"react\"\nimport { FaCopy, FaCheckSquare } from \"react-icons/fa\"\nimport styles from \"./CopyCodeBl"
},
{
"path": "components/Footer/Footer.module.css",
"chars": 907,
"preview": ".footer {\n background-color: var(--bg);\n padding: 16px;\n color: var(--text);\n margin-top: 2rem;\n border-t"
},
{
"path": "components/Footer/Footer.tsx",
"chars": 1463,
"preview": "import Link from \"next/link\"\nimport styles from \"./Footer.module.css\"\nimport { BsGithub, BsBoxArrowUpRight } from \"react"
},
{
"path": "components/Header/Header.module.css",
"chars": 703,
"preview": ".topBanner {\n\tbackground-color: var(--bg);\n\tcolor: var(--text);\n\theight: var(--headerHeight);\n\twidth: 100%;\n\tpadding: 0 "
},
{
"path": "components/Header/Header.tsx",
"chars": 939,
"preview": "import Link from \"next/link\"\nimport { IoMenuSharp, IoCloseSharp } from \"react-icons/io5\"\nimport { ThemeSwitcher } from \""
},
{
"path": "components/Layout/Layout.module.css",
"chars": 398,
"preview": ".layoutContainer {\n display: flex;\n min-height: calc(100vh - var(--headerHeight));\n margin-top: 3rem;\n}\n\n.layoutConta"
},
{
"path": "components/Layout/Layout.tsx",
"chars": 1077,
"preview": "import { useState } from \"react\"\nimport { Footer } from \"../Footer/Footer\"\nimport { Header } from \"../Header/Header\"\nimp"
},
{
"path": "components/MaintainerCard/MaintainerCard.module.css",
"chars": 786,
"preview": ".maintainerContainer {\n align-items: center;\n border: 0.15rem solid var(--primaryLt);\n border-radius: 0.5rem;\n "
},
{
"path": "components/MaintainerCard/MaintainerCard.tsx",
"chars": 811,
"preview": "/* eslint-disable @next/next/no-img-element */\nimport styles from \"./MaintainerCard.module.css\"\nimport { IMaintainer } f"
},
{
"path": "components/Nav/NavItem.tsx",
"chars": 590,
"preview": "import Link from \"next/link\"\nimport styles from \"./NavPrimary.module.css\"\nimport { IPage } from \"../../data/pages\"\n\nexpo"
},
{
"path": "components/Nav/NavPrimary.module.css",
"chars": 923,
"preview": ".navPrimary {\n display: none; \n}\n\n.navPrimaryMobile {\n position: fixed;\n left: 0;\n right: 0;\n top: var(--headerHei"
},
{
"path": "components/Nav/NavPrimary.tsx",
"chars": 536,
"preview": "import { NavItem } from \"./NavItem\"\nimport { pages } from \"../../data/pages\"\nimport styles from \"./NavPrimary.module.css"
},
{
"path": "components/Nav/NavPrimaryMobile.tsx",
"chars": 612,
"preview": "import { NavItem } from \"./NavItem\"\nimport { pages } from \"../../data/pages\"\nimport styles from \"./NavPrimary.module.css"
},
{
"path": "components/NavPage/NavPage.module.css",
"chars": 270,
"preview": ".pageNav {\n\tbackground-color: var(--highlight);\n\tpadding: 8px 16px;\n}\n\n.pageNavTitle {\n\tfont-weight: bold;\n\tfont-size: 1"
},
{
"path": "components/NavPage/NavPage.tsx",
"chars": 609,
"preview": "import styles from \"./NavPage.module.css\"\nimport { IPageNavigationItem } from \"../../data/pageNavigationLists\"\n\ninterfac"
},
{
"path": "components/PageUpdated/PageUpdated.module.css",
"chars": 122,
"preview": ".pageLastUpdated {\r\n\tmargin-top: 5rem;\r\n}\r\n\r\n@media (min-width: 800px) {\r\n\t.pageLastUpdated {\r\n\t\ttext-align: right;\r\n\t}\r"
},
{
"path": "components/PageUpdated/PageUpdated.tsx",
"chars": 250,
"preview": "import styles from \"./PageUpdated.module.css\"\ninterface IPageUpdatedProps {\n\tdate: string\n}\nexport const PageUpdated = ("
},
{
"path": "components/SkipLink/SkipLink.module.css",
"chars": 394,
"preview": ".skipToMainContent {\n display: flex;\n justify-content: center;\n}\n\n.skipToMainContent a {\n position: absolute;\n color"
},
{
"path": "components/SkipLink/SkipLink.tsx",
"chars": 173,
"preview": "import styles from \"./SkipLink.module.css\"\n\nexport const SkipLink = () => (\n\t<div className={styles.skipToMainContent}>\n"
},
{
"path": "components/TemplateSection/TemplateSection.module.css",
"chars": 68,
"preview": ".infoSection {\n\tmargin-top: 2rem;\n\twidth: 100%;\n\tmax-width: 52rem;\n}"
},
{
"path": "components/TemplateSection/TemplateSection.tsx",
"chars": 429,
"preview": "import { ReactNode } from \"react\"\nimport styles from \"./TemplateSection.module.css\"\n\ninterface ITemplateSectionProps {\n\t"
},
{
"path": "components/ThemeSwitcher/ThemeSwitcher.module.css",
"chars": 1081,
"preview": ".themeBtn {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 0.5rem;\n\tborder: 1px solid var(--primaryLt);\n\tpadding: 0.2rem 0."
},
{
"path": "components/ThemeSwitcher/ThemeSwitcher.tsx",
"chars": 2434,
"preview": "import { useState, useRef, useEffect, FocusEvent } from \"react\"\nimport { BsCircleHalf } from \"react-icons/bs\"\nimport { u"
},
{
"path": "components/ThemeSwitcher/themes.ts",
"chars": 297,
"preview": "import { BsCircleHalf, BsFillSunFill, BsFillMoonFill } from \"react-icons/bs\"\n\nexport const themes = [\n\t{ label: \"Device "
},
{
"path": "components/TipOfTheDay/TipOfTheDay.module.css",
"chars": 156,
"preview": ".card {\n\tbackground-color: var(--bg);\n\tborder: 1px solid var(--primaryLt);\n\tpadding: 1rem;\n\tborder-radius: 12px;\n\tcolor:"
},
{
"path": "components/TipOfTheDay/TipOfTheDay.tsx",
"chars": 875,
"preview": "import { useEffect, useState } from \"react\"\nimport styles from \"./TipOfTheDay.module.css\"\nimport { Tips } from \"../../da"
},
{
"path": "components/WorkInProgress/WorkInProgress.module.css",
"chars": 72,
"preview": ".WIPContainer {\n background-color: var(--highlight);\n padding: 32px;\n}"
},
{
"path": "components/WorkInProgress/WorkInProgress.tsx",
"chars": 391,
"preview": "import styles from \"./WorkInProgress.module.css\"\n\nexport const WorkInProgress = () => {\n\treturn (\n\t\t<section className={"
},
{
"path": "customHooks/useOnClickOutside.ts",
"chars": 548,
"preview": "import { RefObject } from \"react\"\nimport { useEventListener } from \"usehooks-ts\"\n\ntype Handler = (event: MouseEvent) => "
},
{
"path": "data/maintainers.ts",
"chars": 837,
"preview": "export interface IMaintainer {\n\timage: string\n\tfullName: string\n\tdescription: string\n\tgithubLink: string\n}\n\nexport const"
},
{
"path": "data/pageNavigationLists.ts",
"chars": 5361,
"preview": "export interface IPageNavigationItem {\n\tlinkName: string\n\thref: string\n}\n\nexport const audioPageNavigation: IPageNavigat"
},
{
"path": "data/pages.ts",
"chars": 1338,
"preview": "export interface IPage {\n\tname: string\n\thref: string\n\tcontent?: string\n}\n\nexport const pages: IPage[] = [\n\t{ name: \"Home"
},
{
"path": "data/tipsOfTheDay.ts",
"chars": 2506,
"preview": "export interface ITips {\n\ttitle: string\n\tdescription: string\n\ttipLink: string\n\ttipLinkLabel: string\n}\n\nexport const Tips"
},
{
"path": "docs-style-guide.md",
"chars": 5066,
"preview": "# About this guide 👋\n\nWelcome to our simple and straightforward style guide! We created this guide for our community of "
},
{
"path": "next-env.d.ts",
"chars": 201,
"preview": "/// <reference types=\"next\" />\n/// <reference types=\"next/image-types/global\" />\n\n// NOTE: This file should not be edite"
},
{
"path": "next-i18next.config.js",
"chars": 82,
"preview": "module.exports = {\n\ti18n: {\n\t\tlocales: [\"en\", \"sv\"],\n\t\tdefaultLocale: \"en\",\n\t},\n}\n"
},
{
"path": "next.config.js",
"chars": 194,
"preview": "/** @type {import('next').NextConfig} */\n\nconst { i18n } = require(\"./next-i18next.config\")\n\nconst nextConfig = {\n\treact"
},
{
"path": "package.json",
"chars": 1049,
"preview": "{\n\t\"name\": \"accessible-web-dev\",\n\t\"version\": \"0.1.0\",\n\t\"private\": true,\n\t\"scripts\": {\n\t\t\"dev\": \"next dev\",\n\t\t\"build\": \"n"
},
{
"path": "pages/404.tsx",
"chars": 1212,
"preview": "/* eslint-disable @next/next/no-img-element */\nimport notFoundImage from \"../public/404.png\"\nimport Image from \"next/leg"
},
{
"path": "pages/[content].tsx",
"chars": 4593,
"preview": "import type { GetStaticPaths, GetStaticProps, NextPage } from \"next\"\n\nimport { Layout } from \"../components/Layout/Layou"
},
{
"path": "pages/_app.tsx",
"chars": 1319,
"preview": "import \"../styles/globals.css\"\nimport type { AppProps } from \"next/app\"\nimport Head from \"next/head\"\nimport { appWithTra"
},
{
"path": "pages/about.tsx",
"chars": 2058,
"preview": "import type { GetStaticProps, NextPage } from \"next\"\nimport { Layout } from \"../components/Layout/Layout\"\nimport Head fr"
},
{
"path": "pages/api/hello.ts",
"chars": 303,
"preview": "// Next.js API route support: https://nextjs.org/docs/api-routes/introduction\nimport type { NextApiRequest, NextApiRespo"
},
{
"path": "pages/docs/Style Guide.md",
"chars": 5257,
"preview": "# About this guide 👋\n\nWelcome to our simple and straightforward style guide! We created this guide for our community of "
},
{
"path": "pages/index.tsx",
"chars": 1875,
"preview": "import type { GetStaticProps, NextPage } from \"next\"\nimport { Layout } from \"../components/Layout/Layout\"\nimport Head fr"
},
{
"path": "public/locales/en/common.json",
"chars": 3,
"preview": "{}\n"
},
{
"path": "public/locales/en/homepage.json",
"chars": 937,
"preview": "{\n\t\"pageTitle\": \"Home - Accessible Web Dev\",\n\t\"metaContent\": \"Learn the basics about web accessibility in a clear and ea"
},
{
"path": "public/locales/sv/common.json",
"chars": 3,
"preview": "{}\n"
},
{
"path": "public/locales/sv/homepage.json",
"chars": 941,
"preview": "{\n\t\"pageTitle\": \"Hem - Accessible Web Dev\",\n\t\"metaContent\": \"Lär dig om webtillgänglighet på ett lätt och tydligt sätt.\""
},
{
"path": "public/site.webmanifest",
"chars": 263,
"preview": "{\"name\":\"\",\"short_name\":\"\",\"icons\":[{\"src\":\"/android-chrome-192x192.png\",\"sizes\":\"192x192\",\"type\":\"image/png\"},{\"src\":\"/"
},
{
"path": "styles/about.module.css",
"chars": 68,
"preview": ".aboutRow {\n display:flex;\n flex-wrap: wrap;\n gap: 1rem;\n}\n"
},
{
"path": "styles/globals.css",
"chars": 1748,
"preview": "@import url(\"https://fonts.googleapis.com/css2?family=Noto+Sans:wght@400;500;600;700;800;900&display=swap\");\n\n:root,\n:ro"
},
{
"path": "tsconfig.json",
"chars": 477,
"preview": "{\n\t\"compilerOptions\": {\n\t\t\"target\": \"es5\",\n\t\t\"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n\t\t\"allowJs\": true,\n\t\t\"skipLibChec"
},
{
"path": "utils.ts",
"chars": 112,
"preview": "export const capitalizeRoute = (phrase: string) => {\n\treturn phrase.charAt(0).toUpperCase() + phrase.slice(1)\n}\n"
}
]
About this extraction
This page contains the full source code of the AccessibleForAll/AccessibleWebDev GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 96 files (192.0 KB), approximately 54.8k tokens, and a symbol index with 24 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.