본문 바로가기

FrontEnd

[번역] Mobile First Design vs Desktop First Design

반응형

원문 : https://ishadeed.com/article/the-state-of-mobile-first-and-desktop-first/

 

The State Of Mobile First and Desktop First - Ahmad Shadeed

Is mobile first or desktop first still relevant today? An article that explores both with pros and cons for each.

ishadeed.com

모바일 퍼스트

  • 모바일 버전 css를 먼저 작성한 다음 더 큰 버전을 작성
.section {
    padding: 2rem 1rem;
}

@media (min-width: 62.5rem) {
    .section {
        display: flex;
        align-items: center;
        gap: 1rem;
        padding: 4rem 2rem;
    }
}

(min-width : 62.5rem) 쿼리 조건은 width가 62.5rem보다 같거나 크면을 의미함.

더 큰 뷰포트에서 더 큰 패딩과  간격을 의미.

더 큰 뷰포트에서 더 큰 패딩과  간격

데스크톱 퍼스트

  • 데스크톱 버전 css를 먼저 작성하고 더 작은 버전을 작성
.section {
    display: flex;
    align-items: center;
    gap: 1rem;
    padding: 4rem 2rem;
}

@media (max-width: 62.5rem) {
    .section {
        display: block;
        padding: 2rem 1rem;
    }
}

먼저 더 큰 뷰포트 크기에 대한 CSS를 작성한 다음 CSS 미디어 쿼리를 사용하여 더 작은 크기에 대한 CSS를 변경함.
(max-width : 62.5rem) 쿼리 조건은 width가 62.5rem보다 같거나 작으면을 의미함.

모바일 퍼스트 작업 방식

  1. 전체 페이지를 모바일 퍼스트로 작성하고, 각 중단점에 대한 컴포넌트 별 css를 작성 (직렬)
  2. 컴포넌트 별로 모바일 퍼스트로 작성하고, 중단점에 대한 컴포넌트 별 CSS를 작성하는 작업을 반복 (병렬)

각 컴포넌트가 서로 영향을 안미치게 하려면 2번 방식이 좋음

작은 크기 중단점 먼저 CSS 작성

예시로 hero 영역 CSS를 보자.

.hero {
  display: flex;
  align-items: flex-end;
  background-image: url('hero.jpg');
  background-size: cover;
  background-repeat: no-repeat;
}

.hero__title {
  font-size: 1rem;
}

.hero__thumb {
  display: none;
}

@media (min-width: 60rem) {
  .hero {
    align-items: center;
    background-image: initial;
    background-color: #7ecaff;
  }

  .hero__title {
    font-size: 2rem;
  }

  .hero__thumb {
    max-width: 320px;
    display: block;
  }
}

폰트 사이즈와 배경 정도만 재정의해주면 됨.

 

네비게이션은 생각보다 재정의 할 요소들이 많음.

.nav {
    position: fixed;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    overflow-y: auto;
    padding-top: 2rem; /* Space for the toggle */
}

.nav__toggle {
    position: absolute;
    right: 1rem;
    top: 1rem;
}

.nav__item {
    padding: 1rem;
    display: block;
}

.nav__item:not(:last-child) {
    border-bottom: 1px solid #fff;
}

/* Desktop styles */
@media (min-width: 60rem) {
    .nav {
        position: initial;
        width: initial;
        height: initial;
        overflow-y: initial;
        display: flex;
        align-items: center;
        padding-top: 0;
        background-color: blue;
    }

    .nav__toggle {
        display: none;
    }

    .nav__item:hover {
        color: blue;
        background-color: initial;
    }

    .nav__item:not(:last-child) {
        border-bottom: 0;
        border-left: 1px solid #fff;
    }
}

즉, 데스크톱 버전 CSS를 위해 많은 CSS를 재정의해야함.

또한 셀렉티비티 문제가 있음.

모바일 버전이 셀렉티비티가 더 높으면 데스크톱 버전이 동작안함.

.nav__item {
    border-bottom: 0;
}

:not 선택자 떄문에 셀렉티비티가 높아졌기 때문

이를 예방하려면, 모바일 버전의 셀렉티비티를 낮추거나, 대칭꼴로 선택자를 적용함.

.nav .nav__item {
    border-bottom: 0;
}

/* Or */

.nav__item:not(:last-child) {
    border-bottom: 0;
    border-left: 1px solid #fff;
}

데스크톱 퍼스트 작업 방식

.nav {
    display: flex;
    align-items: center;
    background-color: blue;
}

.nav__toggle {
    position: absolute;
    right: 1rem;
    top: 1rem;
}

.nav__item {
    padding: 1rem;
    display: block;
}

.nav__item:hover {
    color: blue;
    background-color: initial;
}

.nav__item:not(:last-child) {
    border-bottom: 0;
    border-left: 1px solid #fff;
}

@media (max-width: 25rem) {
    .nav {
        display: block;
        position: fixed;
        left: 0;
        top: 0;
        width: 100%;
        height: 100%;
        overflow-y: auto;
        padding-top: 2rem; /* Space for the toggle */
    }

    .nav__toggle {
        display: block;
    }

    .nav__item:not(:last-child) {
        border-bottom: 1px solid #fff;
    }
}

max-width로 뷰포트가 넓어졌을 때의 디자인이 추가되도록 하기 때문에, 재정의가 적음

생각해보면 모바일 퍼스트는 min-width 범위가 모든 미디어 쿼리에서 겹친다.

미디어 쿼리에 의해 레이아웃이 변경되는 시점도 테스트 해주는것이 좋다.(layout flickering.) 


스타일 범위 지정(Scoping Styles)

이 게시물(this great Article)에서 설명한 하이브리드 접근 방식을 사용한다.

  • 특정 컴포넌트는 각 중단점에서 다르게 보인다.
    • 각 뷰포트 별 CSS를 재정의를 고려하지 않을 수 있도록 작성한다.

 

.nav {
    /* Base styles: not related to any viewport size */
}

/* Desktop styles */
@media (min-width: 800px) {
    .nav { ... }
}

/* Mobile styles */
@media (max-width: 799px) {
    .nav { ... }
}

특정 컴포넌트는 대부분의 중단점에서 비슷할 수 있다. 이 경우는 필요한 경우만 작성한다.

.section {
    padding: 1rem;
}

/* Desktop styles */
@media (min-width: 800px) {
    .section {
        padding: 2rem 1rem;
    }
}

미래의 반응형 디자인 작업방식

최신 css 레이아웃 방법을 사용하면 미디어 쿼리 없이 쉽게 반응형 디자인을 구축할 수 있음

따라서 뷰포트 크기에 따라 복잡하게 바뀌는 컴포넌트를 제외하면,

대부분의 반응형 디자인은 화면 크기에 따라 무엇을 숨기느냐 / 보이느냐의 이야기가 될 것임

 

아래 그림에서 큰 차이가 있는 부분은 헤더와 네비게이션임.

이 둘은 min-width, max-width 미디어 쿼리를 혼합해서 사용하면 좋을 것임

나머지는 min-width 정도면 충분할 것임

모바일 네비게이션을 살펴보자

이 디자인을 모바일 퍼스트 CSS로 구현하면 많은 재정의가 발생함.

.header {
    /* Base styles */
}

/* Desktop styles */
@media (min-width: 1000px) {
    .nav__toggle,
    .nav__close {
        display: none;
    }
}

/* Mobile styles */
@media (max-width: 999px) {
    .nav {
        position: fixed;
        left: 0;
        top: 0;
        width: 100%;
        height: 100%;
        background-color: #4777dB;
    }
}

hero 영역은 flexbox면 충분함. 그리고 .hero_thumb만 조건부로 순서를 바꿔주면 됨.

<section class="hero">
    <div class="wrapper">
        <img src="thumb.jpg" alt="" />
        <h2><!-- Headline --></h2>
        <p><!-- Description --></p>
    </div>
</section>
.hero {
    display: flex;
    flex-direction: column;
}

@media (min-width: 1000px) {
    flex-direction: row;
}

@media (max-width: 999px) {
    .hero__thumb {
        order: -1;
    }
}

max-width 미디어 쿼리로 범위를 지정하여 order: -1을 한 번만 작성함.
이를 모바일 퍼스트 만으로 작성하면 다음과 같이 될 것임.

.hero__thumb {
    order: -1;
}

@media (min-width: 1000px) {
    .hero__thumb {
        order: initial;
    }
}

 

위 CSS는 다음과 같은 문제가 있음에 주의하자.

  • 시각적 순서와 DOM 순서의 불일치
  • CSS 중복

해당 작업의 플로우 차트는 다음과 같다.

미디어 쿼리에 동일한 중단점 크기 사용 피하기

min-width와 max-width에 동일한 중단점 크기를 사용하면 어떻게 될까?

@media (max-width: 500px) {
    .nav {
        display: none; 
    }
}

@media (min-width: 500px) { 
    .nav__toggle {
        display: none; 
    }
}

500px에선 아무것도 안보이게 된다.

개발자 도구를 사용해 수동으로 테스트 하는 경우 아니면 이런 문제가 발생하는지도 모르고 넘어갈 수 있음.

@media (max-width: 499px) {
    .nav {
        display: none; 
    }
}

@media (min-width: 500px) { 
    .nav__toggle {
        display: none; 
    }
}

디자이너에게 모바일 퍼스트가 좋지 않은 이유

  • 스크롤이 짜증남
  • 작은 화면은 창의성을 제한함

최신 CSS는 디바이스 너비에 무관하게  디자인할 수 있게 해줌

Flexbox Wrapping

참고 : How to Make a Media Query-less responsive Card Component 

아래와 같이 너비에 따라 변경되는 카드 레이아웃은 flexbox로 쉽게 작성할 수 있음

flex-basis의 합 600px만큼 컨테이너 너비를 확보하지 못하면 줄바꿈되도록 flex-wrap을 사용할 수 있음.

Minmax

minmax에 대한 더 자세한 내용은 this article 참조.

  • 최소 200px 크기로 전체 컨테이너에 최대한 많은 요소를 넣으려 함.
  • 하나의 아이템만 존재하면 화면을 자동으로 채움

https://defensivecss.dev/tip/auto-fit-fill/

.wrapper {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  grid-gap: 1rem;
}

뷰포트 단위와 비교 함수

뷰포트 단위와 비교 함수를 사용하면 폰트 크기, 패딩, 마진 등의 변경을 줄일 수 있음.

.title {
    font-size: clamp(16px, (1rem + 5vw), 50px);
}

.hero {
    padding: clamp(2rem, 10vmax, 10rem) 1rem;
}

.sidebar {
    flex-basis: max(30vw, 150px);
}

컨테이너 쿼리

뷰포트 너비가 아닌 컨테이너 너비에 반응하도록 반응형 디자인을 적용할 수 있음.

.wrapper {
  contain: layout inline-size;
}

@container (min-width: 250px) {
  .pagination {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
  }

  .pagination li:not(:last-child) {
    margin-bottom: 0;
  }
}

@container (min-width: 500px) {
  .pagination {
    justify-content: center;
  }

  .pagination__item {
    display: block;
  }
}
반응형