Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions docs/src/pages/css-in-js/advanced/GlobalClassName.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from 'react';
import styled from 'styled-components';
import { TextField, NoSsr } from '@material-ui/core';

const StyledTextField = styled(TextField)`
label.focused {
color: green;
}
.MuiOutlinedInput-root {
fieldset {
border-color: red;
}
&:hover fieldset {
border-color: yellow;
}
&.focused fieldset {
border-color: green;
}
}
`;

export default function GlobalClassName() {
return (
<NoSsr>
<StyledTextField label="Deterministic" variant="outlined" id="deterministic-outlined-input" />
</NoSsr>
);
}
4 changes: 2 additions & 2 deletions docs/src/pages/css-in-js/advanced/ThemeNesting.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ function DeepChild() {
);
}

function Theme() {
function ThemeNesting() {
return (
<div>
<ThemeProvider
Expand All @@ -50,4 +50,4 @@ function Theme() {
);
}

export default Theme;
export default ThemeNesting;
206 changes: 158 additions & 48 deletions docs/src/pages/css-in-js/advanced/advanced.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ const DeepChild = withTheme(DeepChildRaw);
### Theme nesting

You can nest multiple theme providers.
This can be really useful when dealing with different area of your application that have distinct appearance from each other.
This can be really useful when dealing with different areas of your application that have distinct appearance from each other.

```jsx
<ThemeProvider theme={outerTheme}>
Expand All @@ -83,6 +83,85 @@ You can extend the outer theme by providing a function:
</ThemeProvider>
```

## Overriding styles - `classes` prop

The `makeStyles` (hook generator) and `withStyles` (HOC) APIs allow the creation of multiple style rules per style sheet. Each style rule has its own class name.
The class names are provided to the component with the `classes` variable.
The is particularly useful when styling nested elements in a component.

```jsx
// A style sheet
const useStyles = makeStyles({
root: {}, // a style rule
label: {}, // a nested style rule
});

function Nested(props) {
const classes = useStyles();
return (
<button className={classes.root}> // 'jss1'
<span className={classes.label}> // 'jss2'
nested
</span>
</button>
);
}

function Parent() {
return <Nested />
}
```

However, the class names are often non-deterministic. How can a parent component override the style of a nested element?

### withStyles

This is the simplest case. the wrapped component accepts a `classes` prop,
it simply merges the class names provided with the style sheet.

```jsx
const Nested = withStyles({
root: {}, // a style rule
label: {}, // a nested style rule
})({ classes }) => (
<button className={classes.root}>
<span className={classes.label}> // 'jss2 my-label'
Nested
</span>
</button>
));

function Parent() {
return <Nested classes={{ label: 'my-label' }} />
}
```

### makeStyles

The hook API requires a bit more work. You have to forward the parent props to the hook as a first argument.

```jsx
const useStyles = makeStyles({
root: {}, // a style rule
label: {}, // a nested style rule
});

function Nested(props) {
const classes = useStyles(props);
return (
<button className={classes.root}>
<span className={classes.label}> // 'jss2 my-label'
nested
</span>
</button>
);
}

function Parent() {
return <Nested classes={{ label: 'my-label' }} />
}
```

## JSS plugins

JSS uses plugins to extend its core, allowing you to cherry-pick the features you need,
Expand Down Expand Up @@ -323,55 +402,46 @@ You can [follow the server side guide](/guides/server-rendering/) for a more det
We have [an official plugin](https://github.com/hupe1980/gatsby-plugin-material-ui) that enables server-side rendering for `@material-ui/styles`.
Refer to the plugin's page for setup and usage instructions.

Refer to [this example](https://github.com/mui-org/material-ui/blob/next/examples/gatsby-next/pages/_document.js) for an up-to-date usage example.
Refer to [this example project](https://github.com/mui-org/material-ui/blob/next/examples/gatsby-next) for an up-to-date usage example.

### Next.js

You need to have a custom `pages/_document.js`, then copy [this logic](https://github.com/mui-org/material-ui/blob/next/examples/nextjs-next/pages/_document.js) to inject the server-side rendered styles into the `<head>` element.

Refer to [this example](https://github.com/mui-org/material-ui/blob/next/examples/nextjs-next/pages/_document.js) for an up-to-date usage example.
Refer to [this example project](https://github.com/mui-org/material-ui/blob/next/examples/nextjs-next) for an up-to-date usage example.

## Class names

You may have noticed that the class names generated by `@material-ui/styles` are **non-deterministic**,
so you can't rely on them to stay the same.
The class names are generated by [the class name generator](/css-in-js/api/#creategenerateclassname-options-class-name-generator).

Let's take the following style as an example:
### Default

```jsx
By default, the class names generated by `@material-ui/styles` are **non-deterministic**; you can't rely on them to stay the same. Let's take the following style as an example:

```js
const useStyles = makeStyles({
root: {
opacity: 1,
},
}, {
name: 'AppBar',
});
```

This will generate a class name such as `AppBar-root-123`. However, the following CSS won't work:

```css
.AppBar-root-123 {
opacity: 0.6;
}
```
This will generate a class name such as `makeStyles-root-123`.

You have to use the `classes` property of a component to override them.
The non-deterministic nature of the class names enables optimization for development and production –
they are easy to debug in development, and as short as possible in production:
You have to use the `classes` prop of a component to override the styles.
The non-deterministic nature of the class names enables style isolation.

- In **development**, the class name will be: `.AppBar-root-123`, following this logic:
- In **development**, the class name is: `.makeStyles-root-123`, following this logic:

```js
const sheetName = 'AppBar';
const sheetName = 'makeStyles';
const ruleName = 'root';
const identifier = 123;

const className = `${sheetName}-${ruleName}-${identifier}`;
```

- In **production**, the class name will be: `.jss123`, following this logic:
- In **production**, the class name is: `.jss123`, following this logic:

```js
const productionPrefix = 'jss';
Expand All @@ -380,48 +450,88 @@ const identifier = 123;
const className = `${productionPrefix}-${identifier}`;
```

If you don't like this default behavior, you can change it.
JSS allows you to supply a [custom class name generator](https://cssinjs.org/jss-api/#generate-your-class-names).
### With `@material-ui/core`

## Global CSS
The generated class names of the `@material-ui/core` components behave differently.
When the following conditions are met, the class names are **deterministic**:

### `jss-plugin-global`
- Only one theme provider is used (**No theme nesting**)
- The style sheet has a name that starts with `Mui`. (All Material-UI components)
- The `disableGlobal` option of the [class name generator](/css-in-js/api/#creategenerateclassname-options-class-name-generator) is `false`. (The default)

The [`jss-plugin-global`](#jss-plugins) plugin is installed in the default preset. You can use it to define global class names.
These conditions are met with the most common use cases of `@material-ui/core`.
For instance, this style sheet:

{{"demo": "pages/css-in-js/advanced/GlobalCss.js"}}
```jsx
const useStyles = makeStyles({
root: { /* … */ },
label: { /* … */ },
outlined: {
/* … */
'&$disabled': { /* … */ },
},
outlinedPrimary: {
/* … */
'&:hover': { /* … */ },
},
disabled: {},
}, { name: 'MuiButton' });
```

### Hybrid
generates the following class names you that can override:

You can also combine JSS generated class names with global ones.
```css
.MuiButton-root { /* … */ }
.MuiButton-label { /* … */ }
.MuiButton-outlined { /* … */ }
.MuiButton-outlined.disabled { /* … */ }
.MuiButton-outlinedPrimary: { /* … */ }
.MuiButton-outlinedPrimary:hover { /* … */ }
```

{{"demo": "pages/css-in-js/advanced/HybridGlobalCss.js"}}
*This is a simplification of the `@material-ui/core/Button` component's style sheet.*

Customization of the TextField can be cumbersome with the [`classes` API](#overriding-styles-classes-prop), where you have to define the the classes prop.
It's easier to use the default values, as described above. For example:

### Deterministic class names
```jsx
import styled from 'styled-components';
import { TextField } from '@material-ui/core';

const StyledTextField = styled(TextField)`
label.focused {
color: green; 💚
}
.MuiOutlinedInput-root {
fieldset {
border-color: red; ❤️
}
&:hover fieldset {
border-color: yellow; 💛
}
&.focused fieldset {
border-color: green; 💚
}
}
`;
```

We provide an option to make the class names **deterministic** with the [`dangerouslyUseGlobalCSS`](/css-in-js/api/#creategenerateclassname-options-class-name-generator) option. When turned on, the class names will look like this:
{{"demo": "pages/css-in-js/advanced/GlobalClassName.js"}}

- development: `.AppBar-root`
- production: `.AppBar-root`
## Global CSS

⚠️ **Be cautious when using `dangerouslyUseGlobalCSS`.**
Relying on it for code running in production has the following implications:
### `jss-plugin-global`

- It's harder to keep track of `classes` API changes between major releases.
- Global CSS is inherently fragile.
The [`jss-plugin-global`](#jss-plugins) plugin is installed in the default preset.
You can use it to define global class names.

⚠️ When using `dangerouslyUseGlobalCSS` standalone (without Material-UI), you should name your style sheets using the `options` parameter:
{{"demo": "pages/css-in-js/advanced/GlobalCss.js"}}

```jsx
// Hook
const useStyles = makeStyles(styles, { name: 'button' });
### Hybrid

// Styled-components
const Button = styled(styles, { name: 'button' })(ButtonBase);
You can also combine JSS generated class names with global ones.

// Higher-order component
const Button = withStyles(styles, { name: 'button' })(ButtonBase);
```
{{"demo": "pages/css-in-js/advanced/HybridGlobalCss.js"}}

## CSS prefixes

Expand Down
19 changes: 9 additions & 10 deletions docs/src/pages/css-in-js/api/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ A function which returns [a class name generator function](http://cssinjs.org/js
#### Arguments

1. `options` (*Object* [optional]):
- `options.dangerouslyUseGlobalCSS` (*Boolean* [optional]): Defaults to `false`. Makes the Material-UI class names deterministic.
- `options.disableGlobal` (*Boolan* [optional]): Defaults to `false`. Disable the generation of deterministic class names.
- `options.productionPrefix` (*String* [optional]): Defaults to `'jss'`. The string used to prefix the class names in production.
- `options.seed` (*String* [optional]): Defaults to `''`. The string used to uniquely identify the generator. It can be used to avoid class name collisions when using multiple generators.
- `options.seed` (*String* [optional]): Defaults to `''`. The string used to uniquely identify the generator. It can be used to avoid class name collisions when using multiple generators in the same document.

#### Returns

Expand All @@ -24,7 +24,6 @@ import React from 'react';
import { StylesProvider, createGenerateClassName } from '@material-ui/styles';

const generateClassName = createGenerateClassName({
dangerouslyUseGlobalCSS: true,
productionPrefix: 'c',
});

Expand Down Expand Up @@ -212,11 +211,11 @@ It should preferably be used at **the root of your component tree**.

| Name | Type | Default | Description |
|:-----|:-----|:--------|:------------|
| <span class="prop-name required">children&nbsp;*</span> | <span class="prop-type">node</span> | | Your component tree. |
| <span class="prop-name">disableGeneration</span> | <span class="prop-type">bool</span> | false | You can disable the generation of the styles with this option. It can be useful when traversing the React tree outside of the HTML rendering step on the server. Let's say you are using react-apollo to extract all the queries made by the interface server-side. You can significantly speed up the traversal with this property. |
| <span class="prop-name">generateClassName</span> | <span class="prop-type">func</span> | | JSS's class name generator. |
| <span class="prop-name">injectFirst</span> | <span class="prop-type">bool</span> | false | By default, the styles are injected last in the <head> element of your page. They gain more specificity than any other style sheet on your page e.g. CSS modules, styled components. If you want to override the Material-UI's styles, set this prop. |
| <span class="prop-name">jss</span> | <span class="prop-type">object</span> | | JSS's instance. |
| children&nbsp;* | node | | Your component tree. |
| disableGeneration | bool | false | You can disable the generation of the styles with this option. It can be useful when traversing the React tree outside of the HTML rendering step on the server. Let's say you are using react-apollo to extract all the queries made by the interface server-side. You can significantly speed up the traversal with this property. |
| generateClassName | func | | JSS's class name generator. |
| injectFirst | bool | false | By default, the styles are injected last in the <head> element of the page. As a result, they gain more specificity than any other style sheet. If you want to override Material-UI's styles, set this prop. |
| jss | object | | JSS's instance. |

#### Examples

Expand All @@ -243,8 +242,8 @@ It should preferably be used at **the root of your component tree**.

| Name | Type | Default | Description |
|:-----|:-----|:--------|:------------|
| <span class="prop-name required">children&nbsp;*</span> | <span class="prop-type">node</span> | | Your component tree. |
| <span class="prop-name required">theme&nbsp;*</span> | <span class="prop-type">union:&nbsp;object&nbsp;&#124;&nbsp;func</span> | | A theme object. You can provide a function to extend the outer theme. |
| children&nbsp;* | node | | Your component tree. |
| theme&nbsp;* | union:&nbsp;object&nbsp;&#124;&nbsp;func | | A theme object. You can provide a function to extend the outer theme. |

#### Examples

Expand Down
Loading