-
Notifications
You must be signed in to change notification settings - Fork 1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
117 changed files
with
46,758 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
--- | ||
slug: accessibility | ||
description: Accessibility is a core feature that's built-in | ||
--- | ||
|
||
# Accessibility (aka a11y) | ||
|
||
We built Redwood to make building websites more accessible (we write all the config so you don't have to), but Redwood's also built to help you make more accessible websites. | ||
Accessibility shouldn't be a nice-to-have. | ||
It should be a given from the start. | ||
A core feature that's built-in and well-supported. | ||
|
||
There's a lot of great tooling out there that'll not only help you build accessible websites, but also help you learn exactly what that means. | ||
|
||
> **Does tooling obviate the need for manual testing?** | ||
> | ||
> No—even with all the tooling in the world, manual testing is still important, especially for accessibility. | ||
> But just because tooling doesn't catch everything doesn't mean it's not valuable. | ||
> It'd be much harder to learn what to look for without it. | ||
## Accessible Routing | ||
|
||
For single-page applications (SPAs), accessibility starts with the router. | ||
Without a full-page refresh, you just can't be sure that things like announcements and focus are being taken care of the way they're supposed to be. | ||
Here's a great example of [how disorienting SPAs can be to screen-reader users](https://www.youtube.com/watch?v=NKTdNv8JpuM). | ||
On navigation, nothing's announced. | ||
The lack of an announcement isn't just buggy behavior—it's broken. | ||
|
||
Normally, the onus would be on you as a developer to announce to screen-reader users that they've navigated somewhere new. | ||
That's a lot to ask—and hard to get right—especially when you're just trying to build your app. | ||
|
||
Luckily, if you're writing thoughtful content and marking it up semantically, there's nothing you have to do! | ||
The router automatically announces pages on navigation, and looks for announcements in this order: | ||
|
||
1. The `RouteAnnouncement` component | ||
2. The page's `<h1>` | ||
3. `document.title` | ||
4. `location.pathname` | ||
|
||
The reason for this order is that announcements should be as specific as possible. | ||
more specific usually means more descriptive, and more descriptive usually means that users can not only orient themselves and navigate through the content, but also find it again. | ||
|
||
> If you're not sure if your content is descriptive enough, see the [W3 guidelines](https://www.w3.org/WAI/WCAG21/Techniques/general/G88.html). | ||
Even though Redwood looks for a `RouteAnnouncement` component first, you don't have to have one on every page—it's more than ok for the `<h1>` to be what's announced. | ||
`RouteAnnouncement` is there for when the situation calls for a custom announcement. | ||
|
||
### `RouteAnnouncement` | ||
|
||
The way `RouteAnnouncement` works is simple: its children will be announced. | ||
Note that this can be something on the page or can be something that's visually hidden using the `visuallyHidden` prop: | ||
|
||
```jsx title="web/src/pages/HomePage/HomePage.js" | ||
import { RouteAnnouncement } from '@redwoodjs/router' | ||
|
||
const HomePage = () => { | ||
return ( | ||
// This will still be visible | ||
<RouteAnnouncement> | ||
<h1>Welcome to my site!</h1> | ||
</RouteAnnouncement> | ||
) | ||
} | ||
|
||
export default HomePage | ||
``` | ||
|
||
```jsx title="web/src/pages/AboutPage/AboutPage.js" | ||
import { RouteAnnouncement } from '@redwoodjs/router' | ||
|
||
const AboutPage = () => { | ||
return ( | ||
<h1>Welcome to my site!</h1> | ||
// This won't be visible | ||
// highlight-start | ||
<RouteAnnouncement visuallyHidden> | ||
All about me | ||
</RouteAnnouncement> | ||
// highlight-end | ||
) | ||
} | ||
|
||
export default AboutPage | ||
``` | ||
|
||
`visuallyHidden` shouldn't be the first thing you reach for—it's good to maintain parity between your site's visual and audible experiences. | ||
But it's there if you need it. | ||
|
||
## Focus | ||
|
||
On page change, Redwood Router resets focus to the top of the DOM so that users can navigate through the new page. | ||
While this is the expected behavior (and the behavior you usually want), for some pages—especially those with a lot of navigation—it can be cumbersome for users to have tab through navigation before getting to the main point. | ||
(And that goes for every page change!) | ||
|
||
Right now, there's two ways to alleviate this: with skip links or the `RouteFocus` component. | ||
|
||
### Skip Links | ||
|
||
Since the main content isn't usually the first thing on the page, it's a best practice to provide a shortcut for keyboard and screen-reader users to skip to it. | ||
Skip links do just that, and if you generate a layout using the `--skipLink` option, you'll get one with a skip link: | ||
|
||
``` | ||
yarn rw g layout main --skipLink | ||
``` | ||
|
||
```jsx title="web/src/layouts/MainLayout/MainLayout.js" | ||
import { SkipNavLink, SkipNavContent } from '@redwoodjs/router' | ||
import '@redwoodjs/router/skip-nav.css' | ||
|
||
const MainLayout = ({ children }) => { | ||
return ( | ||
<> | ||
<SkipNavLink /> | ||
<nav></nav> | ||
<SkipNavContent /> | ||
<main>{children}</main> | ||
</> | ||
) | ||
} | ||
|
||
export default MainLayout | ||
``` | ||
|
||
`SkipNavLink` renders a link that remains hidden till focused and `SkipNavContent` renders a div as the target for the link. | ||
The code for these components comes from Reach UI. For more details, see [Reach UI's docs](https://reach.tech/skip-nav/#reach-skip-nav). | ||
|
||
One thing you'll probably want to do is change the URL the skip link sends the user to when activated. | ||
You can do that by changing the `contentId` and `id` props of `SkipNavLink` and `SkipNavContent` respectively: | ||
|
||
```jsx | ||
<SkipNavLink contentId="main-content" /> | ||
{/* ... */} | ||
<SkipNavContent id="main-content" /> | ||
``` | ||
|
||
If you'd prefer to implement your own skip link, [Ben Myers' blog](https://benmyers.dev/blog/skip-links/) is a great resource, and a great place to read about accessibility in general. | ||
|
||
### `RouteFocus` | ||
|
||
Sometimes you don't want to just skip the nav, but send a user somewhere. | ||
In this situation, you of course have the foresight that that place is where the user wants to be. | ||
So please use this at your discretion—sending a user to an unexpected location can be worse than sending them back the top. | ||
|
||
Having said that, if you know that on a particular page change a user's focus is better off being directed to a particular element, the `RouteFocus` component is what you want: | ||
|
||
```jsx title="web/src/pages/ContactPage/ContactPage.js" | ||
// highlight-next-line | ||
import { RouteFocus } from '@redwoodjs/router' | ||
|
||
const ContactPage = () => ( | ||
<nav> | ||
{/* Way too much nav... */} | ||
</nav> | ||
|
||
// The contact form the user actually wants to interact with | ||
// highlight-start | ||
<RouteFocus> | ||
<TextField name="name" /> | ||
</RouteFocus> | ||
// highlight-end | ||
) | ||
|
||
export default ContactPage | ||
``` | ||
|
||
`RouteFocus` tells the router to send focus to it's child on page change. In the example above, when the user navigates to the contact page, the name text field on the form is focused—the first field of the form they're here to fill out. | ||
|
||
<div class="video-container"> | ||
<iframe src="https://www.youtube.com/embed/T1zs77LU68w?t=3240" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; modestbranding; showinfo=0; fullscreen"></iframe> | ||
</div> |
194 changes: 194 additions & 0 deletions
194
docs/versioned_docs/version-7.1/app-configuration-redwood-toml.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,194 @@ | ||
--- | ||
title: App Configuration | ||
description: Configure your app with redwood.toml | ||
--- | ||
|
||
# App Configuration: redwood.toml | ||
|
||
One of the premier places you can configure your Redwood app is `redwood.toml`. By default, `redwood.toml` lists the following configuration options: | ||
|
||
```toml title="redwood.toml" | ||
[web] | ||
title = "Redwood App" | ||
port = 8910 | ||
apiUrl = "/.redwood/functions" | ||
includeEnvironmentVariables = [] | ||
[api] | ||
port = 8911 | ||
[browser] | ||
open = true | ||
[notifications] | ||
versionUpdates = ["latest"] | ||
``` | ||
|
||
These are listed by default because they're the ones that you're most likely to configure, but there are plenty more available. | ||
|
||
You can think of `redwood.toml` as a frontend for configuring Redwood's build tools. | ||
For certain options, instead of having to configure build tools directly, there's quick access via `redwood.toml`. | ||
|
||
## [web] | ||
|
||
| Key | Description | Default | | ||
| :---------------------------- | :--------------------------------------------------------- | :---------------------- | | ||
| `title` | Title of your Redwood app | `'Redwood App'` | | ||
| `port` | Port for the web server to listen at | `8910` | | ||
| `apiUrl` | URL to your api server. This can be a relative URL in which case it acts like a proxy, or a fully-qualified URL | `'/.redwood/functions'` | | ||
| `includeEnvironmentVariables` | Environment variables made available to the web side during dev and build | `[]` | | ||
| `host` | Hostname for the web server to listen at | Defaults to `'0.0.0.0'` in production and `'::'` in development | | ||
| `apiGraphQLUrl` | URL to your GraphQL function | `'${apiUrl}/graphql'` | | ||
| `apiDbAuthUrl` | URL to your dbAuth function | `'${apiUrl}/auth'` | | ||
| `sourceMap` | Enable source maps for production builds | `false` | | ||
| `a11y` | Enable storybook `addon-a11y` and `eslint-plugin-jsx-a11y` | `true` | | ||
|
||
### Customizing the GraphQL Endpoint | ||
|
||
By default, Redwood derives the GraphQL endpoint from `apiUrl` such that it's `${apiUrl}/graphql`, (with the default `apiUrl`, `./redwood/functions/graphql`). | ||
But sometimes you want to host your api side somewhere else. | ||
There's two ways you can do this: | ||
|
||
1. Change `apiUrl`: | ||
|
||
```toml title="redwood.toml" | ||
[web] | ||
apiUrl = "https://api.coolredwoodapp.com" | ||
``` | ||
|
||
Now the GraphQL endpoint is at `https://api.coolredwoodapp.com/graphql`. | ||
|
||
2. Change `apiGraphQLUrl`: | ||
|
||
```diff title="redwood.toml" | ||
[web] | ||
apiUrl = "/.redwood/functions" | ||
+ apiGraphQLUrl = "https://api.coolredwoodapp.com/graphql" | ||
``` | ||
|
||
### Customizing the dbAuth Endpoint | ||
|
||
Similarly, if you're using dbAuth, you may decide to host it somewhere else. | ||
To do this without affecting your other endpoints, you can add `apiDbAuthUrl` to your `redwood.toml`: | ||
|
||
```diff title="redwood.toml" | ||
[web] | ||
apiUrl = "/.redwood/functions" | ||
+ apiDbAuthUrl = "https://api.coolredwoodapp.com/auth" | ||
``` | ||
|
||
:::tip | ||
|
||
If you host your web and api sides at different domains and don't use a proxy, make sure you have [CORS](./cors.md) configured. | ||
Otherwise browser security features may block client requests. | ||
|
||
::: | ||
|
||
### includeEnvironmentVariables | ||
|
||
`includeEnvironmentVariables` is the set of environment variables that should be available to your web side during dev and build. | ||
Use it to include env vars like public keys for third-party services you've defined in your `.env` file: | ||
|
||
```toml title="redwood.toml" | ||
[web] | ||
includeEnvironmentVariables = ["PUBLIC_KEY"] | ||
``` | ||
|
||
```text title=".env" | ||
PUBLIC_KEY=... | ||
``` | ||
|
||
Instead of including them in `includeEnvironmentVariables`, you can also prefix them with `REDWOOD_ENV_` (see [Environment Variables](environment-variables.md#web)). | ||
|
||
:::caution `includeEnvironmentVariables` isn't for secrets | ||
|
||
Don't make secrets available to your web side. Everything in `includeEnvironmentVariables` is included in the bundle. | ||
|
||
::: | ||
|
||
## [api] | ||
|
||
| Key | Description | Default | | ||
| :------------- | :---------------------------------- | :------------------------- | | ||
| `port` | Port for the api server to listen at | `8911` | | ||
| `host` | Hostname for the api server to listen at | Defaults to `'0.0.0.0'` in production and `'::'` in development | | ||
| `debugPort` | Port for the debugger to listen at | `18911` | | ||
| `serverConfig` | [Deprecated; use the [server file](./docker.md#using-the-server-file) instead] Path to the `server.config.js` file | `'./api/server.config.js'` | | ||
|
||
## [browser] | ||
|
||
```toml title="redwood.toml" | ||
[browser] | ||
open = true | ||
``` | ||
|
||
Setting `open` to `true` opens your browser to `http://${web.host}:${web.port}` (by default, `http://localhost:8910`) after the dev server starts. | ||
If you want your browser to stop opening when you run `yarn rw dev`, set this to `false`. | ||
(Or just remove it entirely.) | ||
|
||
There's actually a lot more you can do here. For more, see Vite's docs on [`preview.open`](https://vitejs.dev/config/preview-options.html#preview-open). | ||
|
||
## [generate] | ||
|
||
```toml title="redwood.toml" | ||
[generate] | ||
tests = true | ||
stories = true | ||
``` | ||
|
||
Many of Redwood's generators create Jest tests or Storybook stories. | ||
Understandably, this can be lot of files, and sometimes you don't want all of them, either because you don't plan on using Jest or Storybook, or are just getting started and don't want the overhead. | ||
These options allows you to disable the generation of test and story files. | ||
|
||
## [cli] | ||
|
||
```toml title="redwood.toml" | ||
[notifications] | ||
versionUpdates = ["latest"] | ||
``` | ||
|
||
There are new versions of the framework all the time—a major every couple months, a minor every week or two, and patches when appropriate. | ||
And if you're on an experimental release line, like canary, there's new versions every day, multiple times. | ||
|
||
If you'd like to get notified (at most, once a day) when there's a new version, set `versionUpdates` to include the version tags you're interested in. | ||
|
||
## Using Environment Variables in `redwood.toml` | ||
|
||
You may find yourself wanting to change keys in `redwood.toml` based on the environment you're deploying to. | ||
For example, you may want to point to a different `apiUrl` in your staging environment. | ||
|
||
You can do so with environment variables. | ||
Let's look at an example: | ||
|
||
```toml title="redwood.toml" | ||
[web] | ||
// highlight-start | ||
title = "App running on ${APP_TITLE}" | ||
port = "${PORT:8910}" | ||
apiUrl = "${API_URL:/.redwood/functions}" | ||
// highlight-end | ||
includeEnvironmentVariables = [] | ||
``` | ||
|
||
This `${<envVar>:[fallback]}` syntax does the following: | ||
|
||
- sets `title` by interpolating the env var `APP_TITLE` | ||
- sets `port` to the env var `PORT`, falling back to `8910` | ||
- sets `apiUrl` to the env var `API_URL`, falling back to `/.redwood/functions` (the default) | ||
|
||
That's pretty much all there is to it. | ||
Just remember two things: | ||
|
||
1. fallback is always a string | ||
2. these values are interpolated at build time | ||
|
||
## Running in a Container or VM | ||
|
||
To run a Redwood app in a container or VM, you'll want to set both the web and api's `host` to `0.0.0.0` to allow network connections to and from the host: | ||
|
||
```toml title="redwood.toml" | ||
[web] | ||
host = '0.0.0.0' | ||
[api] | ||
host = '0.0.0.0' | ||
``` | ||
|
||
You can also configure these values via `REDWOOD_WEB_HOST` and `REDWOOD_API_HOST`. | ||
And if you set `NODE_ENV` to production, these will be the defaults anyway. |
Oops, something went wrong.