UnoCSS Repository
Reimagine Atomic CSS
시각적 기능에 기반한 이름을 가지는, 단일 목적을 수행하는 작은 클래스들을 선호하는 CSS 아키텍쳐 접근법
예) 이런 것들
.m-0 {
margin: 0;
}
.text-red {
color: red;
}
/* ... */
프레임워크 - Tailwind CSS, Windi CSS, Tachyons
라이브러리 - Bootstrap, Chakra UI
Tailwind는 큰 용량의 utility CSS 생성 → 시작과 HMR 오래 걸림 → Windi CSS를 대안으로 사용했음
Windi CSS - 0 dependency, on-demand → Tailwind보다 20~100x 빠름 → Tailwind가 JIT 엔진을 개발하게 됨
기존에 Atomic CSS를 만드는 방식 → 필요할 수도 있는 모든 CSS utility를 만드는 방식
예 )SCSS로 CSS utility 생성
// style.scss
@for $i from 1 through 10 {
.m-#{$i} {
margin: $i / 4 rem;
}
}
위 코드는 아래로 컴파일 됨
.m-1 { margin: 0.25 rem; }
.m-2 { margin: 0.5 rem; }
/* ... */
.m-10 { margin: 2.5 rem; }
Tailwind에서는 PurgeCSS로 해결 → prod에서만 해결
on-demand 방식에서는 생성 단계와 스캐닝 단계의 순서를 뒤집어 cost 절약함과 동시에 미리 생성하는 방식에서 해결하기 어려운 동적 need를 해결할 수 있는 유연함도 갖춤
이상적인 Atomic CSS라면 사용법이 직관적이고 동일해야 함, 그러나 어떤 때 작동하고 어떤 때 작동하지 않는지 파악하기 어려움
e.g. Tailwind CSS - border-2 ~ 8까지는 2~8px, border-10은 작동하지 않음
.border-10 {
border-width: 10px;
}
// tailwind.config.js
module.exports = {
theme: {
borderWidth: {
DEFAULT: '1px',
0: '0',
2: '2px',
3: '3px',
4: '4px',
6: '6px',
8: '8px',
10: '10px' // <-- here
}
}
}
UnoCSS - the instant atomic CSS engine with maximum performance and flexibility.
“최대 성능과 유연성을 가진 즉석 Atomic CSS 엔진”
core utility가 없기 때문에 framework라기보다는 engine이다 → 모든 기능은 preset이나 inline configuration을 통해 제공됨
직관적이고 완벽한 커스터마이징 가능
UnoCSS의 목표 - 직관성과 커스터마이징
자신만의 utility를 단번에 정의 가능
rules: [
['m-1', { margin: '0.25rem' }]
]
코드에서 m-1이 발견되면 아래 CSS 생성:
.m-1 { margin: 0.25rem; }
matcher를 RegExp로 바꾸고 body를 함수로 바꾸면 됨
rules: [
[/^m-(\\d+)$/, ([, d]) => ({ margin: `${d / 4}rem` })],
[/^p-(\\d+)$/, match => ({ padding: `${match[1] / 4}rem` })],
]
<div class="m-100">
<button class="m-3">
<icon class="p-5" />
My Button
</button>
</div>
위 코드가 있으면 아래 CSS가 생성됨
.m-100 { margin: 25rem; }
.m-3 { margin: 0.75rem; }
.p-5 { padding: 1.25rem; }
variants: [
// support `hover:` for all rules
{
match: s => s.startsWith('hover:') ? s.slice(6) : null,
selector: s => `${s}:hover`,
},
// support `!` prefix to make the rule important
{
match: s => s.startsWith('!') ? s.slice(1) : null,
rewrite: (entries) => {
// append ` !important` to all css values
entries.forEach(e => e[1] += ' !important')
return entries
},
}
],
Tailwind CSS, Windi CSS, Bootstrap 등 기존 프레임워크 방식과 호환되는 다양한 프리셋 제공
지금까지는 Tailwind의 행동을 따라하는 방법을 보여줬음
지금부터는 UnoCSS의 진정한 힘
<button class="bg-blue-400 hover:bg-blue-500 text-sm text-white font-mono font-light py-2 px-4 rounded border-2 border-blue-200 dark:bg-blue-500 dark:hover:bg-blue-600">
Button
</button>
위 코드를 아래처럼 쓸 수 있음:
<button
bg="blue-400 hover:blue-500 dark:blue-500 dark:hover:blue-600"
text="sm white"
font="mono light"
p="y-2 x-4"
border="2 rounded blue-200"
>
Button
</button>
<div class="m-2 rounded text-teal-400" />
위 코드를 아래처럼 쓸 수 있음:
<div m-2 rounded text-teal-400 />
제작자가 아이콘에 진심, 순수 CSS만으로 아이콘 표시 가능
<!-- A basic anchor icon from Phosphor icons -->
<div class="i-ph-anchor-simple-thin" />
<!-- An orange alarm from Material Design Icons -->
<div class="i-mdi-alarm text-orange-400 hover:text-teal-400" />
<!-- A large Vue logo -->
<div class="i-logos-vue text-3xl" />
<!-- Sun in light mode, Moon in dark mode, from Carbon -->
<button class="i-carbon-sun dark:i-carbon-moon" />
<!-- Twemoji of laugh, turns to tear on hovering -->
<div class="i-twemoji-grinning-face-with-smiling-eyes hover:i-twemoji-face-with-tears-of-joy" />
Tailwind/Windi - preflight로 native element를 reset하고 CSS 변수 fallback 제공 → 다른 UI 프레임워크와 함께 사용하면 conflict 발생 가능
UnoCSS - preflight를 유저가 알아서 하게 함 (다른 프레임워크와 함께 사용 가능)
Vite plugin에 scoped-vue
모드 → 각 컴포넌트마다 scoped 스타일을 생성해 컴포넌트 라이브러리로 안전하게 만들 수 있음 (유저 CSS와 conflict 걱정 X)
<template>
<div class="m-2 rounded"><slot></div>
<template>
<!-- the following will be inject in the bundler -->
<style scoped>
.m-2{margin:0.5rem;}
.rounded{border-radius:0.25rem;}
</style>