CSS(SCSS) 기초 정리

Ryan Cho·2025년 9월 5일
post-thumbnail

회사에 퍼블리셔가 따로 있다. FE개발자인데 퍼블리싱 썩을까봐 위기감 느껴서 정리한다.

reset.scss (Foundation)

/* universal selector -> 모든 요소에 대해 적용. 하지만 가상요소는 포함안되서 따로 해줘야함 */
*,
*::before,
*::after {
  margin: 0;
  padding: 0;
  box-sizing: inherit;
  
  ...
}

box-sizing을 왜 border-box라고 하지 않고 inherit을 한 이유는 다음과 같다.

...
body {
   box-sizing: border-box;
}

inherit 으로 작성해, 만약 특정 클래스의 box-sizingborder-box가 아니더라도, 해당 클래스의 자식요소는 inherit된 box-sizing을 상속받기에 좀 더 유연하다.

참고) box-sizing: border-box의 개념

box-sizing: border-box는 width,height의 결정방식을 padding, border를 포함하여 계산됨. 의도한 레이아웃의 크기를 정확히 구현할 수 있음. 필수 설정

CSS단위 px, %, em, rem, vw, vh

(여기서 말하는 lengths는 padding, margin에 해당함)

  • px: 절대 크기
  • %: font의 경우 parent기준 계산 / lengths의 경우 "parent's width"기준으로 계산
  • em: font의 경우 parent기준 계산 / lengths의 경우 "현재요소"를 기준으로 계산
  • rem: root의 font-size 기준으로 계산된 비율 (font, lengths 동일)
  • vw: 뷰포트 기준 width
  • vh: 뷰포트 기준 height

rem 단위를 사용하기 편한 세팅?

rem단위는 root의 font-size 기준으로 계산된 비율이라고 했다.
대체로 브라우저의 font-size는 16px인데, 1rem = 16px이면 계산하기 얼마나 귀찮은가, 그래서 계산하기 쉽게
font-size를 전역으로 10px로 고정하고 싶지만, px은 고정된 단위이고 사용자의 기본 폰트 조절 등에 영향을 받지 않는다.

그래서 브라우저의 font-size: 16px을 이용한다.
계산상 10px은 브라우저의 font-size의 62.5%에 해당한다. 이를 reset.scss에 적용한다.
이러면 이제 rem 단위를 사용하면서 px을 rem으로 변환할때 * 0.1 만 하면 되니 편해지고 유연하게 대응이 가능하다.
(위는 구시대적 방식임. 현대 css의 안티패턴)

rem 단위의 선택적, 현실적 사용

html {
	font-size: 설정안함 (브라우저 기본값 사용)
}

// SCSS 함수로 계산 자동화
@function rem($px) {
  @return #{$px / 16}rem;
}

Typography & Content Spacing, Component Spacing에만 rem 사용

외의 고정된 elements 레이아웃, 아이콘, 보더 등은 px쓰는게 적절하다

.text {
  font-size: rem(18);   
  line-height: rem(28); 
  margin: rem(24) 0;   
  padding: rem(16);  
}

.card {
  padding: rem(20);
  margin-bottom: rem(32);
  border-radius: rem(8);
}

Display 속성 block, inline, inline-block

1) display: block
- 한 줄을 모두 차지함
- width, height 설정 가능
- margin, padding 모든 방향 적용 가능
- <div> <p> <h1> <header>

2) display: inline
- 줄바꿈 없이 옆으로 나란히 배치
- width, height 설정 불가
- margin, padding은 좌우만 가능
- <span> <a> <em> <strong>

3) display: inline-block
- 옆으로 나란히 배치 (inline 속성)
- width, height 설정 가능 (block 속성)
- margin, padding 모든 방향 적용 가능 (block 속성)
- <button> <input> <select>

참고, inline -> inline-block 언제?

예를들어 <a> 태그를 사용하고, 이는 기본적으로 display: inline 속성을 가진다.
따라서 width, height 속성이 무시되고, padding/margin의 상하 간격이 무시됨.
이런 문제를 해결하고 display: block특성도 가지게 하기 위해 display: inline-block사용.

display: inline 속성은 쉽게 말해 text의 속성을 따른다고 보면 된다.
즉, 부모의 text-align: center 하나로 a태그자체를 가운데 정렬이 가능하다.

(button태그였다면 기본적으로 inline-block임)

CSS Cascade

동일한 selector에 여러 스타일을 적용할경우, CSS는 Cascade라는 스타일 우선순위 적용 결정 알고리즘을 가진다.

우선순위에 결정에 대해

  • !important (가장 높음)
  • id의 갯수, class의 갯수, element의 갯수의 총합으로 계산
  • Pseudo class도 class로 계산됨.
    따라서 Pseudo class를 쓸거면 적용할 부분에 동일한 우선순위로 설정후 Pseudo를 사용해야함.
    (예 ::hover가 우선순위에 밀리면 적용되지 않음)

SASS(SCSS)

&(앰퍼샌드)

&(앰퍼샌드) 는 sass문법으로 scss에서 제일 많이 쓰는거 같다.

&는 (직전) 부모 선택자를 참조한다.

이 설명이 끝이고, 이로 인한 응용 가능성이 매우 높다

기본

.parent {
  color: blue;
  &:hover {  // css 컴파일 시 .parent:hover{}
    color: red;
  }
}

클래스 조합

.btn {
  padding: 10px;
  
  &.active {      // .btn.active 일때 적용
    background: red;
  }
  
  &.disabled {    // .btn.disabled 일때 적용
    opacity: 0.5;
  }
}

BEM 방식

.block {
  display: block;
  
  &__element {           // .block__element 컴파일
    color: blue;
    
    &--modifier {        // .block__element--modifier 컴파일
      font-weight: bold;
    }
  }
  
  &--theme-dark {        // .block--theme-dark 컴파일
    background: black;
  }
}

Attribute select

.input {
  border: 1px solid gray;
  &[disabled] { opacity: 0.5; }
}

선택자 조합

.item {
	margin-top: 0;
    
	& + & { // 두번째 .item 부터 적용
    	margin-top: 16px;
    }
}

& 중첩 주의사항

중첩된 부모-자식 클래스에서 &를 쓸때 주의해야함.
예를들어, 카드컴포넌트에 hover시 rotate되며 카드 뒷면을 보여주고싶은데,
앞면 뒷면 클래스를 .card__side--front .card__side--back 으로 나눴다고 가정.

.card{
	perspective: 170rem;
    ...
    
    &:hover {
		&__side--front { // 이딴식으로 쓰면 망한다.
        	transform: rotateY(180deg);
        }
    }
}

위 코드의 &:hover블록의 컴파일 결과는 다음과 같다.

.card:hover hover__side--front{}

참으로 개떡같지 않은가. &는 직전 부모를 참조한다는 사실을 잊지마라.
그럼 해당 요구사항은 다음과 같이 수정 가능하다.

.card{
  // "perspective" 란 3D 변환 요소들이 화면에서 어떻게 보일지 결정. 뷰어와 3D 객체 사이의 거리를 나타냄.
  // 너무 가까우면 왜곡이 심하고, 너무 큰값이면 평면적인(2D) 효과로 보임, 'none' 이면 2D효과로 보임.
	perspective: 140rem;
    ...
    
    &:hover &__side--front {
        	transform: rotateY(180deg);
    }
}

비슷한 예시로, hover시에 hover되지 않는 요소들에 대한 스타일정의는 다음과 같이 적용 가능하다.

.부모{
	&:hover &__photo:not(:hover) {
    	transform: scale(.95);
  }
}

참고) 형제 요소 선택자

위의 예시들은 전부 부모-자식 클래스 관계에서의 스타일 적용이다.

.부모 {
	&:hover .자식 {}
}

위 구조는 다음과 같으며

.부모 {
	&:hover{
      .자식 {}
    }
}

부모-자식 간 클래스 일때만 적용된다.
인라인으로 &:hover공백.자식{}
여기서 공백으로서 자식 선택자가 적용된다.

만약 형제 요소끼리 클래스를 적용하고싶다면?

  1. 직전 형제 요소 선택자 + 사용
  2. 모든 형제 요소 선택자 ~ 사용
<form class="form">
<input class="input"/>
<label class="label"></label>
</form>

이 구조에서 input - label 형제 요소들의 스타일적용의 예시.

.form {  
	.input {}    
    .label {}    
    
    .input:focus + .label { // 혹은 ~
    	label에 스타일
    }
}

위 html구조처럼 label이 input 바로 다음에 놓여있기때문에 +를 사용 가능.

Pseudo Class, Pseudo Element

Pseudo Class

너무 많다... :hover :active :first-child :last-child 등등,, 알아서 잘 써라

:not()

pseudo class를 부정하기 위한 pseudo class

.list {
	&:not(:last-child){
      // ex) 마지막요소 빼고 아래 적용
		margin-bottom: 20px;
	}
}

Pseudo Element

::after 가상요소

::after::before의 차이는 삽입되는 위치의 차이.
가장 많이 사용되는 용도는 리스트의 불릿아이콘, 화살표 아이콘 등
장식요소에 불과하기때문에 굳이 html에 포함시키지 않아서 사용함.

.btn {
position: relative;
...
	&::after {
   		content: "";
        position: absolute;
        ...
    }
}

content 요소는 필수

position: absolute

부모가 position: relativeposition: absolute가 적용되는데,
위 코드의 .btn 이 부모인가?
아니다. 즉, Pseudo Class는 자식요소처럼 작동함

추가) z-index

z-index 속성은 항상 position 요소가 존재할때만 작동한다

Media Query (mobile-first, desktop-first)

반응형 UI를 구성할때 Media Query설정은 필수이고, 대부분의 경우 Mobile-First Design을 적용한다.
다만 일부의 경우 Desktop-First Design도 적용하긴 하기에 둘다 정리한다.

SCSS로 @mixin을 통해 구성한다.

Mobile-First

이 경우 defualt style이 mobile뷰에 해당하고, 미디어쿼리를 적용할 때는 tablet > desktop 순으로 작성해야한다.

@mixin respond($breakpoint) {
  @if ($breakpoint == tablet) {
    @media (min-width: 768px) {
      @content;
    }
  }

  @if ($breakpoint == desktop) {
    @media (min-width: 1024px) {
      @content;
    }
  }
}

Desktop-First

사실 거의 안쓴다, TailwindCSS, MaterialUI 등 프레임워크도 mobile-first만을 지원하지만 특수한 경우를 위해서라면,, defualt style 이 desktop뷰에 해당하고, 큰 화면 순으로 미디어쿼리를 적용한다.

@mixin respond($breakpoint) {
  @if ($breakpoint == small-desktop) {
    @media (max-width: 1200px) { @content; }
  }
  @if ($breakpoint == tablet) {
    @media (max-width: 900px) { @content; }
  }
  @if ($breakpoint == phone) {
    @media (max-width: 600px) { @content; }
  }
}

Flex Box

현대 css에서 가장 많이 쓰일 속성인거같다. display: flex 를 사용하면 쉽게 반응형 레이아웃을 구성 가능하다.
** display: inline-flex 도 있지만 쓸 이유가 없는 쓰레기속성이다.

Flex Box - Container

display: flex 를 적용하는, 즉 컨테이너 클래스에 사용가능한 속성들을 정리해본다.
우선 flexbox는 항상 X-axis와 Y-axis를 기준으로 main-axis와 cross-axis가 정해져서 사용된다.

일단 저런방향으로 axis가 존재한다는걸 인지하고 접근해야한다.

flex-direction: main-axis와 cross-axis를 결정짓는 속성

  • row (default)
  • row-reverse
  • column
  • column-reverse

row 계열일때 main-axis는 X축, column 계열일때 main-axis는 Y축이 된다.
아래 그림으로 이해가 빠를것이다.

justify-content: main-axis의 positioning

  • flex-start (default)
  • flex-end: 방향만 반대
  • center: main-axis 중앙정렬
  • space-between: 아이템별 간격을 동일하게 모든 main-axis 범위 내에 할당
  • space-around: main-axis의 양쪽 끝에는 x만큼의 간격을, 아이템들 사이에는 2x만큼의 간격을
  • space-evenly: main-axis의 양쪽 끝의 간격을 포함해 모든 아이템의 간격이 동일

align-items: 아이템들의 cross-axis의 positioning

만약 아이템들의 높이가 제각각 이라고 가정하면 이해가 쉬울것이다.

  • stretch (default) : 모든 아이템의 높이를 동일하게하도록 늘림
  • center: cross-axis 중앙정렬
  • flex-start: cross-axis를 아이템의 시작 지점에 할당함
  • flex-end: cross-axis를 아이템의 마지막 지점에 할당함
  • baseline: cross-axis를 font를 기준으로 정렬 (font기준 center)

flex-wrap

  • no-wrap (default)
  • wrap: flex-container가 더이상 여유가 없을때 새로운 라인(flex 라인)을 만들어서 overflow가 일어나지 않게 만든다.

align-content: 여러 flex라인 자체의 cross-axis의 positioning

개별 아이템들에 대한 속성이 align-items였다면, 이는 한단계 높은 범위, 즉 flex 라인 자체의 위치를 결정한다.
이 속성은 flex-wrap: wrap 일때에만 의미가 있다.

  • stretch (default)
  • center
  • flex-start
  • flex-end
  • baseline

Flex Box - Items

컨테이너 안의 개별 아이템들에 대한 속성이다.

align-self: align-items속성을 개별 아이템에게 override

-> 최상단 부모에 align-items: center가 적용되어있지만 일부 자식 요소는 해당 컨테이너를 전부 채워야 할 경우, 해당 자식 요소에만 align-self: stretch를 적용할 수 있다.
이는 부모 자식간의 중첩flex일지라도 적용이 가능하다.

**align-self를 적용한 자식클래스에 display:flex, align-items등 적용한 상태여도 무방

order: 낮은 순으로 main-axis에 배치될 순서가 결정됨 (default == 0)

flex-grow: main-axis에 배치된 간격 사이를 차지할지 결정

  • 0 (default) : 기본적으로 아이템은 고유한 크기를 유지하며, 공간이 남아도 늘어나지 않음.
  • 1 이상: 아이템을 flexible하게 변화. 남은 공간을 차지하며 늘어나며 비율에 따라 공간 분배.

flex-basis: width를 결정

flex-shrink: 얼마나 줄어들지 결정 (default == 1)

0으로 설정할 경우 줄어들지 않고 심지어 flex-basis로 width를 고정했다면 해당 값이 뷰포트가 줄어들어도 고정됨.
즉, 새로운 flex 라인 자체를 생성하지 않고 오버플로우를 발생시킴.

flex: flex-grow flex-shrink flex-basis

위 세가지 옵션을 한번에 쓰기위한 값. ex) flex: 0 1 200px;

flex: 1의 의미 -> 1 1 0(%)

자주 사용되는 flex: 1 은 아이템들이 남은 공간을 동등하게 나누어서 최대한 차지하려고 만들게된다.
** 아이템들에게 공통적으로 적용해야 의미가 있다

.sidebar-layout {display: flex;}
.sidebar { flex: 0 0 250px; }  // 고정 사이드바
.content { flex: 1; }          // 나머지 공간

flex: 1 vs margin: auto

예를들어 row방향의 flex박스에 정렬이 필요할때,
justify-content: space-between 처럼 균등 분배하는게 아닌, 일부 아이템의 위치만 조정하고싶을때, 원하는 요소에 flex: 1을 사용해도 되고, margin-left: auto 혹은 margin-right: auto를 사용할 수 있다.
차이점으로는, flex: 1는 크기 조절에 초점을 맞춘것이고, margin-left: auto는 위치 조절에 초점을 맞춘 것이다. 요소에 배경색을 입힐경우 차이를 볼 수 있다.

Grid

display: grid 로 사용가능하다. flex-box가 1차원공간을 다뤘다면, 이는 2차원 공간을 다룬다.
복잡한 레이아웃 구성에 적합하다.

Grid - Container

grid-template-rows

grid template의 row설정을 하며, row별 높이를 지정한다.
위 이미지의 예시로는 다음과 같이 표현이 가능하다.
grid-template-rows: 150px 150px;
grid-template-rows: repeat(2, 150px);

grid-template-columns

grid template의 column설정을 하며, column별 너비를 지정한다.
위 이미지의 예시로는 다음과 같이 표현이 가능하다.
grid-template-columns: 150px 150px 150px;
grid-template-rows: repeat(3, 150px);

template설정의 추가 개념

fr -> 최대한 남는 공간을 차지

grid-template-columns: repeat(3, 1fr) 이라고 할 경우, 세 컬럼이 모두 동등하게 공간을 차지한다. 혹은 1fr 3fr 1fr 이런식으로 사용한다면 비율에 맞게 공간을 분배한다.

auto-fill, auto-fit, min-content, max-content, minmax()

  • auto-fill
    grid-template-columns: repeat(auto-fill, 100px) 와 같이 사용한다.
    이 경우엔 전체 width에 맞게 100px씩 분배하여 해당 갯수만큼 cell을 생성한다.
  • auto-fit
    grid-template-columns: repeat(auto-fit, 100px) 와 같이 사용한다.
    이 경우에는 실제 아이템의 갯수만큼 cell을 생성한다.
  • min-content
    아이템의 최소 크기만큼을 차지한다.
  • max-content
    아이템의 최대 크기만큼을 차지한다.
  • minmax()
    minmax(최소값, 최대값) 처럼 사용하며, 매우 유연하고 실무에서는 이를 주로 쓴다

gap, grid-row-gap / grid-column-gap

row, column의 셀 사이 gap을 지정하며, 같을 경우 gap만으로 정의한다.

grid-auto-flow: column/row;

정의한 그리드 셀의 갯수보다 아이템이 많을때 어떤방향으로 넘칠지 결정 가능하다.
** dense를 추가하면 빈 셀 없이 모든 아이템들이 셀을 채움. (full packed grid)
ex) grid-auto-flow: row dense;

grid-auto-rows

넘친 아이템의 높이를 결정

grid-auto-columns

넘친 아이템의 너비를 결정

align-items

셀을 차지하는 아이템들의 positioning (stretch / center/ start/ end)

justify-items

vertically,horizontally positioning

justify-content

flex-box와 동일

align-content

flex-box와 동일

실무) 만약 grid-template에 몇개의 아이템이 존재할 지 모르며, 반응형레이아웃이 필요할 경우?

auto-fit과, minmax()를 활용해서 반응형 그리드를 만들 수 있다.
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)) 처럼 선언할 경우,
브라우저의 사이즈에 따라 하나의 셀은 최소단위 200px을 지키기 위해 width가 부족할 경우, 새로운 row로
아이템을 이동시킴.
** 이 경우 grid-auto-rows 사용

Grid - Item

grid-row-start / grid-row-end / grid-column-start / grid-column-end / grid-area

각 아이템을 cell이라고 부르는데, display: grid의 cell의 위치를 다른 cell위치에 명시적으로 옮기고 싶을 경우, row가 시작하는 지점, 끝나는 지점, column의 시작 좌표, 끝나는 좌표 정보로 이동시킬 수 있다.

위 이미지를 참고하여 아래처럼 표현이 가능하다.

grid-row: start point / end point;
grid-column: start point / end point;

혹은

grid-area: row-start / column-start / row-end / column-end;

추가) span, -1

grid-row 혹은, grid-column의 좌표를 표현할때 다음과 같이 표현도 가능하다.

  • span: 좌표 대신 셀의 갯수로 표현 가능
    ex) grid-row: 3 / span 5; => 3번 row부터 cell 5개 기준으로 row를 차지함.

  • -1: 차지할 수 있는 모든 공간을 차지
    ex) grid-column: 1 / -1;

profile
From frontend to fullstack

0개의 댓글