본문 바로가기
Software/JS & TS & React

styled-components 사용법 [part 2 /2]

by lovey25 2022. 11. 14.
반응형

지난 포스팅에 이어서 styled-components 사용법에 대한 내용이며 이번에는 좀 더 헷갈렸던 고급 사용법에 대해서 공부해 보겠습니다.

의사 선택자(pseudo-selector)

선택자에 옵션을 줘서 일부 요소만 선택하는 방법입니다. 영어로는 pseudo라는 명칭을 사용하던데 많은 CSS 자료에서 "의사"라고 번역을 하네요.

& (ampersand)

"&"기호를 하나만 사용하게 되면 '모든 요소'를 의미합니다. 그냥 이렇게만 얘기하면 무슨 얘기인지 잘 이해가 안 되죠?! 저는 그랬습니다. ㅡ.,ㅡ 암튼 예제를 보면 이해가 쉽게 됩니다. 이 예제는 'styled-components' 공식 홈페이지의 예제를 그대로 가져와서 주석만 번역했습니다.

그리고 아래 내용을 이해하기 위해서는 CSS의 기본 문법에 대한 지식이 필요합니다. CSS공부를 시작하시는 분은 여기서 CSS 기본 문법을 먼저 훑어보시길 추천드립니다.

import styled from "styled-components";

const Thing = styled.div`
  color: blue;
  &:hover {
    color: red; // <Thing>요소 중 마우스가 올라간 모든 요소의 글씨는 붉은색
  }
  & ~ & {
    background: tomato; // <Thing>의 형제 요소중 모든 <Thing>의 배경은 토마토색
  }
  & + & {
    background: lime; // <Thing> 바로 다음에 오는 <Thing>의 배경은 라임색
  }
  &.something {
    background: orange; // <Thing>요소 중 클래스명이 ".something"이면 배경을 오랜지색
  }
  .something-else & {
    border: 1px solid; // 클래스명이 ".something-else"인 모든 요소중 자식 요소에 <Thing>이 있으면 테두리 그리기
  }
`;

export default function App() {
  return (
    <div className="App">
      <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>
    </div>
  );
}

결과는 다음과 같습니다. 

여기서 한 가지 제가 헷갈린 부분이 있어서 언급을 하자면 17행의 ".something-else &" 선택자입니다. "Thing"이라는 이름으로 정의된 선택자이기 때문에 <Thing> 요소 중 클래스명이 'something-else'인 것의 자식 중에서 모든 <Thing> 요소를 선택하라는 의미로 이해를 했는데 'something-else'의 태그명은 'Thing'이 아니라 'div'입니다. 다시 말해서 Thing이라는 이름으로 styled-conponents가 정의되었지만 "&"를 명시하지 않으면 클래스 선택자는 모든 태그를 대상으로 한다는 겁니다.

&&(double ampersand)

'&'기호 2개를 붙여서 사용하게 되면 스타일 우선순위를 끌어올릴 수 있습니다. 복수의 조건이 모두 만족하는 경우 특정 스타일을 더 우선하여 적용하고자 할 때 사용할 수 있습니다.

import styled from "styled-components";

export default function App() {
  const St = {
    Box: styled.label`
      font-size: 5em;
      input {
        display: none;
      }
    `,
    Star: styled.label`
      color: #ccc;
      cursor: pointer;
      :hover {
        color: #fffc35;
      }
      input:checked + && {
        color: blue;
      }
      input:checked ~ & {
        color: red;
      }
    `,
  };
  return (
    <St.Box>
      <input id="star1" type="checkbox" />
      <St.Star htmlFor="star1">&#9733;</St.Star>
      <input id="star2" type="checkbox" />
      <St.Star htmlFor="star2">&#9733;</St.Star>
      <input id="star3" type="checkbox" />
      <St.Star htmlFor="star3">&#9733;</St.Star>
    </St.Box>
  );
}

체크박스의 input과 별 모양 label을 세트로 묶어서 별을 클릭하면 체크박스의 선택이 토글 되고 그리고 체크박스의 선택 여부에 따라서 별의 색상이 변하도록 했습니다. 아무 별이나 클릭하면 그 오른쪽에 있는 모든 별은 붉은색이 됩니다. 하지만 선택이 된 별은 파란색으로 표시하기 위해서 "input:checked + &&"으로 우선순위를 끌어올렸습니다. 결과는 다음과 같습니다.

속성 사용하기

스타일에 속성을 줄 수도 있습니다. 요소별로 속성을 주고 거기에 맞는 스타일을 적용하는 방법으로 사용하는 건데요. 아래 예시는 <Star>라는 스타일 요소에 글자색, 글자크기 2가지의 스타일을 정의했습니다. 그런데 글자크기는 매개변수(pr)를 받아서 전달되는 속성(size)에 따라 다르게 적용할 수 있도록 되어 있습니다. 만약 전달되는 속성이 없는 경우에는 "2em"을 기본값을 가지도록 하였습니다.

import styled from "styled-components";

export function App() {
  const Star = styled.span.attrs((pr) => ({
    size: pr.size || "2em",
  }))`
    color: tomato;
    font-size: ${(pr) => pr.size};
  `;
  return (
    <>
      <Star>&#9733;</Star>
      <Star size="4em">&#9733;</Star>
      <Star size="8em">&#9733;</Star>
    </>
  );
}

결과는 다음과 같습니다. 토마토 색이 입혀진 별이 3개 나왔는데 첫 번째 별은 'size'라는 속성이 없기 때문에 '2em'크기가 반영되었고 두 번째와 세 번째 별은 각각 지정된 속성인 '4em'과 '8em'이 각각 지정되었습니다.

이 방법은 아래와 같이 좀 더 간단하게도 표현될 수 있습니다.

  const Star = styled.span`
    color: tomato;
    font-size: ${({ size }) => size || "2em"};
  `;

이 속성을 이용하면 이미 정의한 스타일을 이용해서 새로운 스타일을 만들 수 있습니다. 마치 클래스를 상속해서 쓰는 것과 비슷한 느낌입니다. 아래 예시를 보시면 위에서 정의한 <Star>라는 스타일을 드대로 가져와서 <Newstar>라는 새로운 스타일을 지정했습니다. 그리고 새로운 스타일에는 'border: 1px solid;'라는 외곽선을 그리는 스타일을 추가했습니다. 그리고 2번째 별에 이 새로운 <Newstar>의 스타일을 적용했습니다.

import styled from "styled-components";

export function App() {
  const Star = styled.span.attrs((pr) => ({
    size: pr.size || "2em",
  }))`
    color: tomato;
    font-size: ${(pr) => pr.size};
  `;
  const Newstar = styled(Star)`
    border: 1px solid;
  `;
  return (
    <>
      <Star>&#9733;</Star>
      <Newstar size="4em">&#9733;</Newstar>
      <Star size="8em">&#9733;</Star>
    </>
  );
}

결과는 다음과 같습니다. 원래 <Star>와 똑같은 결과물에 외곽선만 추가된 것을 확인할 수 있습니다.

애니메이션

"keyframes"를 이용한 애니메이션을 적용하는 방법입니다. 'styled-components'에서 'keyframes'를 불러온 다음 적용하고자 하는 동작을 정의해줍니다. 그리고 css 'animation'을 이용해서 정의된 동작을 스타일에 적용해 줍니다. 

import styled, { keyframes } from "styled-components";

export function App() {
  const rotate = keyframes`
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
`;
  const Star = styled.div`
    display: inline-block;
    font-size: 8em;
    line-height: 1em;
    animation: ${rotate} 5s linear infinite;
  `;

  return (
    <>
      <Star>&#11088;</Star>
    </>
  );
}

아래는 결과입니다.

 

끝!

반응형

댓글