Skip to content

Commit 1a1c880

Browse files
committed
Adjust ARIA labels, attributes.
1 parent 052fc7d commit 1a1c880

File tree

5 files changed

+133
-63
lines changed

5 files changed

+133
-63
lines changed

src/ak-spinner/ak-spinner.builder.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ import { type Spinner } from "./ak-spinner.js";
55
import { html } from "lit";
66
import { ifDefined } from "lit/directives/if-defined.js";
77

8-
export type AkSpinnerProps = Partial<Pick<Spinner, "label">> & { inline?: boolean; size?: string };
8+
export type AkSpinnerProps = Partial<Pick<Spinner, "ariaLabel">> & {
9+
inline?: boolean;
10+
size?: string;
11+
};
912

1013
/**
1114
* @summary Helper function to create a Spinner component programmatically
@@ -15,11 +18,11 @@ export type AkSpinnerProps = Partial<Pick<Spinner, "label">> & { inline?: boolea
1518
* @see {@link Spinner} - The underlying web component
1619
*/
1720
export function akSpinner(options: AkSpinnerProps = { inline: false }) {
18-
const { size, label, inline } = options;
21+
const { size, ariaLabel, inline } = options;
1922

2023
return html`<ak-spinner
2124
size=${ifDefined(size)}
22-
label=${ifDefined(label)}
25+
aria-label=${ifDefined(ariaLabel!)}
2326
?inline=${!!inline}
2427
></ak-spinner>`;
2528
}

src/ak-spinner/ak-spinner.component.ts

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import styles from "./ak-spinner.css";
44
import { msg } from "@lit/localize";
55
import { html, LitElement } from "lit";
66
import { property } from "lit/decorators.js";
7-
import { ifDefined } from "lit/directives/if-defined.js";
87

98
/**
109
* Spinner size variants. Prefer T-shirt sizes when possible.
@@ -31,8 +30,8 @@ export interface ISpinner {
3130
* The spinner also supports an `inline` boolean attribute that sets the diameter
3231
* to 1em, allowing it to scale with surrounding text.
3332
*
34-
* @csspart spinner - The SVG element for the spinner container
35-
* @csspart circle - The SVG circle element for the actual spinning part
33+
* @csspart container - The SVG element for the spinner container
34+
* @csspart shape - The SVG circle element for the actual spinning part
3635
*
3736
* @cssprop --pf-v5-c-spinner--AnimationDuration - Duration of the spinning animation
3837
* @cssprop --pf-v5-c-spinner--AnimationTimingFunction - Timing function for the animation
@@ -53,17 +52,55 @@ export interface ISpinner {
5352
export class Spinner extends LitElement {
5453
static override readonly styles = [styles, keyframes];
5554

56-
@property()
57-
public label = msg("Loading...");
55+
#internals = this.attachInternals();
56+
57+
@property({ type: String, useDefault: true })
58+
public size: SpinnerSize = "md";
59+
60+
@property({ type: Boolean, useDefault: true })
61+
public inline = false;
62+
63+
public override get role() {
64+
return this.#internals.role || "progressbar";
65+
}
66+
67+
public override set role(value: string) {
68+
this.#internals.role = value;
69+
}
70+
71+
#defaultAriaLabel = msg("Loading spinner", {
72+
id: "ak-spinner.ariaLabel",
73+
desc: "Accessible label for spinner",
74+
});
75+
76+
public override get ariaLabel(): string | null {
77+
return (
78+
this.getAttribute("aria-label") || this.#internals.ariaLabel || this.#defaultAriaLabel
79+
);
80+
}
81+
82+
public override set ariaLabel(value: string | null) {
83+
this.#internals.ariaLabel = value;
84+
}
85+
86+
public override connectedCallback() {
87+
super.connectedCallback();
88+
89+
this.role = "progressbar";
90+
91+
const initialRoleDescription = this.getAttribute("aria-label");
92+
93+
this.#internals.ariaLabel = initialRoleDescription || this.#defaultAriaLabel;
94+
}
5895

5996
public override render() {
6097
return html`<svg
61-
part="spinner"
62-
role="progressbar"
98+
part="container"
6399
viewBox="0 0 100 100"
64-
aria-label=${ifDefined(this.label)}
100+
role="img"
101+
aria-label=${msg("Spinner icon")}
65102
>
66-
<circle part="circle" cx="50" cy="50" r="45" fill="none" />
103+
<circle part="shape" cx="50" cy="50" r="45" fill="none" />
67104
</svg>`;
68105
}
69106
}

src/ak-spinner/ak-spinner.css

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
--spinner--Height: var(--spinner--diameter);
2828
}
2929

30-
[part="spinner"] {
30+
[part="container"] {
3131
width: var(--spinner--Width);
3232
height: var(--spinner--Height);
3333
animation: pf-v5-c-spinner-animation-rotate calc(var(--spinner--AnimationDuration) * 2)
@@ -54,7 +54,7 @@
5454
--spinner--diameter: var(--spinner--m-xl--diameter);
5555
}
5656

57-
[part="circle"] {
57+
[part="shape"] {
5858
width: 100%;
5959
height: 100%;
6060
stroke: var(--spinner--Color);

src/ak-spinner/ak-spinner.stories.ts

Lines changed: 61 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ const metadata: Meta<AkSpinnerProps> = {
2020
defaultValue: { summary: "md" },
2121
},
2222
},
23-
label: {
23+
ariaLabel: {
2424
control: "text",
2525
description: "Accessible label for screen readers",
2626
table: {
@@ -39,7 +39,6 @@ const metadata: Meta<AkSpinnerProps> = {
3939
},
4040
args: {
4141
size: "md",
42-
label: "Loading...",
4342
inline: false,
4443
},
4544
parameters: {
@@ -115,7 +114,11 @@ export const CustomColor = () => html`
115114
`;
116115

117116
export const AllSizes = () => html`
118-
<div style="display: flex; flex-direction: column; gap: 16px;">
117+
<main
118+
tabindex="0"
119+
aria-label="All sizes"
120+
style="display: flex; flex-direction: column; gap: 16px;"
121+
>
119122
<div>
120123
<h3>Small (sm)</h3>
121124
<ak-spinner size="sm"></ak-spinner>
@@ -132,7 +135,7 @@ export const AllSizes = () => html`
132135
<h3>Extra Large (xl)</h3>
133136
<ak-spinner size="xl"></ak-spinner>
134137
</div>
135-
</div>
138+
</main>
136139
`;
137140

138141
export const InlineSpinners: Story = {
@@ -144,7 +147,11 @@ export const InlineSpinners: Story = {
144147
},
145148
},
146149
render: () => html`
147-
<div style="display: flex; flex-direction: column; gap: 1rem;">
150+
<main
151+
tabindex="0"
152+
aria-label="Inline spinners"
153+
style="display: flex; flex-direction: column; gap: 1rem;"
154+
>
148155
<div style="font-size: 14px;">
149156
Small text with <ak-spinner inline label="Loading small"></ak-spinner> inline
150157
spinner
@@ -161,7 +168,7 @@ export const InlineSpinners: Story = {
161168
Extra large text with
162169
<ak-spinner inline label="Loading extra large"></ak-spinner> inline spinner
163170
</div>
164-
</div>
171+
</main>
165172
`,
166173
};
167174

@@ -174,7 +181,11 @@ export const CustomAnimations: Story = {
174181
},
175182
},
176183
render: () => html`
177-
<div style="display: flex; gap: 2rem; align-items: center; flex-wrap: wrap;">
184+
<main
185+
tabindex="0"
186+
aria-label="Custom animations"
187+
style="display: flex; gap: 2rem; align-items: center; flex-wrap: wrap;"
188+
>
178189
<div style="text-align: center;">
179190
<ak-spinner
180191
size="lg"
@@ -207,7 +218,7 @@ export const CustomAnimations: Story = {
207218
></ak-spinner>
208219
<div style="margin-top: 0.5rem; font-size: 0.875rem;">Thick stroke</div>
209220
</div>
210-
</div>
221+
</main>
211222
`,
212223
};
213224

@@ -220,24 +231,40 @@ export const LoadingStates: Story = {
220231
},
221232
},
222233
render: () => html`
223-
<div style="display: flex; flex-direction: column; gap: 2rem;">
234+
<main
235+
tabindex="0"
236+
aria-label="Loading states"
237+
style="display: flex; flex-direction: column; gap: 2rem;"
238+
>
224239
<!-- Page loading -->
225-
<div
240+
<section
241+
id="demo-region-1"
226242
style="text-align: center; padding: 3rem; border: 1px dashed #ccc; border-radius: 8px;"
243+
aria-busy="true"
244+
aria-live="polite"
245+
aria-label="Demo Region 1"
246+
aria-describedby="demo-region-1-description"
227247
>
228-
<ak-spinner size="xl" label="Loading page content"></ak-spinner>
229-
<div style="margin-top: 1rem; color: #666;">Loading page content...</div>
230-
</div>
248+
<ak-spinner size="xl"></ak-spinner>
249+
<div id="demo-region-1-description" style="margin-top: 1rem; color: #666;">
250+
Loading page content...
251+
</div>
252+
</section>
231253
232254
<!-- Card loading -->
233-
<div
255+
<section
234256
style="padding: 1.5rem; border: 1px solid #e5e7eb; border-radius: 8px; background: #f9fafb;"
257+
id="demo-region-1"
258+
aria-busy="true"
259+
aria-live="polite"
260+
aria-label="Demo Region 2"
261+
aria-describedby="demo-region-2-description"
235262
>
236263
<div style="display: flex; align-items: center; gap: 1rem;">
237-
<ak-spinner size="md" label="Loading card data"></ak-spinner>
238-
<div>Loading card data...</div>
264+
<ak-spinner size="md"></ak-spinner>
265+
<div id="demo-region-2-description">Loading card data...</div>
239266
</div>
240-
</div>
267+
</section>
241268
242269
<!-- List item loading -->
243270
<div style="display: flex; flex-direction: column; gap: 0.5rem;">
@@ -254,7 +281,7 @@ export const LoadingStates: Story = {
254281
<div>Loading list item...</div>
255282
</div>
256283
</div>
257-
</div>
284+
</main>
258285
`,
259286
};
260287

@@ -267,7 +294,11 @@ export const UsingBuilderFunction: Story = {
267294
},
268295
},
269296
render: () => html`
270-
<div style="display: flex; flex-direction: column; gap: 1.5rem;">
297+
<main
298+
tabindex="0"
299+
aria-label="Using Builder Function"
300+
style="display: flex; flex-direction: column; gap: 1.5rem;"
301+
>
271302
<div>
272303
<h4>Basic spinner with builder:</h4>
273304
<div style="display: flex; align-items: center; gap: 1rem;">
@@ -279,19 +310,19 @@ export const UsingBuilderFunction: Story = {
279310
<div>
280311
<h4>Custom size and label:</h4>
281312
<div style="display: flex; align-items: center; gap: 1rem;">
282-
${akSpinner({ size: "lg", label: "Processing data..." })}
313+
${akSpinner({ size: "lg", ariaLabel: "Processing data..." })}
283314
<span>Large spinner with custom label</span>
284315
</div>
285316
</div>
286317
287318
<div>
288319
<h4>Inline spinner:</h4>
289320
<p>
290-
Processing your request ${akSpinner({ inline: true, label: "Processing" })}
321+
Processing your request ${akSpinner({ inline: true, ariaLabel: "Processing" })}
291322
please wait...
292323
</p>
293324
</div>
294-
</div>
325+
</main>
295326
`,
296327
};
297328

@@ -304,21 +335,20 @@ export const AccessibilityExample: Story = {
304335
},
305336
},
306337
render: () => html`
307-
<div style="display: flex; flex-direction: column; gap: 2rem;">
308-
<div>
338+
<main tabindex="0" aria-label="All sizes">
309339
<h4>Spinners with descriptive labels:</h4>
310340
<div style="display: flex; gap: 2rem; flex-wrap: wrap;">
311341
<div style="text-align: center;">
312-
<ak-spinner size="md" label="Loading user profile data"></ak-spinner>
313-
<div style="margin-top: 0.5rem; font-size: 0.875rem;">User Profile</div>
342+
<ak-spinner id="demo-accessibility-spinner-1" size="md" aria-label="Loading user profile data"></ak-spinner>
343+
<div aria-busy="true" aria-describedby="demo-accessibility-spinner-1" style="margin-top: 0.5rem; font-size: 0.875rem;">User Profile</div>
314344
</div>
315345
<div style="text-align: center;">
316-
<ak-spinner size="md" label="Uploading document, please wait"></ak-spinner>
317-
<div style="margin-top: 0.5rem; font-size: 0.875rem;">File Upload</div>
346+
<ak-spinner id="demo-accessibility-spinner-2" size="md" aria-label="Uploading document, please wait"></ak-spinner>
347+
<div aria-busy="true" aria-describedby="demo-accessibility-spinner-2" style="margin-top: 0.5rem; font-size: 0.875rem;">File Upload</div>
318348
</div>
319349
<div style="text-align: center;">
320-
<ak-spinner size="md" label="Saving changes to database"></ak-spinner>
321-
<div style="margin-top: 0.5rem; font-size: 0.875rem;">Save Operation</div>
350+
<ak-spinner id="demo-accessibility-spinner-3" size="md" aria-label="Saving changes to database"></ak-spinner>
351+
<div aria-busy="true" aria-describedby="demo-accessibility-spinner-3" style="margin-top: 0.5rem; font-size: 0.875rem;">Save Operation</div>
322352
</div>
323353
</div>
324354
</div>
@@ -335,6 +365,6 @@ export const AccessibilityExample: Story = {
335365
<li>Consider pairing with visually hidden text for additional context</li>
336366
</ul>
337367
</div>
338-
</div>
368+
</main>
339369
`,
340370
};

0 commit comments

Comments
 (0)