Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(homepage): a11y errors #1483

Merged
merged 17 commits into from
Mar 8, 2022
Merged

fix(homepage): a11y errors #1483

merged 17 commits into from
Mar 8, 2022

Conversation

0618
Copy link
Contributor

@0618 0618 commented Mar 5, 2022

Description of changes

before after
Screen Shot 2022-03-02 at 16 18 32 ![image](https://user-images.githubusercontent.com/11983489/156858311-0bc87028-429e-4b9f-a10c-7879075b82f5.png) image
6 X Empty form label
1 X Language missing or invalid
6 X Empty button
5 X Broken ARIA reference
image image
image image

Other code change

  • fix type error
  • rename variables
  • change format. double quote to single quote.

Note

There are still some a11y alerts on the homepage and I didn't fix them because:

  • 1 X Missing first level heading: I'm not sure what to use as h1.
  • Redundant link: both links on the bottom are "https://docs.amplify.aws/console/"
  • 2 X Noscript element: those are empty tags

TODO:

Issue #, if available

https://app.asana.com/0/1201736086077838/1201759796183705/f

Description of how you validated changes

  • ran yarn test

Checklist

  • PR description included
  • yarn test passes
  • Tests are updated
  • Relevant documentation is changed or added (and PR referenced)

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@changeset-bot
Copy link

changeset-bot bot commented Mar 5, 2022

🦋 Changeset detected

Latest commit: 7a40db3

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 4 packages
Name Type
@aws-amplify/ui-react Patch
@aws-amplify/ui Patch
@aws-amplify/ui-vue Patch
@aws-amplify/ui-angular Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

}
value={frameworkInstallScript}
/>
<code className="install-code__container">
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<code> tag might be more proper here

size={size}
variation={variation}
isLoading={copied}
className={className}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add className because of the modification on docs/src/pages/index.page.tsx

@@ -113,6 +113,7 @@ export const Header = ({ platform, colorMode, setColorMode }) => {
<Button
className="docs-header-menu-button"
onClick={() => setExpanded(!expanded)}
aria-label="Docs header menu button"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we may eventually use variables for aria-label for i18n.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use ariaLabel instead

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@@ -45,7 +45,7 @@ const getCSPContent = (context: Readonly<HtmlProps>) => {
class MyDocument extends Document {
render() {
return (
<Html>
<Html lang="en-us">
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we may eventually use variables for lang

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you create a ticket for the docs for a language switcher if it doesn't already exist?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -33,7 +33,7 @@ const MenuPrimitive: Primitive<MenuProps, 'div'> = (
<DropdownMenuTrigger asChild={true}>
{trigger ?? (
<MenuButton
role="button"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure why we need to add it for a button, so removed it

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did it cause an accessibility error? I vaguely remember that without role="button" I couldn't use my own button with the Radix UI Dropdown Menu. Can you make sure removing this didn't break the Menu?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, it doesn't cause any a11y error because it is a button. It would cause error if it's a <div> or something but uses as a button

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested it and the menu button still works correctly. Proceed 👍

@@ -15,11 +15,9 @@ export const RatingIcon: React.FC<RatingIconProps> = ({
className,
}) => {
return (
<View as="span" className={classNames(`amplify-rating-icon-container`)}>
<View as="label" className={classNames(`amplify-rating-label`)}>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure what's the label for, so removed it

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'm not sure why there's a label here either. @jacoblogan Do you remember why we used a label here? We should take another look at the accessibility of this component. If we test this in VoiceOver does it read out the visually hidden text? Is there potentially a better way to label a rating component that we haven't seen yet?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the label tag was a remnant of earlier designs when this component was not read only. Looking at some other rating components out there they use a label and hidden checkbox to mark user selections. @reesscot brought up a good point that we could have customers who are targeting this label for styling and so if we don't need to remove it, then it would be best to leave it in. @0618 other than the extra clutter, was the label causing any problems? If not then I agree that we should leave it in as to not create a potentially breaking change for customers.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jacoblogan yes, "empty label" is a type of a11y error, so I removed it but kept the class selector in this PR. Is that ok?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense. If it's causing a11y error let's remove it.

@@ -19,29 +19,29 @@ export const RatingMixedIcon: React.FC<RatingMixedIconProps> = ({
}) => {
const widthPercentage = `${(value % 1) * 100}%`;
return (
<View as="span" className={classNames(`amplify-rating-icon-container`)}>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure why we need classNames here, so removed it

@@ -35,7 +35,7 @@
width: 1em;
}

.amplify-rating-label {
.amplify-rating-icon-place {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since the label tag is changed to span, rename the class

@0618 0618 temporarily deployed to ci March 5, 2022 00:55 Inactive
@0618 0618 temporarily deployed to ci March 5, 2022 00:55 Inactive
@0618 0618 temporarily deployed to ci March 5, 2022 00:55 Inactive
@0618 0618 temporarily deployed to ci March 5, 2022 00:55 Inactive
Copy link
Contributor

@reesscot reesscot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for fixing these issues. Have some change requests below:

@@ -35,7 +35,7 @@
width: 1em;
}

.amplify-rating-label {
.amplify-rating-icon-place {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we can rename classnames without a breaking change, since customers could be depending on them. These files affect customer apps and not just the docs.

Copy link
Contributor

@zchenwei zchenwei Mar 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, making changes to class name are breaking changes. I believe my demo app has use some of the classnames for custom styling

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reverted back.

/**
* Changes href of the button.
*/
href?: string;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure about this change. Most buttons are not a tags, so it seems misleading to offer an href prop for them. Where are we using a button with an href?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are using it in some places on the docs site where we do <Button as={Link} or <Button as="a". I agree it is a bit weird to have href on the button type. I wonder if there is a better way to get a link that looks like a button?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1.. Should we just use an a tag in the case?

Copy link
Contributor Author

@0618 0618 Mar 8, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point! I think we should not use buttons as anchor or vice versa. The best way is to make button-link and link-button CSS styles.

I'll remove the type change here and add a ticket for refactor.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated. Revert change.

Cut a ticket to use Link to refactor the "button-links"

@@ -7,17 +7,18 @@
box-shadow: var(--amplify-components-card-box-shadow);
display: inline-block;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Anyone know why cards are inline-block? Seems like they should just be display: block, then you wouldn't need the width:100%. @dbanksdesign thoughts?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense to me.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing this is a breaking change as well I think

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Changed to block

@@ -46,6 +46,8 @@ const TextFieldPrimitive = <Multiline extends boolean>(

const fieldId = useStableId(id);
const descriptionId = useStableId();
const labelId = useStableId();
const ariaDescribedBy = labelHidden ? labelId : descriptionId;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm guessing this is indented to fix an accessibility error where the description text is missing when labelHidden is true. I don't think it makes sense for a field to have the same text for label and description since it would be redundant for screen reader customers (might be read twice).

I'm seeing two accessibility issues here:

  1. Current we hide the entire description if labelHidden is true (meaning that we hide valuable accessibility descriptions app developers could provide for users, even if it's visually hidden). We might as well still render the description, but hide it if labelHidden is true.
  2. When app devs don't provide a description (which is most of the time), we still set ariaDescribedBy to an id that doesn't match an element in the DOM. I think this is the "5 X Broken ARIA reference" you are referring to, right?

I think the behavior should actually be changed to something like the following to fix these issues:

  1. Update FieldDescription component to not return null when the labelHidden is true (it should still have a description, just be wrapped in VisuallyHidden component). We'll need to verify this doesn't break any styling, it may affect the field when flex direction is row (thinking it may leave extra room).
  2. Change above logic to set ariaDescribedBy to undefined when description is empty (there's no reason to have an ariaDescribedBy prop id set if there's no description text. And there is usually no description text.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree @reesscot's fixes

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, agree! Thanks for the findings here and fixing this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated! Converted the change and hide FieldDescription if labelHidden is true, instead of FieldDescription returning null.

@@ -15,11 +15,9 @@ export const RatingIcon: React.FC<RatingIconProps> = ({
className,
}) => {
return (
<View as="span" className={classNames(`amplify-rating-icon-container`)}>
<View as="label" className={classNames(`amplify-rating-label`)}>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'm not sure why there's a label here either. @jacoblogan Do you remember why we used a label here? We should take another look at the accessibility of this component. If we test this in VoiceOver does it read out the visually hidden text? Is there potentially a better way to label a rating component that we haven't seen yet?

@@ -61,6 +61,7 @@ export function SignIn() {
className="amplify-flex"
disabled={isPending}
>
<legend>Sign in</legend>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this be translated? Also, this changes the design of the Authenticator. Should this be visually hidden @ErikCH @wlee221

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes this will change the design of Authenticator. It would be better if you can ping them to take a look at

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this fix an accessibility issue?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, there's an alert that fieldset missing legend

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I forgot to add a amplify-visually-hidden class...I'll do that

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, adding visually-hidden should be sufficient.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated. Added amplify-visually-hidden

@@ -113,6 +113,7 @@ export const Header = ({ platform, colorMode, setColorMode }) => {
<Button
className="docs-header-menu-button"
onClick={() => setExpanded(!expanded)}
aria-label="Docs header menu button"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use ariaLabel instead

as="label"
className={classNames(`amplify-rating-label`)}
as="span"
className="amplify-rating-icon-place"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't change this classname

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Contributor

@zchenwei zchenwei left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am just half way through it. Publishing a few comments first

@@ -25,6 +25,8 @@ import { HomePrimitivePreview } from './HomePrimitivePreview';
import { Copy } from '@/components/Copy';
import { Footer } from '@/components/Layout/Footer';

import type { SandpackThemeProp } from '@codesandbox/sandpack-react';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI, we are going to remove sandpack in favor of react-live. #1395 @dbanksdesign

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

potentially... there are some potential security issues with it since it uses eval, still up for debate

@@ -61,6 +61,7 @@ export function SignIn() {
className="amplify-flex"
disabled={isPending}
>
<legend>Sign in</legend>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes this will change the design of Authenticator. It would be better if you can ping them to take a look at

@0618 0618 temporarily deployed to ci March 8, 2022 00:48 Inactive
@0618 0618 temporarily deployed to ci March 8, 2022 00:48 Inactive
@0618 0618 temporarily deployed to ci March 8, 2022 00:48 Inactive
@0618 0618 temporarily deployed to ci March 8, 2022 00:48 Inactive
@0618 0618 temporarily deployed to ci March 8, 2022 05:13 Inactive
@0618 0618 temporarily deployed to ci March 8, 2022 18:43 Inactive
@0618 0618 temporarily deployed to ci March 8, 2022 18:43 Inactive
@0618 0618 temporarily deployed to ci March 8, 2022 19:41 Inactive
@0618 0618 temporarily deployed to ci March 8, 2022 19:41 Inactive
@0618 0618 temporarily deployed to ci March 8, 2022 19:41 Inactive
@0618 0618 temporarily deployed to ci March 8, 2022 19:41 Inactive
Copy link
Contributor

@dbanksdesign dbanksdesign left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Just one non-blocking comment

}) => (
<Text
className={classNames(ComponentClassNames.FieldDescription, {
'amplify-visually-hidden': labelHidden,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be ComponentClassNames.VisuallyHidden

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@@ -0,0 +1,8 @@
---
'@aws-amplify/ui-react': patch
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

with the new attributes being added and the structure of some of the components being updated do we want this to be a minor bump instead of a patch?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point...

I checked the semver doc

Minor version Y (x.Y.z | x > 0) MUST be incremented if new, backwards compatible functionality is introduced to the public API. It MUST be incremented if any public API functionality is marked as deprecated. It MAY be incremented if substantial new functionality or improvements are introduced within the private code. It MAY include patch level changes. Patch version MUST be reset to 0 when minor version is incremented.

Since we don't have any new features in this PR, I think it's not a MUST.

Copy link
Contributor

@jacoblogan jacoblogan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@0618 0618 temporarily deployed to ci March 8, 2022 20:23 Inactive
@0618 0618 temporarily deployed to ci March 8, 2022 20:23 Inactive
@0618 0618 temporarily deployed to ci March 8, 2022 20:23 Inactive
@0618 0618 temporarily deployed to ci March 8, 2022 20:23 Inactive
@@ -61,6 +61,9 @@ export function SignIn() {
className="amplify-flex"
disabled={isPending}
>
<VisuallyHidden>
<legend>Sign in</legend>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please split this change out into a separate PR and include the Vue and Angular SignIn screens as well. I'd like this to be reviewed in a non-docs change PR since it's more about Authenticator accessibility than strictly the docs.

@@ -50,6 +50,7 @@ const SliderFieldPrimitive: Primitive<SliderFieldProps, typeof Root> = (
const fieldId = useStableId(id);
const labelId = useStableId();
const descriptionId = useStableId();
const ariaDescribedBy = labelHidden ? labelId : descriptionId;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const ariaDescribedBy = labelHidden ? labelId : descriptionId;
const ariaDescribedBy = descriptiveText ? descriptionId : undefined; // only set `ariaDescribedBy` if descriptiveText is provided

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please also add a unit test for this.

Copy link
Contributor Author

@0618 0618 Mar 8, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Weird...I removed the code in this commit 94bb127 but it still shows up in the latest PR...

NVM, I missed SliderField.tsx, only changed TextField.tsx

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, can we please split these changes out into a separate PR, as we will need to make these changes in all the Field components that use the FieldDescription component.

@@ -98,7 +99,7 @@ const TextFieldPrimitive = <Multiline extends boolean>(
testId={testId}
{...flexContainerStyleProps}
>
<Label htmlFor={fieldId} visuallyHidden={labelHidden}>
<Label htmlFor={fieldId} visuallyHidden={labelHidden} id={labelId}>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need this? the htmlFor is handling the link to the input field.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed.

@@ -15,11 +15,9 @@ export const RatingIcon: React.FC<RatingIconProps> = ({
className,
}) => {
return (
<View as="span" className={classNames(`amplify-rating-icon-container`)}>
<View as="label" className={classNames(`amplify-rating-label`)}>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense. If it's causing a11y error let's remove it.

@@ -33,7 +33,7 @@ const MenuPrimitive: Primitive<MenuProps, 'div'> = (
<DropdownMenuTrigger asChild={true}>
{trigger ?? (
<MenuButton
role="button"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested it and the menu button still works correctly. Proceed 👍

@@ -50,6 +50,7 @@ const SliderFieldPrimitive: Primitive<SliderFieldProps, typeof Root> = (
const fieldId = useStableId(id);
const labelId = useStableId();
const descriptionId = useStableId();
const ariaDescribedBy = labelHidden ? labelId : descriptionId;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, can we please split these changes out into a separate PR, as we will need to make these changes in all the Field components that use the FieldDescription component.

@lgtm-com
Copy link

lgtm-com bot commented Mar 8, 2022

This pull request introduces 1 alert when merging dde5608 into 8ead973 - view on LGTM.com

new alerts:

  • 1 for Unused variable, import, function or class

@0618 0618 temporarily deployed to ci March 8, 2022 22:11 Inactive
@0618 0618 temporarily deployed to ci March 8, 2022 22:11 Inactive
@0618 0618 temporarily deployed to ci March 8, 2022 22:11 Inactive
@0618 0618 temporarily deployed to ci March 8, 2022 22:11 Inactive
@@ -1,4 +1,5 @@
import * as React from 'react';
import classNames from 'classnames';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove unused import

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for catching! removed

Copy link
Contributor

@reesscot reesscot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, just one small change (removing a now unused import)

@0618 0618 merged commit b97cb91 into main Mar 8, 2022
@0618 0618 deleted the fix-a11y-home branch March 8, 2022 23:09
@github-actions github-actions bot mentioned this pull request Mar 8, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants