Vue mastery의 utility-first-css-with-tailwind 강의 내용을 정리한 글입니다.
유틸리티 퍼스트 CSS란?
- 유틸리티 클래스란 예측 가능한 이름을 지닌 저수준 디자인 토큰
- 유틸리티 CSS를 우선적으로 조합하여 CSS를 만드는 것
- 디자인 토큰을 CSS 규칙에 우선 반영하는 것
- 원자 클래스 라고도 합니다.
- 유틸리티 CSS 토큰의 조합형 사용을 지향합니다.
- CSS보다는 HTML 자체를 변경하는 것을 권장합니다.
- CSS를 수정하여 변경하는 경우 변경 사항이 의도하지 않은 영향을 미치지 않도록 각별히 주의해야 합니다.
- 그러나 HTML만 수정하여 변경하는 경우 변경 사항이 수정하는 바로 그 HTML에만 영향을 미친다는 것을 확실히 알 수 있습니다.
Tailwind CSS를 사용하는 이유
- 이미 제작된 유틸리티 클래스. 직접 만들 필요 없음
- 동적 클래스의 JIT 컴파일, 반응형 디자인 등 유용한 기능을 제공합니다.
- 해당 프레임워크를 쉽게 커스터마이징 할 수 있습니다.
기본(base) 유틸리티 클래스 활용
CSS 선언과 유틸리티 클래스는 항상 1대1 관계는 아닙니다.
변경하고픈 선언을 공식 홈페이지(https://tailwindcss.com/docs/installation)에 검색하며 작업합니다.
- h는 높이를 의미하므로 h-[60px]는 높이: 60px를 의미합니다.
- m은 margin을 의미하므로 mb는 margin-bottom을 의미하므로 mb-[25px]는 margin-bottom: 25px를 의미합니다.
- my는 Y축의 여백을 나타내며 여기에는 상단 여백과 하단 여백이 포함됩니다.
- mx는 X축의 여백을 나타내며 여백 왼쪽 및 여백 오른쪽을 포함합니다.
- bg-gradient-to-r, from-[#16c0b0], to-[84cf6a] 콤보는 그래디언트 규칙인 background: linear-gradient(-90deg, #84cf6a, #16c0b0)에 사용됩니다.
- to-r은 '오른쪽으로'를 의미합니다.
- to-l을 사용해 '왼쪽-기울기 반대 방향'으로 그래디언트를 적용할 수 있습니다.
- border, border-solid 및 border-[#d8d8d8]은 border 규칙 border: 1px solid #d8d8d8;에 사용됩니다.
- border 클래스는 테두리 너비: 1px를 의미합니다.
- 테두리 너비 2px 및 3px에 은 border-2 또는 border-3가 됩니다.
- border-dashed, border-dotted, border-solid와 같은 테두리 스타일을 지정할 수 있습니다.
- 테두리 색상을 지정하기 위해 한 쌍의 괄호를 사용하고 있습니다.
- bg-white는 배경색: 흰색입니다. 또는 bg-[#d8d8d8]과 같이 대괄호 구문과 함께 고유한 특정 색상을 사용할 수 있습니다.
- float-right는 float: right입니다.
대괄호 구문
대괄호 구문은 정확한 크기(단위 포함)을 지정하기 위한 방법입니다.
해당 장황함이 맘에들지 않으면 Tailwind의 기본 클래스를 사용할 수 있습니다. (적당히 4px의 배율로 리팩터링)
Just In Time
Tailwind CSS는 기본적으로 다음과 같은 동적 클래스들을 스타일시트에 포함하지 않습니다.
코드 사용을 스캔하고, 필요한 경우에만 CSS 파일에 포함합니다.
- from-[#16c0b0]
- to-[#84cf6a]
- border-[#d8d8d8]
- h-16
- mb-6
- my-6
- mx-24
- p-7
JIT 엔진 생성 클래스와 베이스 유틸리티 클래스의 차이점
반응형 디자인
플렉스 규칙
이전과 동일하게 공식 문서 참고
- flex > display: flex;
- flex-row > flex-direction: row;
- flex-wrap > flex-wrap: wrap;
반응형 디자인
아래 CSS의 .product-image가 적용된 블록 엘리먼트 다음과 같이 동작함
- 860px보다 뷰포트가 작으면 한줄 통째로 차지
- 크면 한 줄 절반씩 차지
@media only screen and (min-width: 860px) {
.product-image {
width: 50%;
}
.product-info {
width: 50%;
margin-left: 0;
}
}
우리가 원하는 860에 가장 가까운 modifier는 md입니다.
Tailwind CSS 설정 변경을 통해 breakpoint의 수치 커스터마이징도 가능합니다.
동적 스타일
동적 스타일의 경우 기본적으로 Vue3의 경우 객체, 배열 문법 사용이 가능합니다.
리액트의 경우는 classNames 패키지를 사용하면 됩니다.
배열 문법을 이용한 동적 스타일
<button
class="w-40 h-16 m-8 p-5 text-lg leading-none text-white text-center rounded-[5px] bg-[#39495c] cursor-pointer btn-shading-bn"
:class="inStock ?
[] :
['bg-[#d8d8d8]', 'cursor-not-allowed']"
:disabled="!inStock"
v-on:click="addToCart"
>
객체 문법을 이용한 동적 스타일
Tailwind CSS는 JIT시 코드를 실행하여 사용하는 Class명을 스캔하지 않습니다. 즉, 정적 분석을 수행합니다.
따라서 아래 코드는 스타일이 제대로 적용되지 않습니다.
<div
...
:class="'bg-[' + variant.color + ']'"
...
>
</div>
JIT가 필요한 경우 이를 유의할 필요가 있습니다.
<div
v-for="(variant, index) in variants"
class="w-12 h-12 mt-2 border-2 border-solid border-[#d8d8d8] rounded-[50%]"
:class="{
green: 'bg-[green]',
blue: 'bg-[blue]'
}[variant.color]"
:key="variant.id"
@mouseover="updateVariant(index)"
>
</div>
Tailwind CSS를 사용할 때, 항상 유틸리티 클래스의 전체 이름을 사용해야 함을 기억합니다.
Tailwind CSS 커스터마이징
색상 커스터마이징
hex 코드는 사용자 친화적이지 않습니다.
우리가 선택한 사용자 정의 클래스 이름이 있으면 더 도움이 될 것입니다.
📃 tailwind.config.js를 다음과 같이 수정하면 됩니다.
module.exports = {
content: [
"./src/**/*.vue",
"index.html"
],
theme: {
extend: {
colors: {
'ocean': '#16c0b0',
'leaf': '#84cf6a',
'mist': '#d8d8d8',
'midnight': '#39495c',
}
},
},
plugins: [],
}
이제 아래 클래스명을
- from-[#16c0b0]
- to-[#84cf6a]
- border-[#d8d8d8]
- bg-[#39495c]
- bg-[#d8d8d8]
다음과 같이 사용할 수 있습니다
- from-ocean
- to-leaf
- border-mist
- bg-night
- bg-mist
테마 커스터마이징
테마를 커스터마이징 하여 기본 색상을 재정의할 수 있습니다.
이제 테마에서 우리가 정의한 4가지 색상만 사용할 수 있습니다.
즉, 기본 설정에 존재하는 white와 같은 색상을 사용할 수 없음에 유의합니다.
📃 tailwind.config.js
module.exports = {
content: [
"./src/**/*.vue",
"index.html",
],
theme: {
colors: {
'ocean': '#16c0b0',
'leaf': '#84cf6a',
'mist': '#d8d8d8',
'midnight': '#39495c',
},
extend: {
},
},
plugins: [],
}
반응형 디자인 breakpoint 커스터마이징
theme: {
colors: {
'ocean': '#16c0b0',
'leaf': '#84cf6a',
'mist': '#d8d8d8',
'midnight': '#39495c',
'cloud': '#ffffff',
},
screens: {
'sm': '660px',
'md': '860px',
'lg': '1060px',
'xl': '1260px',
'2xl': '1460px',
},
extend: {
},
},
커스텀 클래스
tailwind CSS는 코드 재사용 목적으로 유틸리티 클래스를 생성하는 것을 추천하지 않습니다.
하지만 새로운 유틸리티를 만들 목적이라면, 이는 괜찮습니다.
클래스 이름은 추측/예측 가능해야 하며 이름을 변경하지 않고 클래스의 CSS를 변경할 가능성이 거의 없어야 합니다.
예를 들어 아래 클래스의 경우, mist 색상, 2px border를 의미하며, CSS 값의 변경은 반드시 이름 변경을 유발합니다.
📃 src/assets/main.css
mist-border-2 {
border: 2px solid #d8d8d8;
}
braket안에 css 선언을 직접 넣는 방법도 있습니다.
<img class = "w-[70%] m-10 p-4 [border:2px_solid_#ffffff]"
v-bind:src="image"
/>
이 사용 방법에는 논란이 있으나, 어쨌든 Tailwind CSS를 사용하고 있음을 의미하므로 md와 같은 모디파이어를 사용할 수 있습니다.
<img class = "w-[70%] m-10 p-4 md:[border:2px_solid_#ffffff]"
v-bind:src="image"
/>
커스텀 클래스에서 모디파이어를 사용하려면 몇가지 추가 작업이 필요합니다.
해당 CSS를 config.js가 모르기 때문입니다.
디렉티브(지시문)
@layer utilities {
.mist-border-2 {
@apply border-2;
@apply border-solid;
@apply border-mist;
}
/* button shading (barely noticeable) */.btn-shading-bn {
box-shadow: inset 0 -0.6em 1em -0.35em rgba(0, 0, 0, 0.17),
inset 0 0.6em 2em -0.3em rgba(255, 255, 255, 0.15),
inset 0 0 0em 0.05em rgba(255, 255, 255, 0.12);
}
}
레이어 시스템
- 베이스 (base)
- 컴포넌트 (components)
- 유틸리티 (utilities)
방금 작업은 커스텀 클래스를 이용해 Tailwind의 유틸리티 레이어을 확장한 것입니다.
이 계층화된 시스템은 또한 스타일이 충돌할 때마다 다른 레이어를 재정의할 레이어를 제어합니다.
컴포넌트 레이어는 베이스 레이어를 재정의하지만 유틸리티는 컴포넌트와 베이스 레이어를 모두 재정의합니다.
하지만 여기서는 유틸리티 레이어만 사용하기 때문에 이 이점은 그다지 중요하지 않습니다.
계층화된 시스템을 활용하여 코드를 성공적으로 정리했습니다.
코드 재사용 패턴
유틸리티 우선 접근 방식의 규칙을 어기지 않고 스타일을 추출하고 재사용하는 방법은 다옴가 같습니다.
1. 컴포넌트로 추출하기
유틸리티가 포함된 전체 엘리먼트를 컴포넌트로 사용합니다.
2. 컨테이너로 추출하기
특정 관심사를 바탕으로 컴포넌트를 분리할 수 있습니다.
예를 들어 아래 두 개의 디비전에서 반응형 디자인의 공통관심사를
<div class="w-[100%] md:w-[50%]">
...
</div>
<div class="w-[100%] md:w-[50%] ml-2.5 md:ml-0 ">
...
</div>
하나의 ResponsiveWidth로 뽑아냅니다.
<template>
<div class="w-[100%] md:w-[50%]">
<slot></slot>
</div>
</template>
해당 컴포넌트를 아래와 같이 사용할 수 있습니다.
<script setup>
import ResponsiveWidth from './ResponsiveWidth.vue'
...
</script>
...
<div class="flex flex-row flex-wrap">
<ResponsiveWidth>
<img class = "w-[70%] m-10 p-4 md:mist-border-2"
v-bind:src="image"
/>
</ResponsiveWidth>
<ResponsiveWidth>
<div class="ml-2.5 md:ml-0">
<h1>{{ title }}</h1>
...
</div>
<ResponsiveWidth>
'FrontEnd' 카테고리의 다른 글
Vue3 리렌더링 최적화 with ComputedEager (0) | 2023.01.10 |
---|---|
npm의 checksum과 integrity checksum(EINTEGRITY) 오류 해결 방법 (0) | 2023.01.10 |
[프론트엔드 커리어] 프론트엔드 엔지니어의 가성비 곡선 (0) | 2023.01.05 |
[Vue3] defineProps를 컴포저블에서 사용할 수 없는 이유 (0) | 2023.01.05 |
[Vue3] Vue3로 접근성 고려한 Form(양식) 개발하기 (0) | 2023.01.04 |