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 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//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 ``` 9. To run the whole project locally: ```bash npm run dev ``` 10. Make your changes 11. Stage your changes: ```bash git add ``` 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 "" ``` 13. Push your commits to your local repository ```bash git push origin ``` 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. ![Screenshot of Accessible Web Dev homepage showing a clean, accessible interface with key topics and resources.](/public/images/websiteScreenshot.png) --- ## 🚀 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(null) const PreWithRef = (preProps: React.HTMLAttributes) => (
	)

	useEffect(() => {
		if (ref.current) {
			const element = ref.current
			setScrollableRegion(element.clientWidth < element.scrollWidth)
		}
	}, [])

	return (
		
{languageType}
{codeSnippet}
) } ================================================ FILE: components/ContentTemplates/AlertsTemplate.tsx ================================================ import { WorkInProgress } from "../WorkInProgress/WorkInProgress" export const AlertsTemplate = () => { return } ================================================ FILE: components/ContentTemplates/AnimationsTemplate.tsx ================================================ import { WorkInProgress } from "../WorkInProgress/WorkInProgress" export const AnimationsTemplate = () => { return } ================================================ 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 ( <>

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.

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.

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.

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.

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.

There are two types of captions: Open Captions and Closed Captions.

Open Captions

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.

Closed Captions

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.

Auto-generated captions

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.

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.

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.

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.

`} />

Video upload services such as YouTube and Vimeo allow you to upload captions in various file formats including VTT, WebVTT and SRT.

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.

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.

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.

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.

) } ================================================ 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 ( <>

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.

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.

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.

What is needed:

  • A nav element will let users know that this is a navigation landmark. Landmarks make things easier to find, especially for screen reader users.
  • An ordered list will let users know that the order of the items is important.
  • Links within the list will let users know that they can click to navigate to that area of the website.
  • Since the last element in a breadcrumb trail should represent the current page, making it a link is optional.

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.

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.

Aria-hidden can be used to hide any dividers between links.

  1. Website root
  2. Website level 1
  3. Website level 2
`} />

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).

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.

) } ================================================ 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 ( <>

Buttons are interactive elements that perform an action when pressed. That action could be something like submitting a form or opening a menu.

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.

`} languageType={"html"} />

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:

  • Activate when a user presses the enter key
  • Activate when a user presses the space key
  • Be included in the tab sequence
  • Be given the explicit role of button

A text button is a button which has only text inside it.

Text here`} languageType={"html"} />

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.

Buttons with identical text

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?

  • Product 1{" "}
  • Product 2{" "}
  • Product 3{" "}
  • Product 4{" "}
  • Product 5{" "}

We could use CSS to visibly hide some extra text:

Add to basket Product 1 `} languageType={"html"} />

This would then read out "Add to basket product 1" to screen reader users.

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.

We could also use an aria-label.

Add to basket`} languageType={"html"} />

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.{" "}

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"

Text `} languageType={"html"} /> Text `} languageType={"html"} />

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.

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.

Save `} languageType={"html"} /> `} languageType={"html"} />

Make sure that the label you give the icon matches the function of the button, not necessarily describing the actual icon.

Do this:

`} languageType={"html"} />

Don't do this:

`} languageType={"html"} />

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.

Default, hover, focus and active states

These states can be handled with CSS.

  • Default state: 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.
  • Hover state: 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.
  • Focus state: 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.
  • Active state: A button is active in the moment it is pressed. It will often look like it's been pressed.

Disabled state

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.

Disabled button`} languageType={"html"} />

Toggle buttons - pressed and unpressed states

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.

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.

Toggle button`} languageType={"html"} />

Menus and popups - expanded and collapsed state

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.

Button with popup`} languageType={"html"} />

How do you know when it's best to use a button and when it's best to use a link?

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.

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.

Is it taking you to another page or another area of the page? Then it should probably be a link.

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.

) } ================================================ 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 ( <>

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.

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.

The easiest way to explain the issues with CAPTCHAs is with examples.

Visual

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.

Audio

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.

Maths challenge

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.

Alignment challenges

Any CAPTCHA relying on aligning two images are not accessible to people with vision disabilities or motor disabilities.

Click to prove you're not a robot

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.

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.

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.

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.

) } ================================================ FILE: components/ContentTemplates/ChartsTemplate.tsx ================================================ import { WorkInProgress } from "../WorkInProgress/WorkInProgress" export const ChartsTemplate = () => { return } ================================================ 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 ( <>

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.

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.

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.

There are some key considerations when it comes to building forms.

  • Forms should be simple, understandable and have clear labels and instructions on how to input data.
  • Forms must be keyboard operable. Users should be able to use the tab key to navigate through all form controls.
  • Forms must be programmatically labelled to match the form control.
  • Forms should provide validation and error notifications that do not rely on just one sensory characteristic to alert users.
  • Any time limits are communicated up front, and users are able to extend time limits if necessary.

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.

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:{" "}

Phone number (XXX-XXX-XXXX): [text field]

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.

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.

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”.

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.

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.

`} languageType={"html"} />

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).

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.

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.

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.

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.

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.

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.

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.

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.

{ // INFO: move this section! }

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:

`} languageType={"html"} />

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.

) } ================================================ 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 ( <>

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.

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.

screenshot of a table of contents for article about 20 coffee chats with developers, displaying headings for each section

Headings are important for a number of reasons.

  • They give order and structure to the web page, making it easier for users to understand the layout and content of the page.
  • They allow the browser to index and structure the page visually, which helps users find what they are looking for.
  • They allow screen reader users to skip to specific sections of the page, so they can find the information they need quickly.

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.

Writing a good heading is all about being descriptive, and remembering how to nest heading sections within one another in sequential order.

Below is a code snippet example with semantic section headings.

Yoga for Developers

What is Yoga?

The History of Yoga

Yoga's Origin

Yoga in Modern Times

The Benefit of Yoga

Strength Conditioning

Power Yoga

Increasing Flexibility

Gentle Stretches

Stretches for the hands
Prayer Pose
Nerve Gliding Exercises
`} />
  • 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.
  • Headings should not be chosen for style purposes - use CSS to style sections instead.
  • Headings should not be placed in non sequential order to ensure that the structure of the page makes sense.
  1. How many H1s should we have on our webpage?
  2. Why is heading order important?
  3. Why is it important to have accurate and descriptive headings?
  4. How does non-semantic headings affect users of screen readers?
  5. What should we use to change the style of our headings?
  6. 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?
  7. 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?
) } ================================================ 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 ( <>

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.

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.

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.

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".

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?

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.

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.

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.

`} languageType="html" /> The text alternative goes here `} languageType="html" />

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 Links Page for detail of how to use these techniques.

The important thing to remember when using icons as links is to describe the link destination rather than the content of the image.

Google Google `} languageType={"html"} />

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.

Save `} languageType={"html"} /> `} languageType={"html"} />

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.

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.

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.

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.

{/* */}
) } ================================================ 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 ( <>

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.

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?

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.

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.

`} languageType="html" /> The text alternative goes here `} languageType="html" />

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.

Old brown paper texture Blue tiled hexagons
`} languageType="html" />

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.

Cupcakes with pink icing and small sugar heart decorations
`} languageType="html" />

Complex Informative Images

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.

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.

Drawn instructions for how to use a handheld drill
`} languageType="html" />

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.

Magnifying glass used as search button example Triangle used as play button example
Play`} languageType="html" />

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.

Let's take a look at the following image:

VW Beetle Car in pale green in front of a pitched roof house painted in the same colour

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.

Alternative 1: The house is for sale

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.

Basic: House with pitched roof and brown front door.

More detailed: Pale green, one storey house with pitched roof, large windows and steps up to a brown front door. Surrounded by a white picket fence.

Too detailed? 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.

Alternative 2: A car magazine

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.

Basic: Vintage car parked on a street.

More detailed: Soft, pale green vintage VW Beetle parked in front of a house painted in the same pale green.

Too detailed? 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.

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.

Make the day great sign
`} languageType="html" />

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.

{ // TODO: Add an example here of a pie chart or bar chart }

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.

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.

Doughnut Coffee Illustration of a pink doughnut with sprinkles next to a cup of black coffee Doughnut Coffee Illustration of a pink doughnut with sprinkles next to a cup of black coffee`} languageType="html" />
{/* TODO:

Add some points here

*/}
) } ================================================ 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 ( <>

Links, also known as hyperlinks, are a fundamental element in HTML. They connect different pages, or parts of pages, to one another.

In HTML, links are created using the anchor element {`${" "}`}. 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.

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.

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.

Non-descriptive Link Text

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.

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.

Aria-label

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"

An aria-label can also be used to give an image or icon link an accessible name when there is no visible link text.

Read more`} languageType={"html"} />

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.

Hiding elements with CSS

Another solution is to use CSS to hide some extra text. This can be done by adding a visibly hidden {`${""}`}{" "} element within the {`${""}`} element to provide descriptive text that gets read out to screen readers but is not visible to sighted users.

Read more about accessible buttons `} languageType={"html"} />

To improve the accessibility of your website, it's crucial to make links easy to distinguish from non-interactive elements on a page.

Here's how to achieve this:

  • Underline links by default: 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.
  • Focus state: 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.
  • Ensure sufficient color contrast: 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.
  • Don't rely on hover state to convey links: 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.

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.

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.

Google Google `} languageType={"html"} />

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.

) } ================================================ FILE: components/ContentTemplates/ListsTemplate.tsx ================================================ import { WorkInProgress } from "../WorkInProgress/WorkInProgress" export const ListsTemplate = () => { return } ================================================ FILE: components/ContentTemplates/MenusTemplate.tsx ================================================ import { WorkInProgress } from "../WorkInProgress/WorkInProgress" export const MenusTemplate = () => { return } ================================================ 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 ( <>

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.

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.

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.

  • Things behind the modal should become inactive when the modal is open.
  • The focus should move to an element within the modal when it opens.
  • The focus should move back to the original activating element when the modal closes.
  • 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.
  • It should be possible to close the modal by pressing the Escape key.
  • The modal should not be part of the tab order when it is closed.
"}>

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.

HTML

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.

Open dialog

This is a heading

This is some placeholder text within the dialog.

`} languageType={"html"} />

CSS

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.

JavaScript

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.

dialog.showModal()); cancelBtn.addEventListener("click", () => dialog.close());`} languageType={"javascript"} />

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.

HTML

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.

`} languageType={"html"} />

CSS

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.

JavaScript

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.

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.

{ 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"} />
) } ================================================ 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 ( <>

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.

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.

`} languageType={"html"} />

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.

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.

{ // TODO: Fix this code block! }

Mega menus are more complex navigations with nested links to different sections and pages. Examples can often be found on popular clothing chain websites.

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.

In the example below a very simple "Mega Menu" is shown with only two sections. These can be extended as necessary.

`} languageType={"html"} /> { 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"} />

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.

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.

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.

... `} languageType={"html"} /> ... `} languageType={"html"} />
{/* */} ) } ================================================ FILE: components/ContentTemplates/PaginationTemplate.tsx ================================================ import { WorkInProgress } from "../WorkInProgress/WorkInProgress" export const PaginationTemplate = () => { return } ================================================ FILE: components/ContentTemplates/TablesTemplate.tsx ================================================ import { WorkInProgress } from "../WorkInProgress/WorkInProgress" export const TablesTemplate = () => { return } ================================================ FILE: components/ContentTemplates/VideoTemplate.tsx ================================================ import { WorkInProgress } from "../WorkInProgress/WorkInProgress" export const VideoTemplate = () => { return } ================================================ 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 ( ) } 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 ( ) } ================================================ 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 ( <>
AWD
) } ================================================ 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(false) const handleNavClick = () => { setShowNavMobile((prevState) => !prevState) } return ( <>
{showNavMobile && ( )}
{children}
) } ================================================ 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 (
{image ? ( {fullName} ) : (

{image}

)}

{fullName}

{description}

{`${fullName}'s Github`}
) } ================================================ 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 (
  • {page.name}
  • ) } ================================================ 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 ( ) } ================================================ 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 ( ) } ================================================ 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 (
    On This Page
    ) } ================================================ 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) => (

    Page last updated: {date}

    ) ================================================ 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 = () => ( ) ================================================ 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 (

    {title}

    {children}
    ) } ================================================ 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(false) const { theme, setTheme } = useTheme() const buttonRef = useRef(null) const divRef = useRef(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 (
    {showThemeSwitcher && (
      {themes.map(({ label, value, Icon }) => (
    • ))}
    )}
    ) } ================================================ 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(0) const [loading, setLoading] = useState(true) const tipIndex: ITip = Tips[randomIndex] useEffect(() => { const randomNumber = Math.floor(Math.random() * Tips.length) setRandomIndex(randomNumber) setLoading(false) }, []) const createTipOfTheDay = () => { return ( <>

    Tip of the day: {tipIndex.title}

    {tipIndex.description}

    {tipIndex.tipLinkLabel} ) } const tip = loading ? null : createTipOfTheDay() return
    {tip}
    } ================================================ 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 (

    This section is a work in progress

    Please come back at a later date.

    Let us know what information you're looking for.
    ) } ================================================ FILE: customHooks/useOnClickOutside.ts ================================================ import { RefObject } from "react" import { useEventListener } from "usehooks-ts" type Handler = (event: MouseEvent) => void function useOnClickOutside( ref: RefObject, 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 ", 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:** `

    Yoga

    ` - ✅ **Use this:** `

    Yoga for Developers

    ` - Place headings in sequential order. - ❌ **Not this:** `

    Yoga for Developers

    ` `

    What is Yoga?

    ` `

    The History of Yoga

    ` - ✅ **Use this:** `

    Yoga for Developers

    ` `

    What is Yoga?

    ` `

    The History of Yoga

    ` ### Images - Use descriptive alt text for images. - ❌ **Not this:** `![Cat_2019-06-14.jpg](https://en.wikipedia.org/wiki/File:June_odd-eyed-cat.jpg)` - ✅ **Use this:** `![A white cat with a blue eye and golden eye is sitting on a grey couch](https://en.wikipedia.org/wiki/File:June_odd-eyed-cat.jpg)` 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 ================================================ /// /// // 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 ( <> {`Page Not Found - Accessible Web Dev`} <>

    404 - Not Found

    Page not found
    ) } 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 = ({ page }) => { const title = capitalizeRoute(page) // eslint-disable-next-line @typescript-eslint/no-unused-vars const { t } = useTranslation("common") return ( <> {`${title} - Accessible Web Dev`} <>

    {`Accessible ${title}`}

    {page === "alerts" && } {page === "animations" && } {page === "audio" && } {page === "breadcrumbs" && } {page === "buttons" && } {page === "captchas" && } {page === "charts" && } {page === "forms" && } {page === "headings" && } {page === "icons" && } {page === "images" && } {page === "links" && } {page === "lists" && } {page === "menus" && } {page === "modals" && } {page === "navigation" && } {page === "pagination" && } {page === "tables" && } {page === "video" && }
    ) } 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 ( <> ) } 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 = ({ currentMaintainerData }) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { t } = useTranslation("common") return ( <> {`About - Accessible Web Dev`}

    About Us

    Current Maintainers

    {currentMaintainerData.map((maintainer, index) => ( ))}
    ) } 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 ) { 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 environment in your browser - powered by VS Code. ### Jargon Using technical jargon can alienate non-technical readers. - ❌ **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 environment in your browser - powered by VS Code. ## 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:** `

    Yoga

    ` - ✅ **Use this:** `

    Yoga for Developers

    ` - Place headings in chronological order. - ❌ **Not this:** `

    Yoga for Developers

    ` `

    What is Yoga?

    ` `

    The History of Yoga

    ` - ✅ **Use this:** `

    Yoga for Developers

    ` `

    What is Yoga?

    ` `

    The History of Yoga

    ` ### Images - Use descriptive alt text for images. - ❌ **Not this:** `![Cat_2019-06-14.jpg](https://en.wikipedia.org/wiki/File:June_odd-eyed-cat.jpg)` - ✅ **Use this:** `![A white cat with a blue eye and golden eye is sitting on a grey couch](https://en.wikipedia.org/wiki/File:June_odd-eyed-cat.jpg)` 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: pages/index.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 { TipOfTheDay } from "../components/TipOfTheDay/TipOfTheDay" const Home: NextPage = () => { const { t } = useTranslation("homepage") return ( <> {t("pageTitle")}

    Accessible Web Dev

    {t("heading.welcome")}

    {t("para.text1")}

    {t("para.text2")}

    {t("para.text3")}

    {t("para.text4")}{" "} {t("link.githubIssue")}

    ) } export const getStaticProps: GetStaticProps = async (context) => { const locale: string = context?.locale || "" return { props: { ...(await serverSideTranslations(locale, ["common", "homepage"])), // Will be passed to the page component as props }, } } export default Home ================================================ FILE: public/locales/en/common.json ================================================ {} ================================================ FILE: public/locales/en/homepage.json ================================================ { "pageTitle": "Home - Accessible Web Dev", "metaContent": "Learn the basics about web accessibility in a clear and easy to understand way", "heading": { "welcome": "Welcome!" }, "para": { "text1": "Accessible Web Dev is here to make accessibility more understandable for developers and designers as current standards can be hard to understand.", "text2": "This is not a replacement for current standards but hopefully a complement to them and help on your journey to creating more accessible web experiences for everyone.", "text3": "Each page will contain a variety of examples of how to make specific elements or widgets accessible. It will also provide links to resources and further reading.", "text4": "It is currently a work in progress so not all pages are finished (or even started). If you don't find what you are looking for please feel free to" }, "link": { "githubIssue": "raise an issue on GitHub." } } ================================================ FILE: public/locales/sv/common.json ================================================ {} ================================================ FILE: public/locales/sv/homepage.json ================================================ { "pageTitle": "Hem - Accessible Web Dev", "metaContent": "Lär dig om webtillgänglighet på ett lätt och tydligt sätt.", "heading": { "welcome": "Välkommen!" }, "para": { "text1": "Accessible Web Dev finns för att göra tillgänglighet lättare att förstå för webutvecklare och designers eftersom nuvarande standardar är svårlästa.", "text2": "Den här sajten är ett komplemang till de standardar som redan finns för att hjälpa dig på din resa mot att skapa mer tillgängliga webbupplevelser för alla.", "text3": "Varje sida kommer innehålla exempel på hur man gör specifika element eller komponenter mer tillgängliga. Det kommer även finnas länkar till andra sajter där man kan lära sig mer.", "text4": "För nuvarande är den här sajten under uppbyggnad. Vissa sidor är inte kompletta och kanske inte ens påbörjade. Om du inte hittar det du letar efter får du gärna" }, "link": { "githubIssue": "lyfta en fråga på GitHub." } } ================================================ FILE: public/site.webmanifest ================================================ {"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} ================================================ FILE: styles/about.module.css ================================================ .aboutRow { display:flex; flex-wrap: wrap; gap: 1rem; } ================================================ FILE: styles/globals.css ================================================ @import url("https://fonts.googleapis.com/css2?family=Noto+Sans:wght@400;500;600;700;800;900&display=swap"); :root, :root.light { --text: #0D1023; --invertText: #fff; --grey: #c7c7c7; --highlight: #def8f6; --primary: #028378; --primaryLt: #7ddfd7; --bg: #fff; --dark: #0D1023; --borderRadius: 3px; --white: #fff; --headerHeight: 3rem; } :root.dark { --text: #fff; --invertText: #0D1023; --grey: #c7c7c7; --highlight: #028378; --primary: #53fdee; --primaryLt: #028378; --bg: #0D1023; --dark: #0D1023; --borderRadius: 3px; --white: #fff; --headerHeight: 3rem; } @supports (font: -apple-system-body) { html { font: -apple-system-body; } } html, body { padding: 0; margin: 0; font-family: "Noto Sans", -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; color: var(--text); background-color: var(--bg); } a { color: inherit; } * { box-sizing: border-box; } *:focus-visible { outline: 2px transparent solid; box-shadow: 0 0 0 2px #000, 0 0 0 4px #fff; } .active { font-weight: bold; background-color: var(--primary); color: var(--invertText); text-decoration: none; display: inline-block; border-radius: 5px; } .center { display: grid; place-items: center; } [hidden] { display: none; } h2, h3, h4, h5, h6 { margin: 1rem 0 0 0; scroll-margin-top: 5rem; } h1{ font-size: 2rem; margin: 0 0 1rem 0; } h2 { font-size: 1.5rem; } h3 { font-size: 1.1rem; } p { line-height: 1.5; } /*Classes used on multiple content pages*/ .imageContainer { display: flex; gap: 1rem; flex-wrap: wrap; } .blockLink { display: block; } .list > li { margin-bottom: 0.7rem; } .textContainer { max-width: 52rem; } ================================================ FILE: tsconfig.json ================================================ { "compilerOptions": { "target": "es5", "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, "strict": true, "forceConsistentCasingInFileNames": true, "noEmit": true, "esModuleInterop": true, "module": "esnext", "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, "jsx": "preserve", "incremental": true }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], "exclude": ["node_modules"] } ================================================ FILE: utils.ts ================================================ export const capitalizeRoute = (phrase: string) => { return phrase.charAt(0).toUpperCase() + phrase.slice(1) }