영상으로 보기:
https://www.youtube.com/watch?v=iG1ZeFzYWPQ
Panda CSS는 CSS-in-JS 라이브러리로, 웹 애플리케이션에서 현대적이고 반응형인 사용자 인터페이스를 구축하기 위한 유틸리티 클래스, 패턴, 레시피 등을 제공합니다.
자바스크립트 객체를 사용하여 컴포넌트에 스타일을 선언적으로 적용할 수 있어, 순수 문자열 기반의 접근법보다 가독성과 유지보수성이 높습니다.
Panda CSS의 주요 차별화 기능은 런타임 오버헤드가 없다는 점입니다. CSS-in-JS는 런타임 오버헤드가 있어 퍼포먼스를 해친다는 단점을 가지고 있습니다.
Panda CSS는 AST 파싱과 스캐닝을 통해 소스 코드에서 유틸리티 클래스와 패턴을 추출하고, 이를 정적으로 CSS 파일로 생성합니다. 따라서, 런타임에 스타일을 계산하거나 주입할 필요가 없으며, 번들 크기도 줄일 수 있습니다.
동적으로 생성되는 스타일들 또한 빌드타임에 미리 클래스로 생성됩니다.
npm
과 Create React App
을 사용해서 테스트를 해보겠습니다.
npm install react-scripts@latest
최신 버전의 판다 설치
npm i -D @pandacss/dev
판다 설정 파일과 사용하는데 필요한 postcss 설치
npx panda init -p
여기까지 수행하면 아래와 같이 기본 코드들이 생성됩니다.
새로 생성된 폴더 이름이 styled-system이고 이 내부에 css
, patterns
, tokens
, types
폴더가 있습니다.
각 폴더들은 아래와 같은 역할을 담당하는데요,
styled-system/css
스타일 작성을 위한 함수들이 있는 디렉토리
styled-system/tokens
css variables와 토큰들을 질의하기 위한 함수들이 있는 디렉토리
styled-system/patterns
공통 레이아웃 패턴을 적용하는데 사용하는 함수들
functions to implement apply common layout patterns
왜 하필 styled-system
에 유틸 함수들이나 css variables가 생성되었을까요?
그것은 panda.config.mjs 파일 옵션 때문입니다.
import { defineConfig } from "@pandacss/dev"
export default defineConfig({
// css reset을 사용할지 대한 유무
preflight: true,
// panda.css 정의를 사용할 대상 파일들
include: ["./src/**/*.{js,jsx,ts,tsx}", "./pages/**/*.{js,jsx,ts,tsx}"],
// include에서 제외할 파일들
exclude: [],
// 커스텀 theme 사용을 위한 설정 장소
theme: {
extend: {}
},
// css system이 생성되는 장소
outdir: "styled-system",
})
package.json 에 다음과 같이 codegen
명령어를 기입하고 outdir 이름을 변경한다음 prepare
를 실행해보면 새로 기입한 디렉토리 하위에 파일이 생성되는 것을 확인할 수 있습니다.
"scripts": {
...
"prepare": "panda codegen --clean"
},
프로젝트 루트 레이아웃 파일에서 import 하는 css 파일이름을 index.css
라고 가정했을 때
다음과 같이 작성해줍니다.
@layer reset, base, tokens, recipes, utilities;
다음과 같이 App.js를 작성하여 기본적인 panda.css의 css 함수를 적용해보겠습니다.
function App() {
return (
<div className={css({ bg: 'red.400' })}>
hi
<img src={logo} className="App-logo" alt="logo" />
</div>)
}
export default App;
하지만 백그라운드에 red 400 색상이 입혀지지 않았습니다.
panda.config.mjs
파일을 다시 한번 살펴보겠습니다.
import { defineConfig } from "@pandacss/dev"
export default defineConfig({
// Whether to use css reset
preflight: true,
// Where to look for your css declarations
include: ["./src/**/*.{js,jsx,ts,tsx}", "./pages/**/*.{js,jsx,ts,tsx}"],
// Files to exclude
exclude: [],
// Useful for theme customization
theme: {
extend: {}
},
// The output directory for your css system
outdir: "./src/styled-system",
})
panda.css는
include
옵션에 명시해뒀던 파일들에 적용시킨 panda css의 스타일 문법들을 사용해
빌드타임에 atomic css를 생성하기 때문에 정상적인 스타일을 적용시키기 위해서는 빌드를 거쳐야 합니다.
package.json - scripts에 다음과 같이 명령어를 선언합니다.
panda
는 프로덕션 배포 단계에서 사용할 명령어, panda:dev
는 개발 환경에서 사용할 명령어입니다.
{
"panda": "npx panda",
"panda:dev": "npx panda --watch"
}
앱에 정상적으로 스타일을 입히기 위해서는 styles.css를 import 해야 합니다.
이 styles.css는 위에 panda.config.mjs
에서 설정했던 outdir
에 위치해 있습니다.
이 파일을 한번 볼까요?
@layer reset, base, tokens, recipes, utilities;
@import './global.css';
@import './reset.css';
@import './tokens/index.css';
@import './tokens/keyframes.css';
@layer utilities {
.bg_red\.400 {
background: var(--colors-red-400)
}
}
App.js
에서 선언했던 스타일이 css 클래스로 생성되어있습니다.
다음과 같이 App.js를 재작성해줍니다.
import { css } from "./styled-system/css"
import './App.css';
import logo from './logo.svg';
import "./index.css"
import './styled-system/styles.css'
function App() {
return (
<div className={css({ bg: 'red.400' })}>
hi
<img src={logo} className="App-logo" alt="logo" />
</div>)
}
export default App;
예제 코드의 red.400
은 어떻게 사용된 것일까요?
생성된 토큰에 있는 red.400의 모습
예제코드에서는 커스텀 테마를 사용하지 않았지만, panda.css의 panda.config.mjs에서 theme.extend를 설정할 수 있습니다.
import { defineConfig } from "@pandacss/dev"
export default defineConfig({
// Whether to use css reset
preflight: true,
// Where to look for your css declarations
include: ["./src/**/*.{js,jsx,ts,tsx}", "./pages/**/*.{js,jsx,ts,tsx}"],
// Files to exclude
exclude: [],
// Useful for theme customization
theme: {
extend: {
tokens: {
colors: {
dante: {
50: { value: '#00FFFF'},
100: { value: '#F0FFFF'},
200: { value: '#89CFF0'},
300: { value: '#0000FF'},
400: { value: '#7393B3'},
500: { value: '#088F8F'},
600: { value: '##0096FF'},
700: { value: '#5F9EA0'},
800: { value: '#0047AB'},
900: { value: '#6495ED'},
},
}
}
}
},
// The output directory for your css system
outdir: "./src/styled-system",
})
function App() {
return (
<div className={css({ bg: 'dante.400' })}>
hi
<img src={logo} className="App-logo" alt="logo" />
</div>)
}
Vanilla Extract와 비교하면, Vanilla Extract는 사실상 TypeScript로 CSS를 작성하는 라이브러리입니다.
Panda CSS와 Vanilla Extract 모두 zero-runtime CSS-in-JS 라이브러리로, 런타임에 CSS를 생성하지 않으면서 페이지를 더 빨리 로드할 수 있습니다.
Panda CSS와 Vanilla Extract는 비슷한 부분이 많습니다. 문서를 보아도 Vanilla Extract에서 많은 영감을 받았다고 하죠.
Vanilla Extract는 typescript로 CSS를 작성합니다. CSS-in-JS가 아닌 CSS-in-TS이죠.
8개월 전에 vanilla extract를 사용하는 방법에 대한 영상과 글을 공유했었습니다.
당시에는 국내에 많이 퍼지지 않았었는데 요근래 사용하시는 분들이 많아지는 것 같아서 나름 뿌듯한 기분도 듭니다.
BLOG: https://velog.io/@jay/css-in-ts-vanilla-extract
YOUTUBE: https://www.youtube.com/watch?v=74szuw5Qxrg
Pands CSS는 최신 CSS 라이브러리 답게 nextjs의 서버 컴포넌트를 공식적으로 지원합니다.
새롭게 프로젝트를 생성하시는 분들은 도입을 고려해볼만한 라이브러리라고 생각합니다.
판다 .. 너무 귀여워요 ..