지난 글에서 브라우저가 CSS를 어떻게 렌더링하는지 알아봤습니다.

HTML 파싱 → DOM 생성 → CSSOM 생성 → Render Tree → Layout → Paint → Composite

위와 같이 6단계 파이프라인을 통해 CSS가 화면에 그려진다는 것을 확인했습니다.

이제 CSS를 실제로 작성할 때 꼭 알아야 할 기본 개념들을 깊이 있게 살펴보겠습니다. 선택자는 어떻게 동작하고, 왜 어떤 스타일이 다른 스타일을 덮어쓰는지, 그리고 각각의 CSS 단위는 언제 사용해야 하는지 알아보겠습니다.

CSS 규칙의 구조

먼저 CSS 규칙(Rule Set)이 어떻게 구성되어 있는지 정확히 알아야 합니다:

.button {
    color: blue;
    font-size: 16px;
    margin: 10px 0;
}
  • 선택자(Selector): .button - 스타일을 적용할 요소를 선택
  • 선언 블록(Declaration Block): 중괄호 {} 안의 전체 내용
  • 선언(Declaration): color: blue; - 속성과 값의 조합
  • 속성(Property): color, font-size, margin
  • 값(Value): blue, 16px, 10px 0

브라우저는 이 규칙을 파싱해서 CSSOM에 저장하고, 나중에 DOM 요소와 매칭시킵니다. 이 매칭 과정에서 선택자의 역할이 매우 중요합니다.

CSS의 선택자-스타일링 할 요소를 선택하는 방법

기본 선택자들

/* 전체 선택자 - 모든 요소 선택 */
* {
    box-sizing: border-box;
}

/* 타입 선택자 - 특정 태그 선택 */
h1 {
    font-size: 2rem;
    margin: 0 0 1rem 0;
}

/* 클래스 선택자 - 가장 많이 사용 */
.highlight {
    background-color: yellow;
    padding: 0.25rem;
}

/* ID 선택자 - 고유한 요소 */
#header {
    position: sticky;
    top: 0;
}

실무 팁: ID 선택자는 특정성(specificity)이 너무 높아서 나중에 덮어쓰기 어려워집니다. 가능한 한 클래스 선택자를 사용하는 것이 유지보수에 유리합니다.

결합자(Combinator): 요소 간의 관계 표현

결합자는 선택자 사이의 관계를 정의하는 중요한 도구입니다. 브라우저는 DOM 트리를 순회하면서 이 관계를 평가합니다.

후손 선택자 (공백(스페이스))

/* .article 안의 모든 p 태그 (깊이 무관) */
.article p {
    line-height: 1.6;
    margin-bottom: 1rem;
}
<div class="article">
    <p>이것도 선택됨</p>
    <section>
        <p>이것도 선택됨 (깊은 곳에 있어도)</p>
    </section>
</div>

브라우저는 .article을 찾은 다음, 그 안의 모든 깊이에서 p 태그를 찾습니다. 이 과정은 재귀적으로 수행되기 때문에 깊게 중첩된 선택자는 성능에 영향을 줄 수 있습니다.

자식 선택자 (>)

/* .nav의 직접적인 자식 li만 선택 */
.nav > li {
    display: inline-block;
    margin-right: 1rem;
}
<ul class="nav">
    <li>Home</li> <!-- 선택됨 -->
    <li>About
        <ul>
            <li>Team</li> <!-- 선택 안됨 (손자) -->
        </ul>
    </li>
</ul>

자식 선택자는 직계 자식만 선택하므로 후손 선택자보다 성능이 좋습니다. 명확한 구조적 관계를 표현할 때 사용합니다.

인접 형제 선택자 (+)

/* h2 바로 다음에 오는 p만 선택 */
h2 + p {
    font-weight: bold;
    margin-top: 0;
}
<h2>제목</h2>
<p>이 문단만 선택됨 (바로 다음)</p>
<p>이건 선택 안됨</p>

인접 형제 선택자는 특정 요소 바로 다음의 형제 요소만 선택합니다. 제목 다음의 첫 문단에 특별한 스타일을 적용할 때 유용합니다.

일반 형제 선택자 (~)

/* h2 이후에 오는 모든 p 선택 */
h2 ~ p {
    color: #666;
}
<div>
    <p>선택 안됨 (h2 이전)</p>
    <h2>제목</h2>
    <p>선택됨</p>
    <div>중간에 다른 요소</div>
    <p>이것도 선택됨</p>
</div>

속성 선택자: 세밀한 타겟팅

속성 선택자는 HTML 속성을 기반으로 요소를 선택합니다. 폼 요소나 링크를 다룰 때 특히 유용합니다.

/* type 속성이 "text"인 input */
input[type="text"] {
    border: 1px solid #ddd;
    padding: 0.5rem;
}

/* href 속성이 있는 a 태그 */
a[href] {
    color: blue;
}

/* class 속성에 "btn"이 포함된 요소 */
[class*="btn"] {
    cursor: pointer;
    border: none;
}

/* href가 "https://"로 시작하는 링크 */
a[href^="https://"] {
    color: green;
}

/* href가 ".pdf"로 끝나는 링크 */
a[href$=".pdf"]::after {
    content: " (PDF)";
    color: #666;
    font-size: 0.875em;
}

/* data 속성 활용 */
[data-status="active"] {
    background-color: #4CAF50;
}

[data-priority="high"] {
    border-left: 4px solid red;
}

가상 클래스와 가상 요소

가상 클래스는 요소의 특정 상태를 선택하고, 가상 요소는 요소의 특정 부분을 선택합니다.

/* 상태 기반 가상 클래스 */
a:hover { color: red; }
input:focus { outline: 2px solid blue; }
button:disabled { opacity: 0.5; cursor: not-allowed; }
input:checked + label { font-weight: bold; }

/* 구조 기반 가상 클래스 */
tr:nth-child(even) { background-color: #f5f5f5; }
p:first-child { margin-top: 0; }
p:last-child { margin-bottom: 0; }
li:nth-of-type(3n) { color: red; } /* 3의 배수 번째 */

/* 부정 가상 클래스 */
input:not([type="submit"]) {
    border: 1px solid #ccc;
}

/* 가상 요소로 콘텐츠 추가 */
.required::after {
    content: " *";
    color: red;
}

blockquote::before {
    content: "";
    font-size: 2em;
    line-height: 0;
}

/* 첫 글자, 첫 줄 스타일링 */
p::first-letter {
    font-size: 2em;
    font-weight: bold;
}

p::first-line {
    text-transform: uppercase;
}

상속(Inheritance)

CSS 상속은 DOM 트리를 따라 자동으로 이루어집니다. 이는 렌더링 성능 최적화의 핵심 개념입니다.

<div style="font-family: Arial, sans-serif; color: #333; line-height: 1.5;">
    <h1>이 제목은 Arial 폰트와 #333 색상을 상속받습니다</h1>
    <p>이 문단도 모든 스타일을 상속받습니다</p>
    <ul>
        <li>목록 아이템도 마찬가지입니다</li>
    </ul>
</div>

상속되는 속성들 (대부분 텍스트 관련)

텍스트 관련 속성들이 상속되는 이유는 문서 전체의 일관성을 쉽게 유지하기 위함입니다.

.parent {
    /* 이 속성들은 모두 자식에게 상속됨 */
    font-family: "Helvetica Neue", sans-serif;
    font-size: 16px;
    font-weight: 400;
    line-height: 1.5;
    color: #333;
    text-align: center;
    letter-spacing: 0.5px;
    word-spacing: 0.1em;
    text-transform: uppercase;
    
    /* 목록 관련도 상속됨 */
    list-style: none;
    list-style-position: inside;
    
    /* 표 관련 */
    border-collapse: collapse;
    border-spacing: 0;
    
    /* 커서 */
    cursor: pointer;
}

상속되지 않는 속성들 (대부분 박스 모델과 배치)

박스 모델 관련 속성이 상속되지 않는 이유는 각 요소가 독립적인 레이아웃을 가져야 하기 때문입니다.

.parent {
    /* 이 속성들은 상속되지 않음 */
    width: 300px;
    height: 200px;
    margin: 20px;
    padding: 10px;
    border: 1px solid #ddd;
    background-color: #f5f5f5;
    
    position: relative;
    display: flex;
    z-index: 10;
    overflow: hidden;
    float: left;
}

상속 제어하기

때로는 상속을 명시적으로 제어해야 할 필요가 있습니다.

.parent {
    color: red;
    border: 2px solid blue;
}

.child {
    /* 강제로 상속받기 */
    border: inherit; /* 부모의 border를 강제 상속 */
    
    /* 상속 차단하기 */
    color: initial; /* 브라우저 기본값으로 리셋 */
    
    /* 계산된 값 사용 */
    width: unset; /* 상속되는 속성이면 상속, 아니면 initial */
    
    /* 모든 상속 차단 */
    all: unset; /* 모든 속성을 초기값으로 */
}

우선순위

여러 CSS 규칙이 같은 요소를 선택할 때 무엇이 적용될지 결정하는 시스템입니다. 브라우저는 이를 계산해서 CSSOM을 구성합니다.

우선순위 계산법 (점수제)

우선순위는 4개의 숫자로 표현됩니다: [inline, id, class, element]

/* 점수: 0,0,0,1 (태그 1개) */
p { color: black; }

/* 점수: 0,0,1,0 (클래스 1개) */
.text { color: blue; }

/* 점수: 0,0,1,1 (클래스 1개 + 태그 1개) */
.content p { color: green; }

/* 점수: 0,0,2,0 (클래스 2개) */
.article.featured { color: purple; }

/* 점수: 0,1,0,0 (ID 1개) */
#main { color: red; }

/* 점수: 0,1,1,0 (ID 1개 + 클래스 1개) */
#main .text { color: orange; }

/* 점수: 1,0,0,0 (인라인 스타일) */
<p style="color: yellow;">노란 텍스트</p>

실제 계산 예시

실제 개발에서 마주치는 복잡한 선택자의 우선순위를 계산해봅시다.

/* 점수: 0,1,2,2 (ID 1개 + 클래스 2개 + 태그 2개) */
#sidebar .widget-list li.active {
    background-color: #e0e0e0;
}

/* 점수: 0,0,1,3 (클래스 1개 + 태그 3개) */
.content div ul li {
    background-color: white; /* 위 규칙에 의해 덮어쓰임 */
}

/* 점수: 0,0,2,1 (속성 선택자 1개 + 클래스 1개 + 태그 1개) */
input[type="text"].error {
    border-color: red;
}

/* 점수: 0,0,3,0 (가상 클래스도 클래스로 계산) */
.button:hover:focus {
    outline: 2px solid blue;
}

캐스케이딩(Cascading) 규칙

CSS의 ‘C’가 바로 Cascading입니다. 같은 우선순위라면 나중에 작성된 규칙이 적용됩니다:

.button { background: red; }
.button { background: blue; } /* 이게 적용됨 */

/* 하지만 우선순위가 다르면 순서 무관 */
.button { background: red; } /* 점수: 0,0,1,0 */
#nav .button { background: blue; } /* 점수: 0,1,1,0, 이게 적용됨 */

/* 미디어 쿼리 내부의 규칙도 동일한 우선순위 */
.text { color: black; }

@media (max-width: 768px) {
    .text { color: blue; } /* 768px 이하에서만 적용 */
}

!important: 최후의 수단

!important는 일반적인 우선순위 규칙을 무시합니다. 하지만 코드의 유지보수성을 크게 해칩니다.

.button {
    background: red !important; /* 다른 모든 규칙을 무시 */
}

#header .button {
    background: blue; /* important에 의해 무시됨 */
}

/* important끼리는 다시 우선순위 규칙 적용 */
.button {
    background: red !important;
}

#header .button {
    background: blue !important; /* 더 높은 우선순위로 이게 적용 */
}

주의: !important는 나중에 스타일을 덮어쓰기 매우 어렵게 만듭니다. 다음 경우에만 제한적으로 사용하세요:

  • 서드파티 CSS를 덮어써야 할 때
  • 유틸리티 클래스를 만들 때 (예: .hidden { display: none !important; })

CSS 단위: 언제 무엇을 써야 할까?

CSS 단위는 레이아웃과 타이포그래피의 핵심입니다. 각 단위는 특정 용도에 최적화되어 있습니다.

절대 단위: px

픽셀은 가장 직관적이고 예측 가능한 단위입니다. 스크린의 물리적 픽셀과 1:1로 매핑되지는 않지만(디바이스 픽셀 비율 때문에), CSS에서는 일관된 단위로 동작합니다.

.card {
    border: 1px solid #ddd; /* 얇은 선은 px가 최적 */
    border-radius: 8px;     /* 작은 곡률도 px */
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); /* 그림자도 px */
}

.icon {
    width: 24px;  /* 아이콘은 고정 크기가 보통 */
    height: 24px;
}

/* 미디어 쿼리 브레이크포인트도 px */
@media (min-width: 768px) {
    .container {
        max-width: 1200px;
    }
}

px를 사용해야 하는 경우:

  • 보더, 그림자 등 세밀한 디자인 요소
  • 아이콘, 로고 등 고정 크기 요소
  • 1px, 2px 같은 매우 작은 값
  • 미디어 쿼리 브레이크포인트

상대 단위: em

em은 현재 요소의 font-size를 기준으로 계산됩니다. 컴포넌트 내부의 상대적 크기를 표현할 때 유용합니다.

.card {
    font-size: 16px;
    padding: 1em;     /* 16px * 1 = 16px */
    margin: 0.5em 0;  /* 16px * 0.5 = 8px */
}

.card .title {
    font-size: 1.25em;  /* 16px * 1.25 = 20px */
    margin-bottom: 0.5em; /* 20px * 0.5 = 10px (자신의 font-size 기준!) */
}

em의 함정: 중첩될 때 누적됩니다

.parent {
    font-size: 1.2em; /* 기본 16px * 1.2 = 19.2px */
}

.parent .child {
    font-size: 1.2em; /* 19.2px * 1.2 = 23.04px (누적!) */
}

.parent .child .grandchild {
    font-size: 1.2em; /* 23.04px * 1.2 = 27.648px (또 누적!) */
}

이런 누적 문제 때문에 em보다는 rem을 선호하는 경우가 많습니다.

상대 단위: rem

rem은 루트 요소(html)의 font-size를 기준으로 계산됩니다. 일관성 있는 스케일링이 가능합니다.

html {
    font-size: 16px; /* 기본값, 브라우저 설정에 따라 변경 가능 */
}

.section {
    font-size: 1.125rem;  /* 16px * 1.125 = 18px */
    padding: 2rem;        /* 16px * 2 = 32px */
    margin: 1.5rem 0;     /* 16px * 1.5 = 24px */
}

.section .title {
    font-size: 1.5rem;   /* 16px * 1.5 = 24px (항상 일정!) */
}

rem의 장점: 접근성과 일관성

/* 사용자가 브라우저에서 폰트 크기를 150%로 늘리면 */
html { font-size: 24px; } /* 16px * 1.5 */

/* 모든 rem 값이 자동으로 1.5배가 됨 */
.section {
    padding: 2rem; /* 24px * 2 = 48px */
    margin: 1.5rem 0; /* 24px * 1.5 = 36px */
}

퍼센트 단위: %

부모 요소의 해당 속성 값을 기준으로 계산됩니다. 반응형 레이아웃의 기초입니다. 아래는 container 안에 sidebarmain 이 있고, sidebar25%, main75% 의 너비를 가지고 있습니다. sidebar 의 폭은 25% 이므로 부모 요소인 container 의 폭 1200px25%300px이 됩니다.

.container {
    width: 1200px;
    height: 600px;
}

.sidebar {
    width: 25%;      /* 1200px * 0.25 = 300px */
    height: 100%;    /* 600px * 1 = 600px */
}

.main {
    width: 75%;      /* 1200px * 0.75 = 900px */
    padding: 2%;     /* 1200px * 0.02 = 24px (width 기준!) */
}

주의: paddingmargin의 %는 항상 부모의 width를 기준으로 합니다. 이는 수직/수평 여백의 일관성을 위한 설계입니다. 아래 boxcontainer 안에 존재한다면 container의 폭 1200px10%120px이 됩니다.

.box {
    width: 300px;
    height: 200px;
    padding: 10%; /* 120px (1200px의 10%, height와 무관!) */
}

녹색이 container, 핑크색이 box입니다.

개발자 도구로 실제 크기를 확인한 것입니다.

뷰포트 단위: vw, vh, vmin, vmax

뷰포트 단위는 브라우저 창 크기를 기준으로 합니다. 풀스크린 레이아웃이나 반응형 타이포그래피에 유용합니다.

/* 뷰포트 전체 활용 */
.hero {
    width: 100vw;  /* 뷰포트 너비의 100% */
    height: 100vh; /* 뷰포트 높이의 100% */
}

/* 반응형 타이포그래피 */
.title {
    font-size: 4vw; /* 화면이 커질수록 글자도 커짐 */
    
    /* clamp()로 최소/최대 제한 */
    font-size: clamp(1.5rem, 4vw, 3rem); 
    /* 최소 1.5rem, 기본 4vw, 최대 3rem */
}

/* 정사각형 만들기 */
.square {
    width: 20vmin;  /* 뷰포트의 짧은 쪽 기준 */
    height: 20vmin; /* 항상 정사각형 유지 */
}

/* 뷰포트 방향에 따른 대응 */
.responsive-box {
    width: 50vmax;  /* 뷰포트의 긴 쪽의 50% */
    height: 50vmin; /* 뷰포트의 짧은 쪽의 50% */
}

뷰포트 단위 활용 예시:

  • 100vh 문제: 모바일에서 주소창 때문에 스크롤 발생
  • 해결책: min-height: 100vh 또는 CSS 변수 활용

calc()로 단위 조합하기

calc()는 다른 단위들을 수식으로 조합할 수 있게 해줍니다. 복잡한 레이아웃 계산을 CSS에서 직접 처리할 수 있습니다.

.header {
    /* 100vh에서 푸터 높이만큼 빼기 */
    height: calc(100vh - 80px);
    
    /* 50%에서 고정 여백 빼기 */
    width: calc(50% - 20px);
    
    /* rem과 vw 조합 */
    font-size: calc(1rem + 2vw);
}

.grid-item {
    /* 3개 컬럼, 간격 고려 */
    width: calc((100% - 40px) / 3); /* 전체에서 양옆 여백(40px) 빼고 3등분 */
}

/* 복잡한 계산 */
.complex-layout {
    /* 가운데 정렬된 고정 너비 컨테이너 */
    width: min(90%, 1200px);
    margin-left: calc((100% - min(90%, 1200px)) / 2);
}

새로운 단위들: 논리적 속성

최근 CSS에서는 국제화를 고려한 논리적 단위들이 추가되었습니다. 가령 아랍어 같은 경우는 왼쪽이 아니라 오른쪽부터 쓰기 때문에 왼쪽, 오른쪽 대신 시작과 끝이라는 속성으로 일관되게 표현하기 위함입니다.

/* 물리적 속성 (left/right) */
.old-way {
    margin-left: 1rem;
    padding-right: 2rem;
}

/* 논리적 속성 (start/end) */
.new-way {
    margin-inline-start: 1rem;  /* LTR: left, RTL: right */
    padding-inline-end: 2rem;   /* LTR: right, RTL: left */
    
    /* block은 세로 방향 */
    margin-block-start: 1rem;   /* 상단 */
    margin-block-end: 1rem;     /* 하단 */
}

/* 더 간단한 문법 */
.shorthand {
    margin-inline: 1rem 2rem;  /* start end */
    margin-block: 1rem;         /* start와 end 모두 */
}

실무에서의 단위 선택 가이드

타이포그래피 시스템

일관성 있는 타이포그래피를 위한 스케일 시스템을 구축합니다. 이런 시스템은 웹사이트의 전반적인 디자인 일관성을 유지하는 데 도움이 됩니다. tailwind 같은 CSS 프레임워크는 이런 시스템을 미리 제공하는 경우가 많습니다.

/* 기본 설정 */
html {
    font-size: 16px; /* 기준점 설정 */
}

/* 스케일 시스템 (rem 기반) */
.text-xs { font-size: 0.75rem; }   /* 12px */
.text-sm { font-size: 0.875rem; }  /* 14px */
.text-base { font-size: 1rem; }    /* 16px */
.text-lg { font-size: 1.125rem; }  /* 18px */
.text-xl { font-size: 1.25rem; }   /* 20px */
.text-2xl { font-size: 1.5rem; }   /* 24px */
.text-3xl { font-size: 1.875rem; } /* 30px */
.text-4xl { font-size: 2.25rem; }  /* 36px */

/* 반응형 타이포그래피 */
@media (max-width: 768px) {
    html {
        font-size: 14px; /* 모바일에서 기준 크기 조정 */
    }
}

간격 시스템

간격도 일관성 있게 관리해야 디자인의 일관성을 유지할 수 있습니다.

/* 8px 기반 간격 시스템 */
.space-1 { margin: 0.25rem; }  /* 4px */
.space-2 { margin: 0.5rem; }   /* 8px */
.space-3 { margin: 0.75rem; }  /* 12px */
.space-4 { margin: 1rem; }     /* 16px */
.space-5 { margin: 1.25rem; }  /* 20px */
.space-6 { margin: 1.5rem; }   /* 24px */
.space-8 { margin: 2rem; }     /* 32px */
.space-10 { margin: 2.5rem; }  /* 40px */
.space-12 { margin: 3rem; }    /* 48px */
.space-16 { margin: 4rem; }    /* 64px */

/* 방향별 간격 유틸리티 */
.mt-4 { margin-top: 1rem; }
.mb-4 { margin-bottom: 1rem; }
.mx-4 { margin-left: 1rem; margin-right: 1rem; }
.my-4 { margin-top: 1rem; margin-bottom: 1rem; }

컨테이너와 레이아웃

반응형 컨테이너를 만들 때 단위 조합이 중요합니다.

.container {
    max-width: 1200px;    /* 최대 너비는 px로 고정 */
    width: 90%;           /* 기본 너비는 %로 반응형 */
    margin: 0 auto;       /* 가운데 정렬 */
    padding: 0 1rem;      /* 내부 여백은 rem */
}

/* 유동적인 그리드 */
.grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    gap: 2rem;            /* 간격은 rem */
}

/* 고정 사이드바 + 유동 메인 */
.layout {
    display: grid;
    grid-template-columns: 250px 1fr; /* 사이드바 고정, 메인 유동 */
    gap: 2rem;
}

@media (max-width: 768px) {
    .layout {
        grid-template-columns: 1fr; /* 모바일에서 단일 컬럼 */
    }
}

반응형 디자인 패턴

/* 모바일 퍼스트 접근 */
.responsive-text {
    font-size: 1rem; /* 기본: 모바일 */
}

@media (min-width: 768px) {
    .responsive-text {
        font-size: 1.125rem; /* 태블릿 */
    }
}

@media (min-width: 1024px) {
    .responsive-text {
        font-size: 1.25rem; /* 데스크톱 */
    }
}

/* 유동적 타이포그래피 */
.fluid-text {
    /* 뷰포트에 따라 유동적으로 변하되, 최소/최대값 제한 */
    font-size: clamp(1rem, 2.5vw, 2rem);
}

/* 컨테이너 쿼리 (최신 기능) */
.card-container {
    container-type: inline-size;
}

@container (min-width: 400px) {
    .card-container .card {
        display: grid;
        grid-template-columns: 150px 1fr;
    }
}

성능 최적화 팁

선택자 성능

브라우저는 선택자를 오른쪽에서 왼쪽으로 평가합니다.

/* 나쁜 예: 모든 a 태그를 찾고, 그 중 .nav .list li 안에 있는 것 필터링 */
.nav .list li a { }

/* 좋은 예: .nav-link 클래스만 찾으면 됨 */
.nav-link { }

/* 매우 나쁜 예: 모든 태그 검사 */
.container * { }

/* 개선: 필요한 요소만 명시 */
.container > div { }

상속 활용

상속을 잘 활용하면 불필요한 선언을 줄일 수 있습니다.

/* 나쁜 예: 모든 요소에 개별 선언 */
.card h2 { font-family: 'Roboto', sans-serif; }
.card p { font-family: 'Roboto', sans-serif; }
.card span { font-family: 'Roboto', sans-serif; }

/* 좋은 예: 부모에 한 번만 선언 */
.card { font-family: 'Roboto', sans-serif; }

리플로우 최소화

지난 포스트에서 reflow와 repaint, composite 에 대해서 알아봤습니다. width, height 같은 속성은 지오메트리를 다시 계산하고 그 이후 단계를 모두 재수행하므로 성능에 악영향을 끼칩니다. 가능한 한 transformopacity를 사용하세요.

/* 나쁜 예: 리플로우 발생 */
.box:hover {
    width: 200px;
    height: 200px;
    top: -10px;
}

/* 좋은 예: GPU 가속, 리플로우 없음 */
.box:hover {
    transform: scale(1.1) translateY(-10px);
}

정리

이번 포스트는 상당히 길었습니다. 저도 이번에 작성하면서 마진과 패딩 값을 %로 했을 때 무엇을 기준으로 하는지는 처음 알게 되었습니다. CSS를 직접 작성할 일은 많지 않고 가끔 디자인이 필요할 때만 잠깐 다루게되는데 그때마다 기억은 잘 나지않고… 그렇습니다. 다음 포스트는 박스 모델을 다루겠습니다.

선택자와 결합자:

  • 기본 선택자부터 속성 선택자까지 다양한 타겟팅 방법
  • >, +, ~ 결합자로 정확한 요소 관계 표현
  • 가상 클래스와 가상 요소로 동적 스타일링
  • 브라우저가 선택자를 오른쪽에서 왼쪽으로 평가한다는 성능 관점

상속과 우선순위:

  • 텍스트 속성은 상속되고, 박스 모델은 상속되지 않음
  • 특정성 계산법을 통한 우선순위 결정 (인라인 > ID > 클래스 > 태그)
  • 캐스케이딩 규칙과 !important의 절제된 사용

CSS 단위 시스템:

  • px: 세밀한 디자인 요소 (보더, 그림자, 아이콘)
  • rem: 타이포그래피와 간격 시스템 (일관성과 접근성)
  • em: 컴포넌트 내부의 상대적 크기 (누적 주의)
  • %: 부모 기준 반응형 레이아웃
  • vw/vh: 뷰포트 기준 전체 화면 활용
  • calc(): 단위 간 계산으로 복잡한 레이아웃 해결

참고자료: