Skip to content

feat(content): add recipe and tokens#31095

Open
thetaPC wants to merge 26 commits into
ionic-modularfrom
FW-6896
Open

feat(content): add recipe and tokens#31095
thetaPC wants to merge 26 commits into
ionic-modularfrom
FW-6896

Conversation

@thetaPC
Copy link
Copy Markdown
Contributor

@thetaPC thetaPC commented Apr 24, 2026

Issue number: resolves internal


What is the current behavior?

ion-content does not fragment styles based on themes. All 3 themes share one style. However, it's not configured to the Modular Ionic.

What is the new behavior?

  • Defined TypeScript Interface: Renamed content-interface.ts to content.interfaces.ts and added IonContentRecipe type.
  • Defined Theme Defaults: Added per-theme token defaults in ios, md, and ionic theme files.
  • Internal Variable Prefix: Renamed --offset-top / --offset-bottom to --internal-offset-top / --internal-offset-bottom to clearly separate them from the public CSS API.
  • CSS Logical Properties for RTL: Replaced physical-property RTL selectors on .transition-effect and .transition-shadow with the position-horizontal mixin.
  • Removed dead property: Dropped -webkit-overflow-scrolling: touch from styles as this property has been a no-op since iOS 13.
  • Updated Tests: Added unit specs for transitionShadow (present in ios mode, absent in md mode) and the getScrollElement() / getBackgroundElement() element ref methods.
  • New utility: Replaced inline new Promise((resolve) => componentOnReady(...)) patterns with a shared waitForComponent helper, reducing boilerplate and improving type safety.

Does this introduce a breaking change?

  • Yes
  • No

This PR introduces breaking changes to how ion-content is styled.

Migration Path:

  1. CSS Variable Replacements: --background and --color have been removed. Use the new token structure:
--background -> IonContent.background
--color -> IonContent.color

--padding-* is still supported. .ion-padding, .ion-padding-* classes in css/padding.scss set --padding-* directly on the host. ion-content continues to honor those values (falling back to the new token when unset). Existing usage of the utility classes and direct --padding-* overrides will keep working.

New code is encouraged to use the token-based API instead:

--padding-top -> IonContent.padding.top
--padding-end -> IonContent.padding.end
--padding-bottom -> IonContent.padding.bottom
--padding-start -> IonContent.padding.start

Note: core/api.txt only lists the new --ion-content-padding-* variables. The --padding-* overrides remain functional but are no longer part of the documented public API.

If per-component customization is needed, the CSS variables can be used directly.

--background -> --ion-content-background
--color -> --ion-content-color
--padding-top -> --ion-content-padding-top
--padding-end -> --ion-content-padding-end
--padding-bottom -> --ion-content-padding-bottom
--padding-start -> --ion-content-padding-start
  1. Internal-only variables (no replacement): The following CSS variables were previously documented @props on ion-content and have been renamed to the --internal-* namespace, removing them from the public API:
--keyboard-offset -> --internal-keyboard-offset
--offset-top      -> --internal-offset-top
--offset-bottom   -> --internal-offset-bottom

These are managed by ion-content itself (keyboard avoidance and header/footer offsets) and were never intended for consumer override. There is no replacement - any code that was setting them directly should be removed.

  1. Theme classes: Remove any instances that target the theme classes: ion-content.md, ion-content.ios.

Other information

Previews:

The framework (Angular, React, Vue) test apps are not being styled correctly anymore because the new tokens are not being passed to them. This is expected until we can export the tokens as mentioned in the design doc. The functionality in those test apps are still working.

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 24, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
ionic-framework Ready Ready Preview, Comment May 8, 2026 5:33pm

Request Review

flex-shrink: 2;
overscroll-behavior-y: contain;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
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.

-webkit-overflow-scrolling has become obsolete since iOS 13.

contain: size style;
}

:host(.ion-color) .inner-scroll {
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.

There seems to be a lot deleted, but they were just moved around in the file.

@Method()
async getBackgroundElement(): Promise<HTMLElement> {
if (!this.backgroundContentEl) {
await new Promise((resolve) => componentOnReady(this.el, resolve));
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.

Created a util for this since a lot of components are using the same code

:host(.ion-color) .inner-scroll {
background: color.current-color(base);
color: color.current-color(contrast);
}
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.

I didn't make this customizable because all themes are using the same styles. If the community requests it, then we can update the recipe to include it.

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.

What is this referring to?

}

.transition-shadow {
@include mixins.position-horizontal(null, 0);
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.

We can now use this mixin instead of having to use left/right!

@thetaPC thetaPC marked this pull request as ready for review May 5, 2026 15:37
@thetaPC thetaPC requested a review from a team as a code owner May 5, 2026 15:37
@thetaPC thetaPC requested a review from gnbm May 5, 2026 15:37
@thetaPC thetaPC marked this pull request as draft May 5, 2026 16:58

:host(.ion-color) .inner-scroll {
background: color.current-color(base);
color: color.current-color(contrast);
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.

I was going to switch this to use foreground as stated in the ticket but that lead to unwanted changes like:

Image

The text color is not readable in some components. Others, it's too light and I question if it would pass accessibility.

Image

We should look into the foreground values again.

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 foreground color should only be used when it's a text-only component. So if setting color on the content changes its text but not its background. You can look at ion-text as an example of a text-only component that uses color. Since we are changing the background on this component, contrast is the correct text value.

@thetaPC thetaPC marked this pull request as ready for review May 5, 2026 18:09
Copy link
Copy Markdown
Member

@ShaneK ShaneK left a comment

Choose a reason for hiding this comment

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

Looking awesome! Mostly just have a few clarafication/documentation questions

Comment thread core/src/components/content/content.scss
Comment thread core/src/css/core.scss Outdated
Comment thread core/src/components/content/content.scss
Comment thread core/src/components/content/content.scss Outdated
Comment thread core/src/themes/ios/default.tokens.ts Outdated
@thetaPC thetaPC requested review from ShaneK and removed request for gnbm May 7, 2026 00:53
Copy link
Copy Markdown
Member

@ShaneK ShaneK left a comment

Choose a reason for hiding this comment

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

Looks good to me! Just a question and a nit, but non-blocking

Comment thread core/src/global/config.ts Outdated
Comment thread core/src/components/content/content.scss
Copy link
Copy Markdown
Member

@brandyscarney brandyscarney left a comment

Choose a reason for hiding this comment

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

Looking good! Minor changes requested.

Comment on lines +49 to +52
$padding-top: var(--padding-top, var(--ion-content-padding-top, 0px));
$padding-end: var(--padding-end, var(--ion-content-padding-end, 0px));
$padding-bottom: var(--padding-bottom, var(--ion-content-padding-bottom, 0px));
$padding-start: var(--padding-start, var(--ion-content-padding-start, 0px));
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.

Why did we go this route instead of having the .ion-padding class set --ion-content-padding-top, --ion-content-padding-end, etc directly?

customTheme: {
components: {
IonContent: {
background: '#f1f1f1',
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.

How do we determine when this should be used over just keeping the css that was here and updating the variable?

<style>
  ion-content {
    --ion-content-background: #f1f1f1;
  }
</style>

Comment thread BREAKING.md
- Specific theme classes (e.g., `ion-chip.md`) are no longer supported. Style modifications based on the active theme must be implemented using theme tokens rather than direct class targeting.
- The `border-radius` of the `ios` and `md` chip now defaults to `10px` and `8px`, respectively, instead of `16px` in accordance with the iOS and Material Design 3 guidelines. To revert to the previous appearance, set the `shape` to `"round"`, or override the `IonChip.shape.round.border.radius` to specify a different value for global styles and `--ion-chip-shape-round-border-radius` for component-specific styles.

<h4 id="version-9x-content">Content</h4>
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.

Could we clean this section up a bit? It's kind of hard to hard to read through. Maybe we can use a numbered list or dividers?

Comment thread core/src/utils/helpers.ts
* component readiness before accessing internal refs (e.g. in early lifecycle
* hooks like Vue onMounted with the custom elements build).
*/
export const waitForComponent = <T extends Element>(el: T): Promise<T> => {
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.

Nit: should this be called waitForComponentReady?

--offset-bottom: 0px;
--overflow: auto;
/**
* TODO(FW-6698): Remove this comment in v10.
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.

Do we need this comment to begin with?

:host(.ion-color) .inner-scroll {
background: color.current-color(base);
color: color.current-color(contrast);
}
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.

What is this referring to?


.scroll-y {
overflow-y: var(--overflow);
overflow-y: var(--ion-content-overflow);
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.

Should this fall back to auto?


.scroll-x {
overflow-x: var(--overflow);
overflow-x: var(--ion-content-overflow);
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.

Should this fall back to auto?


:host(.ion-color) .inner-scroll {
background: color.current-color(base);
color: color.current-color(contrast);
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 foreground color should only be used when it's a text-only component. So if setting color on the content changes its text but not its background. You can look at ion-text as an example of a text-only component that uses color. Since we are changing the background on this component, contrast is the correct text value.

transform: translateZ(0);
}

// Content: iOS Mode Transition
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.

Maybe we should add this on to FW-7295?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

package: angular @ionic/angular package package: core @ionic/core package

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants