Tailwind CSS

katanazero·2022년 2월 4일
1

css

목록 보기
8/8

Tailwind CSS

TailWind CSS(https://tailwindcss.com/)

A utility-first CSS framework

  1. CSS framework ?

UI 개발자(or 클라이언트 개발자)가 UI 개발 작업을 더 쉽게 하기위해 사용하는 도구
대표적인 CSS framework 는 Bootstrap, Semantic UI, W3.CSS, Bulma, materializecss 등이 존재한다.
여기서 React 또는 Vue 에 친화적인 CSS Framework 도 존재한다.(Vuetify, Quasar, Ant Design, MATERIAL-UI)
Tailwind CSS 도 CSS framework 에 속하며, 차이가 존재를 한다.

  1. 어떤 차이?

기존의 CSS framework 들은 대부분 미리 정해진 디자인의 component 들이 있고, 이를 사용하면서 조합을해나가는 방식이었다.
문제는 UI 개발자가 이를 원하는 형태로 커스텀을 하려고 하면 애로사항이 많았다.

위 UI를 작성하기 위해서는, 아래 미리 정해진 마크업과 스타일링을 따라야한다.

Tailwind CSS 는 저수준의 유틸리티 클래스를 제공하여, HTML 요소에 직접 적용한다.

<!-- tailwind CSS -->

<div class="bg-gray-100 round-xl p-8">Hello World</div>

그렇지만..

HTML 요소에 클래스를 나열하다보면 구문이 매우 길어지고 복잡해지는부분이 확인이 됩니다.

<!-- a lot of utility class names -->
<div class="sm:w-4 md:w-6 lg:w-10 xl:w-full sm:text-sm md:text-base lg:text-base xl:text-2xl flex-1 sm:flex-none bg-black sm:bg-white rounded-md sm:rounded-none">Hello World</div>
  1. 장점 및 단점
  • 장점
    • Tailwind CSS 는 JS에 의존적이지 않고 완전히 분리가 되어있다.
    • 저수준의 Utility 클래스를 제공하기 때문에 이를 조합해서 사용하면 된다.
    • 한 클래스는 한가지 스타일링만 관장한다. 즉, DRY 원칙이 잘 지켜진 CSS framework 이며 서로 스타일링에 대한 간섭이 일어나지 않는다. + 재사용성이 높다.
    • 이미 정해진 클래스명들을 사용하기때문에 네이밍으로 고민 할 필요가 없다.
    • 다른 CSS framework 에 비해, 커스텀이 자유롭다.
  • 단점
    • Tailwind CSS 에서 제공해주는 클래스명을 학습해야하는 수고스러움 발생
    • 클래스를 HTML 요소에 작성하다보면, 매우 길어지므로 가독성이 떨어지고 HTML 구문을 오염시킬수 있다.

Tailwind CSS 를 보통 Atomic CSS 구조를 따른다고 이야기를 합니다.
Atomic CSS 는 단일 목적 클래스를 선호하는 CSS 아키텍쳐를 의미합니다.

기존에는 Sementic CSS 아키텍쳐를 많이 사용했습니다.(지금도 많이 사용함)
대표적으로 BEM(Block-Element-Modify) 방법론을 살펴볼까요?

.header__navigation--navi-text {
  color: red;
}

<form class="search-form">
    <input class="search-form__input"/>
    <button class="search-form__button">Search</button>
</form>

위 클래스명과 HTML 요소에 적용된 클래스를 살표보면 어떤 요소에 스타일링을 적용하는지 의미적으로 잘 보여줍니다.
Sementic CSS 같은 경우 단점이 있었는데, 여러 CSS 클래스에서 반복되는 속성이 문제였습니다.
이를 동일하게 반복되는 속성을 각각의 CSS 클래스로 빼고 이러한 CSS 클래스를 조합해 스타일링을 하는 Atomic CSS 가 대세가 되기 시작하였습니다.

CSS Preprocessor 로 SASS/SCSS 를 사용했다면 아래 코드와 같이 utility css 를 작성했습니다.

@for $i from 1 through 10 {
  .pa-#{$i} {
    padding: $i / 4 rem;
  }
}

// result

.pa-1 { padding: 0.25 rem; }
.pa-2 { padding: 0.5 rem; }
/* ... */

만약 pa-10 을 사용하지 않는다면?
불필요한 계산, CSS 파일 크기에 영향을 미칠겁니다.

Tailwind CSS 는 CDN 으로 제공받는 경우, 크기가 500kb 로 작은 용량이 아닙니다. 사이트를 로드할때 payload 가 크면 UX 에 안좋은 영향이 미치고 컨텐츠를 표시할 기회가 줄어듭니다.
다행스럽게도, Tailwind CSS 는 minify 옵션 및 사용하지 않는 클래스를 제거해주는 기능(Perge CSS)을 제공해주고있습니다.
소스코드를 사전에 스캔하여, 사용되어지는 클래스만을 추출하여 프로덕션에 제공해줍니다.


  • CSS framework 는 언제 사용하면 좋을까?

대규모 개발팀에서 표준적인 스타일링을 할때(표준화)
다른 개발자가 작업한 UI 를 수정하라는 지시가 내려졌다고 가정을 합시다. 만약 CSS framework 가 적용이 되어있다면 시간을 절약하고 모든 팀원들이 해당 framework 를 사용하기때문에 소통도 더 쉽게 가능합니다.

하지만, 단점도 존재합니다. 초보자일때는 때로는 CSS framework 가 독이되는 경우가 있습니다.
전반적인 CSS 기술을 익히지 못하게 되며, 순수 CSS 지식을 갖지 못합니다.(만약 CSS framework 에서 어떤 CSS 이슈를 해결하려면 난처해질지도 모릅니다)


  • Tailwind CSS 활용
  1. JIT(Just-in-Time Mode) 모드

Tailwind 2.1 이상부터 지원되는 기능
스타일을 개발환경에서 모두 포함하는게 아닌 사용하는거 또는 요청하는거만 생성을 해줍니다. 그래서, 개발환경에서 빠르게 빌드해서 작업을 진행할 수 있습니다. (on-demend 형태)
그리고 사용자정의 CSS를 작성하지 않고 임의의 스타일을 생성해줍니다. 이외에도 여러 기능들을 제공해주고 있습니다.
PergeCSS 와는 다른점은 PergeCSS 는 프로덕션에만 적용을 해줍니다.

  1. @apply directive

@apply 로 반복적인 Utility 클래스를 추출이 가능합니다.

@tailwind base;
@tailwind components;
@tailwind utilities;

@layer base {
    h1 {
        @apply text-3xl;
    }

    h2 {
        @apply text-2xl;
    }

    h3 {
        @apply text-xl;
    }
}

@layer components {
    .row {
        @apply flex flex-row;
        @apply items-center justify-center;
    }

    .row-wrap {
        @apply flex flex-row flex-wrap;
        @apply items-center justify-center;
    }

	.foo {
	  color: blue !important;
	}

	.bar {
	  @apply foo;
	}
}

@layer 지시문은 사용자 정의 스타일 집합이 속한 버킷을 알리며, 유효한 버킷은 base, components, utilities 입니다.

  1. Tatilwind CSS 커스텀
module.exports = {
    content: [],
    purge: ['./src/**/*.{js,jsx,ts,tsx}'],
    darkMode: false, // or 'media' or 'class'
    theme: {
        extend: {
            spacing: {
                '0': '10px',
            },
            colors: {
                transparent: 'transparent',
                current: 'currentColor',
                'rose': '#2c55d1',
                'brown': {
                    50: '#fdf8f6',
                    100: '#f2e8e5',
                    200: '#eaddd7',
                    300: '#e0cec7',
                    400: '#d2bab0',
                    500: '#bfa094',
                    600: '#a18072',
                    700: '#977669',
                    800: '#846358',
                    900: '#43302b',
                },
                'tahiti': '#3ab7bf'
            },
        },
    },
    variants: {},
    plugins: [],
}

<div>
  <h1>h1</h1>
  <h2>h2</h2>
  <h3 className="p-0 text-tahiti">h3</h3>
  <h4 className="text-brown-500 hover:text-blue-600">h4 brown</h4>
  <h5 className="text-white bg-rose">h5 bg-rose</h5>
</div>

theme 속성을 활용해서 제공되는 스타일을 커스텀이 가능합니다.

theme 에 사용가능한 key 들은 아래와 이미지와 같습니다.(https://tailwindcss.com/docs/theme)
문서를 확인해보시면, 다양한 key 값들이 있고 이를 통해 커스텀이 가능하다는부분이 확인이 가능합니다.

variants 속성을 활용하면,

responsive, pseudo-class 오버라이딩이 가능합니다.

variants: {
  scale: ['focus-within'],
}

<div className="flex justify-center items-center bg-gray-900 py-2">
  <div className="border-4 border-t-blue-500 border-r-pink-500 border-b-green-500 border-l-yellow-500 w-60 h-60 group bg-white text-black sm:hover:bg-black sm:hover:text-white relative rounded-lg flex flex-col justify-center items-center **hover:scale-102 focus-within:scale-110**">
    <div className="absolute transform transition  group-hover:bg-green-300 group-hover:scale-110 flex justify-center items-center rounded-full cursor-pointer top-[10px] right-[10px] w-[25px] h-[25px] p-[5px] bg-[#07B5D3]">
    <img src="https://img.icons8.com/ios-filled/100/ffffff/checkmark--v1.png"/>
  </div>
  <div className="font-medium text-xl before:content-['👉'] before:mr-3">Thank you 🙏</div>
  <div className="text-sm text-gray-500 first-letter:text-2xl">We appreciate your support.</div>
    <input type="text" />
  </div>
</div>

scale 에 focus-within 가상클래스 선택자만 활성화되어 동작을 합니다.

hover:scale-102 는 더이상 동작하지 않습니다.

해당 속성을 활용하면, 기본으로 제공하는 variants 외에 다른 variants 조합이 가능합니다.

scale 같은 경우는 default variants 로 [’responsive’, ‘hover’, ‘focus’] 를 제공해줍니다. (https://v2.tailwindcss.com/docs/configuring-variants)


직접 Tailwind CSS 를 사용하여 컴포넌트를 작성해보자.

이러한 UI 컴포넌트를 참조하여 작성해보자

import React from 'react';

export default function CardComponent(): React.ReactElement {
    return (
        <div className="relative py-4 px-8 w-96 m-10 shadow-lg rounded-lg">
            <div className="absolute left-0 right-0 top-[-30px] flex items-center justify-end">
                <img className="rounded-full object-cover w-20 h-20 border-indigo-500 border-2" src="https://images.unsplash.com/photo-1499714608240-22fc6ad53fb2?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=334&q=80"/>
            </div>
            <h1 className="text-2xl font-semibold text-gray-800 text-left">
                Design Tools
            </h1>
            <p className="font-mono text-left break-all text-sm font-normal text-gray-600 mt-2">
                Lorem ipsum dolor sit amet consectetur adipisicing elit. Quae dolores deserunt ea doloremque natus error, rerum quas odio quaerat nam ex commodi hic, suscipit in a veritatis pariatur minus consequuntur!
            </p>
            <div className="flex items-center justify-end mt-2">
                <button className="text-sm bg-transparent hover:bg-blue-500 text-blue-700 hover:text-white p-2 border border-blue-500 hover:border-transparent rounded">John Doe</button>
            </div>
        </div>
    )
}

완전히 같게 만들지는 못했지만(머쓱), Tailwind CSS 에서 제공해주는 클래스들을 조합하여 간편하게 스타일링이 가능하다.(어떤 HTML 요소에 종속적이지도 않기에 스타일링이 훨씬 더 간편하다)
기본으로 제공되는 클래스명을 보면 어떤 스타일링을 하는지 유추가 가능하지만, 여러 클래스들을 조합하다보면 길어지는 문제가 있다.

이를 @apply 지시자를 활용하여 Utility 클래스 묶음을 정의해보자.

@layer components {
    .card {
        @apply relative py-4 px-8 w-96 m-10 shadow-lg rounded-lg
    }

    .card__avatar {
        @apply absolute left-0 right-0 top-[-30px] flex items-center justify-end
    }

    .card__avatar > img {
        @apply rounded-full object-cover w-20 h-20 border-indigo-500 border-2
    }

    .card__title {
        @apply text-2xl font-semibold text-gray-800 text-left;
    }

    .card__contents {
        @apply font-mono text-left break-all text-sm font-normal text-gray-600 mt-2;
    }

    .card__actions {
        @apply flex items-center justify-end mt-2;
    }

    .card__actions > button {
        @apply text-sm bg-transparent hover:bg-blue-500 text-blue-700 hover:text-white p-2 border border-blue-500 hover:border-transparent rounded;
    }
}
.card__contents {
    margin-top: 0.5rem;
    word-break: break-all;
    text-align: left;
    font-family: ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;
    font-size: .875rem;
    line-height: 1.25rem;
    font-weight: 400;
    --tw-text-opacity: 1;
    color: rgb(75 85 99/var(--tw-text-opacity));
}

.card__actions {
    margin-top: 0.5rem;
    display: flex;
    align-items: center;
    justify-content: flex-end;
}

@apply 지시자를 활용해서 적용된 CSS Rule
그런데, 뭔가 겹쳐보이는건 나의 착각인가..?

import React from 'react';

export default function CardComponent(): React.ReactElement {
    return (
        <div className="card">
            <div className="card__avatar">
                <img src="https://images.unsplash.com/photo-1499714608240-22fc6ad53fb2?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=334&q=80"/>
            </div>
            <h1 className="card__title">
                Design Tools
            </h1>
            <p className="card__contents">
                Lorem ipsum dolor sit amet consectetur adipisicing elit. Quae dolores deserunt ea doloremque natus error, rerum quas odio quaerat nam ex commodi hic, suscipit in a veritatis pariatur minus consequuntur!
            </p>
            <div className="card__actions">
                <button>John Doe</button>
            </div>
        </div>
    )
}

만약 저걸 SCSS 로 작성을 하였다면..?

@charset "UTF-8";
@use "sass:math";

@import './variables';

@function get-size($column) {
  @if $column == 1 { @return math.div(100%, $grid-cols-num); }
  @else if $column == 2 { @return math.div(100%, $grid-cols-num) * 2; }
  @else if $column == 3 { @return math.div(100%, $grid-cols-num) * 3; }
  @else if $column == 4 { @return math.div(100%, $grid-cols-num) * 4; }
  @else if $column == 5 { @return math.div(100%, $grid-cols-num) * 5; }
  @else if $column == 6 { @return math.div(100%, $grid-cols-num) * 6; }
  @else if $column == 7 { @return math.div(100%, $grid-cols-num) * 7; }
  @else if $column == 8 { @return math.div(100%, $grid-cols-num) * 8; }
  @else if $column == 9 { @return math.div(100%, $grid-cols-num) * 9; }
  @else if $column == 10 { @return math.div(100%, $grid-cols-num) * 10; }
  @else if $column == 11 { @return math.div(100%, $grid-cols-num) * 11; }
  @else if $column == 12 { @return math.div(100%, $grid-cols-num) * 12; }
}
@charset "UTF-8";

// break point
$xs: 640px; // mobile
$md: 960px; // desktop
$lg: 1140px; // large

// grid col num
$grid-cols-num: 12;

// color
$primary-color: #3e7afa;
$secondary-color: #fa3e6d;
$background-color: #2b2c3b;
$dark-color: #4d4e66;
$grey-color: #9c9db0;
$white-color: #fafafa;
$border-color: #c9c9c9;

flex 레이아웃에 사용할, 12컬럼 CSS 클래스 생성 함수 정의 및 변수 정의

.align-items {
  &-start {
    align-items: flex-start;
  }

  &-center {
    align-items: center;
  }

  &-end {
    align-items: flex-end;
  }

  &-stretch {
    align-items: stretch;
  }

  &-baseline {
    align-items: baseline;
  }
}

.justify-contents {
  &-start {
    justify-content: flex-start;
  }

  &-center {
    justify-content: center;
  }

  &-end {
    justify-content: flex-end;
  }

  &-between {
    justify-content: space-between;
  }

  &-around {
    justify-content: space-around;
  }

  &-evenly {
    justify-content: space-evenly;
  }
}

// render-cols(12column grid)
// mobile first
@include responsive-col('') {
  @include render-col(''); // mobile
}

@include responsive-col(tablet) {
  @include render-col('-sm'); // tablet
}

@include responsive-col(desktop) {
  @include render-col('-md'); // desktop
}

@include responsive-col(large) {
  @include render-col('-lg');
}

위와 같이 공용으로 사용할 CSS 클래스 정의 및 사전작업을 해야한다.(네이밍 컨벤션도 생각을 많이해야한다)
그리고 만약 사용하지 않는다면 불필요한 CSS 클래스가 번들된 CSS 파일에 포함이 될거고, 이게 쌓인다면 페이지 페이로드가 커질거다.

.basic-button {

    display: inline-block;
    outline: none;
    padding: 12px 16px;
    cursor: pointer;
    font-weight: 600;
    font-size: 14px;
    border: 1px solid $primary-color;
    background-color: $primary-color;
    color: #ffffff;

    &--block {
      display: block;
      width: 100%;
    }

    &--outline {
      border-color: $primary-color;
      background-color: #ffffff;
      color: $primary-color;
    }

    &--small {
      padding: 8px 12px;
      font-size: 12px;
    }

    &--large {
      padding: 16px 20px;
      font-size: 18px;
    }

    &--disabled {
      border-color: #a6a6a6;
      background-color: #a6a6a6;
      color: #ffffff;
      pointer-events: none;
    }

  }

  .basic-button--disabled.basic-button--outline {
    background-color: #ffffff;
    color: #a6a6a6;
    border-color: #a6a6a6;
  }

위와 같이 button 컴포넌트에 적용할 CSS 클래스를 작성을 하는데, 한 CSS 클래스에 많은 속성들이 있기때문에 이후에 다른 컴포넌트내에 존재하는 버튼UI 를 만들려고 하면 컴포넌트를 새로 작성함과 동시에 CSS 클래스도 새로 작성할 확률이 매우높다.(DRY 원칙에 어긋나고, 재사용성이 떨어질거다)


마무리😎

  • Tailwind CSS 는 유틸리티 클래스만 제공(Atomic CSS 컨셉)하기 때문에 다른 CSS 프레임워크처럼 사전 정의된 스타일과 기능을 가진 컴포넌트를 바로 사용할 수는 없다.

  • Tailwind CSS 를 사용하면 얻을 수 있는 이점이 많습니다.(생산성 향상, DRY, Reusability 등) 그리고 유틸리티 클래스에 상당한 힘이 있다는것을 알 수 있습니다.

  • 어떤 그룹은 해당 CSS framework 로 많은 이점을 얻는반면에, 제한이 되는 부분도 발생을 한다.(예, 디자이너에 창의적인 디자인을 제한을 할 수 있음)

  • https://tailwindcomponents.com/ 에서 Tailwind CSS 를 활용하여 제작된 다양한 UI 컴포넌트가 공유되어있다.

  • 설치 및 구성은 따로 다루지 않았습니다. 이부분은 공식 홈페이지 또는 구글링을 통해 레퍼런스들을 참고하길 바란다.(+ plugins 및 darkmode 에 대해서도 다루지는 않았지만 문서를 보면 확인이 가능하며 기능들이 생각보다 많이 제공되는걸 확인이 가능합니다.)

profile
developer life started : 2016.06.07 ~ 흔한 Front-end 개발자

0개의 댓글