Upgrade to Pro — share decks privately, control downloads, hide ads and more …

從 Styled System 看下一代 CSS 技術

Anna Su
December 19, 2019

從 Styled System 看下一代 CSS 技術

2019/12/19
@ ReactJS.tw

從 CSS 的技術演變到 Styled System 的介紹,分享 React 實作 Styled System 的技巧以及目前市面上常見現成套件的選擇。

https://reactjs.kktix.cc/events/react13

Anna Su

December 19, 2019
Tweet

More Decks by Anna Su

Other Decks in Programming

Transcript

  1. SASS LESS index.sass .breadcrumb { display: flex; } .breadcrumb-item {

    display: flex; &.active { color: $breadcrumb-active-color; } }
  2. BEM OOCSS SMACSS index.sass .breadcrumb { display: flex; } .breadcrumb__item

    { display: flex; &.breadcrumb__item--active { color: $breadcrumb-active-color; } }
  3. CSS Module breadcrumb.sass .breadcrumb { display: flex; .item { display:

    flex; &.active { color: blue; } } } Breadcrumb.js import React from "react"; import styles from "./breadcrumb.scss"; const Breadcrumb = () => { return ( <div className={styles.breadcrumb}> <div className={styles.item}>products</div> <div className={styles.item}>iphone</div> <div className={`${styles.item} ${styles.active}`}> airpods </div> </div> ); } export default Breadcrumb;
  4. CSS Module breadcrumb.sass .breadcrumb { display: flex; .item { display:

    flex; &.active { color: blue; } } } Breadcrumb.js import React from "react"; import classNames from "classnames"; import styles from "./breadcrumb.scss"; const cx = classNames.bind(styles); const Breadcrumb = () => { return ( <div className={cx('breadcrumb')}> <div className={cx('item')}>products</div> <div className={cx('item')}>iphone</div> <div className={cx('item', 'active')}> airpods </div> </div> ); }; export default Breadcrumb; classnames
  5. CSS in JS Breadcrumb.js import React from "react"; import styled

    from "styled-components"; const Wrapper = styled.div` display: flex; `; const Item = styled.a` display: flex; color: ${props => (props.active ? "blue" : "black")}; &::after { content: "/"; } &:last-child { &::after { content: ""; } } `; const Breadcrumb = () => { return ( <Wrapper> <Item>products</Item>
  6. const StyledSlider = styled(Slider)` .ant-slider-handle { width: 24px; height: 24px;

    margin-top: -10px; } `; <StyledSlider defaultValue={30} !/>
  7. import { space, layout, typography, border, color } from 'styled-system';

    const StyledButton = styled.button` cursor: pointer; ${space} ${color} ${layout} ${typography} ${border} `; <StyledButton mr="5px" color="white" bg="blue" fontSize="16px" borderRadius="5px!"> Button !</StyledButton>
  8. import { space, layout, typography, border, color } from 'styled-system';

    const StyledButton = styled.button` cursor: pointer; ${space} ${color} ${layout} ${typography} ${border} `; <StyledButton mr="5px" color="white" bg="blue" fontSize="16px" borderRadius="5px!"> Button !</StyledButton>
  9. import { space, layout, typography, border, color } from 'styled-system';

    const StyledButton = styled.button` cursor: pointer; ${space} ${color} ${layout} ${typography} ${border} `; <StyledButton mr="5px" color="white" bg="blue" fontSize="16px" borderRadius="5px!"> Button !</StyledButton>
  10. import { space, layout, typography, border, color } from 'styled-system';

    const StyledButton = styled.button` cursor: pointer; ${space} ${color} ${layout} ${typography} ${border} `; <StyledButton mr="5px" color="white" bg="blue" fontSize="16px" borderRadius="5px!"> Button !</StyledButton>
  11. import { space, layout, typography, border, color } from 'styled-system';

    const StyledButton = styled.button` cursor: pointer; ${space} ${color} ${layout} ${typography} ${border} `; <StyledButton mr="5px" color="white" bg="blue" fontSize="16px" borderRadius="5px!"> Button !</StyledButton>
  12. const StyledButton = styled.button` padding: ${(props) !=> props.padding}; margin-left: ${(props)

    !=> props.marginLeft}; width: ${(props) !=> props.width}; height: ${(props) !=> props.height}; font-size: ${(props) !=> props.fontSize}; font-family: ${(props) !=> props.fontFamily}; line-height: ${(props) !=> props.lineHeight}; border: ${(props) !=> props.border}; border-radius: ${(props) !=> props.borderRadius}; color: ${(props) !=> props.color}; background-color: ${(props) !=> props.backgroundColor}; cursor: pointer; … `; const StyledButton = styled.button` ${space} ${layout} ${typography} ${border} ${color} cursor: pointer; `;
  13. const theme = { breakpoints: ['768px'], space: [0, 4, 8,

    16, 24, 32, 64], fontSizes: [12, 14, 16, 20, 24, 32], colors: { black: '#000', blacks: ['#ccc', '#999'] white: '#fff', primary: '#456000', }, !!... }
  14. <Box color="primary" !/> export const Box = styled.div` ${color} `;

    const theme = { colors: { black: '#000', white: '#fff', primary: '#456000', }, !!... }
  15. import React from 'react'; import { ThemeProvider } from 'styled-components';

    import theme from './theme'; const App = props !=> ( <ThemeProvider theme={theme}> {!/* application elements !*/} !</ThemeProvider> ) export default App; const theme = { breakpoints: ['768px'], space: [0, 4, 8, 16, 24, 32, 64], fontSizes: [12, 14, 16, 20, 24, 32], colors: { black: '#000', blacks: ['#ccc', '#999'] white: '#fff', primary: '#456000', }, button: { !!... }, breadcrumb: { !!... } … pagination: { !!... } }
  16. <Box m={2} !/> <Box p={[2, 3]} !/> <Box fontSize={[1, 2]}

    !/> <Box color="primary" !/> <Box color="blacks.0" !/> const theme = { breakpoints: ['768px'], space: [0, 4, 8, 16, 24, 32, 64], fontSizes: [12, 14, 16, 20, 24, 32], colors: { black: '#000', blacks: ['#ccc', '#999'] white: '#fff', primary: '#456000', }, button: { !!... }, breadcrumb: { !!... } … pagination: { !!... } }
  17. <Box m={2} !/> <Box p={[2, 3]} !/> <Box fontSize={[1, 2]}

    !/> <Box color="primary" !/> <Box color="blacks.0" !/> const theme = { breakpoints: ['768px'], space: [0, 4, 8, 16, 24, 32, 64], fontSizes: [12, 14, 16, 20, 24, 32], colors: { black: '#000', blacks: ['#ccc', '#999'] white: '#fff', primary: '#456000', }, button: { !!... }, breadcrumb: { !!... } … pagination: { !!... } }
  18. <Box m={2} !/> <Box p={[2, 3]} !/> <Box fontSize={[1, 2]}

    !/> <Box color="primary" !/> <Box color="blacks.0" !/> const theme = { breakpoints: ['768px'], space: [0, 4, 8, 16, 24, 32, 64], fontSizes: [12, 14, 16, 20, 24, 32], colors: { black: '#000', blacks: ['#ccc', '#999'] white: '#fff', primary: '#456000', }, button: { !!... }, breadcrumb: { !!... } … pagination: { !!... } }
  19. const theme = { buttons: { size: { large: {

    height: 40, padding: '0 10px', }, small: { height: 26, padding: '0 10px', }, default: { height: 32, padding: '0 10px', }, }, }, … } Button Button Button Default Small Large
  20. const theme = { buttons: { size: { large: {

    height: 40, padding: '0 10px', }, small: { height: 26, padding: '0 10px', }, default: { height: 32, padding: '0 10px', }, }, }, … } Button Button Button Default Small Large
  21. <Button size="large!">Button!</Button> const Button = styled.button` ${buttonSizeStyle} `; const buttonSizeStyle

    = variant({ prop: 'size', key: 'buttons.size', }); Button Button Button Default Small Large
  22. <Button size="large!">Button!</Button> Button Button Button Default Small Large const theme

    = { buttons: { size: { large: { height: 40, padding: '0 10px', }, small: { height: 26, padding: '0 10px', }, default: { height: 32, padding: '0 10px', }, }, }, … }
  23. const theme = { buttons = { default: { color:

    "white", backgroundColor: "gray" }, primary: { color: "white", backgroundColor: "blue" }, danger: { color: "black", backgroundColor: "tomato" }, … }
  24. const theme = { buttons = { default: { color:

    "white", backgroundColor: "gray" }, primary: { color: "white", backgroundColor: "blue" }, danger: { color: "black", backgroundColor: "tomato" }, … } Button Button Button Primary Default Danger <Button variant='danger'>Button!</Button>
  25. <Article width={[1, 1 / 2, 1 / 4]} !/> const

    theme = { breakpoints: ['576px', '768px'] };
  26. const Article = styled.div` width: 100%; … @media (min-width: 567px)

    { width: 50%; … } @media (min-width: 768px) { width: 25%; … } `; <Article width={[1, 1 / 2, 1 / 4]} !/> const breakpoints = ['576px', '768px'];
  27. Anna Su Front-End Developer [email protected] 935-721-3322 [email protected] 935-721-3322 Anna Su

    Front-End Developer import { flexbox, space, typography } from 'styled-system'; const Info = styled.div` display: flex; ${flexbox} ${space} ${typography} `; <Info justifyContent={['flex-start', 'space-between']} mb={[5, 13]} fontSize={[12, 14]}> !</Info>
  28. theme.js const theme = { breakpoints: ['40em', '52em', '64em'], space:

    [0, 4, 8, 16, 24, 32, 64], colors: { blacks: ['#000', '#ccc', '#aaa', '#eee', '#999'] }, button: { !!... }, breadcrumb: { !!... } … pagination: { !!... } }
  29. const theme = { breakpoints: ['40em', '52em', '64em'], space: [0,

    1, 2, 4, 8, 16, 32, 64], colors: { !!... }, button: { !!... }, breadcrumb: { !!... } … pagination: { !!... } } 有邏輯的 Array space value = 2 的 n 次⽅方 space index = n + 1
  30. const theme = { colors: { primary: '#4560B0', primaryDark: '#233771',

    primaryHover: '#6A7FBF', primaryLight: '#B9C4E3', danger: '#f44336', icon: '#aaa', link: '#4A90E2', border: '#ccc', } } 有語意的 key
  31. const Title = styled.h2` ${space} margin: 0; `; <Title m="16px!">Hello!</Title>

    module 的順序在上⽅方 造成樣式被覆蓋
  32. export const Box = styled.div` box-sizing: border-box; ${space} ${layout} `;

    <Title m="16px!">Hello!</Title> const Title = styled(Box)` line-height: 1.42; font-weight: normal; margin: 0; `; 擴充元件時, 注意樣式被覆蓋
  33. export const Box = styled.div` box-sizing: border-box; ${space} ${layout} `;

    <Title m="16px!">Hello!</Title> export const Title = styled(Box).attrs({ m: 0 })` line-height: 1.42; font-weight: normal; `; 擴充元件時, 建議使⽤用 attach props
  34. const StyledSlider = styled(Slider)` .ant-slider-handle { width: 24px; height: 24px;

    margin-top: -10px; } `; <StyledSlider defaultValue={30} !/> @slider-rail-background-color-hover: #e1e1e1; @slider-track-background-color: @primary-3; @slider-track-background-color-hover: @primary-4; <Slider defaultValue={30}> <SliderTrack bg="red.100" !/> <SliderFilledTrack bg="tomato" !/> <SliderThumb size={5} border="2px solid" borderColor="tomato"!/> !</Slider>
  35. <Slider defaultValue={30}> <SliderTrack bg="red.100" !/> <SliderFilledTrack bg="tomato" !/> <SliderThumb size={5}

    border="2px solid" borderColor="tomato!"> <Box color="tomato" border="5px solid" borderRadius="50%" !/> !</SliderThumb> !</Slider>
  36. ‣ https://styled-system.com/ ‣ Brad Frost | The Technical Side of

    Design Systems | UI Special, CSS Day 2019 ‣ https://material-ui.com/system/basics/ ‣ https://material.io/design/ ‣ https://ant.design/components/slider/ ‣ https://storybook.grommet.io/ Reference