CSS 선택자(CSS Selector)는 스타일을 지정하고자 하는 HTML DOM 요소를 선택할 때 사용되는 CSS 규칙
h1 { }
div { }
* { }
section, h1 { }
#only { }
.widget { }
.center { }
a[href] { }
p[id="only"] { }
p[class~="out"] { }
p[class|="out"] { }
section[id^="sect"] { }
div[class$="2"] { }
div[class*="w"] { }
header h1 {}
header > p { }
section + p { }
section ~ p { }
a:link { }
a:visited { }
a:hover { }
a:active { }
a:focus { }
input:checked + span { }
input:enabled + span { }
input:disabled + span { }
p:first-child { }
ul > li:last-child { }
ul > li:nth-child(2n) { }
section > p:nth-child(2n+1) { }
ul > li:first-child { }
li:last-child { }
div > div:nth-child(4) { }
div:nth-last-child(2) { }
section > p:nth-last-child(2n + 1) { }
p:first-of-type { }
div:last-of-type { }
ul:nth-of-type(2) { }
p:nth-last-of-type(1) { }
input:not([type="password"]) { }
div:not(:nth-of-type(2)) { }
input[type="text"]:valid { }
input[type="text"]:invalid { }
HTML 문서는 기본적인 스타일을 가지고 있다.
하지만 각 브라우저마다 여백 및 글꼴 스타일이 조금씩 다르고 body 태그 기본 스타일에
약간의 여백이 있다.
그래서 아래의 몇 줄을 추가해서 레이아웃을 리셋 시켜주어야 한다.
* {
box-sizing: border-box;
}
body {
margin: 0;
padding: 0;
}
VSCode와 비슷한 레이아웃을 만들고 싶다고 가정할 때,
실제 VS Code의 레이아웃을 분석해 큰 틀에서 추상화하면, 다음과 같이 영역을 나눌 수 있다.
CSS로 화면을 구분할 때에는 수직분할과 수평분할을 차례로 적용한다.
HTML 문서를 통해 레이아웃을 작성하면, 다음과 같이 구성할 수 있다.
<div id="container">
<div class="col w10">
<div class="icon">아이콘 1</div>
<div class="icon">아이콘 2</div>
<div class="icon">아이콘 3</div>
</div>
<div class="col w20">
<div class="row h40">영역1</div>
<div class="row h40">영역2</div>
<div class="row h20">영역3</div>
</div>
<div class="col w70">
<div class="row h80">영역4</div>
<div class="row h20">영역5</div>
</div>
</div>
.w70 { width: 70%; }
.h40 { height: 40%; }
이렇게 클래스 이름과 구현을 1:1로 일치시켜 아주 작은 단위로 CSS를 작성 기법을 Atomic CSS 방법론이라고 한다.
클래스 이름을 선언하는 방법에 대한 이야기는 아래 링크에서 확인할 수 있다.
CSS Architcture: Atomic CSS
flex(flexible)는 "잘 구부러지는, 유연한"이라는 뜻이다. flexbox로 레이아웃을 구성한다는 것은, 박스를 유연하게 늘리거나 줄여 레이아웃을 잡는 방법이다.
부모 박스요소에 display: flex를 적용해, 자식 박스의 방향과 크기를 결정하는 레이아웃 구성방법이다.
기본값으로, display: flex가 적용된 부모 박스의 자식 요소는 왼쪽부터 차례대로 이어 배치된다.
<div id="outer">
<div class="box">box1</div>
<div class="box">box2</div>
<div class="box">box3</div>
</div>
#outer {
display: flex;
border: 1px dotted red;
padding: 10px;
}
.box {
border: 1px solid green;
padding: 10px;
}
flexbox는 박스가 수직으로 분할되는 것이 기본값이다. 그러나 방향을 설정해주면, 수평으로도 분할할 수 있다. flex-direction 속성은 부모 박스요소에 적용한다. 자식 박스에 특별한 속성을 주지 않아도, 부모 요소에 의해 자식 요소가 영향을 받는다. 주요 속성은 다음과 같다.
※주의※
display 속성에 flex를 적용하는 것은 부모 요소에 적용한다. (display: flex)
자식 요소는 flex라는 속성에 값을 적용한다. (flex: 0 1 auto)
자식 박스에 어떠한 속성도 주지 않으면, 왼쪽에서부터 오른쪽으로 콘텐츠의 크기만큼 배치된다. 자식 요소의 flex 속성을 추가하지 않으면, 다음과 같은 기본값이 적용된다.
flex: 0 1 auto;
flex 속성에 적용되는 세 가지 영역은, margin이나 padding에서 띄어쓰기를 기준으로 의미하는 바가 구분되는 것과 동일하다. 그러나, flex 속성에 적용되는 세 가지는 기본 크기를 바탕으로 필요에 따라 늘리거나 줄일 수 있는 값이 적용된다. 각각의 값이 의미하는 것은 다음과 같다.
flex: <grow> <shrink> <basis>
margin이나 padding에서 상하좌우 각 방향을 따로 지정할 수 있었던 것처럼, flex에 적용되는 grow, shrink, basis도 각 값을 따로 지정할 수 있다.
flex-grow: 0;
flex-shrink: 1;
flex-basis: auto;
grow, shrink 속성은 단위가 없고, 비율에 따라 결과가 달라진다. 부모 박스 안에 n개의 자식 박스가 있다고 가정해보자. 각 자식 박스가 갖는 grow값의 총 합이 n이라면, grow 속성의 값을 1로 설정하는 것은 1/n 가로 또는 세로길이를 적용한다는 의미다. grow 속성의 값을 2로 지정하면, 2/n의 길이를 의미한다. HTML 파일을 다음과 같이 변경하고, 첫 번째 자식 박스에 target 클래스를 추가한 다음, 여러가지 방식으로 접근해보자.
<div id="outer">
<div class="box target">.box.target</div>
<div class="box">.box</div>
<div class="box">.box</div>
</div>
target 클래스에서, flex-grow 속성의 값을 1로 변경한다. target 클래스를 가지고 있는 첫 번째 자식박스는, 부모 박스의 가로 길이 중에서 남은 빈 영역만큼 늘어난다. 위 설명대로라면 전체 자식요소가 가진 grow 값의 합은 1+0+0 = 1이므로, target 클래스를 가지고 있는 자식 박스의 가로 크기는 1/1 = 100% 다. 그러나 다른 자식 박스 안에 이미 콘텐츠가 존재하므로, 다른 자식 박스안의 콘텐츠가 담긴 길이를 제외한 나머지 가로 길이가 target 클래스를 가진 자식박스의 가로 길이다.
.target {
flex: 1 1 auto;
}
box 클래스의 flex-grow는 기본값인 0이다. 만약 box 클래스의 flex-grow 속성에 값을 1로 설정하면, 모든 박스가 늘어나려고(grow) 한다. 결과적으로 모든 박스가 동일한 비율로 가로 길이가 늘어난다. (총 grow 값 1+1+1, 각 박스는 1/3씩 크기를 가짐)
.box {
flex: 1 1 auto;
}
flex-grow 속성에 적용하는 값은 비율을 의미한다. 모든 자식 박스의 flex-grow 속성이 0보다 큰 값을 동일하게 가지는 경우, 각 박스의 가로 길이는 동일한 비율을 가진다. 다음 코드는 전부 같은 모양으로 렌더링된다.
.box {
flex: 1 1 auto;
/*
flex: 2 1 auto;
flex: 3 1 auto;
flex: 4 1 auto;
*/
}
.target {
/* flex 값을 지정하지 않음 */
}
box 클래스의 flex-grow 속성에는 1을 그대로 두고, target 클래스의 flex-grow 속성의 값을 변경해보자. target 클래스를 가지고 있는 자식 박스는, 다른 자식 박스와의 비율만큼 크기가 더 커진다.
.box {
flex: 1 1 auto;
}
/*
* 자식 박스가 총 세개인데, target만 2의 비율을 가집니다.
* 2(target) + 1(box2) + 1(box3) = 4 이므로,
* target의 비율은 50% 입니다.
*/
.target {
flex: 2 1 auto;
}
target 클래스에 적용된 flex-grow 속성의 값이 1, box 클래스에 적용된 flex-grow 속성의 값이이 1일 경우, grow가 적용된 전체 값은 1+1+1 = 3이다. 따라서, 각 자식 박스의 가로 길이는 1/3의 길이를 가진다.
target 클래스의 flex-grow 속성의 값이 2, box 클래스의 flex-grow 속성의 값이 1일 경우, grow가 적용된 전체 값은 2+1+1 = 4이다. 따라서, target 클래스를 가지지 못한 나머지 자식 박스는 1/4의 길이를 가진다.
shrink는 grow와 반대로, 설정한 비율만큼 박스 크기가 작아진다. 그러나 flex-grow 속성과 flex-shrink 속성을 함께 사용하는 일은 추천하지 않는다. 비율로 레이아웃을 지정할 경우 flex-grow 속성 또는 flex: [grow] 1 auto와 같이 grow 속성에 변화를 주는 방식을 권장한다. flex-shrink 속성은 width나 이후 설명할 flex-basis 속성에 따른 비율이므로 실제 크기를 예측하기가 어렵기 때문이다. flex-grow 속성으로 비율을 변경하는 경우, flex-shrink 속성은 기본값인 1로 두어도 무방하다.
자식 박스가 flex-grow나 flex-shrink에 의해 늘어나거나 줄어들기 전에 가지는 기본 크기이다. flex-grow가 0일 때, basis 크기를 지정하면 그 크기는 유지된다.
<div id="outer">
<div class="left">메뉴</div>
<div class="right">본문</div>
</div>
/* grow를 0으로 설정해줘야 100px 이상으로 늘어나지 않습니다. */
.left {
flex: 0 1 100px;
}
/* 우측 박스는 grow를 1로 설정해 나머지 공간을 채워줍시다 */
.right {
flex: 1 1 auto;
}
flex-grow 속성의 값이 0인 경우에만 flex-basis 속성의 값이 유지된다. diplay 속성에 flex가 적용된 컨테이너 내부에 존재하는 자식 박스는 경우에 따라, basis로 설정된 크기가 항상 유지되는 것은 아니다. flex-grow 속성의 값이 1 이상인 경우, flex-basis 속성에 적용한 값보다 커질 수도 있다. 실제 레이아웃을 구현하면서 막히는 경우에는, 다음의 원리를 참고할 수 있다.
Flexbox를 원하는 대로 제어하기 위해서는 axis(축)의 개념에 대한 이해가 필요하다.
axis는 main axis 와 cross axis로 구분한다.
main axis은 flex-direction 속성에 의해서 결정된다.
flex-direction의 기본 값인 row 인 상태일 때 main axis 는 가로축이 된다.
cross axis는 여러 개의 main axis와 수직을 이루는 방향이다. main axis가 가로일 때 cross axis는 세로가 된다.
이 axis들을 기준으로 정렬할 수 있는 속성들에 justify-content 와 align-items 가 있다.
justify-content 속성은 main axis를 기준으로 정렬하며, align-items 속성은 cross axis를 기준으로 정렬한다.
flex-direction 속성의 값을 column 으로 준다면 main axis과 cross axis는 서로 바뀐다.
이어지는 내용은 flex-direction 이 row 인 상태, 즉 main axis가 가로인 상태를 기준으로 설명하는 내용이다.
부모 박스에 justify-content 속성을 적용하면, 자식 박스를 수평으로 정렬할 수 있다. 다음은 justify-content 속성의 값이 될 수 있는 주요 옵션이다.
부모 박스에 align-items 속성을 적용하면, 자식 박스를 수직으로 정렬할 수 있다. 다음은 align-items 속성의 값이 될 수 있는 주요 옵션이다.