Skip to content

Hide bit-icon component from screen readers by default #14295

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

Merged
merged 9 commits into from
Apr 18, 2025
3 changes: 3 additions & 0 deletions apps/browser/src/_locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
"appName": {
"message": "Bitwarden"
},
"appLogoLabel": {
"message": "Bitwarden logo"
},
"extName": {
"message": "Bitwarden Password Manager",
"description": "Extension name, MUST be less than 40 characters (Safari restriction)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@
[showBackButton]="showBackButton"
[pageTitle]="''"
>
<bit-icon *ngIf="showLogo" class="tw-inline-flex" [icon]="logo"></bit-icon>
<bit-icon
*ngIf="showLogo"
class="tw-inline-flex"
[icon]="logo"
[ariaLabel]="'appLogoLabel' | i18n"
></bit-icon>

<ng-container slot="end">
<app-pop-out></app-pop-out>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
} from "@bitwarden/auth/angular";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { Icon, IconModule, Translation } from "@bitwarden/components";
import { I18nPipe } from "@bitwarden/ui-common";

Check warning on line 15 in apps/browser/src/auth/popup/extension-anon-layout-wrapper/extension-anon-layout-wrapper.component.ts

View check run for this annotation

Codecov / codecov/patch

apps/browser/src/auth/popup/extension-anon-layout-wrapper/extension-anon-layout-wrapper.component.ts#L15

Added line #L15 was not covered by tests

import { PopOutComponent } from "../../../platform/popup/components/pop-out.component";
import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component";
Expand All @@ -36,6 +37,7 @@
AnonLayoutComponent,
CommonModule,
CurrentAccountComponent,
I18nPipe,
IconModule,
PopOutComponent,
PopupPageComponent,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<div class="tw-mt-10 tw-flex tw-justify-center" *ngIf="loading">
<div>
<bit-icon class="tw-w-72 tw-block tw-mb-4" [icon]="logo"></bit-icon>
<bit-icon class="tw-w-72 tw-block tw-mb-4" [icon]="logo" [ariaLabel]="'appLogoLabel' | i18n">
</bit-icon>
<div class="tw-flex tw-justify-center">
<i
class="bwi bwi-spinner bwi-spin bwi-2x tw-text-muted"
Expand Down
3 changes: 3 additions & 0 deletions apps/web/src/locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
"allApplications": {
"message": "All applications"
},
"appLogoLabel": {
"message": "Bitwarden logo"
},
"criticalApplications": {
"message": "Critical applications"
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
<div class="tw-mt-5 tw-flex tw-justify-center" *ngIf="loading">
<div>
<bit-icon class="tw-w-72 tw-block tw-mb-4" [icon]="logo"></bit-icon>
<bit-icon
class="tw-w-72 tw-block tw-mb-4"
[icon]="logo"
[ariaLabel]="'appLogoLabel' | i18n"
></bit-icon>
<p class="tw-text-center">
<i
class="bwi bwi-spinner bwi-spin bwi-2x tw-text-muted"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
<div class="tw-mt-12 tw-flex tw-justify-center" *ngIf="loading">
<div>
<bit-icon class="tw-w-72 tw-block tw-mb-4" [icon]="logo"></bit-icon>
<bit-icon
class="tw-w-72 tw-block tw-mb-4"
[icon]="logo"
[ariaLabel]="'appLogoLabel' | i18n"
></bit-icon>
<p class="tw-text-center">
<i
class="bwi bwi-spinner bwi-spin bwi-2x tw-text-muted"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
<div class="tw-mt-12 tw-flex tw-justify-center" *ngIf="loading">
<div>
<bit-icon class="tw-w-72 tw-block tw-mb-4" [icon]="bitwardenLogo"></bit-icon>
<bit-icon
class="tw-w-72 tw-block tw-mb-4"
[icon]="bitwardenLogo"
[ariaLabel]="'appLogoLabel' | i18n"
></bit-icon>
<p class="tw-text-center">
<i
class="bwi bwi-spinner bwi-spin bwi-2x tw-text-muted"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
[routerLink]="['/']"
class="tw-w-[128px] tw-block tw-mb-12 [&>*]:tw-align-top"
>
<bit-icon [icon]="logo"></bit-icon>
<bit-icon [icon]="logo" [ariaLabel]="'appLogoLabel' | i18n"></bit-icon>
</a>

<div
Expand Down
14 changes: 9 additions & 5 deletions libs/components/src/icon/icon.component.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,31 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { Component, HostBinding, Input } from "@angular/core";
import { Component, Input } from "@angular/core";
import { DomSanitizer, SafeHtml } from "@angular/platform-browser";

import { Icon, isIcon } from "./icon";

@Component({
selector: "bit-icon",
host: {
"[attr.aria-hidden]": "!ariaLabel",
"[attr.aria-label]": "ariaLabel",
"[innerHtml]": "innerHtml",
},
template: ``,
standalone: true,
})
export class BitIconComponent {
innerHtml: SafeHtml | null = null;

@Input() set icon(icon: Icon) {
if (!isIcon(icon)) {
this.innerHtml = "";
return;
}

const svg = icon.svg;
this.innerHtml = this.domSanitizer.bypassSecurityTrustHtml(svg);
}

@HostBinding() innerHtml: SafeHtml;
@Input() ariaLabel: string | undefined = undefined;

constructor(private domSanitizer: DomSanitizer) {}
}
10 changes: 10 additions & 0 deletions libs/components/src/icon/icon.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,19 @@ import * as stories from "./icon.stories";
```

- **HTML:**

> NOTE: SVG icons are treated as decorative by default and will be `aria-hidden` unless an
> `ariaLabel` is explicitly provided to the `<bit-icon>` component

```html
<bit-icon [icon]="Icons.ExampleIcon"></bit-icon>
```

With `ariaLabel`

```html
<bit-icon [icon]="Icons.ExampleIcon" [ariaLabel]="Your custom label text here"></bit-icon>
```

8. **Ensure your SVG renders properly** according to Figma in both light and dark modes on a client
which supports multiple style modes.
4 changes: 4 additions & 0 deletions libs/components/src/icon/icon.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,9 @@ export const Default: Story = {
mapping: GenericIcons,
control: { type: "select" },
},
ariaLabel: {
control: "text",
description: "the text used by a screen reader to describe the icon",
},
},
};
Loading