[css] grid 파헤치기2 (실습하기 : mobile first)

KoEunseo·2023년 9월 13일
0

파헤쳐보자

목록 보기
30/31

mobile first에 대해서 언뜻 주워듣고 뭔가 느낌적으로 접근을 했었는데, (대충 모바일 우선으로 개발하는 것이려니) 다 의미가 있고 역사가 있어서 생겨난 말이었다.

모바일 기기는 PC보다 고려해야 할 것들이 많다. 요새는 뭐 워낙 발전해서 딱 맞는 말은 아니지만, 어쨌든 기기가 클수록 사용할 수 있는 자원이 많기 때문에 성능이 좋아질 수밖에 없다.(슈퍼컴퓨터 규모를 보면 정말 엄청나다. 괴물같이 보일 지경;) 기기가 작을수록 성능이 떨어진다는 이야기.

모바일 기기에서 너무 많은 연산(or통신)이 돌아가면 발열이 생길 수밖에 없다. 해서 모바일 우선으로 작업을 하자는 것이다.

필자는 디자이너가 없이 작업을 해왔기 때문에 모바일을 먼저 하기보다 pc에서 모바일로 넘어가면서 css 작업을 하는 편이었다.. 그래서 이 얘기를 듣고 충격적일 수밖에 없었다. css 작업도 리페인팅을 하면서 연산이 들어갈 수밖에 없어서(transform, animation 등은 특히나) 페이지 랜딩 속도를 저해시킨다는 건 알고는 있었지만 PC 중심으로 작업하고 사고했기 때문에 별생각이 없었는데,,

여튼 그래서 이전에 과제를 하면서는 PC 먼저 작업했지만 이번에는 모바일을 먼저 해보기로 했다.

화면은 대략 다음과 같다.

블록별로 나누었을때

header - logo, hamburger icon
main - card(hero), list, card, story, card
footer - logo, search bar, follow, gnb

이렇게 된다.
flex를 최대한 배제하고 grid만 쓰려고 해봤다.

html

<!DOCTYPE html>
  <body>
    <div class="wrapper">
      <header class="header">
        <h1 class="logo">
          <a>logo</a>
        </h1>
        <div class="hamburger">
          <img src="MenuIcon.svg" />
        </div>
      </header>
      <main class="main">
        <div class="hero card">
          <img src="imgs.jpg" />
          <div class="card_content">
            <label class="label title">category</label>
            <h2 class="title card_title">
              card title
            </h2>
            <ul class="card_icons">
              <li>
                <a><img src="Icon-1.svg" /></a>
              </li>
              <li>
                <a><img src="Icon-2.svg" /></a>
              </li>
              <li>
                <a><img src="Icon-3.svg" /></a>
              </li>
            </ul>
          </div>
        </div>
        <ol class="list">
          <h2 class="title list_title">list title</h2>
          <li>list conetnt1</li>
          <li>list conetnt2</li>
          <li>list conetnt3</li>
          <li>list conetnt4</li>
          <li>list conetnt5</li>
        </ol>
        <div class="card">
          <img src="imgs.jpg" />
        </div>
        <div class="story">
          <h2 class="title story_title">
            story title
          </h2>
          <p class="story_content">
            story content
          </p>
          <button class="button button_border">read more</button>
        </div>
        <div class="card">
          <img src="imgs.jpg" />
        </div>
      </main>
      <footer class="footer">
        <div class="footer_logo">
          <h1>
            <a>logo</a>
          </h1>
          <p>
            © 2018 Logo
          </p>
        </div>
        <div class="footer_search">
          <input type="email" placeholder="Your E-mail" />
          <button class="button">subscribe</button>
        </div>
        <div class="footer_follow">
          <h2>Follow us:</h2>
          <ul class="footer_follow_list">
            <li>
              <a><img src="instagram.svg" /></a>
            </li>
            <li>
              <a><img src="pinterest.svg" /></a>
            </li>
            <li>
              <a><img src="twitter.svg" /></a>
            </li>
            <li>
              <a><img src="facebook.svg" /></a>
            </li>
          </ul>
        </div>
        <ul class="nav footer_nav">
          <li><a>category1</a></li>
          <li><a>category2</a></li>
          <li><a>category3</a></li>
          <li><a>category4</a></li>
          <li><a>category5</a></li>
          <li><a>category6</a></li>
          <li><a>category7</a></li>
        </ul>
      </footer>
    </div>
  </body>

css

custom color

:root {
  --main-color: #ff565c;
  --dark-color: #1d1f24;
  --border-color: #f2f3f6;
}

layout

.wrapper

wrapper를 만들어서 전체 컴포넌트를 감싸 그리드를 주었다. padding이나 margin을 주기보다 gap을 주기 위함.

  • 처음에는 grid-template-rows를 70px(header) 80%(main) 20%(footer)를 주었는데, 차차 main에 컨텐츠가 차면서 필요성이 사라지게 되어 삭제했다.
.wrapper {
  display: grid;
  gap: 20px;
}

header에 그리드를 주려다가 flex를 주는것으로 바꿨다. 사실 가로 한줄 세로 한줄인데 그리드를 주는 건 손해(?) 인 것 같음. 내가 익숙지 않아서 그런걸수도 있지만... 내부에 컨텐츠도 딱 두개고 양옆 끝에 붙어있기만 하면 되니까 space-between을 주고, 세로로도 가운데 위치하게 하기 위해 align-items에 center를 주었다.
position fixed로 top, left 0 값을 준 건 헤더이기 때문. 일전에 멘토님께서 헤더에는 별말 없어도 꼭 스크롤해도 상단에 고정되도록 처리해야한다고 열변을 토하셨기 때문에 넣어주었다. position fixed를 주었을때 붕 뜨게 되는데, (자리차지를 안하게 됨) 이때 다음 컨텐츠에 헤더가 가려지기 때문에 z-index를 꼭 함께 주어야 한다. 그리고 width 100%이 없으면 가로 사이즈가 컨텐츠만큼으로 변하면서 레이아웃이 망가진다.

.header {
  width: 100%;
  height: 70px;
  border-bottom: 1px solid var(--border-color);
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0 20px;

  position: fixed;
  top: 0;
  left: 0;
  z-index: 1;
}

.main

header가 fixed되면서 자리를 차지하지 않게 되기 때문에 main과 겹치게 된다. 그래서 상단에 90px을 주었는데, 이때 header height + gap 의 결과로 90px을 준 것이다. 이 부분은 height이나 gap 값을 기억하기 힘드니 variable을 통해서 관리하는 것이 좋을 것 같다. 왜냐면 tablet에서부터는 header height이 90px이거든.
여기서도 그리드를 당연히! 주었는데, grid-template-columns: 100%을 줬다.
세로 한줄로 컨텐츠가 꽉 차 있어야 하니까.. 아 그리고 추가로 기본적인 reset을 할 때 img height과 width에 100%를 준다. 그래야 컨텐츠가 생각한만큼 공간을 꽉 차게 차지한다.(이미지는 인라인이라 100%차지하지 않고 이미지 원본의 크기만큼만 공간을 차지하기 때문)

.main {
  margin-top: 90px; /* nav bar + gap */
  padding: 0 20px;
  width: 100%;
  display: grid;
  grid-template-columns: 100%;
  gap: 20px;
}

뭐 요런 느낌?

:root {
  --mobile-header-height: 70px;
  --header-height: 90px;
  --gap: 20px;
}
img {
  width: 100%;
  height: 100%;
}
.main {
  margin-top: var(--mobile-header-heigh) + var(--gap);
  @media screen and (태블릿 이상에서) {
    margin-top: var(--header-heigh) + var(--gap);
  }
}
(+) var 연산하기 위해서는 calc를 써줘야한다.

calc(var(a) - var(b));

footer 내에 section을 네 부분으로 나누어 gap을 20px씩 주었다.
logo section, searchbar section, follow section, gnb section. 태블릿, pc에서의 레이아웃도 각각 다를 예정.

.footer {
  background-color: var(--dark-color);
  padding: 4rem 2rem;
  color: #ffffff;
  display: grid;
  gap: 20px;
}

.main 내 컨텐츠들

grid의 화룡점정은 main 내부에서가 아닐까...^^

.card

기본적으로 카드는 hero와 기본 모양이 있다. hero에서는 컨텐츠가 가운데 정렬, 기본적으로는 왼쪽 정렬이다.
카드를 두개 버전으로 만들기보다 기본 카드를 만들고 hero에서 오버라이딩하도록 의도했다.
비슷한 구조를 가진 컴포넌트들(기본적으로 구조는 같은데 한두개씩 디테일이 다른)은 다 이런 식으로 디자인함.

  1. 카드 내부 컴포넌트들은 상위 컴포넌트인 이미지의 크기만큼의 width와 height을 가지고 있다.
  2. position absolute에 따라 이미지를 기준으로(.card의 position: relative를 기준으로) 레이아웃을 가진다. 이때 top0, left0이니 이미지의 상단과 왼쪽
  3. 이미지의 가운데(align-item: center)에 있고,
  4. 컨텐츠는 각각 grid row 방향으로 30%(category, label) 50%(title) 20%(icons)의 비율을 가지고 있다.
.card {
  width: 100%;        /* 1. */
  position: relative; /* 2. */
}
.card_content {
  width: 100%;   /* 1. */
  height: 100%;  /* 1. */
  display: grid; /* 4. */
  grid-template-rows: 30% 50% 20%; /* 4. */
  align-items: center; /* 3. */
  position: absolute; /* 2. */
  top: 0;             /* 2. */
  left: 0;            /* 2. */
  color: #ffffff;
}
.card_title { ... }
.card_icons { ... }

그리고 생각외로 label(카테고리) 부분이 복병이었는데,
그리드 레이아웃 내에서도 가운데가 아니라 하단에 위치시키려면 어떻게 하는가? 에 대한 문제였다.

-------   ------- 
|     |   |     | 
|  v  |   |     |
|     |   |  v  |
-------   -------
기본적으로 | 이렇게
이렇게 됨 | 하고싶음

mdn

그리드는 부모에서 자식 컴포넌트의 레이아웃을 핸들링한다.
이렇게 그리드 내부에서는 컨트롤하고싶은 자식 컴포넌트에서 직접 align-self를 통해서 컨트롤 할 수 있다.

  • width fit-conent를 주면 컨텐츠만큼의 너비를 갖게 된다.
  • line-height에 height과 같은 값을 주면 세로로 가운데 정렬을 할 수 있다.
.label {
  /* 그리드 내에서도 가운데가 아니라 아래쪽에 위치 */
  align-self: self-end;
  padding: 0 2.5rem;
  width: fit-content;
  height: 4rem;
  line-height: 4rem;
}

.hero와 .card.hero

hero와 card라는 클래스를 같이 가지게 되었을때(.card.hero),
카드 내부 컨텐츠가 justify-items center를 통해 가운데정렬을 하도록 한다.
text-align은 박스모델 내부의 text 정렬이 center가 되도록 한다. 기본적으로는 왼쪽정렬이다.

.hero.card {
  > .card_content {
    justify-items: center;
  }
  > .card_content > .card_title {
    text-align: center;
  }
}

input type에 따른 css

input[type="email"] 이런식으로 input type을 특정할 수 있다.
보통 input[type="text"], input[type="password"]을 많이 쓰지 않을까 싶다.

.footer_search {
  > input[type="email"] {
    border: 0;
    padding-left: 15px;
    height: 100%;
  }
}

nav는 태블릿 사이즈 이상일때 헤더에도 쓰이고, 푸터에서는 모바일에서부터 쓰인다. 그리고 푸터에서는 스크린 너비에 따라 그리드가 다르다.
해서 footer_nav라는 클래스를 또 만들어서 그리드만 따로 컨트롤하도록 했다.
2개의 컬럼이 같은 사이즈로 공간을 차지하도록 했다. (repeat(2, 2fr);)

.nav {
  font-size: 1.4rem;
  font-family: Oswald;
  text-transform: uppercase;
  font-weight: 700;
}
.footer_nav {
  display: grid;
  grid-template-columns: repeat(2, 2fr);
  gap: 18px;
}
profile
주니어 플러터 개발자의 고군분투기

0개의 댓글