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

[zero] Add README with installation and basic usage #40761

Merged
merged 3 commits into from
Feb 1, 2024
Merged
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
251 changes: 251 additions & 0 deletions packages/zero-runtime/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
# zero-runtime

A zero-runtime CSS-in-JS library that extracts the colocated css to it's own css files at build-time.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
A zero-runtime CSS-in-JS library that extracts the colocated css to it's own css files at build-time.
A zero-runtime CSS-in-JS library that extracts colocated CSS to separate CSS files at build time.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Does this sound like that each css definition will have its own css file?

Copy link
Contributor

Choose a reason for hiding this comment

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

It wasn't very clear to me exactly what was intended by the original sentence. Is there another way to phrase this? Maybe it requires more details?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Basically, it depends on the route. So for ex, for next.js, each route might have its own css file. So all styled calls' CSS will be part of that route's css file.
Maybe just - that extracts the colocated CSS to the output CSS files


## Getting started

Zero-runtime supports Next.js and Vite with future support for more bundlers—you must install the corresponding plugin, as shown below.

The package currently has a dependency on `@mui/material` to initialize the theme object, but this is only at build time. There won't be any Material UI code at runtime if you're not using it otherwise—in that case, you can move it to dev dependencies instead (as shown with the plugin packages).

### Next.js

#### Installation

```bash
npm install @mui/zero-runtime @mui/material
npm install --save-dev @mui/zero-next-plugin
```

#### Configuration

In your `next.config.js` file,

1. Import the plugin

```js
const { withZeroPlugin } = require('@mui/zero-next-plugin');
```

2. Create a theme object

```js
const { experimental_extendTheme: extendTheme } = require('@mui/material/styles');
const theme = extendTheme();
Comment on lines +33 to +34
Copy link
Member

Choose a reason for hiding this comment

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

What if we don't mention @mui/material/styles and use a plain theme object?

Suggested change
const { experimental_extendTheme: extendTheme } = require('@mui/material/styles');
const theme = extendTheme();
const theme = {
colorSchemes: {
light: {
palette: {
brand: '...',
}
},
dark: {
palette: {
brand: '...',
}
}
}
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We right now depend on the extendTheme implementation to then call generateCssVars in the zero-runtime util's generateCss. If we have to support plain objects, we can either implement the same in zero-runtime directly or we can move the theme implementation to @mui/theming package that will only have all theme specific logic.

Copy link
Member

@siriwatknp siriwatknp Jan 31, 2024

Choose a reason for hiding this comment

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

Got it. I think we need to revisit the theming for zero-runtime. In my opinion, zero-runtime should work without theme config.

If developer tries to use theme, we throw the error/warning and show them where to add the theme.

Next, the theme object can be a plain object that they can control. Meaning, if I provide:

const theme = {
  typography: {
    h1: {},}
}

I should be able to use it from the theme callback

Then the CSS variables, we should consider as an opt-in feature. Meaning, developers have to follow our structure and pass in generateCssVars to the theme.

I can take care of this task if you agree with the above model.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ideally, yes. It should work even with plain js objects. This was the case actually previously before we agreed on relying on making extendTheme the default. We can still provide css variable token object even if they are not using extendTheme. It would be possible if we extract out the css vars related utilities to either @mui/system or independent package.

```

3. Wrap the exported config object

```js
module.exports = withZeroPlugin(nextConfig, {
theme,
});
```

### Vite

#### Installation

```bash
npm install @mui/zero-runtime @mui/material
npm install --save-dev @mui/zero-vite-plugin
```

#### Configuration

In your vite config file,

1. Import the plugin

```js
import { zeroVitePlugin } from '@mui/zero-vite-plugin';
```

2. Create a theme object

```js
import { experimental_extendTheme as extendTheme } from '@mui/material/styles';
const theme = extendTheme();
```

3. Add the plugin to the `plugins` array. The position does not matter.

```js
export default defineConfig({
plugins: [
zeroVitePlugin({
theme,
}),
// ... Your other plugins.
],
});
```

### Usage

In your source files, you can import the `styled` function from `@mui/zero-runtime`. The usage should be familiar if you've worked with Emotion or styled-components:

```js
import { styled } from '@mui/zero-runtime';

const Heading = styled.h1({
fontSize: '4rem',
fontWeight: 'bold',
padding: '10px 0px',
});

function App() {
return <Heading>Hello</Heading>;
}
```

The zero-runtime package differs from "standard" runtime CSS-in-JS libraries in a few ways:

1. You never get direct access to props in your styled declarations. This is because prop values are only available at runtime, but the CSS is extracted at build time. See [Styling based on runtime values](#styling-based-on-runtime-values) for a workaround.
2. Your styles must be declarative, and must account for all combinations of props that you want to style.
3. The theme lets you declare CSS tokens that become part of the CSS bundle after the build. Any other values and methods that it might have are only available during build time—not at runtime. This leads to smaller bundle sizes.

You can access the same `theme` object that you provided in the bundler config by declaring styles as callbacks—for example:

```js
const Heading = styled.h1(({ theme }) => ({
...theme.typography.h1,
}));
```

Visit the [Default theme viewer](https://mui.com/material-ui/customization/default-theme/) to learn more about the structure of the theme object.

#### Styling variants

The `styled` function must account for all combinations of props. If you're creating a button component that supports a `size` prop and a `color` prop, for example, you can use the `variants` API to define styles for each possible combination of the two:

```jsx
const Button = styled.button(() => ({
border: 'none',
// ... other base css styles to be applied across all prop values.
variants: [
{
// prop combinations
props: {
color: 'primary',
},
// styles to be applied when color="primary" is passed on the component
style: {
color: 'blue',
outline: '1px transparent lightblue',
},
},
{
props: {
color: 'secondary',
},
// styles to be applied when color="secondary" is passed on the component
style: {
color: 'green',
outline: '1px transparent lightgreen',
},
},
{
props: {
size: 'large',
},
style: {
padding: '0.5rem',
},
},
{
props: {
size: 'medium',
},
style: {
padding: '0.25rem',
},
},
{
props: {
size: 'small',
},
style: {
padding: '0.1rem',
},
},
{
props: {
size: 'small',
color: 'primary',
},
style: {
// Styles to be applied when <Button size="small" color="primary" />
},
},
{
// If key value pair doesn't suffice, you can use the callback syntax
// to return `true` if you want the styles to be applied
props({ children }) {
return !!children;
},
style: {
// CSS
},
},
],
}));
```

#### Styling based on runtime values

To style a component based on the runtime values of the props, you can declare a CSS property as a callback—for example:

```tsx
const Heading = styled.h1<{ isError?: boolean }>(({ theme }) => ({
...theme.typography.h1,
color: ({ isError }) => (isError ? 'red' : 'black'),
}));
```

This works through the use of CSS variables and inline styles. The CSS and JSX output will look something like this:

```css
.Heading_class_akjsdfb {
/* Other styles from `theme.typography` */
color: var(--Heading_class_akjsdfb-0);
}
```

```jsx
<h1
style={{
'--Heading_class_akjsdfb-0': ({ isError }) => (isError ? 'red' : 'black'),
}}
>
Hello
</h1>
```

#### Styled component as a CSS selector

All of the components that you create are also available as CSS selectors. For example, for the `Heading` component described in the previous section, you can target that component inside another styled component like this:

```jsx
const Wrapper = styled.div({
[`& .${Heading}`]: {
color: 'blue',
},
});
```

This enables you to override the default `color` of the Heading component rendered inside the Wrapper:

```tsx
<Wrapper>
<Heading>Hello</Heading>
</Wrapper>
```

You can also export any styled component you create and use it as the base for additional components:

```tsx
const ExtraHeading = styled(Heading)({
// ... overridden styled
});
```
Loading