-
Notifications
You must be signed in to change notification settings - Fork 318
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
Conversation
- remove redundant lable - change label to span - refactor and rename
🦋 Changeset detectedLatest commit: 7a40db3 The changes in this PR will be included in the next version bump. This PR includes changesets to release 4 packages
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"> |
There was a problem hiding this comment.
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} |
There was a problem hiding this comment.
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" |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use ariaLabel instead
There was a problem hiding this comment.
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"> |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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" |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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`)}> |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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`)}> |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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
There was a problem hiding this 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 { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense to me.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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:
- 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 iflabelHidden
is true. - 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:
- Update
FieldDescription
component to not return null when thelabelHidden
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). - Change above logic to set
ariaDescribedBy
to undefined when description is empty (there's no reason to have anariaDescribedBy
prop id set if there's no description text. And there is usually no description text.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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`)}> |
There was a problem hiding this comment.
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> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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" |
There was a problem hiding this comment.
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" |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
There was a problem hiding this 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'; |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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> |
There was a problem hiding this comment.
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
Co-authored-by: Scott Rees <[email protected]>
There was a problem hiding this 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, |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
…fy-visually-hidden
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
@@ -61,6 +61,9 @@ export function SignIn() { | |||
className="amplify-flex" | |||
disabled={isPending} | |||
> | |||
<VisuallyHidden> | |||
<legend>Sign in</legend> |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const ariaDescribedBy = labelHidden ? labelId : descriptionId; | |
const ariaDescribedBy = descriptiveText ? descriptionId : undefined; // only set `ariaDescribedBy` if descriptiveText is provided |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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}> |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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`)}> |
There was a problem hiding this comment.
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" |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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.
This pull request introduces 1 alert when merging dde5608 into 8ead973 - view on LGTM.com new alerts:
|
@@ -1,4 +1,5 @@ | |||
import * as React from 'react'; | |||
import classNames from 'classnames'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove unused import
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for catching! removed
There was a problem hiding this 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)
Description of changes

Other code change
Note
There are still some a11y alerts on the homepage and I didn't fix them because:
h1
.TODO:
Issue #, if available
https://app.asana.com/0/1201736086077838/1201759796183705/f
Description of how you validated changes
yarn test
Checklist
yarn test
passesBy submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.