개발 이야기/Front-end

React 라이브러리 #8

sonoa 2023. 4. 6. 09:27
반응형

스타일링 라이브러리

CSS in JS

CSS의 문제점

Global Namespace: 글로벌 변수를 지양해하는 JS와 대치
Dependencies: CSS간의 의존관리
Dead Code Elimination: 안쓰는 CSS 인지 어려움
Minification: 클래스 이름 최소화
Sharing Constants: JS의 코드와 값을 공유하고 싶음
Non-deterministic Resolution: CSS 파일 로드 타이밍 이슈
Isolation: 격리

스타일 컴포넌트 Styled-components

https://styled-components.com
https://styled-components.com/docs/basics

  • 설치하기
  • npm install --save styled-components
  • yarn add styled-components
// StyledComponentsExample.jsx
import React from 'react';

// 설치하고 사용하기 위해서 import
import styled from 'styled-components';

export default function StyledComponentsExample() {
  // Create a Title component that'll render an <h1> tag with some styles
  const Title = styled.h1`
    font-size: 1.5em;
    text-align: center;
    color: palevioletred;
  `;

  // Create a Wrapper component that'll render a <section> tag with some styles
  const Wrapper = styled.section`
    padding: 4em;
    background: papayawhip;
  `;

  const Button = styled.button`
    /* Adapt the colors based on primary prop */
    // 조건식도 넣을수 있다
    background: ${(props) => (props.primary ? 'palevioletred' : 'white')};
    color: ${(props) => (props.primary ? 'white' : 'palevioletred')};

    font-size: 1em;
    margin: 1em;
    padding: 0.25em 1em;
    border: 2px solid palevioletred;
    border-radius: 3px;
  `;

  // A new component based on Button, but with some override styles
  const TomatoButton = styled(Button)`
    color: tomato;
    border-color: tomato;
  `;

  // Component 를 확장하면서 사용가능하다.
  const ReversedButton = (props) => (
    <Button {...props} children={props.children.split('').reverse()} />
  );

  // Use Title and Wrapper like any other React component – except they're styled!
  return (
    <>
      <Wrapper>
        <Title>Hello World!</Title>
      </Wrapper>
      <Button onClick={() => alert('nomal')}>Normal</Button>
      <Button primary onClick={() => alert('primary')}>
        Primary
      </Button>
      <TomatoButton>Tomato</TomatoButton>
      <br />
      <Button as='a' href='#'>
        Link with Button styles
      </Button>
      <TomatoButton as='a' href='#'>
        Link with Tomato Button styles
      </TomatoButton>
      <br />
      <Button as={ReversedButton}>
        Custom Button with Normal Button styles
      </Button>
    </>
  );
}

스타일 컴포넌트 (styled-components)

Automatioc critical CSS: 자동 style injects & 코드 스플릿
No class name bugs: unique, overlap X , misspellings
Easier deletion of CSS: tied to a specific component
Simple dynamic styling: props /global theme
Painless maintenance: styling affecting your component
Automatic vendor prefixing: corrent standard only

정리
CSS in JS CSS의 문제점을 해소
해결책 스타일을 style 태그로 분리
사용법(Template literals) styled.{element}``
styled(스타일드컴포넌트) 상속

Basic

재사용성과 속도를 생각해서 style은 렌더링 되는 부분의 밖에 선언한다.

import React from 'react';
import styled from 'styled-components';

// 밖에 놔라~! (재사용성과 속도를 위해)

// Create a Title component that'll render an <h1> tag with some styles
const Title = styled.h1`
  font-size: 1.5em;
  text-align: center;
  color: palevioletred;
`;

// Create a Wrapper component that'll render a <section> tag with some styles
const Wrapper = styled.section`
  padding: 4em;
  background: papayawhip;
`;

const Button = styled.button`
  /* Adapt the colors based on primary prop */
  background: ${(props) => (props.primary ? 'palevioletred' : 'white')};
  color: ${(props) => (props.primary ? 'white' : 'palevioletred')};

  font-size: 1em;
  margin: 1em;
  padding: 0.25em 1em;
  border: 2px solid palevioletred;
  border-radius: 3px;
`;

// A new component based on Button, but with some override styles
const TomatoButton = styled(Button)`
  color: tomato;
  border-color: tomato;
`;

// Component 를 확장하면서 사용가능하다.
const ReversedButton = (props) => (
  <Button {...props} children={props.children.split('').reverse()} />
);

export default function StyledComponentsExample() {
  // Use Title and Wrapper like any other React component – except they're styled!
  return (
    <>
      <Wrapper>
        <Title>Hello World!</Title>
      </Wrapper>
      <Button onClick={() => alert('nomal')}>Normal</Button>
      <Button primary onClick={() => alert('primary')}>
        Primary
      </Button>
      <TomatoButton>Tomato</TomatoButton>
      <br />
      <Button as='a' href='#'>
        Link with Button styles
      </Button>
      <TomatoButton as='a' href='#'>
        Link with Tomato Button styles
      </TomatoButton>
      <br />
      <Button as={ReversedButton}>
        Custom Button with Normal Button styles
      </Button>
    </>
  );
}

위치에 따라 CSS 적용예시

const Thing = styled.div.attrs((/* props */) => ({ tabIndex: 0 }))`
  color: blue;

  &:hover {
    color: red; // <Thing> when hovered
  }

  & ~ & {
    background: tomato; // <Thing> as a sibling of <Thing>, but maybe not directly next to it
  }

  & + & {
    background: lime; // <Thing> next to <Thing>
  }

  &.something {
    background: orange; // <Thing> tagged with an additional CSS class ".something"
  }

  .something-else & {
    border: 1px solid; // <Thing> inside another element labeled ".something-else"
  }
`;

render(
  <React.Fragment>
    <Thing>Hello world!</Thing>
    <Thing>How ya doing?</Thing>
    <Thing className='something'>The sun is shining...</Thing>
    <div>Pretty nice day today.</div>
    <Thing>Don't you think?</Thing>
    <div className='something-else'>
      <Thing>Splendid.</Thing>
    </div>
  </React.Fragment>
);

input CSS 적용예시

const Input = styled.input.attrs((props) => ({
  // we can define static props
  type: 'text',

  // or we can define dynamic ones
  size: props.size || '1em',
}))`
  color: palevioletred;
  font-size: 1em;
  border: 2px solid palevioletred;
  border-radius: 3px;

  /* here we use the dynamically computed prop */
  margin: ${(props) => props.size};
  padding: ${(props) => props.size};
`;

render(
  <div>
    <Input placeholder='A small text input' />
    <br />
    <Input placeholder='A bigger text input' size='2em' />
  </div>
);

애니메이션

import styled, { keyframes } from 'styled-components';
// Create the keyframes
const rotate = keyframes`
  from {
    transform: rotate(0deg);
  }

  to {
    transform: rotate(360deg);
  }
`;

// Here we create a component that will rotate everything we pass in over two seconds
const Rotate = styled.div`
  display: inline-block;
  animation: ${rotate} 2s linear infinite;
  padding: 2rem 1rem;
  font-size: 1.2rem;
`;

render(<Rotate>&lt; 😍 &gt;</Rotate>);

Theming

import styled, { ThemeProvider } from 'styled-components';
// Define our button, but with the use of props.theme this time
const Button = styled.button`
  font-size: 1em;
  margin: 1em;
  padding: 0.25em 1em;
  border-radius: 3px;

  /* Color the border and text with theme.main */
  color: ${(props) => props.theme.main};
  border: 2px solid ${(props) => props.theme.main};
`;

// We are passing a default theme for Buttons that arent wrapped in the ThemeProvider
Button.defaultProps = {
  theme: {
    main: 'palevioletred',
  },
};

// Define what props.theme will look like
const theme = {
  main: 'mediumseagreen',
};

render(
  <div>
    <Button>Normal</Button>

    <ThemeProvider theme={theme}>
      <Button>Themed</Button>
    </ThemeProvider>
  </div>
);

toggle 을 이용해서 Theming

import React, { useState } from 'react';
import styled, { ThemeProvider } from 'styled-components';

// Define our button, but with the use of props.theme this time
const Button = styled.button`
  font-size: 1em;
  margin: 1em;
  padding: 0.25em 1em;
  border-radius: 3px;

  /* Color the border and text with theme.main */
  color: ${(props) => props.theme.color};
  border: 2px solid ${(props) => props.theme.borderColor};
`;

// Define what props.theme will look like
const defaultTheme = {
  color: 'green',
  borderColor: 'green',
};

const redTheme = {
  color: 'red',
  borderColor: 'red',
};

export default function StyledComponentsExample() {
  const [theme, setTheme] = useState(defaultTheme);
  // Use Title and Wrapper like any other React component – except they're styled!
  return (
    <>
      <div>
        <button
          onClick={() => {
            setTheme(redTheme);
          }}
        >
          red
        </button>
        <button
          onClick={() => {
            setTheme(defaultTheme);
          }}
        >
          green
        </button>
        <ThemeProvider theme={theme}>
          <Button>Normal</Button>
          <Button>Themed</Button>
        </ThemeProvider>
      </div>
    </>
  );
}

Helpers

  • createGlobalStyle
  • 모든 페이지에 공통으로 적용시킬 때 유용함
  • 포함된 div 뿐만 아니라 그 밖의 영역까지 적용됨
import { createGlobalStyle } from 'styled-components'

const GlobalStyle = createGlobalStyle`
  body {
    color: ${props => (props.whiteColor ? 'white' : 'black')};
  }
`

// later in your app

<React.Fragment>
  <GlobalStyle whiteColor />
  <Navigation /> {/* example of other top-level stuff */}
</React.Fragment>
styled-components
& 가상 엘리먼트/ 가상 선택자
Global Style 전역스타일
attrs props addition
keyframes Animation
ThemeProvider Theme
반응형

'개발 이야기 > Front-end' 카테고리의 다른 글

React Styling  (0) 2023.04.08
Next.js  (0) 2023.04.07
React 라이브러리 #7  (0) 2023.04.05
React 공식문서로 디테일 잡기 #8  (0) 2023.04.04
React 라이브러리 #6  (0) 2023.04.03