[CSS in Depth] 2. Working with relative units

Kyoorim LEE·2023년 1월 20일
0

2.1. The power of relative values

CSS는 웹페이지에 동적바인딩(late-binding) 스타일을 부여한다.

동적바인딩(late-binding)
바인딩(binding)이란 프로그램에 사용된 구성 요소의 실제 값 또는 프로퍼티를 결정짓는 행위를 의미. 예를 들어 함수를 호출하는 부분에서 실제 함수가 위치한 메모리를 연결하는 것도 바로 바인딩.

  • 정적 바인딩(static binding) : 실행 시간 전에 일어나고, 실행 시간에는 변하지 않은 상태로 유지되는 바인딩
  • 동적 바인딩(late binding) : 실행 시간에 이루어지거나 실행 시간에 변경되는 바인딩

2.1.1. The struggle for pixel-perfect design

웹 환경에서는 사용자가 브라우저 사이즈를 조절할 수 있으며 이에 맞는 CSS가 적용되어야 한다. 더욱이 사용자가 브라우저 창 사이즈를 새로 설정할 수도 있기 때문에 페이지를 만들 당시 CSS가 그대로 적용될 수 없는 경우들이 생긴다.

이 말은 어떠한 컨텍스트에서도 요소가 배치될 수 있도록 규칙을 명시해야 한다는 것이다. 30인치 스크린에서 보이는 화면이 4인치 화면에서도 문제 없이 랜더링 되어야함을 뜻한다.

오랫동안 디자이너들은 pixel을 통해 이 문제를 해결해왔다. 일반적으로 800px 너비의 centered column 디자인 방식을 취해왔다.

2.1.2. The end of pixel-perfect web

기술이 발전하고 고화질 모니터가 출시되면서 pixel만능 접근법이 힘을 잃기 시작했다. 2000년대 들어서면서 800px 대신 1024px 너비의 디스플레이에 맞는 디자인이 요구되기 시작했으며 그 보다 더 넓은 디스플레이들이 연달아 출시되었고 똑같은 질문들이 반복되었다.

스마트폰의 등장과 함께 한 사이트가 다양한 디스플레이 환경에서 소비되는 현실을 받아들일 수 밖에 없게 되었다. 이로 인해 '반응형' 디자인에 대한 개념이 생겼다.


2.2. Ems and rems

Em은 가장 많이 이용하는 반응형 유닛이다. 1em 은 현재 요소의 font size를 뜻한다.

  • 현재 font-size가 16px로 되어있으므로 1em에 대한 요소의 지역적(loca) 정의는 자연스럽게 16px이다
  • padding을 1em으로 정의했다면, 여기서는 padding = 16px을 뜻한다
  • 반응형 유닛을 이용하여 정의된 값은 브라우저에서 계산된 값이라는 computed value로 평가된다
  • 만약 여기서 padding = 2em으로 바꾸면 computed value는 32px이 된다
<span class="box box-small">Small</span>
<span class="box box-large">Large</span>

//css
.box {
  padding: 1em;
  border-radius: 1em;
  background-color: lightgray;
}
.box-small {
  font-size: 12px;
}
.box-large {
  font-size: 18px;
}
  • box-smallbox-large에 선언된 font-size가 각각 다르므로 .box에 1em으로 적용된 padding과 border-radius가 각각 다르게 적용된다

2.2.1. em과 font-size의 관계

font-size와 관련하여 em은 좀 다르게 적용된다. 앞에서 em은 현재 요소의 font-size를 뜻한다고 말한바 있다. 하지만 만약 font-size: 1.2em이라고 말한다면 이는 무슨 뜻일까? 이 경우 font-size는 상속받은(inherited) font-size를 기준으로 잡게된다.

예시 2.4

<body>
  We love coffee
  <p class="slogan">We love coffee</p> //The slogan inherits its font size from <body>.
</body>
//css
body {
  font-size: 16px;
}
.slogan {
  font-size: 1.2em; // Calculates to 1.2 times the element’s inherited font size
}
  • .slogan<body>로 부터 상속받은 font-size가 16px이므로 이 경우 font-size:1.2rem은 16 * 1.2 = 19.2px 이 된다
  • 만약 부모로부터 font-size를 12px 상속받았을 때 10px을 원하는 경우, 10/12 = 0.8333 em
  • 만약 부모로부터 font-size를 12px 상속받았을 때 16px을 원하는 경우, 16/12 = 1.3333 em
  • 참고로 일반적으로 브라우저의 디폴트 font-size 값은 16px이다

예시 2.6

body {
  font-size: 16px;
}
.slogan {
  font-size: 1.2em; // 16 * 1.2 = 19.2px
  padding: 1.2em; // 19.2 * 23.04px
  background-color: #ccc;

결과

  • font-size는 상속받은 16px * 1.2 이므로 19.2px
  • padding은 현재 font-size은 19.2px이 기준값이 되므로 19.2 * 1.2가 되므로 23.04px이 적용된다

font 크기가 줄어드는 문제

텍스트 크기가 줄어드는 문제는 여러 레벨로 중첩되어있는 상태에서 각 레벨에 em 기준의 font-size를 적용했기 때문이다.

예시 2.8

body {
  font-size: 16px;
}
ul {
  font-size: .8em;
}
// nested lists
<ul>
  <li>Top level
    <ul>
      <li>Second level
        <ul>
          <li>Third level
            <ul>
              <li>Fourth level
                <ul>
                  <li>Fifth level</li>
                  </ul> 
              </li>
            </ul> 
          </li>
        </ul> 
      </li>
    </ul> 
  </li>
</ul>
  • 각 list의 font-size가 부모 list 보다 0.8배씩 줄어들게 된다
  • 반면 우리가 원하는 것은 top-level의 font-size만 규정하고 나머지 하위항목들은 모두 같은 font-size를 유지하는 것이다.
ul {
  font-size: .8em;
}
ul ul {
  font-size: 1em;
}
  • 값을 정의한 후 바로 ul ul을 또 정의해서 다른 값으로 덮어버리는 것은 문제를 해결할 수는 있지만 이상적인 방법은 아니다.
  • 이를 통해 em은 padding, margin, element sizing을 하기에는 편할지 몰라도 font-size에 있어서는 까탈스러운 아이라는 것을 알았다.
  • 이를 해결할 수 있는 방법으로 rem이 있다!!!

2.2.2 font-size를 위한 rem

브라우저는 HTML문서를 파싱하면서 DOM을 그린다. DOM은 트리 구조로 각각의 요소는 노드(node)로 이뤄져있다. <html>요소는 루트(탑레벨)노드이다. 그 밑의 <head><body>는 자식 노드이며 그 밑으로 계속 자식 노드들로 이어진다.

루트 노드는 모든 요소들의 조상으로 :root라는 특별한 pseudo-class 선택자를 가진다. :roothtml선택자와 같은 선택자로 tag보다 더 명시도가 높다.

rem === root em

rem은 root 요소에 대한 상대적 요소이다. 예를 들면 1.2rem은 문서 어디에서 적용을 하건 root 요소의 1.2배 값이라는 똑같은 computed value(계산된 값)을 갖는다.

rem은 em이 갖는 많은 복잡한 문제들을 해결한다. pixel과 em 사이에서 반응형에 더 적절한 솔루션이 되는 것은 물론 더 편리한 사용성을 자랑한다. 그렇다고 모든 요소들을 rem으로 대체해서 사용해도 된다는 것을 의미하지는 않는다.

일반적으로 CSS에서 그렇듯 "케이스 바이 케이스"를 생각해야한다. rem은 연장 가방안에 들어있는 하나의 연장 옵션일 뿐이다. CSS를 잘 하는 것은 언제 어떤 연장을 적절히 사용할지를 아는 것이다. 저자가 선호하는 디폴트 값은 하기와 같다.

rem => font-size
pixel => border
em => padding, margin, border-radius 외 기타 등등


2.3 pixel로 생각하는 것을 멈춰라

수 년 간 root 페이지에서 font-size를 0.625em or 62.5%으로 리셋하는 것이 관행이었다.

하지만 저자는 이 방법을 추천하지 않는다. 이는 브라우저의 font-size 초기값인 16px을 10px로 고정시켜놓는 방법이다. 만일 디자이너가 font-size를 14px로 작업해달라고 요청하면 1.4em 으로 쉽게 계산해서 작업할 수 있다.

언뜻 편리하게 보이는 방법이지만 크게 2가지 문제점이 있다.
1) 많은 중복된 스타일을 만든다 - 10px은 계산은 쉬울 수 있으나 그 자체로는 작은 사이즈이기 때문에 매번 적절한 사이즈를 계산하여 override해야한다. 다시 말하면 모든 페이지에 일일히 1.4rem을 정의하여 적절한 사이즈를 만들어줘야 한다는 것이다. 결론적으로 에러가 날 확률이 높아지고 수정사항이 생길 시 일일히 다 고쳐야하는 수고로움이 생기며 stylesheet의 용량이 커지게 된다.
2) pixel을 기준으로 생각하게 되다 - 1.4rem을 정의하긴 했지만 여전히 머리속으로는 14px을 생각하고 있다는 뜻이다. 반면 반응형을 작업하려면 애매한 값에 친숙해질 필요가 있다. 1.2em이 몇 pixel인지가 중요한 것이 아니라 화면에 어떤 크기로 보이는지가 중요하다는 것이다.

em으로 font-size를 작업하게 되면 이 값이 pixel로 바꾸면 얼마인지를 계속 속으로 계산하며 집착하게 된다. pixel로 계산하는 대신 em 그 자체로 작업하는 습관을 들일 필요가 있다.

아예 pixel로 작업하지 말란 이야기는 물론 아니다. 디자이너와 함께 일하다보면 분명 pixel로 말해야할 때가 있을 것이기 때문이다. 프로젝트 초기에는 기본이 되는 font-size를 정해야하며 회의할 때 절대값으로 논의하는 것이 쉬울 때가 있다.

rem으로 변환하려면 산술이 필요하므로 계산기를 가까이 두는게 좋다. 루트 글꼴 크기를 지정하면 rem이 정의되고 그 시점부터 pixel로 작업할 필요가 없어지게 된다.

2.3.1 정상적인 기본 글꼴 크기 설정

font-size를 14px로 주고 싶다고 가정하면 :root에 초기값을 10px로 지정하는 것이 아니라 14px인 0.875em을 설정하는 것이 좋다. 이렇게 하면 각 페이지마다 14px을 설정해줄 필요가 없고 heading과 같이 예외적인 경우에만 font-size를 별도로 지정해주면 된다.

2.3.2 반응형 만들기

media query를 통해 스크린 크기에 따라 font-size를 조절할 수 있다.

가장 작은 스크린 사이즈에서 사용할 font-size를 :root 에 지정한다.

:root {
	font-size: 0.75em;
}

그리고 그 이상으로 큰 스크린(800px, 1200px)에서 사용할 font-size를 media query로 지정해준다. 이렇게 되면 가장 작은 스크린에서는 font-size가 12px로 렌더링 될 것이고 padding과 border-radius도 그와 비례하게 조정될 것이며 이는 큰 스크린에서 동일하게 적용된다.

2.3.3. 단일 컴포넌트 사이즈 조정

만일 동일한 페이지에서 위 예시와 같이 글자 크기가 작은 버전과 큰 버전을 동시에 만들고 싶다면 어떻게 하면 될까?

1) 각 패널의 부모요소에 font-size: 1rem을 선언하여 페이지 어느 부분에서도 각 패널에서 예상 가능한 font-size를 보여줄 수 있도록 한다.
2) heading의 글자 크기를 rem 대신 em으로 지정하여 font-size:1rem으로 지정한 부모요소의 영향을 받게 한다.
3) 글자 크기가 큰 컴포넌트 선택자를 이용하여 1.2rem을 따로 지정해주면 글자 크기가 작은 버전과 큰 버전을 동시에 볼 수 있다.

profile
oneThing

0개의 댓글