Repository navigation
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
# Conflicts: # core/src/components/button/test/basic/index.html
The ripple follows the theme instead of the mode, and ionic sets rippleEffect to match md. Button falls back to the mode until FW-7893 makes the base theme's config always apply.
There was a problem hiding this comment.
The @prop block is 230+ lines, so it lives in its own partial to keep button.scss starting with its rules. It also makes the docs easier to generate from the recipe later, since a script can rewrite the whole file without touching the stylesheet. Stencil reads @prop comments from the compiled CSS, so the docs output stays the same.
| export const ION_BUTTON_FILLS = ['solid', 'outline', 'clear'] as const; | ||
| export type IonButtonFill = (typeof ION_BUTTON_FILLS)[number]; | ||
|
|
||
| export const ION_BUTTON_SHAPES = ['crisp', 'soft', 'round', 'rectangular'] as const; |
There was a problem hiding this comment.
On next, soft changed its radius with the button's size. Now each shape has one radius per theme, and crisp covers the tighter corner that small buttons used to get, which also gives button the same shapes as badge.
# Conflicts: # core/src/utils/test/theme.spec.ts
# Conflicts: # core/src/themes/ios/default.tokens.ts
ShaneK
left a comment
There was a problem hiding this comment.
Really solid migration, the token values all line up with the old vars! Just a few things, mainly buttons sitting directly in a colored toolbar and the datetime arrow tint.
| }, | ||
| }, | ||
|
|
||
| IonToolbar: { |
There was a problem hiding this comment.
The old toolbar rules skipped colored toolbars with :not(.in-toolbar-color), but a plain ion-toolbar ion-button matches <ion-toolbar color="secondary"> too. With --ion-toolbar-background: lime and --ion-toolbar-color: red set on :root, a solid button placed straight in a secondary toolbar now renders with a red background and lime text, where before it kept its default colors. Could this be scoped to toolbars without a color? The in-toolbar-color class is still added in button.tsx, but nothing reads it now. I think the ios and ionic blocks have the same problem.
There was a problem hiding this comment.
Done in abcef80. Toolbar tokens that depend on the toolbar's color now sit under default (ion-toolbar:not(.ion-color)) and semantic (ion-toolbar.ion-color), so a button placed directly in a colored toolbar keeps its own colors. Keys that apply in every toolbar, like size and shape, stay in a shared IonButtons.IonButton. The color class outranks the deeper ion-buttons selector, so a new spec fails if the shared block sets a key the direct-button blocks set. I kept the in-toolbar-color class, since apps can style it and the back and menu buttons set the same pair.
| }, | ||
| }, | ||
|
|
||
| // Datetime renders primary buttons, so their tints use primary |
There was a problem hiding this comment.
The datetime month arrows are gray, but their focus tint is now rgba(0, 84, 233, 0.12) where it used to be the arrow's own gray at 12%, and hover does the same. Could the .calendar-action-buttons ion-button rule in datetime.md.scss set the clear focus and hover backgrounds too, like it already does for the colors?
There was a problem hiding this comment.
Done in 2d1eeb1. The md rule now sets the clear focus and hover backgrounds too. Step 350 has no -rgb variable, so they use the text color at the matching alpha, which renders the same as the arrow's gray at 12% and 4%.
| * Only `md` mode renders a ripple. When it does, the ripple shows the press, | ||
| * so the button keeps its default background and text color instead of the activated ones. | ||
| */ | ||
| :host(.button-hue-bold.button-fill-#{$fill}.ion-activated) .button-native:has(ion-ripple-effect) { |
There was a problem hiding this comment.
With the ripple on, this rule always wins over the activated one, so activated.background and activated.color never show in md or ionic. Setting --ion-button-hue-bold-solid-activated-background: red on a pressed md button leaves it blue. On the base branch --background-activated did work in md, and BREAKING.md maps it straight to this token. Should the migration note say these only apply with rippleEffect off, or should this reset let them through?
There was a problem hiding this comment.
Done in 8e90b95. I removed the reset, so the activated tokens always apply. md's activated tokens already matched its defaults, and ionic's now do too, so the ripple is still the only pressed feedback in both. --ion-button-hue-bold-solid-activated-background: red now shows on a pressed button in every theme.
| } | ||
|
|
||
| /** | ||
| * Only `md` mode renders a ripple. When it does, the ripple shows the press, |
There was a problem hiding this comment.
This still says only md renders a ripple, but it follows rippleEffect now and ionic turns it on. The ripple definition doc in button.interface.ts says the same thing. Could we update both?
There was a problem hiding this comment.
Done in 8e90b95. The comment went away with the reset rule, and the ripple definition in button.interface.ts now says it renders when rippleEffect is enabled.
| | `--transition` | `IonButton.transition` | `--ion-button-transition` | | ||
|
|
||
| > [!NOTE] | ||
| > `--background-activated-opacity`, `--background-focused-opacity` and `--background-hover-opacity` have been removed with no one-to-one replacement. Each state's background is now a single color that includes its opacity, so set the final color, for example `color-mix(in srgb, var(--ion-color-primary) 12%, transparent)`. States are also drawn on the native element itself, so styles targeting `::part(native)::after` no longer affect them. |
There was a problem hiding this comment.
We avoid color-mix() in theme.ts because it's above our browser floor, so I don't think the migration note should suggest it right now. The v10 minimums being added in #31605 are Chrome 89, Safari 16 and Firefox 89, and color-mix() needs Chrome 111, Safari 16.2 and Firefox 113. We could raise the floor since this is a major, but I'm not sure how many users that Chrome jump would affect, mostly older Android webviews I'd guess. Otherwise, could the example use something like rgba(var(--ion-color-primary-rgb), 0.12) instead?
There was a problem hiding this comment.
Done in 0845111. The example had a second problem: on a solid button, the state background replaces the fill, so a 12% tint alone made the button nearly transparent. The note now layers the tint over the base color with a linear-gradient for solid buttons, and uses rgba(var(--ion-color-primary-rgb), 0.12) for outline and clear.
| - The `border-radius` of the `ios` and `md` button now defaults to `6px` and `999px` instead of `14px` and `4px`, respectively, in accordance with the iOS and Material Design 3 guidelines. To revert to the previous appearance, set the `shape` to `"soft"` for `md` and override the `--border-radius` CSS variable for `ios` to `14px`, or set it to a different value entirely. | ||
| The following breaking changes apply to `ion-button`: | ||
|
|
||
| 1. The `border-radius` of the `ios` button is now `6px` at every size, instead of `6px`, `14px` and `16px` for the small, default and large sizes. The `md` button now defaults to `999px` instead of `4px`. Both follow the iOS and Material Design 3 guidelines. To revert to the previous appearance, set the `shape` to `"soft"` for `md`. For `ios`, set `IonButton.shape.soft.border.radius` for global styles or `--ion-button-shape-soft-border-radius` for component-specific styles to `14px`, and to `6px` and `16px` on small and large buttons. |
There was a problem hiding this comment.
The shape token doesn't have a size axis, so setting IonButton.shape.soft.border.radius can't give 6px, 14px and 16px by size. Could this say the per-size part needs the CSS variable scoped by size, something like ion-button[size="small"] { --ion-button-shape-soft-border-radius: 6px; }?
There was a problem hiding this comment.
Done in e864a6d. The note now says the token has no size and scopes the CSS variable instead: ion-button[size="small"] { --ion-button-shape-soft-border-radius: 6px; }, and 16px for large.
| }, | ||
| }, | ||
|
|
||
| // Outline and clear tints use primary, unless the app sets `--ion-toolbar-color` |
There was a problem hiding this comment.
The focus tints in this block are ionColor('primary', ...) and never read --ion-toolbar-color, so setting it won't change them. Should this say the tints stay primary?
There was a problem hiding this comment.
Done in ab930d6. They stay primary: --ion-toolbar-color has no -rgb variable to tint with, and color-mix() is above the browser floor.
| * Add a component here when it starts rendering a migrated component in its | ||
| * shadow root, or that child renders with no tokens. A spec test checks this. | ||
| */ | ||
| export const INTERNAL_CHILD_COMPONENTS: Record<string, string[]> = { |
There was a problem hiding this comment.
This JSDoc is pretty long compared to the others in this file, and the same explanation shows up in a few other places, like the generateChildBlocks doc and theme.spec.ts. Could we keep one short version here and point the others at it? The HTML/CSS walkthrough feels like PR body material to me. Just a nit though, up to you!
There was a problem hiding this comment.
Done in 7d858d9. INTERNAL_CHILD_COMPONENTS keeps a short version, and the spec's comment points at it. generateChildBlocks already referred to it, so I left that one.
| it('should render the deprecated default size as medium', async () => { | ||
| const page = await newSpecPage({ | ||
| components: [Button], | ||
| html: `<ion-button size="default">Button</ion-button>`, |
There was a problem hiding this comment.
Could we add a check that the default size warning fires, and only once? There's a pattern for that in col.spec.ts. I also didn't see a spinner with its own size on the spinner test page, or a case for the shorter button from #30625, so those two aren't covered either.
There was a problem hiding this comment.
- The spec checks that the
defaultsize warning prints once across a re-render, with a TODO for its v11 removal (FW-7631) - The size spec checks that lowering the min height and padding makes the button shorter (bug: controlling ion-button paddings fails below 12px #30625), as a height check rather than a screenshot
- The sized spinners went on the spinner page in test(button): add missing coverage and consolidate snapshots #31627, so their baseline records the old ionic 20px. The change shows up in chore(button): update snapshots for the migration #31630
| /** In any toolbar */ | ||
| IonButtons?: { | ||
| IonButton?: IonButtonRecipe; | ||
| }; | ||
|
|
||
| /** In a toolbar without a color */ | ||
| default?: { | ||
| IonButton?: IonButtonRecipe; | ||
|
|
||
| IonButtons?: { | ||
| IonButton?: IonButtonRecipe; | ||
| }; | ||
| }; | ||
|
|
||
| /** In a toolbar with a color */ | ||
| semantic?: { |
There was a problem hiding this comment.
Toolbar button tokens are now split three ways:
IonButtons.IonButton: keys that don't depend on the toolbar's color, like size and shapedefault: in a toolbar without acolorsemantic: in a toolbar with acolor
default and semantic add :not(.ion-color) and .ion-color to ion-toolbar, so a button placed directly in a colored toolbar keeps its own colors. That class outranks the deeper ion-toolbar ion-buttons ion-button, so the shared block can't set a key the direct-button blocks set. The new spec in theme.spec.ts fails if it does.
# Conflicts: # core/src/components/button/test/badge/button.e2e.ts # core/src/themes/themes.interfaces.ts
Issue number: resolves #30625
What is the current behavior?
ion-buttonfragments its styles across per-theme stylesheets (.ios,.md,.ionic,.native).What is the new behavior?
ion-buttonto a single token-drivenbutton.scss, withIonButtonRecipe/IonButtonConfigand per-theme tokens::afteroverlayrippleEffectconfig is on instead of in md mode, and ionic sets it to match md. Until FW-7893, button falls back to the mode when no theme sets itmin.heighttoken, so a button can be made shorter than its default by lowering it along with the padding. Lowering only the padding can't shorten a button, which is what bug: controlling ion-button paddings fails below 12px #30625 ran into0.4, which matches its design's white washforegroundfor their label and the page background for their fill, matching the designsizeset on the spinner still appliesIonToolbar.IonButtons.IonButton,IonDatetime.IonButton,IonInputPasswordToggle.IonButton). Toolbar tokens that depend on the toolbar's color sit underdefault(nocolor) andsemantic(with acolor)defaultsize in favor ofmediumSnapshots are in #31630.
Does this introduce a breaking change?
The button's CSS variables are replaced by token-driven custom properties. Use the global token for theme-wide styling, or the CSS variable for component-specific overrides.
iosbutton'sborder-radiusis6pxat every size, andmddefaults to999pxinstead of4px. To revert, setshape="soft"formd. Forios, setIonButton.shape.soft.border.radiusor--ion-button-shape-soft-border-radiusto14px. The token has no size, so scope the CSS variable by size for small (6px) and large (16px) buttons, such asion-button[size="small"].startandendslots ofion-input,ion-selectandion-textareafollow the button'sshapeinstead of always being circles. Forios, setshape="round"to keep them circular.iosclear buttons use the same font size as the other fills, instead of17pxat the default size. To keep it, set--ion-button-size-medium-font-sizeto17px.iosbutton is28px,34pxand50pxtall at the small, default and large sizes, with less padding. To revert, setIonButton.size.<size>.min.heightor--ion-button-size-<size>-min-heightto2.1em,3.1emand3.1em, and the padding to4px 0.9em,13px 1emand17px 1em.md, they're a few pixels smaller at the small and default sizes and larger at the large size.<fill>withsolid,outlineorclear, and<shape>withcrisp,soft,roundorrectangular. Buttons with acolorread the same tokens undersemantic.--backgroundIonButton.hue.bold.<fill>.default.background--ion-button-hue-bold-<fill>-default-background--background-activatedIonButton.hue.bold.<fill>.activated.background--ion-button-hue-bold-<fill>-activated-background--background-focusedIonButton.hue.bold.<fill>.focus.background--ion-button-hue-bold-<fill>-focus-background--background-hoverIonButton.hue.bold.<fill>.hover.background--ion-button-hue-bold-<fill>-hover-background--border-colorIonButton.hue.bold.<fill>.default.border.color--ion-button-hue-bold-<fill>-default-border-color--border-radiusIonButton.shape.<shape>.border.radius--ion-button-shape-<shape>-border-radius--border-styleIonButton.hue.bold.<fill>.default.border.style--ion-button-hue-bold-<fill>-default-border-style--border-widthIonButton.hue.bold.<fill>.default.border.width--ion-button-hue-bold-<fill>-default-border-width--box-shadowIonButton.hue.bold.<fill>.default.box.shadow--ion-button-hue-bold-<fill>-default-box-shadow--colorIonButton.hue.bold.<fill>.default.color--ion-button-hue-bold-<fill>-default-color--color-activatedIonButton.hue.bold.<fill>.activated.color--ion-button-hue-bold-<fill>-activated-color--color-focusedIonButton.hue.bold.<fill>.focus.color--ion-button-hue-bold-<fill>-focus-color--color-hoverIonButton.hue.bold.<fill>.hover.color--ion-button-hue-bold-<fill>-hover-color--ripple-colorIonButton.hue.bold.<fill>.rippleEffect.color--ion-button-hue-bold-<fill>-ripple-effect-color--transitionIonButton.transition--ion-button-transition--background-activated-opacity,--background-focused-opacityand--background-hover-opacityare removed. A state's background replaces the button's instead of tinting it, so the opacity goes in the value: layer the tint over the base color with alinear-gradienton solid buttons, or usergba(var(--ion-color-primary-rgb), 0.12)on outline and clear.--opacityis removed: setopacityonion-button.--padding-*variables are no longer documented, but still work when set on the button itself. Replace<size>withsmall,mediumorlarge:--padding-topIonButton.size.<size>.padding.top--ion-button-size-<size>-padding-top--padding-endIonButton.size.<size>.padding.end--ion-button-size-<size>-padding-end--padding-bottomIonButton.size.<size>.padding.bottom--ion-button-size-<size>-padding-bottom--padding-startIonButton.size.<size>.padding.start--ion-button-size-<size>-padding-start.button-solid/.button-outline/.button-clear.button-fill-solid/.button-fill-outline/.button-fill-clear.button-small/.button-default/.button-large.button-size-small/.button-size-medium/.button-size-large.button-soft/.button-round/.button-rectangular.button-shape-soft/.button-shape-round/.button-shape-rectangular.button-block/.button-full.button-expand-block/.button-expand-fulldefaultsize is deprecated. It logs a warning and renders asmedium.ion-button.mdorion-button.ios.Other information
Why these choices
size.<size>.min.heighttoken sets both, so a square can't drift from the text buttons beside it. Notaspect-ratio: it needs Firefox 89, and Ionic supports Firefox 75. Equalmin-widthandmin-heightmake the same square in every supported browser.next, while text buttons were about 27, 50 and 62px. Sharing one height means picking one, and the kit's values match the icon-only buttons, so no iOS icon-only button changes size.::afteroverlay painted a state color at an opacity, which a theme couldn't set as one value. Each state's token is now the final color, rendering the same color asnext(md's focused solid is still white at 24% over primary). Wherenexttinted withcurrentColororshade, which have no-rgbvariable, the closest variable stands in, a few RGB levels off; a comment marks each one. Nocolor-mix(), since it needs Chrome 111, Safari 16.2 and Firefox 113: tints usergba()or the newoverlay()helper, which paints the same pixels.next. ios has no ripple, so its activated tokens tint the button. No rule depends on the ripple, so an app's override of the activated tokens applies in every theme.0.4because its design draws a 60% white wash over the whole button, and0.4paints the same colors on a white page. Before, ionic stacked the wash and a0.5fade, so disabled buttons were fainter than the design.unset, which is its initial value, so no theme needsborder: none,box-shadow: noneoropacity: 1entries.--padding-*still works on the button: the button readsvar(--padding-*, <token>)and resets the hooks on:host, so.ion-paddingon the button applies, but an ancestor's (likeion-content.ion-padding) doesn't leak in. That matchesion-contentand v9.sizeset on the spinner. The scale starts at the theme's default spinner size, and ios and md set none, so they fall back to the spinner's own size as in v9.IonToolbar: { IonButtons: { IonButton: { … } } }generatesion-toolbar ion-buttons ion-button { --ion-button-*: … }. Toolbar tokens that depend on its color go underdefaultandsemantic, which add:not(.ion-color)and.ion-colortoion-toolbar, so a button placed directly in a colored toolbar keeps its own colors, as onnext. Keys that don't depend on the color, like size and shape, stay in a shared block. That block can't set a key the direct-button blocks set, since the color class outranks it; a spec fails if it does. Datetime and the password toggle render their buttons inside their own shadow root, where page CSS can't reach, so their hosts pass the tokens down by inheritance. A spec fails if a component renders a migrated child without that entry.17pxcame from the iOS 13 list header spec (feat(list-header): redesign list header for iOS spec #19915). The iOS 17 and 18 kits use the same font for plain and filled buttons.@propdocs in_button.docs.scss: keepsbutton.scssstarting with its rules. Stencil reads@propcomments from the compiled CSS, and the docs output (core.json,api.txt,html.html-data.json) lists the same variables as before.Not matched to the design on purpose
rounddefault from feat(button): set default shape to soft for ios and round for md and ionic themes #29404 instead of MD2's4px, to avoid reversing a change that just landedbasefor their border like every other color, so it's lighter than onnextLeft for the parents
Parent rules that set button variables the migration removes, such as
--colorand--background, moved to nested tokens, since they would stop working.--padding-*still works on the button, so the parents' padding rules stay until each parent migrates:ion-buttons, datetime, input, select, textarea, item and list header still set--padding-*on buttons; each moves it to a nested token in its own migrationion-buttonscaps buttons at 32px tall, so the iOS toolbar icon button is 38×32Known gaps
nextrule had no effect (--background-activated: transparent, overridden bybutton.ios.scss) and was ported as a live token; feat(button): sync the ios theme with iOS 17 #31645 removes itTODO(FW-7935)). It fails onnexttoo--ion-background-colorto white. In an app, it follows the pageHow to test
--ion-button-size-medium-min-height: 0and--ion-button-size-medium-padding-top/-bottom: 2pxon a button. It gets shorter (#30625)