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

[system] Fix color-scheme implementation #34639

Merged
merged 33 commits into from Oct 11, 2022
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
88bd060
add resolved mode and add comments
siriwatknp Oct 6, 2022
b1fa4d8
remove unnecessary enableColorScheme
siriwatknp Oct 6, 2022
1964a6b
use palette.mode as color-scheme
siriwatknp Oct 6, 2022
b69e21b
add mode to palette in joy
siriwatknp Oct 6, 2022
d971b0b
remove enableColorScheme test (moved to regression)
siriwatknp Oct 6, 2022
119858c
fix joy dark mode
siriwatknp Oct 6, 2022
c1ae81a
remove omit
siriwatknp Oct 6, 2022
fd3268c
add initial setup from design system
siriwatknp Oct 6, 2022
317b915
revert to make code review easier
siriwatknp Oct 6, 2022
51fa80e
move enable color scheme to CssBaseline
siriwatknp Oct 6, 2022
a79c794
Add CssBaseline to Joy
siriwatknp Oct 6, 2022
b5da1b6
run proptypes
siriwatknp Oct 6, 2022
88b48f7
fix lint
siriwatknp Oct 6, 2022
23c27ee
fix backdrop color
siriwatknp Oct 7, 2022
bea2b11
fix Joy color scheme styles
siriwatknp Oct 7, 2022
784a5f6
add visual regression
siriwatknp Oct 7, 2022
4ae0ff0
fix color scheme styles generation
siriwatknp Oct 6, 2022
d7456f9
fix implementation
siriwatknp Oct 7, 2022
4187694
apply logic to ScopedCssBaseline
siriwatknp Oct 7, 2022
fb0a14b
update regression
siriwatknp Oct 7, 2022
65eaac9
fix mui CssBaseline
siriwatknp Oct 7, 2022
3c61b0b
fix logic and regression
siriwatknp Oct 7, 2022
269d647
fix naming
siriwatknp Oct 9, 2022
2b51a0c
flip the flag to disableColorScheme
siriwatknp Oct 9, 2022
49a4707
Add ScopedCssBaseline to Joy
siriwatknp Oct 9, 2022
c1822b3
Add JoyScopedBaseline regression
siriwatknp Oct 9, 2022
5bc8db9
add unit tests
siriwatknp Oct 9, 2022
44ac5cb
export GlobalStyles
siriwatknp Oct 9, 2022
89681bc
add CssBaseline doc
siriwatknp Oct 9, 2022
8fa3c77
fix tests
siriwatknp Oct 10, 2022
f4a7d86
Update packages/mui-joy/src/styles/defaultTheme.ts
siriwatknp Oct 10, 2022
de1ab16
content fix
siriwatknp Oct 10, 2022
1bfdb50
Merge branch 'system/fix-server-mode' of github.com:siriwatknp/materi…
siriwatknp Oct 10, 2022
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
168 changes: 168 additions & 0 deletions docs/data/joy/components/css-baseline/css-baseline.md
@@ -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.
Copy link
Member

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 :)

Copy link
Member Author

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.


```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>
);
}
```
5 changes: 5 additions & 0 deletions docs/data/joy/pages.ts
Expand Up @@ -76,6 +76,11 @@ const pages = [
{ pathname: '/joy-ui/react-tabs' },
],
},
{
pathname: '/joy-ui/components/utils',
subheader: 'utils',
children: [{ pathname: '/joy-ui/react-css-baseline' }],
},
],
},
{
Expand Down
7 changes: 7 additions & 0 deletions docs/pages/joy-ui/react-css-baseline.js
@@ -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} />;
}
85 changes: 85 additions & 0 deletions packages/mui-joy/src/CssBaseline/CssBaseline.tsx
@@ -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*&/, '')] = {
Copy link
Member

Choose a reason for hiding this comment

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

Can you clarify what is this doing?

theme.getColorSchemeSelector(key).replace(/\s*&/, '')

We have it in many places, maybe we should extract the logic?

Copy link
Member Author

Choose a reason for hiding this comment

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

theme.getColorSchemeSelector(key) returns a string, [data-joy-color-scheme="$key"] &, with an & at the end so that developers can use it to write custom styles in styled or sx like this:

styled('div')(({ theme }) => ({
  [theme.getColorSchemeSelector('dark')]: { ... }
  // easier than writing
  // [`${theme.getColorSchemeSelector('dark')} &`]: { ... }
}))

However, the CssBaseline define the selector as global so the .replace(/\s*&/, '') is required to remove & at the end:

theme.getColorSchemeSelector(key).replace(/\s*&/, '') // '[data-joy-color-scheme="dark"]'

I think this case would be rare but I could provide the attribute from the theme to be used like:

[`[${theme.attribute}="dark"]`]: { ... }

Copy link
Member Author

Choose a reason for hiding this comment

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

Thinking again, I rather go with theme.getColorSchemeSelector(key).replace(/\s*&/, '') for now because I don't want to add a new property to the theme. Let's wait to see how the community would like to use the API.

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;
14 changes: 14 additions & 0 deletions packages/mui-joy/src/CssBaseline/CssBaselineProps.ts
@@ -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;
}
2 changes: 2 additions & 0 deletions packages/mui-joy/src/CssBaseline/index.ts
@@ -0,0 +1,2 @@
export { default } from './CssBaseline';
export * from './CssBaselineProps';
1 change: 1 addition & 0 deletions packages/mui-joy/src/GlobalStyles/index.ts
@@ -0,0 +1 @@
export { GlobalStyles as default } from '@mui/system';
20 changes: 20 additions & 0 deletions packages/mui-joy/src/ScopedCssBaseline/ScopedCssBaseline.test.tsx
@@ -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'],
}));
});