홍승아블로그

emotion

리액트 프레임워크 사용 X

  • 설치

    npm i @emotion/css
    or
    yarn add @emotion/css
  • 설명

    • 추가적인 setup, babel plugin, 다른 환경설정이 요구하지 않는다.
    • auto vendor-prefixing, nested selectors, media queries 지원한다..
      • auto vendor-prefixing
        • webkit- supported by OPERA, SAFARI, GOOGLE CHROME
        • moz- supported by FIREFOX
        • ms- supported by EDGE/INTERNET EXPLORER
    • css 함수 사용 시 classname 자동생성, cx도 포함해서 class 여러개를 통합까지 지원한다.
    • 단, 서버사이드 랜더링 시 추가적인 작업이 요청된다.(https://emotion.sh/docs/ssr#api)
    import { css, cx } from '@emotion/css';
    
    const color = 'white';
    
    render(
      <div
        className={css`
          padding: 32px;
          background-color: hotpink;
          font-size: 24px;
          border-radius: 4px;
          &:hover {
            color: ${color};
          }
        `}
      >
        Hover to change color.
      </div>,
    );

리액트 프레임워크 사용 O

  • @emotion/react

    • 설치

      npm i @emotion/react
      or
      yarn add @emotion/react
    • 설명

      • css prop 지원
        • auto vendor-prefixing, nested selectors, media queries 지원한다..
          • auto vendor-prefixing
            • webkit- supported by OPERA, SAFARI, GOOGLE CHROME
            • moz- supported by FIREFOX
            • ms- supported by EDGE/INTERNET EXPLORER
        • 컴포너트 합성/스타일 지정 시 보일러플레이트 코드를 줄일 수 있다.
      • 서버 사이드 렌더링 수행 시 따로 환경세팅 필요없다.
      • proper pattern 과 환경구성을 적절하게 이용을 위해서는 ESLint 사용을 제안합니다.
      import { css } from '@emotion/react';
      
      const danger = css`
        color: red;
      `;
      
      const base = css`
        background-color: darkgreen;
        color: turquoise;
      `;
      
      render(
        <div>
          <div css={base}>This will be turquoise</div>
          <div css={[danger, base]}>
            This will be also be turquoise since the base styles overwrite the
            danger styles.
          </div>
          <div css={[base, danger]}>This will be red</div>
        </div>,
      );
  • @emotion/styled

    • 설치

      npm i @emotion/styled @emotion/react
      or
      yarn add @emotion/styled @emotion/react
    • 설명 컴포넌트 생성 시 styled.div와 같은 스타일을 선호하는 사용자를 위한 패키지 입니다.

      import styled from '@emotion/styled';
      
      const Button = styled.button`
        padding: 32px;
        background-color: hotpink;
        font-size: 24px;
        border-radius: 4px;
        color: black;
        font-weight: bold;
        &:hover {
          color: white;
        }
      `;
      
      render(<Button>This my button component.</Button>);
    • Styling elements and components

      import styled from '@emotion/styled';
      
      const Button = styled.button`
        color: turquoise;
      `;
      
      render(<Button>This my button component.</Button>);
    • Changing based on props props를 기반으로 컴포넌트의 스타일을 변경할 경우..

      import styled from '@emotion/styled';
      
      const Button = styled.button`
        color: ${props => (props.primary ? 'hotpink' : 'turquoise')};
      `;
      
      const Container = styled.div(props => ({
        display: 'flex',
        flexDirection: props.column && 'column',
      }));
      
      render(
        <Container column>
          <Button>This is a regular button.</Button>
          <Button primary>This is a primary button.</Button>
        </Container>,
      );
    • 컴포넌트 상속을 통한 classname 스타일링

      import styled from '@emotion/styled';
      
      const Basic = ({ className }) => <div className={className}>Some text</div>;
      
      const Fancy = styled(Basic)`
        color: hotpink;
      `;
      
      render(<Fancy />);
    • 스타일은 그대로 적용하면서 엘리먼트만 변경할 경우 withComponent를 활용해서 스타일은 그대로 가져오면서 엘리먼트만 변경이 가능하다. as property를 활용해서도 엘리먼트 변경이 가능하다.

      import styled from '@emotion/styled';
      
      const Section = styled.section`
        background: #333;
        color: #fff;
      `;
      
      // this component has the same styles as Section but it renders an aside
      const Aside = Section.withComponent('aside');
      
      render(
        <div>
          <Section>This is a section</Section>
          <Aside>This is an aside</Aside>
        </div>,
      );
      
      import styled from '@emotion/styled';
      
      const Button = styled.button`
        color: hotpink;
      `;
      
      render(
        // button -> a tag로 변경
        <Button as="a" href="https://github.com/emotion-js/emotion">
          Emotion on GitHub
        </Button>,
      );
    • 컴포넌트를 타겟팅해서 CSS 변경해야할 경우 @emotion/babel-plugin을 사용할 경우 스타일 변경이 가능하다.

      // emotion string styles
      import styled from '@emotion/styled';
      
      const Child = styled.div`
        color: red;
      `;
      
      const Parent = styled.div`
        ${Child} {
          color: green;
        }
      `;
      
      render(
        <div>
          <Parent>
            <Child>Green because I am inside a Parent</Child>
          </Parent>
          <Child>Red because I am not inside a Parent</Child>
        </div>,
      );
      
      // emotion object styles
      import styled from '@emotion/styled';
      
      const Child = styled.div({
        color: 'red',
      });
      
      const Parent = styled.div({
        [Child]: {
          color: 'green',
        },
      });
      
      render(
        <div>
          <Parent>
            <Child>green</Child>
          </Parent>
          <Child>red</Child>
        </div>,
      );
    • 조건에 따른 props 전달해야할 경우 shouldForwardProp 조건으로 true 일경우 렌더링 시 props를 전달하지 않는다.

      import isPropValid from '@emotion/is-prop-valid'
      import styled from '@emotion/styled'
      
      const H1 = styled('h1', {
        // shouldForwardProp 모두 true로 줄 경우 children props로 전달이 안되서 화면이 안보이게 됩니다.
      / // default as/children 순차적으로 호출됨.
        shouldForwardProp: prop => isPropValid(prop) && prop !== 'color'
      })(props => ({
        color: props.color
      }))
      
      render(<H1 color="lightgreen">This is lightgreen.</H1>)
    • 동적 스타일 적용해야할 경우

      import styled from '@emotion/styled';
      import { css } from '@emotion/react';
      
      const dynamicStyle = props => css`
        color: ${props.color};
      `;
      
      const Container = styled.div`
        ${dynamicStyle};
      `;
      render(<Container color="lightgreen">This is lightgreen.</Container>);
    • Nesting components

      import styled from '@emotion/styled';
      
      const Example = styled('span')`
        color: lightgreen;
      
        & > strong {
          color: hotpink;
        }
      `;
      
      render(
        <Example>
          This is <strong>nested</strong>.
        </Example>,
      );
  • Media Queries

    • 기본

      import { css } from '@emotion/react';
      
      render(
        <p
          css={css`
            font-size: 30px;
            @media (min-width: 420px) {
              font-size: 50px;
            }
          `}
        >
          Some text!
        </p>,
      );
    • 미디어쿼리 재사용

      import { css } from '@emotion/react';
      
      const breakpoints = [576, 768, 992, 1200];
      
      const mq = breakpoints.map(bp => `@media (min-width: ${bp}px)`);
      
      render(
        <div>
          <div
            css={
              color: 'green',
              [mq[0]]: {
                color: 'gray',
              },
              [mq[1]]: {
                color: 'hotpink',
              },
            }
          >
            Some text!
          </div>
          <p
            css={css`
              color: green;
              ${mq[0]} {
                color: gray;
              }
              ${mq[1]} {
                color: hotpink;
              }
            `}
          >
            Some other text!
          </p>
        </div>,
      );
    • facepaint 사용

      • 설치
        npm i facepaint
        or
        yarn add facepaint
      import facepaint from 'facepaint';
      
      const breakpoints = [576, 768, 992, 1200];
      
      const mq = facepaint(breakpoints.map(bp => `@media (min-width: ${bp}px)`));
      
      render(
        <div
          css={mq({
            color: ['green', 'gray', 'hotpink'],
          })}
        >
          Some text.
        </div>,
      );
  • Global Styles

    import { Global, css } from '@emotion/react';
    
    render(
      <div>
        <Global
          styles={css`
            .some-class {
              color: hotpink !important;
            }
          `}
        />
        <Global
          styles={
            '.some-class': {
              fontSize: 50,
              textAlign: 'center',
            },
          }
        />
        <div className="some-class">This is hotpink now!</div>
      </div>,
    );
  • Keyframes

    import { css, keyframes } from '@emotion/react';
    
    const bounce = keyframes`
      from, 20%, 53%, 80%, to {
        transform: translate3d(0,0,0);
      }
    
      40%, 43% {
        transform: translate3d(0, -30px, 0);
      }
    
      70% {
        transform: translate3d(0, -15px, 0);
      }
    
      90% {
        transform: translate3d(0,-4px,0);
      }
    `;
    
    render(
      <div
        css={css`
          animation: ${bounce} 1s ease infinite;
        `}
      >
        some bouncing text!
      </div>,
    );
  • 컴포넌트 스타일에 대한 전체적용(spread 연산)

    import { css } from '@emotion/react';
    
    const pinkInput = css`
      background-color: pink;
    `;
    const RedPasswordInput = props => (
      <input
        type="password"
        css={css`
          background-color: red;
          display: block;
        `}
        {...props}
      />
    );
    
    render(
      <div>
        <RedPasswordInput placeholder="red" />
        <RedPasswordInput placeholder="pink" css={pinkInput} />
      </div>,
    );
  • 테마 적용 ThemeProvider로 감싼경우 contextapi로 공유가 되기 때문에 자식컴포넌트에서 사용이 가능하다.

    // css prop
    import { ThemeProvider } from '@emotion/react';
    
    const theme = {
      colors: {
        primary: 'hotpink',
      },
    };
    
    render(
      <ThemeProvider theme={theme}>
        <div css={theme => ({ color: theme.colors.primary })}>
          some other text
        </div>
      </ThemeProvider>,
    );
    
    // styled
    import { ThemeProvider } from '@emotion/react';
    import styled from '@emotion/styled';
    
    const theme = {
      colors: {
        primary: 'hotpink',
      },
    };
    
    const SomeText = styled.div`
      color: ${props => props.theme.colors.primary};
    `;
    
    render(
      <ThemeProvider theme={theme}>
        <SomeText>some text</SomeText>
      </ThemeProvider>,
    );
    
    // useTheme hook
    import { ThemeProvider, useTheme } from '@emotion/react';
    
    const theme = {
      colors: {
        primary: 'hotpink',
      },
    };
    
    function SomeText(props) {
      const theme = useTheme();
      return <div css={ color: theme.colors.primary } {...props} />;
    }
    
    render(
      <ThemeProvider theme={theme}>
        <SomeText>some text</SomeText>
      </ThemeProvider>,
    );
  • className을 값으로 보여줄 경우(labels 사용)

    import { css } from '@emotion/react';
    
    let style = css`
      color: hotpink;
      label: some-name;
    `;
    
    let anotherStyle = css({
      color: 'lightgreen',
      label: 'another-name',
    });
    
    let ShowClassName = ({ className }) => (
      <div className={className}>{className}</div>
    );
    
    render(
      <div>
        <ShowClassName css={style} />
        <ShowClassName css={anotherStyle} />
      </div>,
    );
  • css, cx를 동시에 사용해야할 경우(ClassNames 사용)

    import { ClassNames } from '@emotion/react';
    
    // this might be a component from npm that accepts a wrapperClassName prop
    let SomeComponent = props => (
      <div className={props.wrapperClassName}>
        in the wrapper!
        <div className={props.className}>{props.children}</div>
      </div>
    );
    
    render(
      <ClassNames>
        {({ css, cx }) => (
          <SomeComponent
            wrapperClassName={css({ color: 'green' })}
            className={css`
              color: hotpink;
            `}
          >
            from children!!
          </SomeComponent>
        )}
      </ClassNames>,
    );
  • key값을 지정해야할 경우(CacheProvider 사용)

    import { CacheProvider, css } from '@emotion/react';
    import createCache from '@emotion/cache';
    import { prefixer } from 'stylis';
    
    const customPlugin = () => {};
    
    const myCache = createCache({
      key: 'my-prefix-key',
      stylisPlugins: [
        customPlugin,
        // has to be included manually when customizing `stylisPlugins` if you want
        // to have vendor prefixes added automatically
        prefixer,
      ],
    });
    
    render(
      <CacheProvider value={myCache}>
        <div // class: my-prefix-key-zjik7
          css={css`
            display: flex;
          `}
        >
          <div // class: my-prefix-key-yc3urw
            css={css`
              flex: 1;
              transform: scale(1.1);
              color: hotpink;
            `}
          >
            Some text
          </div>
        </div>
      </CacheProvider>,
    );

Babel Plugin 사용

  • 설치

    npm i -D @emotion/babel-plugin
    or
    yarn add -D @emotion/babel-plugin
  • .babelrc

    {
      "plugins": ["@emotion"]
    }
    
    // env 환경에 따라서 각각 세팅을 해야할 경우
    {
      "env": {
        "production": {
          "plugins": ["@emotion", ...otherBabelPlugins]
        }
      },
      "plugins": ["@emotion"]
    }

브라우저 지원범위

  • emotion은 IE11 포함한 대부분의 브라우저를 지원합니다.

참고페이지

Introduction

이전글
jest
다음글
jotai