CSS in JS 라이브러리

nais·2022년 3월 8일
0
post-thumbnail

CSS in JS

  • css 스타일을 추상화한 javascript 객체 대신, css 스타일 문법을 그대로 사용하여 React 스타일 컴포넌트로 사용할 수 있는 것
  • 현재 사용 중인 스타일만 DOM에 포함
  • javascript 와 css 사이의 상수 및 함수를 교환할 수 있다
  • Global namespace 로 클래스명을 다르게 짓기 위한 고충을 피할 수 있다
  • 미사용 코드를 검출 가능
  • CSS 로드 우선 순위 이슈를 해결할 수 있다

💅styled-components

  • 손쉬운 유지보수 ( 컴포넌트에 영향을 미치는 스타일을 찾기 위해 여러 파일을 검색할 필요 x)
  • 고유한 CSS 클래스 생성 - 스타일 컴포넌트를 추척해서 css 로 작성된 `` 스타일을 고유한 css 클래스 이름으로 변경
  • 스타일 컴포넌트에 의해 자동 생성된 고유 CSS 클래스 이름은 sc-접두사로 시작 !

const AppButton = styld.button`
  background: transparent;
  color: #0a6cff;
`

const Para = styled.p`
	margin-bottom: 10px;
  background-color: #3d5afe;
	font-size: 1rem;
`;
  • 벤더 프리픽스 자동 설정 - 브라우저 벤더 프리픽스(-webkit,-moz,-ms-) 등을 설정으로 스트레스 받을 필요 없이 css 표준 문법을 사용하면 자동으로 처리된다
  • 동적 스타일링 - props 또는 theme 속성을 사용해 컴포넌트 외부에서 스타일을 관리하는 것은 물론이고 수십개의 css 클래스를 손수관리하지 않고 컴포넌트 외부에서 손쉽게 동적으로 스타일 관리 가능
<AppButton theme={**{ primary: "#f10e60" }**}> ... </AppButton>

styled.button`
	background: **${ ({theme}) => theme.primary }**;
`

패키지

npm i -D styled-components

사용법

React 프로젝트에서 styled-components 모듈에서 styled 를 불러온 후, HTML 표준 컴포넌트 이름을 추가하고 `` 백틱 기호로 감싸서 css 코드를 작성하면 된다

**import styled from 'styled-components';**

const SectionHeader = **styled.h2`
  color: #06f;
  font-size: 1.45rem;
`;**

같이 쓰면 좋은 플러그인

babel-plugin-styled-components

→ 클래스 이름 읽기 쉬워지게 되어서 디버깅이 용이하다 , 번들 크기도 더 작아짐

npm i -D babel-plugin-styled-components

props

  • React 컴포넌트 요소로 사용될 때 속성 전달 받는역할
  • props 속성으로 전달 받은 속성에 접근이 가능
  • Style-component 또한 props 속성을 인터폴레이션 ${} 사용하여 처리가능하다
  • 컴포넌트에 속성을 전달하면 Styled 컴포넌트 내부에서 스타일 분기 처리도 가능하다
import styled from 'styled-components';

const Button = styled.button`
  color: **${ props => props.reject ? '#f60' : '#06f' }**;
`;

export default Button;
<Button reject>취소</Button> // color: #f60
<Button>취소</Button>        // color: #06f

스타일 확장

  • style 함수에 styled 컴포넌트 활용해서 정의된 컴포넌트를 전달하면 스타일을 확장(extends)할 수 있다
import styled from 'styled-components';
**import Button from './Button';**

// AppButton 확장
export const FillButton = **styled(Button)**`
  border: 0;
  padding: 0.45em 0.95em 0.6em;
  background-color: ${ props => props.reject ? '#026' : '#06f' };
  color: ${ props => props.reject ? '#09f' : '#fff' };
  font-weight: 600;
`;

컴포넌트 스타일 확장

  • 일반 React 컴포넌트 또한 스타일 확장이 가능
  • 스타일 확장과 비슷하지만 , className 속성을 전달 받도록 해야지 스타일 확장이 적용된다
import styled from 'styled-components';

export const InputStyled = styled.div`
  input[type='checkbox'] {
    display: none;
  }
  input[type='checkbox'] + label {
    height: 30px;
    display: inline-block;
    cursor: pointer;
    padding-left: 30px;
    line-height: 24px;
    background-repeat: no-repeat;
    background-image: url('https://platform-site.yanolja.com/icons/checkbox-unselected.svg?inline');
  }

  input[type='checkbox']:checked + label {
    background-image: url('https://platform-site.yanolja.com/icons/checkbox-selected.svg?inline');
  }
`;

export const WrapperInput = styled(InputStyled)`
  border: 1px solid #e6e6e6;
  padding: 25px;
  display: flex;
  justify-content: space-between;

  button {
    cursor: pointer;
  }
`;
}

스타일 래퍼

  • 컴포넌트, 스타일 컴포넌트를 구분하지 않고 내부 스타일 컴포넌트를 정의하는 것이 관리하기 편하다
  • 컴포넌트 내부에 포함한 스타일 컴포넌트가 많아질 경우, 별도 파일로 분리하는 것이 좋다
// 스타일 컴포넌트
const Container = styled.div`
  display: flex;
  align-items: center;
  justify-content: center;
  font: 14px/1 Verdana;
  color: ${({ primary }) => primary ?? 'hsla(220, 99%, 50%, 0.89)'};
`;

// 컴포넌트
const Counter = ({ primary }) => (
  <Container primary={primary}>{/* ... */}</Container>
);

export default Counter;

중첩규칙

  • 요소의 자식 요소들 중 특정 요소만 스타일 하는 것도 가능하다
const Container = styled.div`
  **output** {
    user-select: none;
    font-size: 40px;
  }
`;
<Container>
	**<output> 9 </output>**
</Container>

가상(유사) 클래스

  • styled 컴포넌트의 css 코드 안에 css 가상 클래스를 설정하면 스타일이 적용됩니다.
const ControlButton = styled.button`
  **:hover, :focus {**
    color: #036;
    font-weight: 400;
  **}**
  
	**:focus {**
    outline: none;
  **}**
  
	**:disabled {**
    filter:grayscale(100%);
    cursor: not-allowed;
    color: #909090;
    font-weight: 300;
  **}**
`;

가상(유사) 요소

  • 스타일 컴포넌트의 css 코드 안에 css 가상 요소를 설정하면 스타일이 적용됩니다.
const ControlButton = styled.button`
  **&.increase** {
		**&::before** {
	    content: '📤';
	  }
	****}
`

css 유틸리디

  • css() 유틸리티 함수를 사용해 믹스인을 구현할 수 있도록 제공
import styled, { css } from 'styled-components';

// CSS 믹스인
const boxMixin = css`
  margin: 20px 10px;
  border: 0;
  padding: 1em;
  font-size: 15px;
  font-weight: bold;
  line-height: 1.7;
  color: #fff;
`;

// Box 컴포넌트 ⬅ CSS 믹스인
const Box = styled.div`
  **${ boxMixin };**
  background: #07f;
`;

// ShadowBox 컴포넌트 ⬅ CSS 믹스인
const ShadowBox = styled.div`
  **${ boxMixin };**
  background: #41b883;
  box-shadow: 0 6px 8px 1px rgba(0,100,30,0.35)
`;

CSS prop

  • 약간의 스타일을 위해서 styled 컴포넌트를 만들지 않기 위함
  • css prop를 사용해서 객체 포맷으로 적용
  • 이 기능 사용하려면 babel 플러그인 설치 해야된다
**import 'styled-components/macro';**

<Button **css="padding: 0.5em 1em;"** />

// 위코드를 바벨을 이용하면 이런 형태로 컴파일 해준다 

const StyledButton = styled(Button)`
  padding: 0.5em 1em;
`

애니메이션

  • CSS 애니메이션 @keyframes 컴포넌트는 컴포넌트 범위 내에서 설정할 수 없어서 styled-components 의 keyframes 모듈을 사용해야 한다
    • Keyframes 모듈
      import styled, { **keyframes** } from 'styled-components';
      
      const rotateKeyframes = **keyframes`
        0%   { transform: translateY(0) }
        25%  { transform: translateY(-20px) rotate(20deg) }
        50%  { transform: translateY(10px) }
        75%  { transform: translateY(-15px) rotate(-20deg) }
        100% { transform: translateY(0) }
      `;**
      
      // 마법 모자 animation 속성에 rotate 값 설정
      const MagicHat = styled.div`
        font-size: 100px;
        animation: **${rotateKeyframes}** 3s infinite cubic-bezier(0.35, 0.29, 0.4, 0.8);
      `;

Global Style

  • style Components 모듈의 CreateGlobalStyle() 함수는 글로벌 스타일을 정의하는 컴포넌트를 반환
import { **createGlobalStyle** } from 'styled-components';

const GlobalStyle = **createGlobalStyle`
  body {
    margin: 0;
    font: 1rem/1.5 "Spoqa Han Sans", Sans-Serif;
    background: ${ ({darken}) => darken ? '#162442' : '#dee1e6' }
    color: ${ ({darken}) => darken ? '#dee1e6' : '#162442' }
  }
  a img {
    border: 0;
  }
`;**

🦹‍♀️ Emotion

  • js 로 css 스타일을 작동하도록 설계된 라이브러리
  • 사용 방법으로는 React 에서 사용하는것, 프레임워크 없이 사용
  • 모던 브라우저 뿐 아니라, IE11 브라우저 또한 지원

React 와 사용하기

  • React 와 사용하려면 @emotion/react 패키지를 설치해야한다

    @emotion/ react의 특징

  • css prop 지원

  • style-component, 요소 스타일을 직접 설정 가능

  • ssr(서버 사이드 렌더링 지원)

  • 테마(theme ) 지원

  • eslint 지원

패키지 설치

React + Emotion

npm i -D @emotion/react

React + Styled Component

styled React 컴포넌트를 사용하려면 @emotion/styled 패키지를 설치합니다.

npm i -D @emotion/styled

작성하는 방법


import styled from '@emotion/styled';

const Button = styled.button`
	color: hotpink;
`;

render(<Button>핫핑크 버튼</Button>);

Babel plugin

Babel 플러그인을 설치하면 스타일 압축/최적화 하고, 소스맵을 제공하는 보다 나은 개발자 경험을 제공합니다.

npm i -D @emotion/babel-plugin

.babelrc 파일의 플러그인을 구성한다

{
	"plugins": ["@emotion", ...다른플러그인]
}

CSS Prop

  • emotion 으로 요소를 스타일링 하는 방법으로는 css prop을 사용하는 것 방법으론 두가지가 있다

1)Babel preset

  • CRA 또는 Babel 구성을 허용하지 않는 다른 도구에서는 정상적 작동을하지 않습니다 .( 우리는 CRA 로 깔았지만 react-app-rewired 를 설치해서 세부 설정을 덮어 씌우기 했다)

2) JSX Pragma

  • css prop 을 사용하려는 소스 파일의 최상단에 jsx 프라그마를 설정
  • 이 옵션은 css prop 함수를 사용하거나 Bable 구성을 사용할 수 없는 프로젝트에 사용할 때 씀(CRA 같은 애들)
  • JSX 프라그마 없이 css prop을 사용하면 [object object ]값을 반환합니다
/** @jsx jsx */
import { jsx } from '@emotion/react';

object 스타일

  • css prop 에 설정 가능한 값의 타입 중 하나는 바로 “객체”
  • 이 방법을 이용시에는 css 문법 (Kebab-case) 대신에 js 문법인 (camelCase)을 사용해야한다
  • ex) background-color → backgroundColor
  • emtion이 제공하는 강력한 패턴
  • 객체 스타일은 Styled 와 함께 사용도 가능
/** @jsx jsx */
import { jsx } from '@emotion/react';

render(
  <div
    **css={{
      backgroundColor: 'hotpink',
      '&:hover': {
        color: 'lightpink'
      }
    }}**
  >
    핫핑크 배경색
  </div>
)

String 스타일

  • 문자 타입을 사용해 스타일 하려면 @emotion/react 패키지에서 css 함수를 추출하여 사용
  • tegged Template 문법을 사용해서 내부에 css 문법 작성
  • 이걸 쓰면 CSS 문법(kebab-case)의 코드 를 사용 가능!
/** @jsx jsx */
import { **css**, jsx } from '@emotion/react';

const color = 'lightpink';

render(
  <div
    **css={css`
      background-color: hotpink;
      &:hover {
        color: ${color};
      }
    `}**
  >
    핫핑크 배경색
  </div>
)

스타일 우선순위

  • 중복된 것들은 나중에 적용된 스타일이 적용
  • 합성된다고 생각하자
/** @jsx jsx */
import { jsx } from '@emotion/react';

const P = props => (
  <p
    css={{
      margin: 0,
      fontSize: 12,
      lineHeight: 1.5,
      fontFamily: 'sans-serif',
      color: 'black'
    }}
    {...props} // <- props 객체는 `className` prop을 포함
  />
);

const ArticleText = props => (
  <P
    css={{
      fontSize: 14,
      fontFamily: 'Georgia, serif',
      color: 'darkgray'
    }}
    {...props} // <- props 객체는 `className` prop을 포함
  />
);

const SmallArticleText = props => (
  <ArticleText
    css={{
      fontSize: 10
    }}
    {...props} // <- props 객체는 `className` prop을 포함
  />
);

Theming

  • @emotion/reaction 패키지에 포함
  • ThemeProvieder 를 앱의 최상위 레벨에 추가하고 Props 를 사용해 테마에 접근
  • 스타일 컴포넌트의 테마 또는 테마를 css prop으로 받아 들이는 역할

css Prop

import { jsx, **ThemeProvider** } from '@emotion/react';

const theme = {
  colors: {
    primary: 'hotpink'
  }
}

render(
  **<ThemeProvider theme={theme}>**
    <div css={**theme** => ({ color: **theme.colors.primary** })}>
      테마 설정을 토대로 색상 표시
    </div>
  **</ThemeProvider>**
)

styled

/** @jsx jsx */
import { jsx, **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>테마 설정을 토대로 색상 표시</SomeText>
  **</ThemeProvider>**
);

useThema

/** @jsx jsx */
import { jsx, 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>테마 설정을 토대로 색상 표시</SomeText>
  </ThemeProvider>
);

Nested Selector

React 컴포넌트 또는 클래스에 선택자를 중첩해 사용하는 방법은 유용하게 쓰인다

/** @jsx jsx */
import { jsx, css } from '@emotion/react';

const paragraph = css`
  color: black;

  a {
    border-bottom: 1px solid currentColor;
    cursor: pointer;
  }
`;

render(
  <p css={paragraph}>
    중첩된 요소를 손쉽게 스타일링 할 수 있습니다.
    <a>아래 방향에 테두리가 그려진 링크</a>
  </p>
);
  • & 을 이용하면 상위 요소가 컴포넌트를 중첩한 상황을 스타일링 할 수 있습니다
import { jsx, css } from '@emotion/react'

const paragraph = css`
  color: black;

  **header &** {
    color: green;
  }
`
render(
  <div>
    **<header>
      <p css={paragraph}>**
        헤더 내부에 위치한 단락은 녹색 글자로 표현됩니다.
      **</p>
    </header>**
    <p css={paragraph}>
      이 단락은 헤더에 포함되지 않았으므로 검정색 글자로 표현됩니다.
    </p>
  </div>
)
profile
왜가 디폴트값인 프론트엔드 개발자

0개의 댓글