Skip to content

Commit b07ce7a

Browse files
MatanShushanclaude
andcommitted
fix(tooltip): automatically generate aria-describedby for accessibility
The tooltip component now automatically generates and sets a unique aria-describedby attribute that references the tooltip content's ID. This eliminates the need for manual configuration and ensures proper screen reader compatibility. Previously, users had to manually provide aria-describedby values, often incorrectly as shown in documentation examples. The component now follows Angular Material's approach by programmatically managing the ARIA relationship. Closes #781 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent d7ac538 commit b07ce7a

File tree

2 files changed

+21
-5
lines changed

2 files changed

+21
-5
lines changed

libs/brain/tooltip/src/lib/brn-tooltip-content.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
viewChild,
2020
ViewEncapsulation,
2121
} from '@angular/core';
22+
2223
import { Subject } from 'rxjs';
2324

2425
/**
@@ -29,6 +30,8 @@ import { Subject } from 'rxjs';
2930
selector: 'brn-tooltip-content',
3031
template: `
3132
<div
33+
[id]="tooltipId"
34+
role="tooltip"
3235
(mouseenter)="_contentHovered.set(true)"
3336
(mouseleave)="_contentHovered.set(false)"
3437
[class]="tooltipClasses()"
@@ -62,6 +65,8 @@ export class BrnTooltipContent implements OnDestroy {
6265

6366
public readonly tooltipClasses = signal('');
6467
public readonly side = signal('above');
68+
/** Unique ID for this tooltip instance */
69+
public readonly tooltipId = `brn-tooltip-${Math.random().toString(36).substring(2, 15)}`;
6570
/** Message to display in the tooltip */
6671
public content: string | TemplateRef<unknown> | null = null;
6772

libs/brain/tooltip/src/lib/brn-tooltip-trigger.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,12 @@ import {
4545
numberAttribute,
4646
type OnDestroy,
4747
type Provider,
48+
Renderer2,
4849
signal,
4950
type TemplateRef,
5051
untracked,
5152
ViewContainerRef,
5253
} from '@angular/core';
53-
import { brnDevMode } from '@spartan-ng/brain/core';
5454
import { Subject } from 'rxjs';
5555
import { take, takeUntil } from 'rxjs/operators';
5656
import { BrnTooltip } from './brn-tooltip';
@@ -122,6 +122,7 @@ export class BrnTooltipTrigger implements OnDestroy, AfterViewInit {
122122
private readonly _dir = inject(Directionality);
123123
private readonly _scrollStrategy = inject(BRN_TOOLTIP_SCROLL_STRATEGY);
124124
private readonly _document = inject(DOCUMENT);
125+
private readonly _renderer = inject(Renderer2);
125126

126127
private _portal?: ComponentPortal<BrnTooltipContent>;
127128
private _viewInitialized = false;
@@ -337,10 +338,6 @@ export class BrnTooltipTrigger implements OnDestroy, AfterViewInit {
337338
this._ngZone.run(() => this.show());
338339
}
339340
});
340-
341-
if (brnDevMode && !this.computedAriaDescribedBy()) {
342-
console.warn('BrnTooltip: "aria-describedby" attribute is required for accessibility');
343-
}
344341
}
345342

346343
/**
@@ -387,6 +384,10 @@ export class BrnTooltipTrigger implements OnDestroy, AfterViewInit {
387384
instance.side.set(this._currentPosition ?? 'above');
388385
instance.afterHidden.pipe(takeUntil(this._destroyed)).subscribe(() => this._detach());
389386
this._updateTooltipContent();
387+
388+
// Automatically set aria-describedby if not manually provided
389+
this._setAriaDescribedBy(instance.tooltipId);
390+
390391
instance.show(delay);
391392
}
392393

@@ -766,4 +767,14 @@ export class BrnTooltipTrigger implements OnDestroy, AfterViewInit {
766767
(style as any).webkitTapHighlightColor = 'transparent';
767768
}
768769
}
770+
771+
/** Sets aria-describedby attribute automatically if not manually provided */
772+
private _setAriaDescribedBy(tooltipId: string): void {
773+
const manualAriaDescribedBy = this.ariaDescribedBy();
774+
775+
// If user provided manual aria-describedby, use it; otherwise auto-set
776+
const ariaDescribedByValue = manualAriaDescribedBy || tooltipId;
777+
778+
this._renderer.setAttribute(this._elementRef.nativeElement, 'aria-describedby', ariaDescribedByValue);
779+
}
769780
}

0 commit comments

Comments
 (0)