Skip to content

Commit

Permalink
[system] Add applyStyles() to theme (#40667)
Browse files Browse the repository at this point in the history
  • Loading branch information
siriwatknp authored Jan 24, 2024
1 parent 36c7500 commit 2a29460
Show file tree
Hide file tree
Showing 17 changed files with 182 additions and 65 deletions.
16 changes: 1 addition & 15 deletions docs/src/modules/brandingTheme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -403,21 +403,7 @@ export const getDesignTokens = (mode: 'light' | 'dark') =>
* ]
*/
applyDarkStyles(css: Parameters<ApplyDarkStyles>[0]) {
if ((this as Theme).vars) {
// If CssVarsProvider is used as a provider,
// returns ':where([data-mui-color-scheme="light|dark"]) &'
const selector = (this as Theme)
.getColorSchemeSelector('dark')
.replace(/(\[[^\]]+\])/, ':where($1)');
return {
[selector]: css,
};
}
if ((this as Theme).palette.mode === 'dark') {
return css;
}

return undefined;
return (this as Theme).applyStyles('dark', css);
},
} as ThemeOptions);

Expand Down
2 changes: 1 addition & 1 deletion packages/mui-joy/src/styles/defaultTheme.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ describe('defaultTheme', () => {
'unstable_sx',
'shouldSkipGeneratingVar',
'generateCssVars',
'applyDarkStyles',
'applyStyles',
]).to.includes(field);
});
});
Expand Down
27 changes: 25 additions & 2 deletions packages/mui-joy/src/styles/extendTheme.test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as React from 'react';
import { expect } from 'chai';
import { createRenderer } from '@mui-internal/test-utils';
import { extendTheme, useTheme, CssVarsProvider } from '@mui/joy/styles';
import { extendTheme, useTheme, CssVarsProvider, styled } from '@mui/joy/styles';

describe('extendTheme', () => {
it('the output contains required fields', () => {
Expand Down Expand Up @@ -31,7 +31,7 @@ describe('extendTheme', () => {
'unstable_sx',
'shouldSkipGeneratingVar',
'generateCssVars',
'applyDarkStyles',
'applyStyles',
]).to.includes(field);
});
});
Expand Down Expand Up @@ -177,5 +177,28 @@ describe('extendTheme', () => {
borderRadius: 'var(--joy-radius-md)',
});
});

it('applyStyles', () => {
const attribute = 'data-custom-color-scheme';
let darkStyles = {};
const Test = styled('div')(({ theme }) => {
darkStyles = theme.applyStyles('dark', {
backgroundColor: 'rgba(0, 0, 0, 0)',
});
return null;
});

render(
<CssVarsProvider attribute={attribute}>
<Test />
</CssVarsProvider>,
);

expect(darkStyles).to.deep.equal({
[`*:where([${attribute}="dark"]) &`]: {
backgroundColor: 'rgba(0, 0, 0, 0)',
},
});
});
});
});
20 changes: 2 additions & 18 deletions packages/mui-joy/src/styles/extendTheme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import {
unstable_createGetCssVar as systemCreateGetCssVar,
unstable_styleFunctionSx as styleFunctionSx,
SxConfig,
CSSObject,
} from '@mui/system';
import { unstable_applyStyles as applyStyles } from '@mui/system/createTheme';
import defaultSxConfig from './sxConfig';
import colors from '../colors';
import defaultShouldSkipGeneratingVar from './shouldSkipGeneratingVar';
Expand Down Expand Up @@ -565,23 +565,6 @@ export default function extendTheme(themeOptions?: CssVarsThemeOptions): Theme {
cssVarPrefix,
getCssVar,
spacing: createSpacing(spacing),
applyDarkStyles(css: CSSObject) {
if ((this as Theme).vars) {
// If CssVarsProvider is used as a provider,
// returns ':where([data-mui-color-scheme="light|dark"]) &'
const selector = (this as Theme)
.getColorSchemeSelector('dark')
.replace(/(\[[^\]]+\])/, ':where($1)');
return {
[selector]: css,
};
}
if ((this as Theme).palette.mode === 'dark') {
return css;
}

return {};
},
} as unknown as Theme; // Need type casting due to module augmentation inside the repo

/**
Expand Down Expand Up @@ -680,6 +663,7 @@ export default function extendTheme(themeOptions?: CssVarsThemeOptions): Theme {
};

theme.shouldSkipGeneratingVar = shouldSkipGeneratingVar;
theme.applyStyles = applyStyles;

return theme;
}
3 changes: 2 additions & 1 deletion packages/mui-joy/src/styles/types/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
SystemProps as SystemSystemProps,
CSSObject,
SxConfig,
ApplyStyles,
} from '@mui/system';
import { DefaultColorScheme, ExtendedColorScheme } from './colorScheme';
import { ColorSystem } from './colorSystem';
Expand Down Expand Up @@ -118,7 +119,7 @@ export interface Theme extends ThemeScales, RuntimeColorSystem {
shouldSkipGeneratingVar: (keys: string[], value: string | number) => boolean;
unstable_sxConfig: SxConfig;
unstable_sx: (props: SxProps) => CSSObject;
applyDarkStyles: (css: CSSObject) => CSSObject;
applyStyles: ApplyStyles<DefaultColorScheme | ExtendedColorScheme>;
}

export type SxProps = SystemSxProps<Theme>;
Expand Down
2 changes: 1 addition & 1 deletion packages/mui-material/src/Avatar/Avatar.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ const AvatarRoot = styled('div', {
}
: {
backgroundColor: theme.palette.grey[400],
...theme.applyDarkStyles({ backgroundColor: theme.palette.grey[600] }),
...theme.applyStyles('dark', { backgroundColor: theme.palette.grey[600] }),
}),
},
},
Expand Down
1 change: 0 additions & 1 deletion packages/mui-material/src/styles/createTheme.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ export interface Theme extends BaseTheme {
components?: Components<BaseTheme>;
unstable_sx: (props: SxProps<Theme>) => CSSObject;
unstable_sxConfig: SxConfig;
applyDarkStyles: (css: CSSObject) => CSSObject;
}

/**
Expand Down
15 changes: 0 additions & 15 deletions packages/mui-material/src/styles/createTheme.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,21 +43,6 @@ function createTheme(options = {}, ...args) {
typography: createTypography(palette, typographyInput),
transitions: createTransitions(transitionsInput),
zIndex: { ...zIndex },
applyDarkStyles(css) {
if (this.vars) {
// If CssVarsProvider is used as a provider,
// returns ':where([data-mui-color-scheme="light|dark"]) &'
const selector = this.getColorSchemeSelector('dark').replace(/(\[[^\]]+\])/, ':where($1)');
return {
[selector]: css,
};
}
if (this.palette.mode === 'dark') {
return css;
}

return {};
},
});

muiTheme = deepmerge(muiTheme, other);
Expand Down
8 changes: 4 additions & 4 deletions packages/mui-material/src/styles/createTheme.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ describe('createTheme', () => {
});
});

it('should apply dark styles when using applyDarkStyles if mode="dark"', function test() {
it('should apply dark styles when using applyStyles if mode="dark"', function test() {
const darkTheme = createTheme({
palette: {
mode: 'dark',
Expand All @@ -260,7 +260,7 @@ describe('createTheme', () => {

const Test = styled('div')(({ theme }) => ({
backgroundColor: 'rgb(255, 255, 255)',
...theme.applyDarkStyles({
...theme.applyStyles('dark', {
backgroundColor: 'rgb(0, 0, 0)',
}),
}));
Expand All @@ -276,12 +276,12 @@ describe('createTheme', () => {
});
});

it('should apply dark styles when using applyDarkStyles if mode="light"', function test() {
it('should not apply dark styles when using applyStyles if mode="light"', function test() {
const lightTheme = createTheme();

const Test = styled('div')(({ theme }) => ({
backgroundColor: 'rgb(255, 255, 255)',
...theme.applyDarkStyles({
...theme.applyStyles('dark', {
backgroundColor: 'rgb(0, 0, 0)',
}),
}));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { OverridableStringUnion } from '@mui/types';
import { SxConfig, SxProps, CSSObject } from '@mui/system';
import { SxConfig, SxProps, CSSObject, ApplyStyles } from '@mui/system';
import { ThemeOptions, Theme } from './createTheme';
import { Palette, PaletteOptions } from './createPalette';
import { Shadows } from './shadows';
Expand Down Expand Up @@ -432,6 +432,7 @@ export interface CssVarsTheme extends ColorSystem {
shouldSkipGeneratingVar: (keys: string[], value: string | number) => boolean;
unstable_sxConfig: SxConfig;
unstable_sx: (props: SxProps<CssVarsTheme>) => CSSObject;
applyStyles: ApplyStyles<SupportedColorScheme>;
}

/**
Expand All @@ -443,4 +444,4 @@ export interface CssVarsTheme extends ColorSystem {
export default function experimental_extendTheme(
options?: CssVarsThemeOptions,
...args: object[]
): Omit<Theme, 'palette'> & CssVarsTheme;
): Omit<Theme, 'palette' | 'applyStyles'> & CssVarsTheme;
Original file line number Diff line number Diff line change
Expand Up @@ -505,25 +505,24 @@ describe('experimental_extendTheme', () => {
});
});

it('should use the right selector with applyDarkStyles', function test() {
const defaultTheme = extendTheme();
it('should use the right selector with applyStyles', function test() {
const attribute = 'data-custom-color-scheme';
let darkStyles = {};
const Test = styled('div')(({ theme }) => {
darkStyles = theme.applyDarkStyles({
darkStyles = theme.applyStyles('dark', {
backgroundColor: 'rgba(0, 0, 0, 0)',
});
return null;
});

render(
<CssVarsProvider attribute={attribute} theme={defaultTheme}>
<CssVarsProvider attribute={attribute}>
<Test />
</CssVarsProvider>,
);

expect(darkStyles).to.deep.equal({
[`:where([${attribute}="dark"]) &`]: {
[`*:where([${attribute}="dark"]) &`]: {
backgroundColor: 'rgba(0, 0, 0, 0)',
},
});
Expand Down
85 changes: 85 additions & 0 deletions packages/mui-system/src/createTheme/applyStyles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { CSSObject } from '@mui/styled-engine';

export interface ApplyStyles<K extends string> {
(key: K, styles: CSSObject): CSSObject;
}

/**
* A universal utility to style components with multiple color modes. Always use it from the theme object.
* It works with:
* - [Basic theme](https://mui.com/material-ui/customization/dark-mode/)
* - [CSS theme variables](https://mui.com/material-ui/experimental-api/css-theme-variables/overview/)
* - Zero-runtime engine
*
* Tips: Use an array over object spread and place `theme.applyStyles()` last.
*
* ✅ [{ background: '#e5e5e5' }, theme.applyStyles('dark', { background: '#1c1c1c' })]
*
* 🚫 { background: '#e5e5e5', ...theme.applyStyles('dark', { background: '#1c1c1c' })}
*
* @example
* 1. using with `styled`:
* ```jsx
* const Component = styled('div')(({ theme }) => [
* { background: '#e5e5e5' },
* theme.applyStyles('dark', {
* background: '#1c1c1c',
* color: '#fff',
* }),
* ]);
* ```
*
* @example
* 2. using with `sx` prop:
* ```jsx
* <Box sx={theme => [
* { background: '#e5e5e5' },
* theme.applyStyles('dark', {
* background: '#1c1c1c',
* color: '#fff',
* }),
* ]}
* />
* ```
*
* @example
* 3. theming a component:
* ```jsx
* extendTheme({
* components: {
* MuiButton: {
* styleOverrides: {
* root: ({ theme }) => [
* { background: '#e5e5e5' },
* theme.applyStyles('dark', {
* background: '#1c1c1c',
* color: '#fff',
* }),
* ],
* },
* }
* }
* })
*```
*/
export default function applyStyles<K extends string>(key: K, styles: CSSObject) {
// @ts-expect-error this is 'any' type
const theme = this as {
palette: { mode: 'light' | 'dark' };
vars?: any;
getColorSchemeSelector?: (scheme: string) => string;
};
if (theme.vars && typeof theme.getColorSchemeSelector === 'function') {
// If CssVarsProvider is used as a provider,
// returns '* :where([data-mui-color-scheme="light|dark"]) &'
const selector = theme.getColorSchemeSelector(key).replace(/(\[[^\]]+\])/, '*:where($1)');
return {
[selector]: styles,
};
}
if (theme.palette.mode === key) {
return styles;
}

return {};
}
2 changes: 2 additions & 0 deletions packages/mui-system/src/createTheme/createTheme.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Breakpoints, BreakpointsOptions } from './createBreakpoints';
import { Shape, ShapeOptions } from './shape';
import { Spacing, SpacingOptions } from './createSpacing';
import { SxConfig, SxProps } from '../styleFunctionSx';
import { ApplyStyles } from './applyStyles';

export { Breakpoint, BreakpointOverrides } from './createBreakpoints';

Expand Down Expand Up @@ -35,6 +36,7 @@ export interface Theme {
mixins?: unknown;
typography?: unknown;
zIndex?: unknown;
applyStyles: ApplyStyles<'light' | 'dark'>;
unstable_sxConfig: SxConfig;
unstable_sx: (props: SxProps<Theme>) => CSSObject;
}
Expand Down
3 changes: 3 additions & 0 deletions packages/mui-system/src/createTheme/createTheme.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import shape from './shape';
import createSpacing from './createSpacing';
import styleFunctionSx from '../styleFunctionSx/styleFunctionSx';
import defaultSxConfig from '../styleFunctionSx/defaultSxConfig';
import applyStyles from './applyStyles';

function createTheme(options = {}, ...args) {
const {
Expand All @@ -29,6 +30,8 @@ function createTheme(options = {}, ...args) {
other,
);

muiTheme.applyStyles = applyStyles;

muiTheme = args.reduce((acc, argument) => deepmerge(acc, argument), muiTheme);

muiTheme.unstable_sxConfig = {
Expand Down
Loading

0 comments on commit 2a29460

Please sign in to comment.