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
[system] Fix color-scheme implementation #34639
Changes from 31 commits
88bd060
b1fa4d8
1964a6b
b69e21b
d971b0b
119858c
c1ae81a
fd3268c
317b915
51fa80e
a79c794
b5da1b6
88b48f7
23c27ee
bea2b11
784a5f6
4ae0ff0
d7456f9
4187694
fb0a14b
65eaac9
3c61b0b
269d647
2b51a0c
49a4707
c1822b3
5bc8db9
44ac5cb
89681bc
8fa3c77
f4a7d86
de1ab16
1bfdb50
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
--- | ||
product: joy-ui | ||
githubLabel: 'component: CssBaseline' | ||
--- | ||
|
||
# CSS Baseline | ||
|
||
<p class="description">Joy UI provides a CssBaseline component to kickstart an elegant, consistent, and simple baseline to build upon.</p> | ||
|
||
{{"component": "modules/components/ComponentLinkHeader.js", "design": false}} | ||
|
||
## Global reset | ||
|
||
You might be familiar with [normalize.css](https://github.com/necolas/normalize.css), a collection of HTML element and attribute style-normalizations. | ||
|
||
```jsx | ||
import * as React from 'react'; | ||
import { CssVarsProvider } from '@mui/joy/styles'; | ||
import CssBaseline from '@mui/joy/CssBaseline'; | ||
|
||
export default function MyApp() { | ||
return ( | ||
<CssVarsProvider> | ||
{/* must be used under CssVarsProvider */} | ||
<CssBaseline /> | ||
|
||
{/* The rest of your application */} | ||
</CssVarsProvider> | ||
); | ||
} | ||
``` | ||
|
||
## Scoping on children | ||
|
||
However, you might be progressively migrating a website to MUI, using a global reset might not be an option. | ||
It's possible to apply the baseline only to the children by using the `ScopedCssBaseline` component. | ||
|
||
```jsx | ||
import * as React from 'react'; | ||
import { CssVarsProvider } from '@mui/joy/styles'; | ||
import ScopedCssBaseline from '@mui/joy/ScopedCssBaseline'; | ||
import MyApp from './MyApp'; | ||
|
||
export default function MyApp() { | ||
const [root, setRoot] = React.useState(null); | ||
return ( | ||
{/* target the node to ScopedCssBaseline's div */} | ||
<CssVarsProvider colorSchemeNode={root}> | ||
{/* must be used under CssVarsProvider */} | ||
<ScopedCssBaseline ref={(element) => setRoot(element)}> | ||
{/* The rest of your application */} | ||
<MyApp /> | ||
</ScopedCssBaseline> | ||
</CssVarsProvider> | ||
); | ||
} | ||
``` | ||
|
||
⚠️ Make sure you import `ScopedCssBaseline` first to avoid box-sizing conflicts as in the above example. | ||
|
||
## Approach | ||
|
||
### Page | ||
|
||
The `<html>` and `<body>` elements are updated to provide better page-wide defaults. More specifically: | ||
|
||
- The margin in all browsers is removed. | ||
- The default Material Design background color is applied. | ||
It's using `theme.palette.background.body` for standard devices and a white background for print devices. | ||
- The CSS [`color-scheme`](https://web.dev/color-scheme/) is applied by default. You can disable it by setting `disableColorScheme` to true on the `CssBaseline` or `ScopedCssBaseline`. | ||
|
||
### Layout | ||
|
||
- `box-sizing` is set globally on the `<html>` element to `border-box`. | ||
Every element—including `*::before` and `*::after` are declared to inherit this property, | ||
which ensures that the declared width of the element is never exceeded due to padding or border. | ||
siriwatknp marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
### Color scheme | ||
|
||
The CSS [`color-scheme`](https://web.dev/color-scheme/) is applied by default to render proper built-in components on the web. You can disable it by setting `disableColorScheme` to true on the `CssBaseline` or `ScopedCssBaseline`. | ||
|
||
```jsx | ||
<CssVarsProvider> | ||
<CssBaseline disableColorScheme /> | ||
</CssVarsProvider> | ||
|
||
// or | ||
<CssVarsProvider> | ||
<ScopedCssBaseline disableColorScheme > | ||
{/* The rest of your application */} | ||
</ScopedCssBaseline> | ||
</CssVarsProvider> | ||
``` | ||
|
||
### Typography | ||
|
||
- No base font-size is declared on the `<html>`, but 16px is assumed (the browser default). | ||
You can learn more about the implications of changing the `<html>` default font size in [the theme documentation](/material-ui/customization/typography/#html-font-size) page. | ||
- Set the `theme.typography.body1` style on the `<body>` element. | ||
- Set the font-weight to `bold` for the `<b>` and `<strong>` elements. | ||
- Custom font-smoothing is enabled for better display of the default font. | ||
|
||
## Customization | ||
|
||
### CssBaseline | ||
|
||
To custom the styles produced by the `CssBaseline` component, append the `GlobalStyles` next to it: | ||
|
||
```js | ||
import { CssVarsProvider } from '@mui/joy/styles'; | ||
import CssBaseline from '@mui/joy/CssBaseline'; | ||
import GlobalStyles from '@mui/joy/GlobalStyles'; | ||
|
||
function App() { | ||
return ( | ||
<CssVarsProvider> | ||
<CssBaseline /> {/* CssBaseline must come first */} | ||
<GlobalStyles | ||
styles={{ | ||
// CSS object styles | ||
html: { | ||
// ... | ||
}, | ||
body: { | ||
// ... | ||
}, | ||
}} | ||
/> | ||
</CssVarsProvider> | ||
); | ||
} | ||
``` | ||
|
||
### ScopedCssBaseline | ||
|
||
You can customize it using the [themed components](https://mui.com/joy-ui/customization/themed-components/) approach. The component identifier is `JoyScopedCssBaseline` which contains only the `root` slot. | ||
|
||
```js | ||
import { CssVarsProvider, extendTheme } from '@mui/joy/styles'; | ||
import ScopedCssBaseline from '@mui/joy/ScopedCssBaseline'; | ||
import MyApp from './MyApp'; | ||
|
||
const theme = extendTheme({ | ||
components: { | ||
JoyScopedCssBaseline: { | ||
styleOverrides: { | ||
root: ({ theme }) => ({ | ||
// ...CSS object styles | ||
}) | ||
} | ||
} | ||
} | ||
}) | ||
|
||
export default function MyApp() { | ||
const [root, setRoot] = React.useState(null); | ||
return ( | ||
{/* target the node to ScopedCssBaseline's div */} | ||
<CssVarsProvider colorSchemeNode={root}> | ||
{/* must be used under CssVarsProvider */} | ||
<ScopedCssBaseline ref={(element) => setRoot(element)}> | ||
{/* The rest of your application */} | ||
<MyApp /> | ||
</ScopedCssBaseline> | ||
</CssVarsProvider> | ||
); | ||
} | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import * as React from 'react'; | ||
import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs'; | ||
import * as pageProps from 'docs/data/joy/components/css-baseline/css-baseline.md?@mui/markdown'; | ||
|
||
export default function Page() { | ||
return <MarkdownDocs {...pageProps} />; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
import * as React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import { GlobalStyles } from '@mui/system'; | ||
import { Theme, DefaultColorScheme, ColorSystem } from '../styles/types'; | ||
import { CssBaselineProps } from './CssBaselineProps'; | ||
|
||
/** | ||
* Kickstart an elegant, consistent, and simple baseline to build upon. | ||
*/ | ||
function CssBaseline({ children, disableColorScheme = false }: CssBaselineProps) { | ||
return ( | ||
<React.Fragment> | ||
<GlobalStyles | ||
styles={(theme: Theme) => { | ||
const colorSchemeStyles: Record<string, any> = {}; | ||
if (!disableColorScheme) { | ||
// The CssBaseline is wrapped inside a CssVarsProvider | ||
( | ||
Object.entries(theme.colorSchemes) as Array<[DefaultColorScheme, ColorSystem]> | ||
).forEach(([key, scheme]) => { | ||
colorSchemeStyles[theme.getColorSchemeSelector(key).replace(/\s*&/, '')] = { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you clarify what is this doing?
We have it in many places, maybe we should extract the logic? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
styled('div')(({ theme }) => ({
[theme.getColorSchemeSelector('dark')]: { ... }
// easier than writing
// [`${theme.getColorSchemeSelector('dark')} &`]: { ... }
})) However, the CssBaseline define the selector as global so the
[`[${theme.attribute}="dark"]`]: { ... } There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thinking again, I rather go with |
||
colorScheme: scheme.palette?.mode, | ||
}; | ||
}); | ||
} | ||
return { | ||
html: { | ||
WebkitFontSmoothing: 'antialiased', | ||
MozOsxFontSmoothing: 'grayscale', | ||
// Change from `box-sizing: content-box` so that `width` | ||
// is not affected by `padding` or `border`. | ||
boxSizing: 'border-box', | ||
// Fix font resize problem in iOS | ||
WebkitTextSizeAdjust: '100%', | ||
}, | ||
'*, *::before, *::after': { | ||
boxSizing: 'inherit', | ||
}, | ||
'strong, b': { | ||
fontWeight: 'bold', | ||
}, | ||
body: { | ||
margin: 0, // Remove the margin in all browsers. | ||
color: theme.vars.palette.text.primary, | ||
...(theme.typography.body1 as any), | ||
backgroundColor: theme.vars.palette.background.body, | ||
'@media print': { | ||
// Save printer ink. | ||
backgroundColor: theme.vars.palette.common.white, | ||
}, | ||
// Add support for document.body.requestFullScreen(). | ||
// Other elements, if background transparent, are not supported. | ||
'&::backdrop': { | ||
backgroundColor: theme.vars.palette.background.backdrop, | ||
}, | ||
}, | ||
...colorSchemeStyles, | ||
}; | ||
}} | ||
/> | ||
{children} | ||
</React.Fragment> | ||
); | ||
} | ||
|
||
CssBaseline.propTypes /* remove-proptypes */ = { | ||
// ----------------------------- Warning -------------------------------- | ||
// | These PropTypes are generated from the TypeScript type definitions | | ||
// | To update them edit TypeScript types and run "yarn proptypes" | | ||
// ---------------------------------------------------------------------- | ||
/** | ||
* You can wrap a node. | ||
*/ | ||
children: PropTypes.node, | ||
/** | ||
* Disable `color-scheme` CSS property. | ||
* | ||
* For more details, check out https://developer.mozilla.org/en-US/docs/Web/CSS/color-scheme | ||
* For browser support, check out https://caniuse.com/?search=color-scheme | ||
* @default false | ||
*/ | ||
disableColorScheme: PropTypes.bool, | ||
} as any; | ||
|
||
export default CssBaseline; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
export interface CssBaselineProps { | ||
/** | ||
* You can wrap a node. | ||
*/ | ||
children?: React.ReactNode; | ||
/** | ||
* Disable `color-scheme` CSS property. | ||
* | ||
* For more details, check out https://developer.mozilla.org/en-US/docs/Web/CSS/color-scheme | ||
* For browser support, check out https://caniuse.com/?search=color-scheme | ||
* @default false | ||
*/ | ||
disableColorScheme?: boolean; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export { default } from './CssBaseline'; | ||
export * from './CssBaselineProps'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { GlobalStyles as default } from '@mui/system'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import * as React from 'react'; | ||
import { createRenderer, describeConformance } from 'test/utils'; | ||
import { ThemeProvider } from '@mui/joy/styles'; | ||
import ScopedCssBaseline, { scopedCssBaselineClasses as classes } from '@mui/joy/ScopedCssBaseline'; | ||
|
||
describe('<ScopedCssBaseline />', () => { | ||
const { render } = createRenderer(); | ||
|
||
describeConformance(<ScopedCssBaseline />, () => ({ | ||
classes, | ||
inheritComponent: 'div', | ||
render, | ||
ThemeProvider, | ||
muiName: 'JoyScopedCssBaseline', | ||
refInstanceof: window.HTMLDivElement, | ||
testComponentPropWith: 'span', | ||
testVariantProps: { disableColorScheme: true }, | ||
skip: ['classesRoot', 'componentsProp'], | ||
})); | ||
}); |
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.
We probably should specify that it is used, this is why we are mentioning it :)
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.
I think it is mentioned to give the reader a clue that it is a similar purpose. I checked
normalize.css
and we are not using the same global reset.