Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
0a3f41d
feat(spinner): add recipe and tokens
thetaPC Mar 17, 2026
5eee3a8
chore(spinner): run build
thetaPC Mar 17, 2026
75a325a
test(color): update test page and snapshots
thetaPC Mar 17, 2026
53d06e1
chore(spinner): delete old styles
thetaPC Mar 17, 2026
78cfbba
test(spinner): update resize
thetaPC Mar 17, 2026
edb0414
test(spinner): remove diff term
thetaPC Mar 17, 2026
2ffac3e
test(spinner): update spacing
thetaPC Mar 17, 2026
3d89f21
feat(spinner): add recipe and config types
thetaPC Mar 17, 2026
363cddf
feat(spinner): change class names
thetaPC Mar 17, 2026
7b13706
test(spinner): update class
thetaPC Mar 17, 2026
b2a301a
refactor(spinner): remove plural type
thetaPC Mar 18, 2026
5f0020c
test(spinner): update color snapshots
thetaPC Mar 19, 2026
1179d40
refactor(theme): alphabetize imports
thetaPC Mar 19, 2026
4f5ee31
docs(spinner, chip): update get value functions comments
thetaPC Mar 19, 2026
2cd0043
Merge branch 'FW-6860' of github.com:ionic-team/ionic-framework into …
thetaPC Mar 19, 2026
4c1c6d2
feat(spinner): change type names
thetaPC Mar 19, 2026
ee53528
refactor(spinner): change existing config type name
thetaPC Mar 19, 2026
8019941
docs(chip, spinner): update method descriptions
thetaPC Mar 20, 2026
0aef08c
feat(chip, spinner): remove old CSS variables
thetaPC Mar 23, 2026
cb80321
feat(spinner): rename internal interface
thetaPC Mar 23, 2026
5388749
docs(breaking): update to include spinner
thetaPC Mar 23, 2026
de06eb8
feat(spinner): update --color to be internal
thetaPC Mar 23, 2026
7e1f630
feat(button, spinner): update color when spinner is inside button
thetaPC Mar 23, 2026
3c5afb3
fix(spinner): add missing token
thetaPC Mar 23, 2026
8668db4
docs(chip, spinner): clarify how defaults work
thetaPC Mar 23, 2026
528cb40
refactor(spinner): update return type
thetaPC Mar 23, 2026
e00d1f0
revert(BREAKING): leave radio group as is
thetaPC Mar 23, 2026
062a7c7
docs(BREAKING): update chip to include ionic modular changes
thetaPC Mar 23, 2026
95b1b41
docs(chip, spinner): add CSS variables to comments
thetaPC Mar 23, 2026
a8892a9
docs(chip): update logic comment
thetaPC Mar 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions core/api.txt
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR description says:

CSS Overrides: Ensure selectors align with the new slotted element logic and variable names (e.g., --ion-spinner-color).

--color should no longer be used. Setting the value, IonSpinner.color, within the tokens file should be used to change the color.

But we still have this variable, why?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because we discussed that it would be revisited as we add more components. Should I just remove them?

Copy link
Copy Markdown
Member

@brandyscarney brandyscarney Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I thought that was just about the focus variables. I don't see a reason they can't override the --color on components directly? Developers might want it to be different than the global theme.

Copy link
Copy Markdown
Contributor Author

@thetaPC thetaPC Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then they can override them per component by using --ion-spinner-color instead of --color. I ended up removing the old CSS variables since having both might be confusing of when to use them, the same for chip: 0aef08c and 7e1f630

Original file line number Diff line number Diff line change
Expand Up @@ -2309,10 +2309,7 @@ ion-spinner,prop,mode,"ios" | "md",undefined,false,false
ion-spinner,prop,name,"bubbles" | "circles" | "circular" | "crescent" | "dots" | "lines" | "lines-sharp" | "lines-sharp-small" | "lines-small" | undefined,undefined,false,false
ion-spinner,prop,paused,boolean,false,false,false
ion-spinner,prop,size,"large" | "medium" | "small" | "xlarge" | "xsmall" | undefined,undefined,false,false
ion-spinner,prop,theme,"ios" | "md" | "ionic",undefined,false,false
ion-spinner,css-prop,--color,ionic
ion-spinner,css-prop,--color,ios
ion-spinner,css-prop,--color,md
ion-spinner,css-prop,--color

ion-split-pane,shadow
ion-split-pane,prop,contentId,string | undefined,undefined,false,true
Expand Down
12 changes: 2 additions & 10 deletions core/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3881,13 +3881,9 @@ export namespace Components {
*/
"paused": boolean;
/**
* Set to `"xsmall"` for the smallest size. Set to `"small"` for a smaller size. Set to `"medium"` for a medium size. Set to `"large"` for a large size. Set to `"xlarge"` for the largest size. Defaults to `"xsmall"` for the `ionic` theme, undefined for all other themes.
* Set to `"xsmall"` for the smallest size. Set to `"small"` for a smaller size. Set to `"medium"` for a medium size. Set to `"large"` for a large size. Set to `"xlarge"` for the largest size. Defaults to `"medium"`.
*/
"size"?: 'xsmall' | 'small' | 'medium' | 'large' | 'xlarge';
/**
* The theme determines the visual appearance of the component.
*/
"theme"?: "ios" | "md" | "ionic";
}
interface IonSplitPane {
/**
Expand Down Expand Up @@ -9911,13 +9907,9 @@ declare namespace LocalJSX {
*/
"paused"?: boolean;
/**
* Set to `"xsmall"` for the smallest size. Set to `"small"` for a smaller size. Set to `"medium"` for a medium size. Set to `"large"` for a large size. Set to `"xlarge"` for the largest size. Defaults to `"xsmall"` for the `ionic` theme, undefined for all other themes.
* Set to `"xsmall"` for the smallest size. Set to `"small"` for a smaller size. Set to `"medium"` for a medium size. Set to `"large"` for a large size. Set to `"xlarge"` for the largest size. Defaults to `"medium"`.
*/
"size"?: 'xsmall' | 'small' | 'medium' | 'large' | 'xlarge';
/**
* The theme determines the visual appearance of the component.
*/
"theme"?: "ios" | "md" | "ionic";
}
interface IonSplitPane {
/**
Expand Down
62 changes: 0 additions & 62 deletions core/src/components/spinner/spinner.ionic.scss

This file was deleted.

14 changes: 0 additions & 14 deletions core/src/components/spinner/spinner.native.scss

This file was deleted.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ionic theme for spinner overwrites the medium since it's one of those components that differs to the set value of medium. I decided to not implement it since it's specific to the project. A workaround would be to set it:

ion-spinner {
  --ion-color-medium-bold: #626262;
}

Original file line number Diff line number Diff line change
@@ -1,32 +1,29 @@
@import "../../themes/mixins";
// Required to use the current-color function
@import "../../themes/functions.color";
@use "../../themes/mixins" as mixins;
@use "../../themes/functions.color" as colors;

// Spinners
// Spinner: Common Styles
// --------------------------------------------------

:host {
/**
* @prop --color: Color of the spinner
*/
--color: var(--ion-spinner-color);

display: inline-block;
position: relative;

width: 28px;
height: 28px;

color: var(--color);

user-select: none;
}

:host(.ion-color) {
color: current-color(base);
color: colors.current-color(base);
}

svg {
@include transform-origin(center);
@include mixins.transform-origin(center);

position: absolute;

Expand All @@ -45,9 +42,10 @@ svg {
transform: translateZ(0);
}

// Spinner: lines / lines-small / lines-sharp / lines-sharp-small
// Spinner Names
Comment thread
brandyscarney marked this conversation as resolved.
// --------------------------------------------------

// lines / lines-small / lines-sharp / lines-sharp-small
:host(.spinner-lines) line,
:host(.spinner-lines-small) line,
:host(.spinner-lines-sharp) line,
Expand All @@ -63,52 +61,58 @@ svg {
animation: spinner-fade-out 1s linear infinite;
}

// Spinner: bubbles
// --------------------------------------------------
:host(.spinner-lines) line {
stroke-width: var(--ion-spinner-lines-stroke-width);
}

:host(.spinner-lines-small) line {
stroke-width: var(--ion-spinner-lines-small-stroke-width);
}

:host(.spinner-lines-sharp) line {
stroke-width: var(--ion-spinner-lines-sharp-stroke-width);
}

:host(.spinner-lines-sharp-small) line {
stroke-width: var(--ion-spinner-lines-sharp-small-stroke-width);
}

// bubbles
:host(.spinner-bubbles) svg {
animation: spinner-scale-out 1s linear infinite;
fill: currentColor;
}

// Spinner: circles
// --------------------------------------------------

// circles
:host(.spinner-circles) svg {
animation: spinner-fade-out 1s linear infinite;
fill: currentColor;
}

// Spinner: crescent
// --------------------------------------------------

:host(.spinner-crescent) circle {
fill: transparent;
stroke-width: 4px;
stroke-width: var(--ion-spinner-crescent-stroke-width);
stroke-dasharray: 128px;
stroke-dashoffset: 82px;
stroke: currentColor;
}

// crescent
:host(.spinner-crescent) svg {
animation: spinner-rotate 1s linear infinite;
}

// Spinner: dots
// --------------------------------------------------

// dots
:host(.spinner-dots) circle {
stroke-width: 0;
stroke-width: var(--ion-spinner-dots-stroke-width);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm having a hard time finding where this is defined. Is it actually set anywhere yet?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fill: currentColor;
}

:host(.spinner-dots) svg {
animation: spinner-dots 1s linear infinite;
}

// Spinner: circular
// --------------------------------------------------

// circular
:host(.spinner-circular) svg {
animation: spinner-circular linear infinite;
}
Expand All @@ -118,13 +122,42 @@ svg {
stroke: currentColor;
stroke-dasharray: 80px, 200px;
stroke-dashoffset: 0px;
stroke-width: 5.6;
stroke-width: var(--ion-spinner-circular-stroke-width);
fill: none;
}

// Spinner: paused
// Spinner Sizes
// --------------------------------------------------

:host(.spinner-xsmall) {
width: var(--ion-spinner-size-xsmall-width);
height: var(--ion-spinner-size-xsmall-height);
}

:host(.spinner-small) {
width: var(--ion-spinner-size-small-width);
height: var(--ion-spinner-size-small-height);
}

:host(.spinner-medium) {
width: var(--ion-spinner-size-medium-width);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The old code had width: 28px; height: 28px as defaults on :host. Now dimensions come entirely from the token system with no fallback values. If the tokens aren't loaded for some reason, the spinner collapses to zero size. Is that expected on this branch, or should we add fallbacks like var(--ion-spinner-size-medium-width, 28px)?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I've been purposely been avoiding setting hard coded values as defaults. Developers wouldn't experience this if they use any of the in built themes. If they decide to make their own, then I would expect that they would know to set their own values either by using their own or copying it from one of the in built themes. If we find out that we should be setting defaults, then we can always come back to add them.

height: var(--ion-spinner-size-medium-height);
}

:host(.spinner-large) {
width: var(--ion-spinner-size-large-width);
height: var(--ion-spinner-size-large-height);
}

:host(.spinner-xlarge) {
width: var(--ion-spinner-size-xlarge-width);
height: var(--ion-spinner-size-xlarge-height);
}

// Spinner States
// --------------------------------------------------

// Paused
:host(.spinner-paused),
:host(.spinner-paused) svg,
:host(.spinner-paused) circle {
Expand Down
45 changes: 16 additions & 29 deletions core/src/components/spinner/spinner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Component, Host, Prop, h } from '@stencil/core';
import { createColorClasses } from '@utils/theme';

import { config } from '../../global/config';
import { getIonTheme, getIonMode } from '../../global/ionic-global';
import { getIonMode } from '../../global/ionic-global';
import type { Color } from '../../interface';

import type { SpinnerTypes } from './spinner-configs';
Expand All @@ -12,15 +12,10 @@ import type { SpinnerConfig } from './spinner-interface';

/**
* @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.
*/
@Component({
tag: 'ion-spinner',
styleUrls: {
ios: 'spinner.native.scss',
md: 'spinner.native.scss',
ionic: 'spinner.ionic.scss',
},
styleUrl: 'spinner.scss',
shadow: true,
})
export class Spinner implements ComponentInterface {
Expand Down Expand Up @@ -54,7 +49,7 @@ export class Spinner implements ComponentInterface {
* Set to `"large"` for a large size.
* Set to `"xlarge"` for the largest size.
*
* Defaults to `"xsmall"` for the `ionic` theme, undefined for all other themes.
* Defaults to `"medium"`.
Comment thread
brandyscarney marked this conversation as resolved.
Outdated
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This says "Defaults to medium" but the ionic theme overrides it to xsmall via the config. Should the JSDoc mention that the default can vary by theme? Something like "Defaults to medium unless overridden by theme configuration." Chip has the same situation so not a blocker, just wondering if we want to be more accurate here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does this sound? 8668db4

*/
@Prop() size?: 'xsmall' | 'small' | 'medium' | 'large' | 'xlarge';

Expand All @@ -67,29 +62,22 @@ export class Spinner implements ComponentInterface {
return mode === 'ios' ? 'lines' : 'circular';
}

private getSize(): string | undefined {
const theme = getIonTheme(this);
const { size } = this;

// TODO(ROU-10912): Remove theme check when sizes are defined for all themes.
if (theme !== 'ionic') {
return undefined;
}

if (size === undefined) {
return 'xsmall';
}
/**
* Set the size based on the custom theme config
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be get the size, and also it's not just based on the config but also whether or not its set on the component.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated it for chip too: 4f5ee31

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we reword this something like:

/**
 * Gets the spinner size. Uses the `size` property if set, otherwise
 * checks the theme config and falls back to 'medium' if neither is provided.
 */

Could replace 'medium' with the default if desired.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

*/
get sizeValue(): string {
const sizeConfig = config.getObjectValue('IonSpinner.size', 'medium') as string;
console.log('sizeConfig', sizeConfig);
const size = this.size || sizeConfig;

return size;
}

render() {
const self = this;
const theme = getIonTheme(self);
const spinnerName = self.getName();
const size = this.getSize();
const { duration: animatedDuration, color, paused, sizeValue } = this;
const spinnerName = this.getName();
const spinner = SPINNERS[spinnerName] ?? SPINNERS['lines'];
const duration = typeof self.duration === 'number' && self.duration > 10 ? self.duration : spinner.dur;
const duration = typeof animatedDuration === 'number' && animatedDuration > 10 ? animatedDuration : spinner.dur;
const svgs: SVGElement[] = [];

if (spinner.circles !== undefined) {
Expand All @@ -104,11 +92,10 @@ export class Spinner implements ComponentInterface {

return (
<Host
class={createColorClasses(self.color, {
[theme]: true,
class={createColorClasses(color, {
[`spinner-${spinnerName}`]: true,
'spinner-paused': self.paused || config.getBoolean('_testing'),
[`spinner-${size}`]: size !== undefined,
'spinner-paused': paused || config.getBoolean('_testing'),
[`spinner-${sizeValue}`]: true,
})}
role="progressbar"
style={spinner.elmDuration ? { animationDuration: duration + 'ms' } : {}}
Expand Down
2 changes: 1 addition & 1 deletion core/src/components/spinner/test/basic/spinner.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ configs().forEach(({ title, screenshot, config }) => {
test('should not have visual regressions', async ({ page }) => {
await page.setIonViewport();

await expect(page).toHaveScreenshot(screenshot(`spinner-basic-diff`));
await expect(page).toHaveScreenshot(screenshot('spinner-basic'));
});
});
});
Expand Down
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only removed the word diff from the test file name to line up with the naming conventions of the other components.

File renamed without changes
Loading
Loading