@material-ui/system
Styled system & style functions for building powerful design systems.
开始使用
@material-ui/system
提供了一些底层工具函数,我们称之为 "style functions",它们可以用于建立强大的设计系统。 以下是这个库的一些特点:
- ⚛️可以在组件的 props 上直接访问主题属性
- 🦋使得 UI 具有一致性
- 🌈可以方便的书写的响应式 (reponsive) 样式
- 🦎可以和任何主题对象 (theme object) 配合
- 💅使用广为流行的 CSS-in-JS 样式方案
- 📦体积小 4KB gzipped
- 🚀 快速,性能不是运行时问题
值得关注的是,整个仓库的函数都是无副作用的(side-effect free),它们拥有这样的类型签名: ({ theme, ...style })=> style
。
演示
在开始章节的余下部分,我们会配合styled-components 作为演示例子(因为这个库具有普遍性)。 或者,你也可以使用 JSS。 另外,以下的例子都直接使用了 Material-UI 的 默认 主题对象。
import React from 'react';
import styled, { ThemeProvider } from 'styled-components';
import NoSsr from '@material-ui/core/NoSsr';
import { createMuiTheme } from '@material-ui/core/styles';
import { palette, spacing, typography } from '@material-ui/system';
const Box = styled.div`${palette}${spacing}${typography}`;
// or import Box from '@material-ui/core/Box';
const theme = createMuiTheme();
export default function Demo() {
return (
<NoSsr>
<ThemeProvider theme={theme}>
<Box
color="primary.main"
bgcolor="background.paper"
fontFamily="h6.fontFamily"
fontSize={{ xs: 'h6.fontSize', sm: 'h4.fontSize', md: 'h3.fontSize' }}
p={{ xs: 2, sm: 3, md: 4 }}
>
@material-ui/system
</Box>
</ThemeProvider>
</NoSsr>
);
}
安装
// 使用 npm
npm install @material-ui/system
// 使用 yarn
yarn add @material-ui/system
创建组件
首先我们来创建一个 Box
组件。 一开始,我们给他增加 间距(spacing)
和 颜色(palette)
两个样式参数。
import styled from 'styled-components';
import { spacing, palette } from '@material-ui/system';
const Box = styled.div`${spacing}${palette}`;
export default Box;
这个 Box 组件现在就支持了 间距属性 和 颜色属性。 例如,你可以提供一个间距属性:p
和一个颜色属性: color
。
<Box p="1rem" color="grey">Give me some space!</Box>
这个组件可以接受任何有效的CSS值作为样式。
主题
大部分时候,你会需要使用主题来保持 UI 的一致性, 那你会需要有一组预设好的间距和颜色值。 你可以使用主题提供者(theme provider) 来注入你的定制方案。
import React from 'react'
import { ThemeProvider } from 'styled-components'
const theme = {
spacing: 4,
palette: {
primary: '#007bff',
},
};
export default function App() {
return (
<ThemeProvider theme={theme}>
{/* children */}
</ThemeProvider>
)
}
现在,你可以提供一个乘数来乘上你预设的间距作为实际间距:
<Box p={1}>4px</Box>
<Box p={2}>8px</Box>
<Box p={-1}>-4px</Box>
同时你也可以使用主色(primary color):
<Box color="primary">blue</Box>
其他
为了使 Box 组件更实用,我们已预置了一些样式函数,下面是完整列表:
If you are already using @material-ui/core
, you can use the Box component (using JSS internally):
import Box from '@material-ui/core/Box';
互操作性
@material-ui/system
适用于大多数 CSS-in-JS 库,包括 JSS, styled-components, 还有 emotion。
如果你已经在使用 @material-ui/core
,我们推荐你使用 JSS 方案,这样有助于减少包大小。
JSS
import React from 'react';
import { styled } from '@material-ui/core/styles';
import { compose, spacing, palette } from '@material-ui/system';
const Box = styled('div')(compose(spacing, palette));
export default function JSS() {
return (
<Box color="white" bgcolor="palevioletred" p={1}>
JSS
</Box>
);
}
import React from 'react';
import styled from 'styled-components';
import { palette, spacing } from '@material-ui/system';
import NoSsr from '@material-ui/core/NoSsr';
const Box = styled.div`
${palette}
${spacing}
`;
export default function StyledComponents() {
return (
<NoSsr>
<Box color="white" bgcolor="palevioletred" p={1}>
Styled components
</Box>
</NoSsr>
);
}
import React from 'react';
import styled from '@emotion/styled';
import { palette, spacing } from '@material-ui/system';
import NoSsr from '@material-ui/core/NoSsr';
const Box = styled.div`
${palette}
${spacing}
`;
export default function Emotion() {
return (
<NoSsr>
<Box color="white" bgcolor="palevioletred" p={1}>
Emotion
</Box>
</NoSsr>
);
}
响应式(Responsive)
所有 的属性都是响应式的,我们支持3种不同的 Api。 如下的配置是默认断点(breakpoints) 配置,不过同时它们是可定制的。
const values = {
xs: 0,
sm: 600,
md: 960,
lg: 1280,
xl: 1920,
};
const theme = {
breakpoints: {
keys: ['xs', 'sm', 'md', 'lg', 'xl'],
up: key => `@media (min-width:${values[key]}px)`,
},
};
Array
<Box p={[2, 3, 4]} />
/**
* 输出:
*
* padding: 16px;
* @media (min-width: 600px) {
* padding: 24px;
* }
* @media (min-width: 960px) {
* padding: 32px;
* }
*/
Object
<Box p={{ xs: 2, sm: 3, md: 4 }} />
/**
* 输出:
*
* padding: 16px;
* @media (min-width: 600px) {
* padding: 24px;
* }
* @media (min-width: 960px) {
* padding: 32px;
* }
*/
Collocation
If you want to group the breakpoint values, you can use the breakpoints()
helper.
import { compose, spacing, palette, breakpoints } from '@material-ui/system';
import styled from 'styled-components';
const Box = styled.div`
${breakpoints(
compose(
spacing,
palette,
),
)}
`;
<Box
p={2}
sm={{ p: 3 }}
md={{ p: 4 }}
/>
/**
* Outputs:
*
* padding: 16px;
* @media (min-width: 600px) {
* padding: 24px;
* }
* @media (min-width: 960px) {
* padding: 32px;
* }
*/
<Box xs={{ fontSize: 12 }} sm={{ fontSize: 18 }} md={{ fontSize: 24 }}>
Collocation API
</Box>
定制样式属性
style(options) => style function
你可以使用这个函数来创建你自己的样式工具。
Not all CSS properties are supported. 不过如果你想,你可以支持一个新的属性。 改变的主题路径的前缀也是可以的。
参数
options
(Object):options.prop
(String):指定样式函数会被什么属性触发options.cssProperty
(String|Boolean [optional]):默认是options.prop
。 使用 CSS 属性。 你可以传递false
禁用它。 禁用的情况下,这个属性会被作为样式对象应用于本身。 它可以用来 渲染变体(rendering variants)。options.themeKey
(String [optional]):主题路径的前缀。options.transform
(Function [optional]):指定在输出 CSS 值之前经过的转换
返回结果
style function
:被创建的样式函数。
例子
You can create a component that supports some CSS grid properties like grid-gap
. By supplying spacing
as the themeKey
you can reuse logic enabling the behavior we see in other spacing properties like padding
.
import styled from 'styled-components';
import { style } from '@material-ui/system';
import { Box } from '@material-ui/core';
const gridGap = style({
prop: 'gridGap',
themeKey: 'spacing',
});
const Grid = styled(Box)`${gridGap}`;
const example = <Grid display="grid" gridGap={[2, 3]}>...</Grid>;
You can also customize the prop name by adding both a prop
and cssProperty
and transform the value by adding a transform
function.
import styled from 'styled-components';
import { style } from '@material-ui/system';
const borderColor = style({
prop: 'bc',
cssProperty: 'borderColor',
themeKey: 'palette',
transform: value => `${value} !important`,
});
const Colored = styled.div`${borderColor}`;
const example = <Colored bc="primary.main">...</Colored>;
compose(...style functions) => style function
组合多个不同的样式函数。
返回结果
style function
:被创建的样式函数。
例子
import { style, compose } from '@material-ui/system'
export const textColor = style({
prop: 'color',
themeKey: 'palette',
});
export const bgcolor = style({
prop: 'bgcolor',
cssProperty: 'backgroundColor',
themeKey: 'palette',
});
const palette = compose(textColor, bgcolor);
变种
帮助函数 style()
也可用于根据主题来返回不同的属性给样式对象。 在在这个例子中, variant
属性可以是 theme.typography
对象的任意键(key)。
import React from 'react';
import styled, { ThemeProvider } from 'styled-components';
import NoSsr from '@material-ui/core/NoSsr';
import { style, typography } from '@material-ui/system';
const variant = style({
prop: 'variant',
cssProperty: false,
themeKey: 'typography',
});
// ⚠ Text is already defined in the global context:
// https://developer.mozilla.org/en-US/docs/Web/API/Text/Text.
const Text = styled.span`
font-family: Helvetica;
${variant}
${typography}
`;
const theme = {
typography: {
h1: {
fontSize: 30,
lineHeight: 1.5,
},
h2: {
fontSize: 25,
lineHeight: 1.5,
},
},
};
export default function Variant() {
return (
<NoSsr>
<ThemeProvider theme={theme}>
<div>
<Text variant="h1" as="div">
variant=h1
</Text>
<Text variant="h1" fontWeight={300} as="div">
fontWeight=300
</Text>
<Text variant="h2" as="div">
variant=h2
</Text>
</div>
</ThemeProvider>
</NoSsr>
);
}
import React from 'react';
import styled, { ThemeProvider } from 'styled-components';
import NoSsr from '@material-ui/core/NoSsr';
import { createMuiTheme } from '@material-ui/core/styles';
import { compose, spacing, palette, css } from '@material-ui/system';
const Box = styled.div`
${css(compose(spacing, palette))}
`;
const theme = createMuiTheme();
export default function CssProp() {
return (
<NoSsr>
<ThemeProvider theme={theme}>
<Box color="white" css={{ bgcolor: 'palevioletred', p: 1, textTransform: 'uppercase' }}>
CssProp
</Box>
</ThemeProvider>
</NoSsr>
);
}
它是如何工作的
styled-system在解释它如何工作方面做得很好 。 它可以协助为这种“style function”概念建立神经模型。
实际用例
在实践中,Box 组件可以节省我们很多时间。 在这个例子中,我们演示了如何做一个 Banner 组件。
You have lost connection to the internet. This app is offline.
现有技术
@material-ui/system
从不同地方借鉴了一些想法和 API:
- Tachyons (2014年) 是第一个促进了 原子 CSS 模式(Atomic CSS pattern) 发展的CSS库。
- TachyTachyons (2017年) 跟随了 Tailwind CSS 的脚步 他们让原子CSS更受欢迎。
- Twitter Bootstrap 在v2,v3, 和v4中一步步介绍了原子类名 The way they group their "Helper classes" was used as inspiration.
- 在 React 世界中, Styled System (2017年) 是第一个推动样式函数的。 它可以做出一个通用的 Box 组件来已经衍生其他组件,这种方式可以替换原子CSS之中的辅助原子类的做法。
- Large companies such as Pinterest, GitHub, and Segment.io are using the same approach in different flavours:
- 实际实现 和 对象响应API(object responsive API) 受到 Smooth-UI's system的启发。