원문 : https://www.joshwcomeau.com/css/css-variables-for-react-devs/
CSS 변수 정의 / 사용
일반 CSS 변수와 동일하게 선언
사용 시 var로 감싸서 사용함
- 기존 CSS 속성의 차이점
- var() 함수를 사용하여 해당 값에 액세스할 수 있습니다
- 커스텀 속성은 두 개의 대시로 시작해야 합니다.
- CSS 변수는 지역변수입니다.
- 색상과 픽셀뿐만 아니라 모든 타입의 값을 보유할 수 있습니다.
- CSS 변수가 정의되지 않은 경우 기본값을 지정할 수 있습니다.
- var(--primary-color, pink)는 필요한 경우 분홍색으로 대체됩니다.
CSS 변수는 지역변수입니다.
<style>
p {
--highlight-color: yellow;
}
/* 효과 없음*/
em {
background: var(--highlight-color);
}
</style>
<h1>
This heading has some <em>emphasized text</em>!
</h1>
CSS 변수 전역 사용 방법
/*
This variable will be available everywhere,
because every element is a descendant of
the HTML tag:
*/
html {
--color-red: hsl(0deg 80% 50%);
--color-blue: hsl(270deg 75% 60%);
}
/* 혹은 */
:root {
--color-red: hsl(0deg 80% 50%);
--color-blue: hsl(270deg 75% 60%);
}
CSS 변수가 일반적인 CSS 속성과 다른 점은 var() 함수를 사용하여 해당 값에 액세스할 수 있다는 것입니다.
리액트 앱에서
styled-components를 씁시다.
아래와 같은 디자인 토큰이 있다면...
const COLORS = {
text: 'black',
background: 'white',
primary: 'rebeccapurple',
};
const SIZES = [
8,
16,
24,
32,
/* And so on */
];
React 앱에서 이를 필요로 하는 컴포넌트로 직접 가져올 수 있습니다. (soso)
import { COLORS } from '../constants';
const Button = styled.button`
background: ${COLORS.primary};
`;
theme을 사용할 수 있습니다.(not good)
// components/App.js
import { ThemeProvider } from 'styled-components';
import { COLORS } from '../constants';
// This element wraps our entire application,
// to make the theme available via context.
const App = ({ children }) => {
return (
<ThemeProvider theme={{ colors: COLORS }}>
{children}
</ThemeProvider>
);
};
// Elsewhere…
const Button = styled.button`
background: ${(props) => props.theme.colors.primary};
`;
CSS 변수와 createGlobalStyle을 사용합니다. (Best)
import { createGlobalStyle } from 'styled-components';
const GlobalStyles = createGlobalStyle`
html {
--color-text: black;
--color-background: white;
--color-primary: rebeccapurple;
}
`;
const App = ({ children }) => {
return (
<>
<GlobalStyles />
{children}
</>
);
};
변수 대신 값을 바꾸자
아래 컴포넌트에 반응형 스타일을 추가하고 싶음. 모바일이면 높이를 키우자. (44px and 48px tall)
CSS 변수가 없으면?
const Button = styled.button`
/* Omitted other styles for brevity */
min-height: 32px;
@media (pointer: coarse) {
min-height: 48px;
}
`;
- 필요한 경우 버튼이 커질 수 있도록 height 대신 min-height를 사용하고 있습니다.
- 사용자가 기본 글꼴 크기를 늘리거나 텍스트를 줄 바꿈해야 하는 경우 높이가 커질 수 있습니다.
- 너비 기반 미디어 쿼리를 사용하는 대신 pointer:coarse를 사용하고 있습니다.
- 우리는 실제로 화면의 크기에 관심이 없습니다. 정확히 클릭하거나 탭할 수 있는지 여부에 관심이 있습니다.
- 이 미디어 쿼리는 사용자의 기본 입력 메커니즘이 괜찮은지 거친지 여부를 추적합니다.
- 마우스나 트랙패드는 픽셀에 대한 위치를 제어할 수 있기 때문에 "fine"으로 간주됩니다.
- 손가락이나 Wii 리모컨은 덜 정확합니다. (coarse)
탭할 수 있는 요소는 버튼만이 아닙니다. TextInput도 수정해 줍니다.
DRY를 위해 ThemeProvider를 이용하기
const App = ({ children }) => {
return (
<ThemeProvider
theme={{
colors: COLORS,
coarseTapHeight: 48,
fineTapHeight: 32,
}}
>
{children}
</ThemeProvider>
);
};
아래와 같이 사용합니다만, 이제 탭 할 수 있는 모든 컴포넌트에 아래와 같은 코드를 작성해야 합니다...
const Button = styled.button`
min-height: ${(props) => props.theme.fineTapHeight}px;
@media (pointer: coarse) {
min-height: ${(props) => props.theme.coarseTapHeight}px;
}
`;
const TextInput = styled.input`
min-height: ${(props) => props.theme.fineTapHeight}px;
@media (pointer: coarse) {
min-height: ${(props) => props.theme.coarseTapHeight}px;
}
`;
DRY를 위해 Css Variables를 이용하기
컴포넌트 별로 미디어 쿼리를 적용하는 대신, 변수가 뷰포트 사이즈에 반응하게 하면 되지 않을까요?
const GlobalStyles = createGlobalStyle`
html {
--min-tap-target-height: 32px;
@media (pointer: coarse) {
--min-tap-target-height: 48px;
}
}
`;
const Button = styled.button`
min-height: var(--min-tap-target-height);
`;
const TextInput = styled.input`
min-height: var(--min-tap-target-height);
`;
- 중단점 항목을 한 곳에서 통합하여 이제 단일 진실 원천을 갖게 되었습니다.
- 이전에는 방심하는 개발자가 실수로 중단점 중 하나를 삭제하여 일관성 없는 동작을 초래할 수 있었습니다. 이제 탄력적인 변수로 패키징됩니다.
- 우리가 왜 이것을 하는지에 대해 더 명확하게 할 수 있습니다.
- min-tap-target-height라는 이름을 지정하여 처음부터 min-height 값을 설정해야 하는 이유를 전달합니다.
- 더 선언적입니다!
- 포인터 유형에 따라 각 구성 요소가 어떻게(how) 변경되어야 하는지 지정하는 대신 값이 어떠해야(what) 하는지 알려줍니다.
"최소 지식의 원칙("Principle of Least Knowledge")"은
코드가 코드베이스의 완전히 다른 부분에 "도달"해서는 안되고 바로 인접한 항목에만 액세스할 수 있어야 한다는 아이디어입니다.
같은 생각이 여기에 적용되는 것 같아요.
"Principle of Least Knowledge"
테마 변수에도 비슷한 아이디어를 적용할 수 있습니다.
모든 창 사이즈에 대해 동일한 크기를 사용하는 대신, 각 중단점에 대한 커스텀 크기를 가질 수 있습니다.
const GlobalStyles = createGlobalStyle`
html {
--space-sm: 8px;
--space-md: 16px;
@media (min-width: 1024px) {
--space-sm: 16px;
--space-md: 32px;
}
}
`;
// Elsewhere...
const Paragraph = styled.p`
padding: var(--space-sm);
`;
새로운 가능성
지금까지는 개발 측면에서 문제를 다루었다면, 이제 UX관점에서의 문제를 다뤄봅시다.
아무 속성이나 애니메이팅 하기
해당 버튼 만들기 :
다크 모드 반짝임 고치기
값을 얻고 설정하기
const GlobalStyles = createGlobalStyle`
html {
--color-text: black;
--color-background: white;
--color-primary: rebeccapurple;
}
`;
- CSS 변수를 인스턴스화하는 데에도 사용할 수 있고,
- JS에서 raw 값이 필요한 모든 곳에서 가져올 수도 있습니다.
const GlobalStyles = createGlobalStyle`
html {
--color-text: ${COLORS.text};
--color-background: ${COLORS.background};
--color-primary: ${COLORS.primary};
}
`;
getComputedStyle(document.documentElement)
.getPropertyValue('--color-primary');
// 🚨 This is an editable code snippet!! 🚨
// Try changing the value of `color-primary`,
// and check out the site's logo in the top left!
document.documentElement.style.setProperty(
'--color-primary',
'hsl(230deg, 100%, 67%)'
);
JS에서 CSS 변수를 가져오고 설정할 일은 거의 없습니다.
임베디드 SVG 내에서 CSS 변수를 사용할 수도 있습니다 😮
단점
타입 없음
테마에 CSS 변수를 사용하는 것의 가장 큰 단점은
Typescript 또는 Flow를 통해 타입을 정적으로 입력할 방법이 없다는 것입니다.
(스타일을 객체에 보관하고, 해당 객체에 접근하는 함수를 노출하면 되긴 합니다. Check out This tweet)
브라우저 지원
IE를 지원하지 않습니다.
느슨하지 않음
const Ymca = styled.abbr`
font-size: 1rem;
@media (max-width: ${(p) => p.bp.desktop}) {
font-size: 1.25rem;
}
`;
'FrontEnd' 카테고리의 다른 글
Styled-Components(CSS-in-js) 잘 활용하기 (0) | 2022.06.25 |
---|---|
The Difference Between VAC(View Asset Component) Pattern and Container/Presenter Pattern (0) | 2022.06.25 |
React Portal과 타입스크립트로 모달 만들기 (0) | 2022.06.22 |
Intersection Observer와 React.lazy로 성능 개선하기 (0) | 2022.06.21 |
리액트 쿼리 : 폼 (2) | 2022.06.20 |