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.
| /** 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
| * 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 reset gone, the ionic theme in ios mode has no press feedback when an app sets rippleEffect: false. Before, ios never rendered the ripple, so the ::after shade showed on press and a solid primary button went from rgb(0, 84, 233) to rgb(0, 74, 205). Now it stays at rgb(0, 84, 233), and outline and clear stay white instead of tinting. md mode is unchanged, since the old reset already hid the shade whenever md rendered the ripple element, even with rippleEffect: false. I don't see a clean fix, because shade activated tokens would cover the ripple and a ripple-only reset brings back the dead tokens from this thread. Could the "The ripple is the pressed feedback" comment in the ionic tokens at least say it only applies when rippleEffect is on? Or would you rather keep a shade for the no-ripple case?
| expect(clashes).toEqual([]); | ||
| }); |
There was a problem hiding this comment.
| expect(clashes).toEqual([]); | |
| }); | |
| expect(clashes).toEqual([]); | |
| }); | |
| /** | |
| * A button placed directly in a colored toolbar keeps its own colors, so the | |
| * button tokens that read the toolbar colors must sit under `default`. | |
| */ | |
| it('should not give toolbar colors to a button placed directly in a colored toolbar', () => { | |
| for (const theme of [iosTheme, mdTheme, ionicTheme]) { | |
| const css = generateComponentsThemeCSS(theme.components ?? {}); | |
| expect(css).not.toMatch(/ion-toolbar ion-button\s*\{[^}]*--ion-toolbar-(color|background)/); | |
| } | |
| }); |
The default split in abcef80 fixes the colored toolbar case, but nothing fails if the hue tokens move back under a plain IonToolbar.IonButton. With that revert in all three themes every spec still passes, and no test page puts an ion-button straight into an <ion-toolbar color="...">. Could we add something like this so it can't quietly regress? It passes at head and fails if even one theme is reverted.
There was a problem hiding this comment.
Thanks for spotting the missing coverage. I added it as an e2e in the toolbar colors tests instead, so it checks what the user sees rather than the generated CSS. It sets --ion-toolbar-background and --ion-toolbar-color on :root, like your original repro, and compares a button in a colored toolbar with one outside it. It passes now and fails for every fill when the md tokens move back under IonToolbar.IonButton. Done in b9d6022.
| - 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`. The token has no size, so scope the CSS variable by size for small and large buttons, for example `ion-button[size="small"] { --ion-button-shape-soft-border-radius: 6px; }` and `16px` for `size="large"`. |
There was a problem hiding this comment.
| 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`. The token has no size, so scope the CSS variable by size for small and large buttons, for example `ion-button[size="small"] { --ion-button-shape-soft-border-radius: 6px; }` and `16px` for `size="large"`. | |
| 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`. The token has no size, so scope the CSS variable by size class for small and large buttons, for example `ion-button.button-size-small { --ion-button-shape-soft-border-radius: 6px; }` and `16px` for `.button-size-large`. The size class also covers buttons without a `size` that render small, such as buttons inside an `ion-item`. |
The [size="small"] example I suggested last round misses buttons that render small without a size attribute, like a button in an ion-item or ion-item-divider, or one made small through IonButton.size in the theme config. Those were 6px in v8, but with this recipe they come out 14px. Scoping by the .button-size-small and .button-size-large classes, which the renamed classes table already lists, gives 6px, 14px and 16px everywhere they used to apply. Sorry, that one's on me.
| }, | ||
| }, | ||
|
|
||
| // Outline and clear tints use primary, unless the app sets `--ion-toolbar-color` |
There was a problem hiding this comment.
The md toolbar buttons have the same limitation, but the md comment says the outline and clear tints use the toolbar's text color, "#424242 unless set". They read --ion-text-color-rgb, so with --ion-toolbar-color: #fff on a dark toolbar the text goes white but the hover and focus tints stay gray at 4% and 12%. Before this PR they followed currentColor, so they were white too. Could the md comment match this one? Something like:
// Outline and clear tints use `--ion-text-color`, since `--ion-toolbar-color` has no `-rgb` variable| }, | ||
| }, | ||
|
|
||
| default: { |
There was a problem hiding this comment.
| default: { | |
| // In a toolbar without a color | |
| default: { |
Nit: md and ios label the semantic group with // In a toolbar with a color, but this default group has nothing, and a few lines down default also means the resting state, so it's easy to misread. Could ios and ionic get the same comment above their default groups, plus // In a toolbar with a color above ionic's semantic? Up to you!
There was a problem hiding this comment.
Added // In a toolbar without a color above default in all three themes, and // In a toolbar with a color above ionic's semantic. Done in aa8f5c1.
…ar keeps its colors
Co-authored-by: Shane <561207+ShaneK@users.noreply.github.com>
Co-authored-by: Shane <561207+ShaneK@users.noreply.github.com>
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)