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

Add a11y properties to icon component #646

Merged
merged 17 commits into from
Jan 12, 2024

Conversation

brentswisher
Copy link
Contributor

@brentswisher brentswisher commented Nov 13, 2023

This change: (check at least one)

  • Adds a new feature
  • Fixes a bug
  • Improves maintainability
  • Improves documentation
  • Is a release activity

Is this a breaking change? (check one)

  • Yes
  • No

Is the: (complete all)

  • Title of this pull request clear, concise, and indicative of the issue number it addresses, if any?
  • Test suite(s) passing?
  • Code coverage maximal?
  • Changeset added?
  • Component status page up to date?

What does this change address?
Adds both a a11y-title and and a11y-hidden property to the icon component. This is a replacement for the previous description which is now marked as deprecated and will be removed in the next major release (#467). It also replaces the use of aria-label with setting a title in the svg of the icon which after talking to @sirrah-tam is the preferred method.

How does this change work?
Adds the a11y-title and a11y-hidden property and maps them to a title and aria-hidden attribute on the icon svg. The previous functionality of aria-hidden automatically being set if no description being provided is preserved here to enable adding the new attributes without a breaking change.

Additional context
See #467 to track the breaking change that follows this in the next major release.

Added both a11y-label and a11y-hidden properties to the icon component.
Also marks the previous icon property description as deprecated. This is
in preparation of the v14 release which will remove the description
propererty and add an error if either a11y-label or a11y-hidden
are not provided.
Copy link

changeset-bot bot commented Nov 13, 2023

🦋 Changeset detected

Latest commit: 3d34847

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

This PR includes changesets to release 1 package
Name Type
@ithaka/pharos Minor

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

Copy link
Contributor

github-actions bot commented Nov 13, 2023

size-limit report 📦

Path Size
packages/pharos/lib/index.js 53.26 KB (+0.29% 🔺)

@brentswisher brentswisher force-pushed the feature/add-icon-a11y-properties branch from 20b1eb7 to 4dd8bfb Compare November 27, 2023 22:00
The most accesssible way to label an svg icon is to use a title
element in the svg itself. This is better than using aria-label
which should be reserved for cases where an accessible name can
not be derived. See this page for more details:
https://developer.mozilla.org/en-US/docs/Web/SVG/Element/title
@brentswisher brentswisher force-pushed the feature/add-icon-a11y-properties branch from 4dd8bfb to bf9bb0c Compare November 27, 2023 22:28
Because aria-hidden is a boolean attribute, it should be set to true
when the icon is hidden, and not exist at all when it is visible. Lit's
nothing attribute enables this behavior:
https://lit.dev/docs/templates/expressions/#removing-attribute This
also updates the logic to add aria-hidden if there is not description
or a11y-title to preserve the current behavior until description is
removed.
If the title is not provided, fall back to the description. Previously,
it only fell back if the a11y-title was not provided, not an empty
string. This made the storybook stories fail.
@brentswisher brentswisher marked this pull request as ready for review November 28, 2023 22:26
@brentswisher brentswisher requested a review from a team as a code owner November 28, 2023 22:26
@brentswisher brentswisher requested review from eslawski, jialin-he, mtorres3, sirrah-tam and daneah and removed request for a team and eslawski November 28, 2023 22:26
packages/pharos/src/components/icon/pharos-icon.ts Outdated Show resolved Hide resolved
return html`
<svg
xmlns="http://www.w3.org/2000/svg"
version="1.1"
viewBox="0 0 ${size} ${size}"
class="icon"
role="img"
aria-hidden=${this.description === ''}
aria-label=${this.description || ''}
aria-hidden=${hideIcon || nothing}
Copy link
Member

Choose a reason for hiding this comment

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

I think there's better shorthand for this unless I'm missing something:

Suggested change
aria-hidden=${hideIcon || nothing}
?aria-hidden=${hideIcon}

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 think this results in the attribute being present, but empty aria-hidden="" which is the same as aria-hidden="true" to a browser. https://lit.dev/docs/templates/expressions/#removing-attribute

I'll test that out though, would be nice if we didn't need to import nothing as it's not particularly clear what it does by its name.

Copy link
Member

Choose a reason for hiding this comment

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

Here are the relevant portion of the docs that I think mean the attribute will indeed be removed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Okay, I think I might see what is happening. When using ?aria-hidden=${hideIcon} I get a bunch of failing unit tests from other components. That matched what I saw before when trying it. I had thought it was because it was not removing the aria-hidden attribute when hideIcon was false. The tests are actually failing because when hideIcon is set to true, it is rendering the svg as aria-hidden="" which the axe plugin we use doesn't seem to recognize as being hidden.

With the nothing syntax aria-hidden is either not present or set explicitly to aria-hidden="true" which axe does seem to recognize. I'll see if there is a bug report for axe about that or if it is expected and we can go from there, it does seem to be rendering correctly in the browser with either syntax.

Copy link
Contributor

Choose a reason for hiding this comment

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

A lil bit more research, I think that aria-hidden needs to have an explicit value set per the spec because it's a state and not a boolean attribute. So would need to be "true", "false", or undefined.

Reference: https://stackoverflow.com/questions/55061083/does-aria-hidden-need-a-value

Copy link
Member

Choose a reason for hiding this comment

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

This makes sense. @brentswisher @sirrah-tam do we want to make a11yHidden align with the aria-hidden spec for convergence/consistency's sake, and keep the rendering line here as-is?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Okay, I have updated this so that a11yHidden is a string attribute which allows the same arguments as aria-hidden, as defined by the new shared type.

The question now is, if someone sets a11y-hidden="false" Do we:

  1. Render aria-hidden="false" on the svg
  2. Not render aria-hidden at all? (Which this code does now)

@sirrah-tam is wondering if it will be confusing to consumers if it isn't rendered at all. I am on the fence, the WCAG spec specifically calls out some browser issues with setting it to "false" even thought that is the official API. Not sure which is more helpful to Pharos consumers, guardrails or fully matching the official aria API in the matching a11y attribute.

Curious your thoughts @daneah ?

Copy link
Member

Choose a reason for hiding this comment

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

The overlap of meaning of JavaScript boolean true|false values and undefined with the string values "true"|"false"|"undefined" makes this challenging. I note that in Button's pressed state we handle everything explicitly as a string, so that they're always truthy ("true"|"false"|"undefined" are all truthy, as strings). I feel like that is closest to authoring raw HTML as well; if what you type is what ends up in the browser, most attributes then become either boolean (that is, present or not) or string.

In Button we codified this even more directly for its pressed state; note that its type is 'true' | 'false' | 'undefined' | undefined (different than what you have). I feel that the spec is really forcing aria-hidden to be a thing whose value is really a string, which gets coerced to a boolean or to a "idk, let the browser decide" behavior. I would vote we stick to that because I'd rather people maintain a mental model of the web than add a layer of indirection and muddy understanding when using these. My two cents.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Pharos Icon a11y excalidraw

I have worked with Mat a bit to come up with this and believe the most recent code matches these expectations. I can also reuse the ariaHiddenState from the button PR once that gets merged into develop for consistency.

Copy link
Member

Choose a reason for hiding this comment

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

This is very helpful, thank you for documenting!

Co-authored-by: Dane Hillard <github@danehillard.com>
brentswisher and others added 2 commits November 29, 2023 11:38
Co-authored-by: Mat Harris <hurris88@gmail.com>
Co-authored-by: Mat Harris <hurris88@gmail.com>
As we add a11y-* attributes as part of our accessibility work, we need
a global place to store shared types that multiple components can use.
Because the end result of adding this attribute is setting a string
value, it makes sense to have the prop be a string. The end result is
that aria-hidden is either set to the string true, or not set at all.
@brentswisher brentswisher merged commit 05eca95 into develop Jan 12, 2024
11 checks passed
@brentswisher brentswisher deleted the feature/add-icon-a11y-properties branch January 12, 2024 14:00
@github-actions github-actions bot mentioned this pull request Jan 19, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants