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