CSS Reset의 선언과 구조를 이해해 봅니다.
원문 링크 : https://www.joshwcomeau.com/css/custom-css-reset/
기존의 CSS Reset을 사용하지 않는 이유
가장 유명한 CSS Reset은 에릭 마이어의 작품(CSS Reset)입니다.
해당 설정은 수십 년간 업데이트 되고있지 않으며, CSS와 브라우저, 기술 생태계도 많이 변했습니다.
따라서 모던 CSS를 위한 새로운 Reset을 사용할 필요가 있습니다.
- 이는 현재는 전혀 사용되지 않는 레거시 브라우저를 지원할 필요가 없습니다.
- auto prefixer, polifill 등의 기술 지원을 받을 수 있습니다.
- 최신 스펙을 이용해 가독성, 간결함을 유지할 수 있습니다.
- 개발 생산성과 개발 경험을 향상시킵니다.
- 다른 CSS Rest과 마찬가지로 디자인과는 무관합니다.
- 모든 프로젝트에 해당 설정을 적용할 수 있습니다.
전체 CSS Reset CSS
9가지 목적을 수행하기 위함이며, 이게 전부입니다.
매우 짧지만 꽤 많은 내용을 다루고 있습니다.
/*
1. Use a more-intuitive box-sizing model.
*/
*, *::before, *::after {
box-sizing: border-box;
}
/*
2. Remove default margin
*/
* {
margin: 0;
}
/*
3. Allow percentage-based heights in the application
*/
html, body {
height: 100%;
}
/*
Typographic tweaks!
4. Add accessible line-height
5. Improve text rendering
*/
body {
line-height: 1.5;
-webkit-font-smoothing: antialiased;
}
/*
6. Improve media defaults
*/
img, picture, video, canvas, svg {
display: block;
max-width: 100%;
}
/*
7. Remove built-in form typography styles
*/
input, button, textarea, select {
font: inherit;
}
/*
8. Avoid text overflows
*/
p, h1, h2, h3, h4, h5, h6 {
overflow-wrap: break-word;
}
/*
9. Create a root stacking context
*/
#root, #__next {
isolation: isolate;
}
역사적으로 CSS Reset의 목표는 모든 브라우저의 기능적, 디자인적 일관성을 실현하는 것이었습니다.
하지만 위의 CSS Reset은 해당 목표를 위한 것이 아닙니다.
- 예전과 다르게 모던 브라우저는 CSS 사양을 충실하게 구현합니다.
- 때때로 브라우저 기본 스타일이 의미있을 때가 있습니다.
- <em> 태그의 스타일을 Reset한 후 font-style: italic을 재정의하는 것은 의미가 없습니다.
1. 박스 크기 모델(Box-sizing model)
box-sizing의 디폴트 값은 content-box입니다.
콘텐츠 상자는 콘텐츠를 포함하는 CSS BOX 모델의 사각형이며,
마진, 보더, 패딩의 안에서 실질적인 컨텐츠만을 포함하는 영역입니다.
따라서 박스 전체의 크기는 보더 + 패딩 + 컨텐츠를 다 더해야 알 수 있습니다.
상자 전체 사각형(분홍색 테두리 영역)은 border-box라 합니다.
따라서 width:100%을 설정하는 것은, 해당 content 영역의 너비만 부모의 너비를 상속하도록 하는 것입니다.
만약 부모 또한 content-box를 따르면 어떻게 될까요?
아래 CSS는 해당 문제를 보여줍니다.
- 자식 컨텐츠 너비는 부모의 컨텐츠 너비만을 상속합니다.
- 부모의 실제 박스 크기는 border도 계산해야 합니다.
- 자식의 실제 박스 크기는 border와 padding도 계산해야 합니다.
이는 정말 직관적이지 않습니다.
box-sizing: border-box;를 사용하면,
너비와 높이는 content + border + padding를 전부 고려한 값이 됩니다.
해당 값을 준수하기 위해 content 영역이 작아지게 됩니다.
*, *::before, *::after {
box-sizing: border-box;
}
이제 부모 상자의 전체 너비는 200px이 되고, 컨텐츠 영역의 너비는 (200-4)px이 됩니다.
이제 자식 상자는 부모 상자의 전체 너비 200px을 상속하며, 컨텐츠 너비는 156px (200px - 40px - 4px)이 됩니다.
*선택자를 사용하면 성능에 안좋지 않냐구요? 아닙니다.(not bad for performance)
(주 : *를 다른 것과 같이 사용하면 성능에 문제가 생김. 선택자는 바텀 노드부터 루트로 순회하며 적용하기 때문.
어차피 각 노드에는 기본적으로 스타일을 적용해야 하기 때문에, 선택자 규칙이 얼마나 복잡한지가 가장 성능에 큰 영향을 미친다)
2. 기본 마진 제거
* {
margin: 0;
}
브라우저는 여백에 대한 상식적인 가정을 합니다. 예를 들어 h1은 기본적으로 단락(p)보다 더 많은 여백을 포함합니다.
이러한 가정은 워드 프로세서 프로그램의 맥락에서는 합리적이지만 최신 웹 응용 프로그램에는 정확하지 않을 수 있습니다.
또한 마진은 매우 트리키(tricky devil)합니다. 저는 일단 제거하고 시작하겠습니다.
3. 퍼센트 기반 높이
html, body, #__next {
height: 100%;
}
- 요소의 너비는 부모를 기준으로 계산됩니다.
- 요소의 높이는 자식을 기준으로 계산됩니다.
따라서 아래와 같이 CSS를 적용하면, 우리가 원하는대로 동작합니다.
vh를 사용하지 않는 이유는 해당 링크를 참조하세요.
4. 줄 높이 조작하기
body {
line-height: 1.5;
}
line-height는 단락의 각 텍스트 줄 사이의 세로 간격을 제어합니다.
기본값은 브라우저마다 다르지만 1.2 정도 되는 경향이 있습니다.
이 단위 없는 숫자는 글꼴 크기에 따른 비율입니다. em 단위와 같은 기능을 합니다.
즉, ine-height가 1.2이면 각 줄 높이는 요소의 font-size보다 20% 더 커집니다.
사실 이 기본값은 문제가 있습니다.
난독증 환자를 위한 권장값은 1.5 이상 입니다.(line-height should be at least 1.5)
하지만 그렇다고 모든 요소에 해당 값을 적용하면, 폰트가 큰 heading같은 경우 큰 문제가 생깁니다.
제가 이해한 권장은 body에 대한 것입니다.
따라서 아래와 같은 스니펫을 사용할 수 있습니다.
보통 1rem = 16px이므로 1폰트사이즈 + 8px의 줄 높이를 설정합니다.
font-size가 커질 수록 간격이 타이트해지게 됩니다.
* {
line-height: calc(1em + 0.5rem);
}
위 스니펫을 사용할 시 주의점이 있습니다.
em 단위는 잘 상속되지 않으므로 모든 요소(*)를 셀렉터로 적용해야 합니다.
이는 em을 모든 요소에 대해 계산하지 않는다는 것을 의미합니다.
이와 유사한 테크닉들을 Using calc to figure out optimal line-height 에서 찾아볼 수 있습니다.
5. 폰트 부드럽게 하기(Font smoothing)
body {
-webkit-font-smoothing: antialiased;
}
이 설정은 논란의 여지가 있을 수 있습니다.
MacOS 컴퓨터에서 브라우저는 기본적으로 "하위 픽셀 안티앨리어싱(subpixel antialiasing)"을 사용합니다.
이는 각 픽셀 내의 R/G/B 값을 활용하여 텍스트를 더 쉽게 읽을 수 있도록 하는 기술입니다.
과거에는 텍스트 대비를 개선했기 때문에 이는 접근성 향상으로 여겨졌습니다.
"안티 앨리어스"로 전환하는 것을 옹호하는 유명한 블로그 게시물인 Stop “Fixing” Font Smoothing을 읽어보셨을 것입니다.
문제가 있습니다. 이 기사는 높은 DPI를 보유한 "레티나" 디스플레이 시대 이전인 2012년에 작성되었습니다.
오늘날의 픽셀은 훨씬 작아 육안으로는 보이지 않습니다.
2018년에 출시된 MacOS Mojave에서 Apple은 운영 체제 전체에서 하위 픽셀 안티앨리어싱을 비활성화했습니다.
나는 그들이 그것이 최신 하드웨어에는 장점보다 단점이 많다는 것을 깨달았다고 생각합니다.
그런데 이상하게도, Chrome 및 Safari와 같은 MacOS 브라우저는 여전히 기본적으로 하위 픽셀 앤티앨리어싱을 사용합니다.
-webkit-font-smoothing을 antialiased로 설정하여 명시적으로 해제해야 합니다.
MacOS는 하위 픽셀 안티앨리어싱을 사용하는 유일한 운영 체제이므로
이 규칙은 Windows, Linux 또는 모바일 장치에 영향을 미치지 않습니다.
6. 합리적인 미디어 기본값
img, picture, video, canvas, svg {
display: block;
max-width: 100%;
}
여기 이상한 점이 있습니다. 이미지는 본디 "인라인" 요소로 간주됩니다.
이는 <em>이나 <strong>과 같이 단락 중간에 사용해야 함을 의미합니다.
이것은 대부분의 이미지 사용 사례와 맞지 않습니다.
일반적으로 저는 단락, 머리글 또는 사이드바를 취급하는 것과 동일한 방식으로 이미지를 취급합니다.
즉 이미지는 레이아웃 요소입니다.
그러나 레이아웃에서 인라인 요소를 사용하려고 하면 이상한 일이 발생합니다.
마진, 패딩 또는 보더가 아닌 신비한 4px 간격을 보게 됩니다.
이는 브라우저가 line-height로 추가하는 "인라인 마법 공간"입니다.
이미지에 대해 display: block을 설정하면 이 이상한 문제 전체를 피해갈 수 있습니다.
max-width: 100%는 충분히 크지 않은 컨테이너에서 이미지가 넘치는 것을 막아줍니다.
대부분의 블록 수준 요소는 부모에 맞게 자동으로 커지거나 줄어들지만 <img>와 같은 미디어 요소는 특별합니다.
이러한 미디어 요소들은 대체된 요소(replaced element)로 알려져 있으며 동일한 규칙을 따르지 않습니다.
이미지의 "원래" 크기가 800×600인 경우
<img> 요소는 너비가 500px인 상위 요소에 배치하더라도 너비가 800px가 됩니다.
max-width: 100% 규칙은 해당 이미지가 컨테이너를 넘어 커지는 것을 방지하며,
훨씬 더 합리적인 기본 동작처럼 느껴집니다.
7. form 요소 폰트 상속
input, button, textarea, select {
font: inherit;
}
또 다른 이상한 점이 있습니다.
상호작용 요소들은 부모로부터 타이포그래피 스타일을 상속받지 않습니다.
대신 자신들만의 이상한 스타일을 갖고 있습니다.
- 예를 들어 <textarea>는 시스템 기본 모노스페이스 글꼴을 사용합니다.
- Text input은 시스템 기본 sans-serif 글꼴을 사용합니다.
- 둘 다 미세한 글꼴 크기(Chrome에서는 13.333px)를 선택합니다.
모바일 장치에서 13px 텍스트를 읽는 것은 매우 어렵습니다.
작은 글꼴 크기의 입력에 초점을 맞추면 브라우저가 자동으로 확대되어 텍스트를 더 쉽게 읽을 수 있습니다만,
이는 사용자에게 좋은 경험을 제공하지 않습니다.
input, button, textarea, select {
font-size: 1rem;
}
이렇게 하면 자동 확대/축소 문제가 해결되지만 미봉책일 뿐입니다.
대신 근본 원인을 해결해 보겠습니다. 양식 입력의 고유한 타이포그래피 스타일을 없애면 됩니다.
다른 요소와 마찬가지로 부모의 스타일을 상속하도록 합니다.
input, button, textarea, select {
font: inherit;
}
8. 줄 바꿈(Word wrapping)
p, h1, h2, h3, h4, h5, h6 {
overflow-wrap: break-word;
}
CSS 텍스트는 한 줄에 모든 문자를 넣을 공간이 충분하지 않으면 자동으로 줄 바꿈됩니다.
기본적으로 알고리즘은 "soft wrap(부드러운 줄바꿈)" 기회를 찾습니다.
이들은 알고리즘이 분할할 수 있는 문자입니다.
영어에서는 공백과 하이픈만 부드럽게 줄바꿈할 수 있지만 이는 언어마다 다릅니다.
줄 바꿈 기회가 없고 컨테이너에 크기가 맞지 않으면 텍스트가 오버플로됩니다.
이로 인해 성가신 레이아웃 문제가 발생할 수 있습니다.
여기서는 가로 스크롤바를 추가합니다.
다른 상황에서는 텍스트가 다른 요소와 겹치거나 이미지/비디오 뒤로 들어갈 수도 있습니다.
overflow-wrap 속성을 사용하면 줄 바꿈 알고리즘을 조정하여
소프트 랩 기회를 찾을 수 없을 때 하드 랩을 사용할 수 있는 권한을 부여할 수 있습니다.
완벽한 솔루션이라 할 수는 없습니다만. 최소한 레이아웃을 망치는 문제는 피할 수 있습니다.
p {
overflow-wrap: break-word;
hyphens: auto;
}
모든 요소(*)에 적용할 수 있는 유사한 룰도 있습니다.(suggesting a similar rule)
(주: 내가 알기로 저 속성은 depreciated 되었음)
hyphens:auto는 하이픈(하이픈을 지원하는 언어)을 사용하여 하드 랩을 나타냅니다.
또한 하드 랩을 훨씬 더 일반적으로 만듭니다.
hyphens:auto 설정은 산만할 수 있으므로 상황에 따라 적용 여부를 결정합니다.
9. 루트 쌓임 맥락(Root stacking context)
#root, #__next {
isolation: isolate;
}
이 마지막 항목은 선택 사항입니다. 일반적으로 React와 같은 JS 프레임워크를 사용하는 경우에만 필요합니다.
“What The Heck, z-index??”에서 본 것처럼 isolation 속성을 사용하면
z-index를 설정할 필요 없이 새로운 쌓임 컨텍스트를 만들 수 있습니다.
이 속성은 우선 순위가 높은 특정 요소(modal, dropwons, tooltips)가
항상 애플리케이션의 다른 요소 위에 표시되도록 보장할 수 있으므로 유용합니다.
이상한 스태킹 컨텍스트 버그, z-index 경쟁이 없습니다.
주의할 점은 프레임워크와 일치하도록 셀렉터를 조정해야 한다는 것입니다.
애플리케이션이 렌더링되는 최상위 요소를 선택해야 합니다.
예를 들어 create-react-app은 <div id="root">를 사용하므로 올바른 선택자는 #root입니다.
참고
'FrontEnd' 카테고리의 다른 글
[React] 리액트 Children, React Children API의 모든 것 (0) | 2023.02.15 |
---|---|
Stitches와 Radix를 이용해 디자인 시스템 만들기 (0) | 2023.02.14 |
가장 일반적이며 기초적인 CSS 문제와 해결 방법 [번역] (0) | 2023.02.13 |
자바스크립트의 프로토타입과 문맥, this (0) | 2023.02.11 |
[번역] 3분 동안 CSS 타이포그래피(폰트) 기본 속성 정리하기 (0) | 2023.02.09 |