Primitives
Progress
A bar that reflects the completion progress of a task.
The bar reflects data-value against data-min / data-max and moves data-state between loading, complete and indeterminate.
Pass a numeric value for a determinate bar, or null for indeterminate ("loading…"). The directive owns ARIA + state; the visual fill is yours via [forProgressIndicator].
When to choose
- Progress:
role="progressbar", for a task advancing toward completion.valueacceptsnullfor the indeterminate "working…" state, andannounceCompletionannounces the end of the task. - Meter:
role="meter", for a measurement inside a known range (disk used, battery, score), always determinate and bucketed into quality bands. Screen readers announce the two roles differently, so the choice is meaning rather than appearance: if the number is not going anywhere, it is a meter.
Anatomy
<div forProgress [value]="uploaded()" [max]="100" announceCompletion>
<div forProgressIndicator></div>
</div>
Examples
Indeterminate
A null value puts the bar in indeterminate mode, for loading states whose duration cannot be predicted. In that mode aria-valuenow is omitted and data-state reflects indeterminate.
Custom value label
getValueLabel maps value and max to a human string used for aria-valuetext, so screen readers announce '84 MB of 200 MB' instead of a bare number. The same function feeds the visible caption.
API
ForProgress
| Property | Type | Description |
|---|---|---|
value | input | Current progress in [0, max] (one-way; display-only). null = indeterminate.Default: null |
max | input | Upper bound. A non-positive max is clamped to 1 for ARIA so aria-valuemax always exceeds aria-valuemin (0).Default: 100 |
getValueLabel | input | Override for aria-valuetext (e.g. "Step 3 of 5").Default: — |
announceCompletion | input | Announce Complete (or the label) once via aria-live on the loading→complete transition.Default: — |
ariaLabel | input | Accessible name for the progressbar. Prefer a visible label referenced via aria-labelledby when one exists.Default: null |
| Data attribute | Values | |
|---|---|---|
data-state | indeterminate | loading | complete | |
data-value | clamped value (absent while indeterminate) | |
data-min | 0 | |
data-max | the max value | |
data-percentage | 0–100 (absent while indeterminate) |
ForProgressIndicator
Visual fill paired with [forProgress]. Reflects the same state so width / transform can be driven from CSS, plus the --for-progress-percentage custom property (e.g. 25%) for use directly in transform / width.
| Data attribute | Values | |
|---|---|---|
data-state | indeterminate | loading | complete | |
data-value | clamped value (absent while indeterminate) | |
data-min | 0 | |
data-max | the max value | |
data-percentage | 0–100 (absent while indeterminate) |
Accessibility
Implements the WAI-ARIA Meter pattern, using the progressbar role.
role="progressbar"is announced as "progressbar" with the current value as a percentage (or youraria-valuetextifgetValueLabelis set).- Indeterminate omits
aria-valuenow. Per spec, the absence ofaria-valuenowis what tells AT the bar is indeterminate. The directive enforces this;data-state="indeterminate"is the CSS hook. - Announce sparingly.
announceCompletionis opt-in; only enable it on flows where the user explicitly waits for completion (uploads, submissions). For background activity, the silent state change is enough. - Keep the bar focusable only if it has actions. A vanilla
[forProgress]is non-interactive and should not be in the tab order.
Styling
forty-cdk ships no styles: put your own class on each piece and key your CSS off the data-* attributes listed under API, not off the for* selectors (Styling forty-cdk explains why).
CSS custom properties
| Property | Meaning | |
|---|---|---|
--for-progress-percentage | Completion as a CSS percentage (e.g. 25%), set on [forProgressIndicator]. Absent while indeterminate (value === null). |
.indicator[data-state='loading'] {
width: var(--for-progress-percentage, 0%);
}
Wrapping in a design system
Subclass the root and re-provide FOR_PROGRESS_CONTEXT with useExisting pointing at the subclass, since Angular does not inherit a directive's providers; Wrapping non-form roots walks the pattern.