Add Progress bar guidance - #1386
Conversation
LeaVerou
left a comment
There was a problem hiding this comment.
See comments for some more specific things. Bigger picture feedback is that the bulk of this guide is several large code examples, and it's not always clear what the agent is meant to extract from each of these.
In general, we try to avoid large code examples as they mix important bits with boilerplate, and thus can be wasteful in terms of tokens and less clear which bits are the actual guidance vs scaffolding. Instead, extract the meaningful guidance into short prose, and use (short!) code snippets for those things that are best explained in code (complementing the prose).
|
|
||
| 1. **Use the native `<progress>` element**: It provides built-in accessibility and platform-consistent behavior. | ||
| 2. **Define the state**: | ||
| * **Determinate**: Set the `max` and `value` attributes. Note that `<progress>` does **not** support a `min` attribute; the minimum is always `0`. |
There was a problem hiding this comment.
We don't need to set the max attribute if we're using the default 0-1 scale (though it may still be better for clarity).
I'm not sure we need to mention that it doesn't support a min, <progress> is old enough that this is known.
| 1. **Use the native `<progress>` element**: It provides built-in accessibility and platform-consistent behavior. | ||
| 2. **Define the state**: | ||
| * **Determinate**: Set the `max` and `value` attributes. Note that `<progress>` does **not** support a `min` attribute; the minimum is always `0`. | ||
| * **Indeterminate**: Omit the `value` attribute for tasks of unknown duration to set the progress bar into an indeterminate state. You can also use the `:indeterminate` pseudo-class to explicitly put the progress bar in an indeterminate state. |
There was a problem hiding this comment.
The :indeterminate (or any other) pseudo-class cannot affect state, it just matches only when the progress bar is already in an indeterminate state.
| 2. **Define the state**: | ||
| * **Determinate**: Set the `max` and `value` attributes. Note that `<progress>` does **not** support a `min` attribute; the minimum is always `0`. | ||
| * **Indeterminate**: Omit the `value` attribute for tasks of unknown duration to set the progress bar into an indeterminate state. You can also use the `:indeterminate` pseudo-class to explicitly put the progress bar in an indeterminate state. | ||
| * **Mandatory:** If a progress bar needs to change from determinate to indeterminate, you must remove the value attribute (`element.removeAttribute('value')`); |
| * **Determinate**: Set the `max` and `value` attributes. Note that `<progress>` does **not** support a `min` attribute; the minimum is always `0`. | ||
| * **Indeterminate**: Omit the `value` attribute for tasks of unknown duration to set the progress bar into an indeterminate state. You can also use the `:indeterminate` pseudo-class to explicitly put the progress bar in an indeterminate state. | ||
| * **Mandatory:** If a progress bar needs to change from determinate to indeterminate, you must remove the value attribute (`element.removeAttribute('value')`); | ||
| 3. **Standardize Styles**: Use `appearance: none` and `border: unset` to normalize the progress bar across different browsers before applying more customized styles. |
There was a problem hiding this comment.
Why border: unset? I'd just set whatever border we need (or border: 0 if we don't need one). Reset styles makes sense more broadly, here we're only dealing with a single element, and we know how we want to style it.
| * **Indeterminate**: Omit the `value` attribute for tasks of unknown duration to set the progress bar into an indeterminate state. You can also use the `:indeterminate` pseudo-class to explicitly put the progress bar in an indeterminate state. | ||
| * **Mandatory:** If a progress bar needs to change from determinate to indeterminate, you must remove the value attribute (`element.removeAttribute('value')`); | ||
| 3. **Standardize Styles**: Use `appearance: none` and `border: unset` to normalize the progress bar across different browsers before applying more customized styles. | ||
| 4. **Ensure Accessibility**: |
There was a problem hiding this comment.
Nice. Might want to make this a separate sub-section.
ping @knowler to review this too
| 4. **Ensure Accessibility**: | ||
| * **Associate a label**: Use `<label for="...">`, `aria-labelledby`, or `aria-label`. | ||
| * **Contextual state**: Use `aria-describedby` to reference the loading progress of a section of a page. Use `aria-busy="true"` on the container being updated. Set `aria-busy` to `"false"` when the task is complete. | ||
| * **Forced Announcements**: Set `tabindex="-1"` and call `.focus()` on the element in JavaScript when significant updates occur to force screen readers to announce the new progress. |
There was a problem hiding this comment.
Might want to clarify that "the element" here means "the <progress> element".
| <!-- Link to progress-ring and spinner for circular styling --> | ||
| # Build a Styled Progress Bar | ||
|
|
||
| The `<progress>` element is the semantic way to represent the completion progress of a task, like downloading or uploading information or completing part of a multi-step process. Using the native element helps with accessibility, as screen readers have an implicit `role="progressbar"` and can announce values. |
There was a problem hiding this comment.
Once #1371 is merged, we should add GUIDE_REF references to them here. This is for a horizontal styled progress bar, whereas these are for a circular one.
| ## Best Practices | ||
|
|
||
| * **DO** use `<progress>` for task completion. Use the `<meter>` element for scalar measurements. | ||
| * **DO** use `accent-color` for simple branding and `color-scheme` for dark mode support. |
There was a problem hiding this comment.
Note that this will stop working once we apply our own styles (with appearance: none). We can use accentcolor if supported to bring it back, but there's no web-feature for it currently. So this is more of an alternative path rather than complementary. Still worth mentioning though, but I'd move it to the intro, i.e. "if all you need is to customize the colors, you might want to just do that. This guide is for deeper customizations"
| * **Mandatory:** **DO NOT** add a fallback value inside the `<progress>` element. It is not used by assistive technology and ignored by all modern browsers. | ||
| * **DO NOT** use `<progress>` for scroll position indicators; use scroll-driven animations instead. | ||
| * **DO** respect `prefers-reduced-motion` if you apply custom animations. | ||
| * **DO** ensure proper contrast between the progress bar and track when adden custom styles. |
There was a problem hiding this comment.
Typo: "adden"
Should we move the last two bullets to the a11y section?
|
|
||
| color-scheme: light dark; | ||
| /* Set accent color for progress and other built-in UI elements */ | ||
| accent-color: var(--brand-color, AccentColor); /* Use system accent color as a fallback */ |
There was a problem hiding this comment.
Please note that AccentColor is not supported in every browser, and there is no web-feature id for it so we can't just use it.
(We can however detect support and apply it conditionally — or even extract that into a --default-accent-color variable)
Fixes #1246
Open to feedback on fleshing out the Fallbacks section. Should the fully custom approach to styling be considered a fallback? Also, feedback on some of the accessibility approaches here would be great.