Skip to content
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

[TypeScript] Add CSS vars type augmentation for Material UI #33211

Merged
merged 22 commits into from Jun 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -27,7 +27,7 @@ The structure of this object is nearly identical to the theme structure, the onl

## Usage

`Experimental_CssVarsProvider` is a new experimental provider that attaches all generated CSS variables to the theme and puts them in React's context. Children elements under this provider will also be able to read the CSS variables from the theme.
`Experimental_CssVarsProvider` is a new experimental provider that attaches all generated CSS variables to the theme and puts them in React's context. Children elements under this provider will also be able to read the CSS variables from the `theme.vars`.

```js
import { Experimental_CssVarsProvider as CssVarsProvider } from '@mui/material/styles';
Expand All @@ -37,6 +37,8 @@ function App() {
}
```

If you use TypeScript, check out the [theme types setup](#typescript).

### Customizing components

Because the CSS variables API is an experimental feature, it is currently only supported by the `Button` component.
Expand Down Expand Up @@ -196,6 +198,26 @@ export function onRenderBody({ setPreBodyComponents }) {
}
```

### TypeScript

You need to import the theme augmentation to include `theme.vars` and other utilities related to CSS variables to the theme:

```ts
// this can be the root file of you application
import type {} from '@mui/material/themeCssVarsAugmentation';
```

Then, you will be able to access `theme.vars` in any of the styling APIs, for example the `styled`:

```ts
import { styled } from '@mui/material/styles';

const StyledComponent = styled('button')(({ theme }) => ({
// typed-safe
color: theme.vars.palette.primary.main,
}));
```

## API

### `<CssVarsProvider>` props
Expand Down
2 changes: 1 addition & 1 deletion packages/mui-material/src/FilledInput/FilledInput.js
Expand Up @@ -98,7 +98,7 @@ const FilledInputRoot = styled(InputBaseRoot, {
'&:before': {
borderBottom: `1px solid ${
theme.vars
? `rgba(${theme.vars.palette.common.onBackgroundChannel} / ${theme.vars.opacity.inputTouchBottomLine})`
? `rgba(${theme.vars.palette.common.onBackgroundChannel} / ${theme.vars.opacity.inputUnderline})`
: bottomLineColor
}`,
left: 0,
Expand Down
2 changes: 1 addition & 1 deletion packages/mui-material/src/Input/Input.js
Expand Up @@ -45,7 +45,7 @@ const InputRoot = styled(InputBaseRoot, {
const light = theme.palette.mode === 'light';
let bottomLineColor = light ? 'rgba(0, 0, 0, 0.42)' : 'rgba(255, 255, 255, 0.7)';
if (theme.vars) {
bottomLineColor = `rgba(${theme.vars.palette.common.onBackgroundChannel} / ${theme.vars.opacity.inputTouchBottomLine})`;
bottomLineColor = `rgba(${theme.vars.palette.common.onBackgroundChannel} / ${theme.vars.opacity.inputUnderline})`;
}
return {
position: 'relative',
Expand Down
28 changes: 0 additions & 28 deletions packages/mui-material/src/styles/CssVarsProvider.d.ts

This file was deleted.

72 changes: 72 additions & 0 deletions packages/mui-material/src/styles/CssVarsProvider.spec.tsx
@@ -0,0 +1,72 @@
import * as React from 'react';
import {
experimental_extendTheme as extendTheme,
Experimental_CssVarsProvider as CssVarsProvider,
styled,
useTheme,
Overlays,
} from '@mui/material/styles';
import type {} from '@mui/material/themeCssVarsAugmentation';

const customTheme = extendTheme({
colorSchemes: {
light: {
opacity: {
inputPlaceholder: 0.1,
inputUnderline: 0.1,
},
overlays: Array(25).fill('') as Overlays,
palette: {
AppBar: {
darkBg: '',
darkColor: '',
defaultBg: '',
},
// @ts-expect-error
mode: '',
getContrastText: () => '',
tonalOffset: 1,
},
},
dark: {
opacity: {},
palette: {},
},
},
components: {
MuiButton: {
styleOverrides: {
root: ({ theme }) => ({
color: theme.vars.palette.primary.main,
}),
},
},
},
});

const TestStyled = styled('div')(({ theme }) => ({
// test that `theme.vars` works
color: theme.vars.palette.primary.main,
Copy link
Member

Choose a reason for hiding this comment

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

I am a bit confused, how those the types work here?

Copy link
Member Author

Choose a reason for hiding this comment

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

It tests that you can access theme.vars in styled. It would throw an error if it does not work. I will add a comment on this.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

Copy link
Member

Choose a reason for hiding this comment

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

I am a bit confused. In order for this to work developers need to import

import type {} from '@mui/material/themeCssVarsAugmentation';

How does it work here without importing it. This was my initial question.

Copy link
Member Author

Choose a reason for hiding this comment

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

Got it. The module augmentation is a part of the repo so that's why it is included without the import. I can add it to make it explicit.

Copy link
Member

Choose a reason for hiding this comment

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

Okay, let's do it :)

// test that `theme.getColorSchemeSelector` works
[theme.getColorSchemeSelector('dark')]: {
color: theme.vars.palette.common.onBackground,
},
}));

const TestUseTheme = () => {
const theme = useTheme();
// test that `theme` from useTheme has access to CSS vars
return <div style={{ background: theme.vars.palette.common.background }}>test</div>;
};

<CssVarsProvider theme={customTheme}>
<TestStyled
sx={(theme) => ({
// test that `theme` in sx has access to CSS vars
[theme.getColorSchemeSelector('dark')]: {
border: '1px solid',
borderColor: theme.vars.palette.divider,
},
})}
/>
</CssVarsProvider>;
2 changes: 1 addition & 1 deletion packages/mui-material/src/styles/CssVarsProvider.test.js
Expand Up @@ -200,7 +200,7 @@ describe('[Material UI] CssVarsProvider', () => {
expect(screen.getByTestId('opacity').textContent).to.equal(
JSON.stringify({
inputPlaceholder: 'var(--mui-opacity-inputPlaceholder)',
inputTouchBottomLine: 'var(--mui-opacity-inputTouchBottomLine)',
inputUnderline: 'var(--mui-opacity-inputUnderline)',
switchTrackDisabled: 'var(--mui-opacity-switchTrackDisabled)',
switchTrack: 'var(--mui-opacity-switchTrack)',
}),
Expand Down
@@ -1,6 +1,9 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { unstable_createCssVarsProvider as createCssVarsProvider } from '@mui/system';
import experimental_extendTheme from './experimental_extendTheme';
import experimental_extendTheme, {
SupportedColorScheme,
CssVarsTheme,
} from './experimental_extendTheme';
import createTypography from './createTypography';

const defaultTheme = experimental_extendTheme();
Expand All @@ -9,7 +12,7 @@ const {
CssVarsProvider: Experimental_CssVarsProvider,
useColorScheme,
getInitColorSchemeScript,
} = createCssVarsProvider({
} = createCssVarsProvider<SupportedColorScheme, CssVarsTheme>({
theme: defaultTheme,
attribute: 'data-mui-color-scheme',
modeStorageKey: 'mui-mode',
Expand All @@ -28,7 +31,8 @@ const {
return newTheme;
},
shouldSkipGeneratingVar: (keys) =>
!!keys[0].match(/(typography|mixins|breakpoints|direction|transitions)/),
!!keys[0].match(/(typography|mixins|breakpoints|direction|transitions)/) ||
(keys[0] === 'palette' && !!keys[1]?.match(/(mode|contrastThreshold|tonalOffset)/)),
siriwatknp marked this conversation as resolved.
Show resolved Hide resolved
});

export { useColorScheme, getInitColorSchemeScript, Experimental_CssVarsProvider };
107 changes: 0 additions & 107 deletions packages/mui-material/src/styles/createPalette.d.ts
Expand Up @@ -105,113 +105,6 @@ export interface Channels {
contrastTextChannel: string;
}

export interface PaletteWithChannels {
common: CommonColors & {
background: string;
onBackground: string;
backgroundChannel: string;
onBackgroundChannel: string;
};
mode: PaletteMode;
contrastThreshold: number;
tonalOffset: PaletteTonalOffset;
primary: PaletteColor & Channels;
secondary: PaletteColor & Channels;
error: PaletteColor & Channels;
warning: PaletteColor & Channels;
info: PaletteColor & Channels;
success: PaletteColor & Channels;
grey: Color & { darkChannel: string };
text: TypeText & { primaryChannel: string; secondaryChannel: string };
divider: TypeDivider;
dividerChannel: TypeDivider;
action: TypeAction & { activeChannel: string; selectedChannel: string };
background: TypeBackground;
getContrastText: (background: string) => string;
augmentColor: (options: PaletteAugmentColorOptions) => PaletteColor;
// component tokens
Alert: {
errorColor: string;
infoColor: string;
successColor: string;
warningColor: string;
errorFilledBg: string;
infoFilledBg: string;
successFilledBg: string;
warningFilledBg: string;
errorStandardBg: string;
infoStandardBg: string;
successStandardBg: string;
warningStandardBg: string;
errorIconColor: string;
infoIconColor: string;
successIconColor: string;
warningIconColor: string;
};
AppBar: {
defaultBg: string;
darkBg: string;
darkColor: string;
};
Avatar: {
defaultBg: string;
};
Chip: {
defaultBorder: string;
defaultAvatarColor: string;
defaultIconColor: string;
};
FilledInput: {
bg: string;
hoverBg: string;
disabledBg: string;
};
LinearProgress: {
primaryBg: string;
secondaryBg: string;
errorBg: string;
infoBg: string;
successBg: string;
warningBg: string;
};
Slider: {
primaryTrack: string;
secondaryTrack: string;
errorTrack: string;
infoTrack: string;
successTrack: string;
warningTrack: string;
};
SnackbarContent: {
bg: string;
};
SpeedDialAction: {
fabHoverBg: string;
};
StepConnector: {
border: string;
};
StepContent: {
border: string;
};
Switch: {
defaultColor: string;
defaultDisabledColor: string;
primaryDisabledColor: string;
secondaryDisabledColor: string;
errorDisabledColor: string;
infoDisabledColor: string;
successDisabledColor: string;
warningDisabledColor: string;
};
TableCell: {
border: string;
};
Tooltip: {
bg: string;
};
}

export type PartialTypeObject = { [P in keyof TypeObject]?: Partial<TypeObject[P]> };

export interface PaletteOptions {
Expand Down
2 changes: 1 addition & 1 deletion packages/mui-material/src/styles/createPalette.spec.ts
Expand Up @@ -17,5 +17,5 @@ import { createTheme, Theme } from '@mui/material/styles';
}

{
const themeCommons: Theme['palette']['common'] = common;
const themeCommons: Pick<Theme['palette']['common'], 'black' | 'white'> = common;
}