-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add multi-theme support to Polaris #10290
Conversation
This PR introduces multi-theme support to `@shopify/polaris-tokens` by implementing a base/variant architecture. The base theme serves as the foundation for any number of variant themes. Variant themes have two primary constraints in that they can only extend the base theme and are the only themes exposed to consumers. One advantage of this pattern is that it simplifies system changes by flattening the relationship between themes. There is no need to consider deeply inherited themes, making updates more predictable. <details> <summary>Deeply inherited vs base/variant themes</summary> **❌ With deeply inherited themes** ```mermaid flowchart TD A[Base Theme] A --> B[Light Theme] B --> C[Light High Contrast Theme] A --> D[Dark Theme] D --> E[Dark High Contrast Theme] A --> F[Dim Theme] A --> G[etc...] ``` **✅ With base/variant themes** ```mermaid flowchart TD A[Base Theme] A --> B[Light Theme] A --> C[Light High Contrast Theme] A --> D[Dark Theme] A --> E[Dark High Contrast Theme] A --> F[Dim Theme] A --> G[etc...] ``` </details> ## Implementation Details This PR focuses on implementing backward compatible multi-theme support for Polaris tokens. It enables the addition of new variant themes while promoting incremental adoption and ensuring compatibility with existing implementations. ### Directory Structure The `src/themes` directory consists of the following components: - Internal base theme and public variant themes. - Default variant theme, which is essentially an alias of the base theme, aliased to the light theme. - Light uplift theme, which is the first variant theme that simply ported `valueExperimental` tokens from our token groups. `valueExperimental` properties were initially introduced as a short-term solution during the SE23 upgrades, allowing token overrides by replacing `value` with `valueExperimental` at build time. - Note: `valueExperimental` references in token groups were kept in this PR to prevent breaking changes and maintain backward compatibility. Although these properties should be allowed to remove, a couple usages were found across the organization. - TypeScript definitions describing the shape of themes, partial themes, theme names, etc. - Utilities for validating themes and consistently creating theme classes/selectors. ### Stylesheet Generation The `toStyleSheet` build step has been updated to generate a backward-compatible [styles.scss](https://unpkg.com/browse/@shopify/polaris-tokens@7.5.2/dist/css/styles.css) file. This file assigns the default variant theme custom properties (`light`) to the `:root` selector and the `light-uplift` variant theme custom properties to the `html.Polaris-Summer-Editions-2023` selector. It should be noted that while the stylesheet remains backward compatible in this PR, the plan is to propose removing the `:root` selector in the next major release. This change aims to flatten the specificity of all variant themes since the `:where()` selector cannot currently be leveraged. For example: - Current specificity breakdown: `:root` has a specificity of 10, `html.Polaris-Summer-Editions-2023` has a specificity of 11. - Next specificity breakdown: `html.p-theme-light` has a specificity of 11, `html.Polaris-Summer-Editions-2023` has a specificity of 11, etc. ### Variant Theme Functionality Variant themes override a subset of the base theme tokens by assigning them to a `partials` object. This `partials` object is then deep merged with the base theme for JavaScript access. Additionally, the `partials` object is used in the `toStyleSheet` build step to generate custom properties and insert them into the variant theme selector. Consequently, variant theme selectors only contain the subset of overridden custom properties, ensuring a minimal stylesheet. It should be noted that if the strategy shifts from global themes to allowing nested themes, the approach would likely change to inserting all custom properties in each class. However, even in such cases, the performance impact of having a large collection of custom properties is expected to be minimal. **Example `styles.scss` updates** ```scss :root { /* Default variant theme custom properties (light) */ } html.Polaris-Summer-Editions-2023 { /* Variant theme custom properties */ /* Note: The above selector is special cased for backward compatibility. See below for updated theme selector structure. */ } html.p-theme-light-high-contrast { /* Variant theme custom properties */ /* Note: Variant theme selectors contain a subset of custom properties overriding the base theme */ } html.p-theme-dark {/* ... */} html.p-theme-dark-high-contrast {/* ... */} html.p-theme-dim {/* ... */} ```
Rework of PR #10153: - The dedicated `@shopify/polaris-tokens/themes` entry point has been removed in favor of accessing the `themes` directly from `@shopify/polaris-tokens`, avoiding `package.json` `exports` and `typesVersions` changes. - Instead of using arguments for each asset builder function, the dependent modules are now accessed directly, reducing unnecessary TypeScript boilerplate. - References to `themes.light` have been replaced with a `themeDefault` alias throughout the library and build scripts. This change should improve clarity and consistency in communicating the behavior. - The build function `toTokenValues` has been renamed to `toValues` as it now operates on `themes` rather than `tokens`. - The root entry point has been updated to expose both `themes` and `themePartials`. The `themes` object contains complete theme objects for each theme, serving as a replacement for the previously proposed `/themes` entry point. Currently, it is designed for building low-level integrations, such as React Native themes. On the other hand, the `themePartials` object contains partial override objects for each theme. Originally used in the `toStyleSheet` build script to generate the smallest possible CSS rule of themed custom properties, it is now being exposed to enable the creation of client-side integrations with minimal impact on bundle size. Note that a separate PR will introduce a `createThemeVariant` hook that accepts partials and dynamically constructs the complete theme object at runtime. **Before:** ```ts import {metadata} from '@shopify/polaris-tokens'; metadata.color['color-bg'].valueExperimental ``` ```ts import * as themes from '@shopify/polaris-tokens/themes'; themes.polarisSummerEditions2023.color['color-bg'] ``` **After:** ```ts import {themes} from '@shopify/polaris-tokens'; themes['Polaris-Summer-Editions-2023'].color['color-bg'] ``` --------- Co-authored-by: Laura Griffee <laura@mailzone.com>
Rework of #10229. - To enhance clarity and avoid ambiguity, we have reintroduced the convention of prefixing all `theme` related objects and types with `meta`. While I was trying to steer us away from this convention, keeping it establishes consistency and makes it easier to differentiate between `theme` constructs with and without metadata. - Introduced a new `createThemeVariant` utility, which accepts a theme partial and constructs a complete `theme` object. This utility simplifies the process of creating theme variants and ensures that all required properties are included. - The AppProvider has been updated to incorporate a new `ThemeContext.Provider` and associated `useTheme` hook. This provider utilizes the `createThemeVariant` utility to expose the active theme to child components. This eliminates the need for several previously proposed exports and hooks, including `themeVars`, `useThemeName`, `useThemeVarDecl`, and `useCSSDecl`. **Before:** ```ts import {themeVars} from '@shopify/polaris'; themeVars.color['color-bg'] // 'var(--p-color-bg)' ``` ```ts import {useThemeVarDecl} from '@shopify/polaris'; function App() { const colorBgDecl = useThemeVarDecl('--p-color-bg'); colorBgDecl // { status: 'success', value: '#123', ... } } ``` **After:** ```ts import {useTheme} from '@shopify/polaris'; function App() { const theme = useTheme() theme.color['color-bg'] // '#123' } ``` --------- Co-authored-by: Laura Griffee <laura@mailzone.com>
/snapit |
🫰✨ Thanks @aaronccasanova! Your snapshots have been published to npm. Test the snapshots by updating your yarn add @shopify/polaris-migrator@0.0.0-snapshot-release-20230831232228 yarn add @shopify/polaris@0.0.0-snapshot-release-20230831232228 yarn add @shopify/polaris-tokens@0.0.0-snapshot-release-20230831232228 yarn add @shopify/stylelint-polaris@0.0.0-snapshot-release-20230831232228 |
] | ||
], | ||
"dependencies": { | ||
"deepmerge": "^4.3.1" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is needed for backward compatibility with the existing metadata
export and can be moved to a devDependency
post v12.
Object.entries(themeDefault).map(createExport), | ||
createExport(['tokens', themeDefault]), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: These exports will be removed in the v12 release as they will no longer be relevant in the new theming architecture.
/snapit |
🫰✨ Thanks @aaronccasanova! Your snapshots have been published to npm. Test the snapshots by updating your yarn add @shopify/polaris-migrator@0.0.0-snapshot-release-20230905190500 yarn add @shopify/polaris@0.0.0-snapshot-release-20230905190500 yarn add @shopify/polaris-tokens@0.0.0-snapshot-release-20230905190500 yarn add @shopify/stylelint-polaris@0.0.0-snapshot-release-20230905190500 |
This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated. # Releases ## @shopify/polaris@11.16.0 ### Minor Changes - [#10290](#10290) [`5939b49cf`](5939b49) Thanks [@aaronccasanova](https://github.com/aaronccasanova)! - - Added multi-theme support - Added multi-theme build artifacts - Added multi-theme runtime access ### Patch Changes - Updated dependencies \[[`5939b49cf`](5939b49)]: - @shopify/polaris-tokens@7.6.0 ## @shopify/polaris-tokens@7.6.0 ### Minor Changes - [#10290](#10290) [`5939b49cf`](5939b49) Thanks [@aaronccasanova](https://github.com/aaronccasanova)! - - Added multi-theme support - Added multi-theme build artifacts - Added multi-theme runtime access ## @shopify/polaris-migrator@0.22.1 ### Patch Changes - Updated dependencies \[[`5939b49cf`](5939b49)]: - @shopify/polaris-tokens@7.6.0 - @shopify/stylelint-polaris@14.0.1 ## @shopify/stylelint-polaris@14.0.1 ### Patch Changes - Updated dependencies \[[`5939b49cf`](5939b49)]: - @shopify/polaris-tokens@7.6.0 ## polaris.shopify.com@0.57.4 ### Patch Changes - Updated dependencies \[[`5939b49cf`](5939b49)]: - @shopify/polaris@11.16.0 - @shopify/polaris-tokens@7.6.0 Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Continuation of #10290 This PR contains breaking changes in both Polaris and Polaris tokens to enable global multi-theme support. ### Polaris changes: - Updated the `AppProvider` component - **Breaking:** Removed `features` class name toggle from `setRootAttributes` - Added a new `theme` prop which accepts a Polaris tokens `ThemeName`. This prop is used for: - Toggling the theme class names on the root element, and - Setting the theme object on the root `ThemeContext.Provider` - Added global theme toggle to Storybook ### Polaris tokens changes: - **Breaking:** Moved the `light-uplift` overrides to base token groups (e.g. `light-uplift` is now the default theme) - Normalized specificity of the default theme > [!NOTE] > Currently, our browser support requirements don't allow us to use the `:where()` selector to flatten our theme class name specificity to `0`. Therefore, I'm proposing a solution that normalizes the specificity to `11`. **Theme selector breakdown:** - `:root, html.p-<default-variant-theme>{...}` - The generated stylesheet outputs the default theme custom properties to both the `:root` selector and a dedicated `html.p-*` selector. This allows the page to apply the default theme via `:root` initially (specificity `10`) and then `html.p-*` when the `AppProvider` initializes (specificity `11`) - `html.p-<variant-theme-1>{...}html.p-<variant-theme-2> {...}html.p-...` - All other variant themes have specificity `11` - Thus, theme classes in the Admin should always have specificity `11` after rendering the `AppProvider` **Example** theme selector output: ![Screenshot 2023-09-12 at 3 12 07 PM](https://github.com/Shopify/polaris/assets/32409546/88166226-512c-4cd5-a1d0-b77305929d5b) - Initialized a new `light-high-contrast-experimental` theme https://github.com/Shopify/polaris/assets/32409546/ad228532-e277-4107-af5c-95511f3e3297 --------- Co-authored-by: Lo Kim <lo.kim@shopify.com>
This PR consolidates the integration of three feature branches that collectively introduce a new base/variant theme architecture to Shopify Polaris. The merged PRs introduce multi-theme support, updated build artifacts, and a new `useTheme` hook for runtime Polaris tokens access. Below, you will find a brief overview of each PR, with links to the original PRs for a more detailed review. ## [Add multi-theme support to Polaris tokens](Shopify#10103) This PR introduces multi-theme support to `@shopify/polaris-tokens` by implementing a base/variant architecture. The base theme serves as the foundation for multiple variant themes, which extend the base theme and are the only themes exposed to consumers. Simplifying system changes by flattening the relationship between themes is the primary advantage of this pattern. The PR also includes implementation details like directory structure, stylesheet generation, and variant theme functionality. **Example `styles.scss` updates** ```scss :root { /* Default variant theme custom properties (light) */ } html.Polaris-Summer-Editions-2023 { /* Variant theme custom properties */ /* Note: The above selector is special cased for backward compatibility. See below for updated theme selector structure. */ } html.p-theme-light-high-contrast { /* Variant theme custom properties */ /* Note: Variant theme selectors contain a subset of custom properties overriding the base theme */ } html.p-theme-dark {/* ... */} html.p-theme-dark-high-contrast {/* ... */} html.p-theme-dim {/* ... */} ``` ## [Add multi-theme build artifacts to Polaris tokens](Shopify#10250) This PR is a rework of Shopify#10153, redefining how the `themes` are accessed from `@shopify/polaris-tokens`. It introduces changes on how asset builder functions are written and replaces reference to `themes.light` with a `themeDefault` alias. ```ts import {themes} from '@shopify/polaris-tokens'; themes['Polaris-Summer-Editions-2023'].color['color-bg'] ``` > Note: Future releases can reduce the bundle size impact of including all themes on one object and enable more flexibility (for example with a `createTheme` util that deep merges variant theme partials). However, that is not needed at this time and can be introduced in a minor release. ## [Add `useTheme` hook for runtime Polaris tokens access](Shopify#10252) This PR is a rework of Shopify#10229, brings updates to the `AppProvider` by incorporating a new `ThemeContext.Provider` and an associated `useTheme` hook. ```tsx import {useTheme} from '@shopify/polaris'; function App() { const theme = useTheme() theme.color['color-bg'] // 'Shopify#123' } ``` --------- Co-authored-by: Laura Griffee <laura@mailzone.com>
This PR consolidates the integration of three feature branches that collectively introduce a new base/variant theme architecture to Shopify Polaris. The merged PRs introduce multi-theme support, updated build artifacts, and a new
useTheme
hook for runtime Polaris tokens access. Below, you will find a brief overview of each PR, with links to the original PRs for a more detailed review.Add multi-theme support to Polaris tokens
This PR introduces multi-theme support to
@shopify/polaris-tokens
by implementing a base/variant architecture. The base theme serves as the foundation for multiple variant themes, which extend the base theme and are the only themes exposed to consumers. Simplifying system changes by flattening the relationship between themes is the primary advantage of this pattern. The PR also includes implementation details like directory structure, stylesheet generation, and variant theme functionality.Example
styles.scss
updatesAdd multi-theme build artifacts to Polaris tokens
This PR is a rework of #10153, redefining how the
themes
are accessed from@shopify/polaris-tokens
. It introduces changes on how asset builder functions are written and replaces reference tothemes.light
with athemeDefault
alias.Add
useTheme
hook for runtime Polaris tokens accessThis PR is a rework of #10229, brings updates to the
AppProvider
by incorporating a newThemeContext.Provider
and an associateduseTheme
hook.