From 184a1267cc9aa7b452ed36f14a9f750c3327bd1f Mon Sep 17 00:00:00 2001 From: Maria Hutt Date: Fri, 17 Apr 2026 11:22:41 -0700 Subject: [PATCH 1/6] feat(progress-bar): add recipe and tokens --- .../progress-bar/{progress-bar.common.scss => progress-bar.scss} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename core/src/components/progress-bar/{progress-bar.common.scss => progress-bar.scss} (100%) diff --git a/core/src/components/progress-bar/progress-bar.common.scss b/core/src/components/progress-bar/progress-bar.scss similarity index 100% rename from core/src/components/progress-bar/progress-bar.common.scss rename to core/src/components/progress-bar/progress-bar.scss From cf033e11c803a9cd1c27311977aafe8b776f04cc Mon Sep 17 00:00:00 2001 From: Maria Hutt Date: Fri, 17 Apr 2026 11:24:39 -0700 Subject: [PATCH 2/6] feat(progress-bar): add recipe and tokens pt2 --- core/src/components.d.ts | 26 +--- .../progress-bar/progress-bar.interfaces.ts | 108 +++++++++++++++++ .../progress-bar/progress-bar.ionic.scss | 6 +- .../progress-bar/progress-bar.native.scss | 6 +- .../components/progress-bar/progress-bar.scss | 111 ++++++++++++++++-- .../components/progress-bar/progress-bar.tsx | 40 ++----- .../progress-bar/test/basic/index.html | 4 +- core/src/themes/ionic/default.tokens.ts | 82 +++++++++++++ core/src/themes/ios/default.tokens.ts | 106 +++++++++++++++++ core/src/themes/md/default.tokens.ts | 96 +++++++++++++++ core/src/themes/themes.interfaces.ts | 3 + packages/angular/src/directives/proxies.ts | 4 +- .../standalone/src/directives/proxies.ts | 4 +- 13 files changed, 525 insertions(+), 71 deletions(-) create mode 100644 core/src/components/progress-bar/progress-bar.interfaces.ts diff --git a/core/src/components.d.ts b/core/src/components.d.ts index 5d1eff6638c..f7ac316bc1f 100644 --- a/core/src/components.d.ts +++ b/core/src/components.d.ts @@ -28,6 +28,7 @@ import { PickerChangeEventDetail } from "./components/picker/picker-interfaces"; import { PickerColumnChangeEventDetail, PickerColumnValue } from "./components/picker-column/picker-column-interfaces"; import { PickerButton, PickerColumn } from "./components/picker-legacy/picker-interface"; import { PopoverSize, PositionAlign, PositionReference, PositionSide, TriggerAction } from "./components/popover/popover-interface"; +import { IonProgressBarShape } from "./components/progress-bar/progress-bar.interfaces"; import { RadioGroupChangeEventDetail, RadioGroupCompareFn } from "./components/radio-group/radio-group-interface"; import { PinFormatter, RangeChangeEventDetail, RangeKnobMoveEndEventDetail, RangeKnobMoveStartEventDetail, RangeValue } from "./components/range/range-interface"; import { RefresherEventDetail } from "./components/refresher/refresher-interface"; @@ -68,6 +69,7 @@ export { PickerChangeEventDetail } from "./components/picker/picker-interfaces"; export { PickerColumnChangeEventDetail, PickerColumnValue } from "./components/picker-column/picker-column-interfaces"; export { PickerButton, PickerColumn } from "./components/picker-legacy/picker-interface"; export { PopoverSize, PositionAlign, PositionReference, PositionSide, TriggerAction } from "./components/popover/popover-interface"; +export { IonProgressBarShape } from "./components/progress-bar/progress-bar.interfaces"; export { RadioGroupChangeEventDetail, RadioGroupCompareFn } from "./components/radio-group/radio-group-interface"; export { PinFormatter, RangeChangeEventDetail, RangeKnobMoveEndEventDetail, RangeKnobMoveStartEventDetail, RangeValue } from "./components/range/range-interface"; export { RefresherEventDetail } from "./components/refresher/refresher-interface"; @@ -2987,23 +2989,15 @@ export namespace Components { * The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics). */ "color"?: Color; - /** - * The mode determines the platform behaviors of the component. - */ - "mode"?: "ios" | "md"; /** * If true, reverse the progress bar direction. * @default false */ "reversed": boolean; /** - * Set to `"round"` for a progress bar with rounded corners, or `"rectangular"` for a progress bar without rounded corners. Defaults to `"round"` for the `ionic` theme, undefined for all other themes. + * Set to `"round"` for a progress bar with rounded corners, or `"rectangular"` for a progress bar without rounded corners. Defaults to `"round"` if both the shape property and theme config are unset. */ - "shape"?: 'round' | 'rectangular'; - /** - * The theme determines the visual appearance of the component. - */ - "theme"?: "ios" | "md" | "ionic"; + "shape"?: IonProgressBarShape; /** * The state of the progress bar, based on if the time the process takes is known or not. Default options are: `"determinate"` (no animation), `"indeterminate"` (animate from left to right). * @default 'determinate' @@ -8911,23 +8905,15 @@ declare namespace LocalJSX { * The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics). */ "color"?: Color; - /** - * The mode determines the platform behaviors of the component. - */ - "mode"?: "ios" | "md"; /** * If true, reverse the progress bar direction. * @default false */ "reversed"?: boolean; /** - * Set to `"round"` for a progress bar with rounded corners, or `"rectangular"` for a progress bar without rounded corners. Defaults to `"round"` for the `ionic` theme, undefined for all other themes. + * Set to `"round"` for a progress bar with rounded corners, or `"rectangular"` for a progress bar without rounded corners. Defaults to `"round"` if both the shape property and theme config are unset. */ - "shape"?: 'round' | 'rectangular'; - /** - * The theme determines the visual appearance of the component. - */ - "theme"?: "ios" | "md" | "ionic"; + "shape"?: IonProgressBarShape; /** * The state of the progress bar, based on if the time the process takes is known or not. Default options are: `"determinate"` (no animation), `"indeterminate"` (animate from left to right). * @default 'determinate' diff --git a/core/src/components/progress-bar/progress-bar.interfaces.ts b/core/src/components/progress-bar/progress-bar.interfaces.ts new file mode 100644 index 00000000000..ecea5560d6f --- /dev/null +++ b/core/src/components/progress-bar/progress-bar.interfaces.ts @@ -0,0 +1,108 @@ +export type IonProgressBarRecipe = { + height?: string | number; + + // Indeterminate type (animated, no specific value) + indeterminate?: { + progress?: { + default?: { + background?: string; + }; + semantic?: { + // Default state of the semantic color (not hover, focus, activated, etc.) + default?: { + background?: string; + }; + }; + }; + buffer?: { + bar?: { + default?: { + background?: string; + }; + semantic?: { + default?: { + background?: string; + }; + }; + solid?: { + default?: { + background?: string; + }; + }; + }; + }; + }; + + // Determinate type (has a specific value, optional buffer) + // it has three parts: + // - progress: the progress bar which represents the current value + // - buffer-bar: the buffer bar which represents the buffer value + // - buffer-circles: the buffer circles which are displayed when there is a buffer value but no progress value (value = 0, buffer > 0), this is optional + determinate?: { + progress?: { + default?: { + background?: string; + }; + semantic?: { + // Default state of the semantic color (not hover, focus, activated, etc.) + default?: { + background?: string; + }; + }; + }; + buffer?: { + bar?: { + default?: { + background?: string; + }; + semantic?: { + default?: { + background?: string; + }; + }; + // When progress bar is solid (buffer = 1) + solid?: { + default?: { + background?: string; + }; + }; + }; + circles?: { + default?: { + background?: string; + }; + semantic?: { + default?: { + background?: string; + }; + }; + // When progress bar is solid (buffer = 1) + solid?: { + default?: { + background?: string; + }; + }; + }; + }; + }; + + // Shape variants + shape?: { + round?: { + border?: { + radius?: string | number; + }; + }; + rectangular?: { + border?: { + radius?: string | number; + }; + }; + }; +}; + +export type IonProgressBarConfig = { + shape?: IonProgressBarShape; +}; + +export type IonProgressBarShape = 'round' | 'rectangular'; diff --git a/core/src/components/progress-bar/progress-bar.ionic.scss b/core/src/components/progress-bar/progress-bar.ionic.scss index 3fad235b07f..877322916a2 100644 --- a/core/src/components/progress-bar/progress-bar.ionic.scss +++ b/core/src/components/progress-bar/progress-bar.ionic.scss @@ -1,12 +1,12 @@ @use "../../themes/ionic/ionic.globals.scss" as globals; -@use "./progress-bar.common.scss"; +@use "./progress-bar.scss"; // Ionic Progress bar // -------------------------------------------------- :host { - --background: #{globals.$ion-bg-neutral-subtle-default}; - --progress-background: #{globals.ion-color(primary, base)}; + --background: #{globals.$ion-bg-neutral-subtle-default}; // for determinate - buffer bar and circles + --progress-background: #{globals.ion-color(primary, base)}; // both determinate and indeterminate height: globals.$ion-scale-100; } diff --git a/core/src/components/progress-bar/progress-bar.native.scss b/core/src/components/progress-bar/progress-bar.native.scss index 5bc8e728b77..0bbb0d9d9e6 100644 --- a/core/src/components/progress-bar/progress-bar.native.scss +++ b/core/src/components/progress-bar/progress-bar.native.scss @@ -1,5 +1,5 @@ @import "../../themes/native/native.globals"; -@import "./progress-bar.common.scss"; +@import "./progress-bar.scss"; // Host has no background by default - this will be added to the progress-buffer-bar :host { @@ -7,8 +7,8 @@ * @prop --background: Background of the progress track, or the buffer bar if `buffer` is set * @prop --progress-background: Background of the progress bar representing the current value */ - --background: #{ion-color(primary, base, 0.3)}; - --progress-background: #{ion-color(primary, base)}; + --background: #{ion-color(primary, base, 0.3)}; // for determinate - buffer bar and circles + --progress-background: #{ion-color(primary, base)}; // both determinate and indeterminate } // Progress Bar: Color diff --git a/core/src/components/progress-bar/progress-bar.scss b/core/src/components/progress-bar/progress-bar.scss index 95541ae1449..6534f08eedc 100644 --- a/core/src/components/progress-bar/progress-bar.scss +++ b/core/src/components/progress-bar/progress-bar.scss @@ -1,15 +1,20 @@ -@import "../../themes/mixins"; +@use "../../themes/mixins" as mixins; -// Progress bar +// Progress Bar // ------------------------------------------------------------------------ -// Host has no background by default - this will be added to the progress-buffer-bar :host { + /** + * @prop --ion-progress-bar-determinate-buffer-bar-default-background: Background of the progress track, or the buffer bar if `buffer` is set + * @prop --ion-progress-bar-determinate-progress-default-background: Background of the progress bar representing the current value + */ + display: block; position: relative; width: 100%; + height: var(--ion-progress-bar-height); contain: strict; @@ -22,7 +27,7 @@ .indeterminate-bar-primary, .indeterminate-bar-secondary, .progress-buffer-bar { - @include position(0, 0, 0, 0); + @include mixins.position(0, 0, 0, 0); position: absolute; width: 100%; @@ -31,7 +36,7 @@ .buffer-circles-container, .buffer-circles { - @include position(0, 0, 0, 0); + @include mixins.position(0, 0, 0, 0); position: absolute; } @@ -60,22 +65,34 @@ .progress, .progress-indeterminate { - background: var(--progress-background); - z-index: 2; } -.progress-buffer-bar { - background: var(--background); +.progress { + background: var(--ion-progress-bar-determinate-progress-default-background); +} +.progress-indeterminate { + background: var(--ion-progress-bar-indeterminate-progress-default-background); +} + +.progress-buffer-bar { z-index: 1; } +:host(.progress-bar-determinate) .progress-buffer-bar { + background: var(--ion-progress-bar-determinate-buffer-bar-default-background); +} + +:host(.progress-bar-indeterminate) .progress-buffer-bar { + background: var(--ion-progress-bar-indeterminate-buffer-bar-default-background); +} + .buffer-circles-container { overflow: hidden; } -// MD based animation on indeterminate type +// Indeterminate Animation // -------------------------------------------------- .indeterminate-bar-primary { @@ -114,7 +131,12 @@ // ------------------------------------------------------------------------ .buffer-circles { - background-image: radial-gradient(ellipse at center, var(--background) 0%, var(--background) 30%, transparent 30%); + background-image: radial-gradient( + ellipse at center, + var(--ion-progress-bar-determinate-buffer-circles-default-background) 0%, + var(--ion-progress-bar-determinate-buffer-circles-default-background) 30%, + transparent 30% + ); /* stylelint-disable property-disallowed-list */ background-repeat: repeat-x; @@ -126,6 +148,73 @@ animation: buffering 450ms infinite linear; } +// Progress Bar: Shape Variants +// ------------------------------------------------------------------------------- + +:host(.progress-bar-shape-round) { + @include mixins.border-radius(var(--ion-progress-bar-shape-round-border-radius)); +} + +:host(.progress-bar-shape-rectangular) { + @include mixins.border-radius(var(--ion-progress-bar-shape-rectangular-border-radius)); +} + +// Progress Bar: Solid (buffer = 1) +// ------------------------------------------------------------------------------- + +:host(.progress-bar-solid.progress-bar-determinate) .progress-buffer-bar { + background: var( + --ion-progress-bar-determinate-buffer-bar-solid-default-background, + var(--ion-progress-bar-determinate-buffer-bar-default-background) + ); +} + +:host(.progress-bar-solid.progress-bar-indeterminate) .progress-buffer-bar { + background: var( + --ion-progress-bar-indeterminate-buffer-bar-solid-default-background, + var(--ion-progress-bar-indeterminate-buffer-bar-default-background) + ); +} + +:host(.progress-bar-solid) .buffer-circles { + background-image: radial-gradient( + ellipse at center, + var(--ion-progress-bar-determinate-buffer-circles-solid-default-background) 0%, + var(--ion-progress-bar-determinate-buffer-circles-solid-default-background) 30%, + transparent 30% + ); +} + +// Progress Bar: Color +// ------------------------------------------------------------------------ + +// Indeterminate +:host(.ion-color) .progress-indeterminate { + background: var(--ion-progress-bar-indeterminate-progress-semantic-default-background); +} + +// Determinate +:host(.ion-color) .progress { + background: var(--ion-progress-bar-determinate-progress-semantic-default-background); +} + +:host(.ion-color.progress-bar-determinate) .progress-buffer-bar { + background: var(--ion-progress-bar-determinate-buffer-bar-semantic-default-background); +} + +:host(.ion-color.progress-bar-indeterminate) .progress-buffer-bar { + background: var(--ion-progress-bar-indeterminate-buffer-bar-semantic-default-background); +} + +:host(.ion-color) .buffer-circles { + background-image: radial-gradient( + ellipse at center, + var(--ion-progress-bar-determinate-buffer-circles-semantic-default-background) 0%, + var(--ion-progress-bar-determinate-buffer-circles-semantic-default-background) 30%, + transparent 30% + ); +} + // Progress Bar: Reversed // ------------------------------------------------------------------------ // If reversed is set to true, the animation will be reversed diff --git a/core/src/components/progress-bar/progress-bar.tsx b/core/src/components/progress-bar/progress-bar.tsx index 729448aff1e..5534e0a32a6 100644 --- a/core/src/components/progress-bar/progress-bar.tsx +++ b/core/src/components/progress-bar/progress-bar.tsx @@ -4,13 +4,11 @@ import { clamp } from '@utils/helpers'; import { createColorClasses } from '@utils/theme'; import { config } from '../../global/config'; -import { getIonTheme } from '../../global/ionic-global'; import type { Color } from '../../interface'; +import type { IonProgressBarShape } from './progress-bar.interfaces'; + /** - * @virtualProp {"ios" | "md"} mode - The mode determines the platform behaviors of the component. - * @virtualProp {"ios" | "md" | "ionic"} theme - The theme determines the visual appearance of the component. - * * @part progress - The progress bar that shows the current value when `type` is `"determinate"` and slides back and forth when `type` is `"indeterminate"`. * @part stream - The animated circles that appear while buffering. This only shows when `buffer` is set and `type` is `"determinate"`. * @part track - The track bar behind the progress bar. If the `buffer` property is set and `type` is `"determinate"` the track will be the @@ -18,11 +16,7 @@ import type { Color } from '../../interface'; */ @Component({ tag: 'ion-progress-bar', - styleUrls: { - ios: 'progress-bar.ios.scss', - md: 'progress-bar.md.scss', - ionic: 'progress-bar.ionic.scss', - }, + styleUrl: 'progress-bar.scss', shadow: true, }) export class ProgressBar implements ComponentInterface { @@ -61,31 +55,22 @@ export class ProgressBar implements ComponentInterface { * Set to `"round"` for a progress bar with rounded corners, or `"rectangular"` * for a progress bar without rounded corners. * - * Defaults to `"round"` for the `ionic` theme, undefined for all other themes. + * Defaults to `"round"` if both the shape property and theme config are unset. */ - @Prop() shape?: 'round' | 'rectangular'; - - private getShape(): string | undefined { - const theme = getIonTheme(this); - const { shape } = this; - - // TODO(ROU-11638): Remove theme check when shapes are defined for all themes. - if (theme !== 'ionic') { - return undefined; - } + @Prop() shape?: IonProgressBarShape; - if (shape === undefined) { - return 'round'; - } - - return shape; + /** + * Gets the progress bar shape. Uses the `shape` property if set, otherwise + * checks the theme config. Returns undefined if neither is set. + */ + get shapeValue(): IonProgressBarShape | undefined { + return this.shape ?? (config.getObjectValue('IonProgressBar.shape') as IonProgressBarShape | undefined); } render() { const { color, type, reversed, value, buffer } = this; const paused = config.getBoolean('_testing'); - const theme = getIonTheme(this); - const shape = this.getShape(); + const shape = this.shapeValue; // If the progress is displayed as a solid bar. const progressSolid = buffer === 1; return ( @@ -95,7 +80,6 @@ export class ProgressBar implements ComponentInterface { aria-valuemin="0" aria-valuemax="1" class={createColorClasses(color, { - [theme]: true, [`progress-bar-${type}`]: true, 'progress-paused': paused, 'progress-bar-reversed': document.dir === 'rtl' ? !reversed : reversed, diff --git a/core/src/components/progress-bar/test/basic/index.html b/core/src/components/progress-bar/test/basic/index.html index 31ab53d9137..da38c6a7faa 100644 --- a/core/src/components/progress-bar/test/basic/index.html +++ b/core/src/components/progress-bar/test/basic/index.html @@ -15,11 +15,11 @@