2P by GN⁺ 3시간전 | ★ favorite | 댓글 1개
  • 몇몇 사이트를 Tailwind에서 시맨틱 HTML과 바닐라 CSS로 옮기며, Tailwind가 제공하던 규칙 중 필요한 것만 직접 재구현함
  • Tailwind의 preflight reset, 색상 팔레트, font scale처럼 익숙한 시스템은 유지하되 CSS 변수와 파일 분리로 바닐라 CSS에 옮겨 담음
  • CSS 대부분은 컴포넌트별 파일로 나누고 고유 클래스를 둬, 한 컴포넌트 수정이 다른 컴포넌트를 몰래 깨뜨릴 가능성을 줄임
  • Tailwind를 떠나는 배경에는 최신 Tailwind의 빌드 시스템 의존성, 2.8MB tailwind.min.css, 바닐라 CSS와의 혼재, CSS 제약이 있음
  • 반응형 설계는 breakpoint보다 CSS gridauto-fit, grid-template-areas를 더 활용하려 하며 @layer, @scope, container queries도 학습 대상으로 삼음

Tailwind에서 배운 구조를 바닐라 CSS로 옮기기

  • 8년 전 Tailwind를 처음 쓸 때는 CSS 코드를 어떻게 구조화해야 할지 몰랐고, 완전한 혼란보다 Tailwind가 훨씬 나은 선택이었음
  • 최근 약 일주일 동안 몇몇 사이트를 Tailwind에서 시맨틱 HTML + 바닐라 CSS로 옮기며, Tailwind가 제공하던 규칙 중 필요한 부분만 직접 선택해 재구현하게 됨
  • A whole cascade of layersHow I write CSS in 2024를 읽으며, 모든 CSS 코드베이스에는 레이아웃·폰트·색상·공통 컴포넌트 같은 서로 다른 관심사를 관리할 시스템이 필요하다는 점이 분명해짐
  • Tailwind에는 reset stylesheet, 색상 팔레트, font scale 같은 시스템이 이미 있었고, 익숙하고 유용한 부분은 바닐라 CSS에서도 모방할 수 있음

CSS 코드베이스에 둔 주요 시스템

  • reset

    • Tailwind의 preflight stylestailwind.css에서 처음 약 200줄 복사해 사용함
    • Tailwind reset에 오래 익숙해져 있었고, 모든 요소에 box-sizing: border-box를 적용하는 규칙은 요소의 너비가 padding을 포함하게 만듦
      * { box-sizing: border-box; }
    
    • html {line-height: 1.5;} 같은 다른 reset 규칙에도 무의식적으로 의존하고 있을 가능성이 있으며, 이런 규칙 없이 CSS를 쓰려면 큰 적응이 필요해 보임
  • components

    • CSS 대부분은 Vue나 React 컴포넌트와 비슷한 방식으로 컴포넌트별 파일에 정리함
    • 각 컴포넌트는 고유 클래스를 갖고, 한 컴포넌트의 CSS가 다른 컴포넌트의 CSS를 덮어쓰지 않도록 구성함
    • 실제로 바꾸고 싶은 CSS의 약 80%가 컴포넌트 파일 안에 있어, 100줄짜리 컴포넌트를 편집할 때는 그 100줄만 생각하면 됨
    • 예를 들어 .zine 컴포넌트는 다음과 같은 HTML을 가질 수 있음
      <figure class="zine horizontal">
          <img src="whatever.jpg">
      </figure>
    
    • CSS는 중첩 선택자로 .horizontal, .vertical, :hover 같은 상태를 컴포넌트 내부에 모음
      .zine {
        ...
        &.horizontal {
          ...
        }
        &.vertical {
          ...
        }
        &:hover {
          ...
        }
      }
    
    • Web Components나 @scope처럼 컴포넌트 간 간섭을 프로그램적으로 막지는 않았지만, 관례를 정해 지키는 것만으로도 크게 나아진 느낌을 줌
  • colours

    • colours.css에는 필요할 때 사용할 수 있는 CSS 변수를 모아 둠
    • 색상은 어렵고 이번 리팩터링에서 색상 사용을 다시 검토하고 싶지 않았기 때문에 기존 방식을 유지함
    • 유일한 규칙은 사이트에서 쓰는 모든 색상을 이 파일에 나열하는 것임
      :root {
        --pink: #fea0c2;
        --pink-light: #F9B9B9;
        --red: #f91a55;
        --orange: rgb(222, 117, 31);
        ...
      }
    
  • font sizes

    • Tailwind에서는 text-lg, xl, 2xl처럼 크기 단계를 고르면 됐기 때문에 em, px, rem 중 무엇을 쓰는지 기억할 필요가 없었음
    • 이를 바닐라 CSS에서도 유지하기 위해 Tailwind에서 가져온 크기 변수를 정의함
      --size-xs: 0.75rem;
        --line-height-xs: 1rem;
    
        --size-sm: 0.875rem;
        --line-height-sm: 1.25rem;
    
    • 폰트 크기는 변수로 지정하며, Tailwind보다 조금 장황하지만 현재로서는 만족스러운 방식임
      h3 {
        font-size: var(--size-lg);
        line-weight: var(--line-weight-lg);
      }
    
  • utilities

    • 여러 컴포넌트에서 반복되는 버튼 같은 요소는 utilities로 분류함
    • 스크린리더 사용자에게만 보여야 하는 요소를 위한 .sr-only 같은 일부 유틸리티 클래스는 Tailwind에서 복사함
    • 이 영역은 작게 유지하고, 변경할 때 조심하려 함
  • base

    • “base” 스타일은 사이트 전체에 직접 적용하는 스타일임
    • 사이트 전체에 많은 스타일을 강제할 만큼 확신이 없기 때문에 이 영역은 매우 작게 유지함
    • 현재 괜찮다고 느끼는 규칙은 <section>a 두 가지이며, <section> 규칙은 나중에 바뀔 수 있음
      /* put a 950px column in the middle of each <section> */
      section {
        --inner-width: 950px;
        padding: 3rem max(1rem, (100% - var(--inner-width))/2);
      }
    
      a {
        color: var(--orange);
      }
    
    • base 스타일은 처음에는 거의 비워두고, 공통으로 원하는 것을 찾을 때 컴포넌트에서 base로 옮기는 상향식 방식이 가장 쉬워 보임
  • spacing

    • padding과 margin을 관리하는 방식은 아직 완전히 정해지지 않았음
    • Tailwind를 쓸 때는 원하는 모양이 나올 때까지 padding과 margin을 여기저기 즉흥적으로 넣었고, 지금은 그보다 더 원칙적인 방식을 찾고 있음
    • 현재는 가능한 한 바깥 레이아웃 컴포넌트가 간격을 책임지도록 하려 함
    • 여러 자식을 가진 <section>에서 자식 사이를 균일하게 띄우고 싶을 때는 다음 규칙을 사용할 수 있음
      section > *+* {
        margin-top: 1rem;
      }
    
  • responsive design

    • Tailwind에서는 md:text-xl처럼 특정 크기 이상에서 text-xl 스타일을 적용하는 미디어 쿼리 기반 문법을 많이 사용했음
    • 지금은 breakpoint를 많이 쓰지 않아도 되는 더 유연한 CSS grid 레이아웃을 만들려고 함
    • auto-fit을 사용하면 큰 화면에서는 2열, 작은 화면에서는 1열을 자동으로 사용할 수 있음
      display: grid;
        grid-template-columns: repeat(auto-fit, minmax(min(100%, 400px), max-content));
        justify-content: center;
    
  • build system

    • 개발 중에는 별도의 빌드 시스템이 필요하지 않음
    • CSS에는 내장 @import 문이 있어 파일을 나눠 가져올 수 있음
      @import "reset.css";
      @import "typography.css";
      @import "colors.css";
    
    • CSS에는 중첩 선택자도 내장되어 있음
      .page {
        h2 { ...}
      }
    
    • 프로덕션용으로 CSS 파일을 묶고 싶다면 esbuild를 사용할 수 있음
      esbuild style.css --bundle --loader:.svg=dataurl  --loader:.woff2=file --outfile=/tmp/out.css
    
    • 보통 CSS와 JS 빌드 시스템 사용을 피하지만, esbuild는 웹 표준 기반이고 정적 Go 바이너리이기 때문에 괜찮다고 봄
    • esbuild에 대해서는 2021년에 esbuild와 Vue 관련 글을 쓴 적이 있음

Tailwind에서 벗어나는 이유

  • Tailwind는 2018년 이후 빌드 시스템 의존성이 훨씬 커졌고, 최신 Tailwind를 빌드 시스템 없이 쓰는 것은 불가능해 보여 수년간 Tailwind v2를 사용해 왔음
  • 빌드 시스템 없이 Tailwind를 쓰는 대안으로 litewind가 있는 것으로 보임
  • Tailwind는 원래 빌드 시스템과 함께 쓰는 것이 전제였지만, 실제로는 그렇게 하지 않았기 때문에 많은 프로젝트에 2.8MB짜리 tailwind.min.css 파일이 남아 있었고 다소 어색하게 느껴짐
  • Tailwind를 처음 쓸 때보다 CSS 실력이 더 좋아짐
  • Tailwind에는 결국 제약이 있으며, CSS에서 이상하거나 특수한 작업을 하고 싶을 때 항상 가능하지는 않음
  • 그런 제약은 매우 유용할 수 있고, 실제로 바닐라 CSS로 옮기면서 Tailwind의 일부 제약을 다시 구현하고 있지만, 이제는 필요한 제약만 골라 쓰고 싶어짐
  • 같은 프로젝트 안에서 바닐라 CSS와 Tailwind가 섞인 사이트들이 생겼고, 이를 유지보수하는 것이 즐겁지 않았음
  • 시맨틱한 HTML을 작성하면 어떤 느낌인지 궁금했음

앞으로 배워보고 싶은 CSS 기능

  • @layer는 cascade layer를 다루는 기능으로, 아직 사용하지 않았지만 배워보고 싶은 기능임
  • @scope는 컴포넌트나 특정 범위 안의 스타일을 다루는 데 관심이 가는 기능임
  • container queries는 컨테이너 기준 반응형 설계를 위한 기능으로 배워보고 싶음
  • subgrid는 CSS grid 관련 기능으로 관심 목록에 있음

Tailwind를 완전히 부정하지 않는 결론

  • 지금은 Tailwind에서 벗어나고 있지만, Tailwind를 쓰기 시작한 것 자체는 여전히 만족스러움
  • Tailwind를 사용하면서 많은 것을 배웠고, tailwind.min.css를 삭제한 뒤에도 사이트 안에서 Tailwind의 일부를 계속 사용할 수 있음
  • wizardzines.com의 CSS를 원래 설계하고 작성한 Melody Starling 덕분에 사이트의 멋지고 재미있는 부분이 만들어졌음
  • CSS 작업 중 CSS Tricks, Smashing Magazine 등에서 많은 CSS 글을 읽었고, CSS 커뮤니티가 실천 방식을 많이 공유해 큰 도움이 됨
Lobste.rs 의견들
  • 개인 프로젝트에서는 더 이상 CSS/JavaScript 프레임워크를 거의 쓰지 않음
    의존성이 없으면 공급망 취약점도 생길 수 없기 때문임. 물론 취약점은 그 한 종류만 있는 건 아니지만 도움이 됨

    • 나도 꽤 순정 HTML/CSS/JS로 돌아왔고, 직접 만든 것만 조금 얹어 쓰는 편임
      프레임워크 피로감, npm audit 과부하, 그리고 LLM 덕분에 구현 방식에 대한 남의 평가를 덜 신경 써도 된다는 점이 합쳐진 결과임. 예를 들면 “왜 React와 Tailwind를 안 쓰냐” 같은 질문들
  • 이건 그냥 CSS가 동작하는 방식
    이걸 모르고 Tailwind를 맹목적으로 쓰는 걸 보면 밖에 나가 구름에 소리치고 싶어짐. 내가 보기엔 Tailwind의 90%는 문법만 다른 인라인 스타일이고, <FONT> 태그보다 한 단계 나은 정도라고 볼 수도 있음

    • 이 댓글의 목적은 잘 모르겠지만, 거의 8년 동안 Tailwind를 쓰면서 Tailwind 사용자를 깎아내리는 이런 댓글을 수없이 봤고, 그중 어떤 것도 Tailwind를 벗어나거나 CSS 실력을 키우는 데 도움이 되지 않았음
      이 블로그 글은 내가 실제로 알아야 했던 내용을 설명한 것임
    • “Tailwind의 90%는 문법만 다른 인라인 스타일”이라는 건 그다지 정확하지 않음
      Tailwind는 인라인 스타일과는 꽤 다르게 동작하고, 오히려 CSS와 훨씬 비슷함. 글에서도 짚듯이 Tailwind를 잘 쓰게 해주는 좋은 습관 상당수는 효과적인 CSS를 쓰는 데도 필요한 습관임. Tailwind는 모든 요소에 암묵적인 스코프가 있는 CSS 블록을 붙여주되, 독특한 DSL을 쓰는 것에 더 가까움
    • CSS가 어떻게 동작하는지 아는 것과 효과적으로 쓰는 법을 아는 건 큰 차이가 있음
      CSS 동작 방식은 잘 알지만, 순정 CSS는 부담스럽고 그래픽 디자인에도 약해서 여전히 Tailwind를 씀. 이 글은 Tailwind를 바탕으로 CSS를 구조화하는 아이디어를 제공함
  • 최근 흐름을 잘 따라가지 못한 입장에서는, 이 글이 현대적인 CSS 관행을 꽤 잘 보여주는 듯함
    영감을 받은 글들로 이어지는 링크가 많은 점도 마음에 듦. 읽을거리로 좋아 보이고, 아직은 "no outer margin"만 읽어봤음
    다만 기본 스타일을 “아래에서 위로” 잡는 접근에는 조금 회의적임. 달리 뭘 할지는 모르겠고 시도해볼 만해 보이지만, 기본 스타일은 본질적으로 까다로운 편임

  • 이 글이 정말 좋았음
    나도 작은 사이트들을 이것저것 많이 만들면서 CSS를 서서히 배웠고, 처음부터 이런 시스템적인 사고를 더 했더라면 도움이 됐을 것 같음. 프레임워크에는 꽤 거부감이 있지만, 쓰지 않다 보니 원하는 대로 구현할 줄 알아도 구조 없는 허공에 떠 있는 느낌을 자주 받았음

  • “Tailwind는 별로니 그냥 CSS를 써라”가 아니라, “Tailwind는 훌륭하지만 이제는 필요 없을 수도 있다”는 식이라 좋음
    CSS를 손으로 구조화하는 데 늘 어려움을 겪었는데, 이 글 덕분에 훨씬 다른 방식으로 생각하게 됨

  • CSS를 정리할 때 이 구조화 기법이 꽤 유용했음: https://rstacruz.github.io/rscss/
    전반적으로 원글에서 jvns가 설명한 내용과 잘 맞고, 그 위에 구조와 정리를 조금 더 얹어줌