CS/프로그래밍

CSS in JS

H.E 2024. 1. 13. 15:05

[ CSS in JS ]

  • CSS스타일을 추상화한 JS객체 대신, CSS스타일 문법을 그대로 사용하여 스타일 컴포넌트로 사용할 수 있는 것
  • 현재 사용 중인 스타일만 DOM에 포함
  • JS와 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');
  }

  input[type='checkbox']:checked + label {
    background-image: url('https://platform-site');
  }
`;

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>
)

'CS > 프로그래밍' 카테고리의 다른 글

고차 컴포넌트 (HOC, High Order Component)  (0) 2024.01.20
CSS vs CSS-in-CSS vs CSS-in-JS  (1) 2024.01.13
화살표 함수와 일반 함수  (0) 2024.01.08
리액트 Hook  (1) 2024.01.02
스코프, 스코프 체인  (1) 2024.01.02