diff --git a/src/components/ProgressBar.cy.tsx b/src/components/ProgressBar.cy.tsx
index 29455601..45cef286 100644
--- a/src/components/ProgressBar.cy.tsx
+++ b/src/components/ProgressBar.cy.tsx
@@ -90,4 +90,48 @@ describe('ProgressBar', () => {
cy.findByRole('progressbar').should('have.attr', 'style').and('include', 'scaleX(0.7)');
});
+
+ it('has ARIA attributes for accessibility', () => {
+ cy.mount(
+
+
+
+ );
+
+ cy.findByRole('progressbar')
+ .should('have.attr', 'aria-valuemin', '0')
+ .should('have.attr', 'aria-valuemax', '1')
+ .should('not.have.attr', 'aria-valuenow');
+ });
+
+ it('has aria-valuenow for controlled progress bar', () => {
+ cy.mount(
+
+
+
+ );
+
+ cy.findByRole('progressbar')
+ .should('have.attr', 'aria-valuemin', '0')
+ .should('have.attr', 'aria-valuemax', '1')
+ .should('have.attr', 'aria-valuenow', '0.5');
+ });
+
+ it('clamps aria-valuenow between 0 and 1', () => {
+ cy.mount(
+
+
+
+ );
+
+ cy.findByRole('progressbar').should('have.attr', 'aria-valuenow', '1');
+
+ cy.mount(
+
+
+
+ );
+
+ cy.findByRole('progressbar').should('have.attr', 'aria-valuenow', '0');
+ });
});
diff --git a/src/components/ProgressBar.tsx b/src/components/ProgressBar.tsx
index 6414f4e5..2c741a3c 100644
--- a/src/components/ProgressBar.tsx
+++ b/src/components/ProgressBar.tsx
@@ -112,7 +112,21 @@ export function ProgressBar({
}
};
- // TODO: add aria-valuenow, aria-valuemax, aria-valuemin
+ // ARIA attributes for progress bar accessibility
+ // Only provide aria-valuenow for controlled progress bars where we know the exact value
+ // For animated progress bars, we omit aria-valuenow as the value changes continuously
+ const ariaProps: {
+ 'aria-valuemin': number;
+ 'aria-valuemax': number;
+ 'aria-valuenow'?: number;
+ } = {
+ 'aria-valuemin': 0,
+ 'aria-valuemax': 1
+ };
+
+ if (controlledProgress && typeof progress === 'number') {
+ ariaProps['aria-valuenow'] = Math.max(0, Math.min(1, progress));
+ }
return (
@@ -125,6 +139,7 @@ export function ProgressBar({
aria-label="notification timer"
className={classNames}
style={style}
+ {...ariaProps}
{...animationEvent}
/>