[DAY14] Responsive Web Design

유진·2023년 8월 20일
0
post-thumbnail

Media Query

Media Query는 어떻게 동작하는가?

  • ‘Desktop Frist로 홈페이지를 만드냐’, ‘Mobile First로 홈페이지를 만드냐’에 따라
    각각 max-width와 min-width 미디어 쿼리를 사용한다.
  • 한 개 이상의 미디어 쿼리를 사용할 수 있다.

max-width

  • 특정 스타일을 0 - 600px 사이에 적용시키고 싶다면? → CSS에서 @media (max-width: 600px) 을 사용하면 된다. 이는 현재 viewport width가 600px 이하인지 물어본다. → 현재 viewport가 600px 이하라면 해당 CSS 코드가 적용된다. 600px보다 크다면 적용되지 않는다.
  • 현재 viewport가 400px인데, @media (max-width: 600px)@media (max-width: 1200px)가 동시에 적용되면? → 600px 이하, 1200px 이하 두 조건 모두 참을 만족하므로 적용된다. → 충돌이 있을 때 마지막 것이 적용된다.
    @media (max-width: 1200px) {
      .section-hero {
        background-color: orangered;
      }
    }
    
    @media (max-width: 600px) {
      .section-hero {
        border: 20px dashed blue;
        background-color: blue;
      }
    }
    @media (max-width: 600px)이 마지막에 있으므로, 600px 이하일 때 충돌하는 background-color는 파란색이 된다

중단점(breakpoint)

  • 중단점(breakpoint)은 우리가 디자인을 바꾸고 싶은 viewport width이다.

중단점을 결정하는 기술

BAD

  • 아이폰, 아이패드 같이 인기 있는 기기를 기준으로 중단점을 설정했다. ⇒ 단점
    • 특정 장치에 대해 최적화할 때 다른 장치의 사용자들을 무시한다. 따라서 사용자에게 더 안 좋은 경험을 부여한다.
    • 코드를 미래에도 유지하기에는 최악의 전이다. 애플에서 새로운 장치가 나오면 미디어 쿼리를 새롭게 정의해야 한다.

GOOD

  • 폰, 태블릿, 데스크 등 다양한 디바이스에서 가장 많이 사용된 너비를 본다. 이를 그룹화해 중단점을 고른다.
  • 대부분의 폰은 300~500px, 태블릿 세로는 600~900px, 가로는 900~1100px, 데스크탑은 1200px을 넘기므로 중단점을 600, 900, 1200px에 설정한다.

PERFECT

  • 디자인이 깨지는 곳에 중단점을 설정한다. 디바이스와 무관하게 디자인만 본다.
  • mobile first나 desktop first로 시작하여 화면 너비를 늘리거나 줄이면서 디자인이 깨지는 순간, 즉, 디자인이 더 이상 용인될 수 없는 순간 중단점을 만든다.

결론

⭐ 우리가 디바이스를 아예 생각하지 않고 중단점을 설정하기란 어려우므로 GOOD 전략과 PERFECT 전략을 함께 사용할 것이다.

실습: 반응형 웹 만들기

  • Desktop First로 시작했으므로 너비를 점점 줄여나가며 max-width를 설정할 것이다.

(Mobile First의 경우 너비를 점점 줄여나가며 min-width를 설정한다.)

meta tag 포함하기

<meta name="viewport" content="width=device-width, initial-scale=1.0" />
  • 반응형 웹에서 이 메타 태그는 매우 중요하다.
  • 해당 태그가 없으면 모바일 장치에서 미디어 쿼리가 작동하지 않는다.
    • 모바일 장치의 브라우저는 페이지가 화면에 맞을 때까지 기본값으로 축소되기 때문이다. 미디어 쿼리에서 원하는 건 이와 다르다.
  • width=device-width, initial-scale=1.0 : 페이지가 화면의 너비와 일치하도록 한다. 모바일 장치에서 화면에 맞추기 위해 페이지를 축소하지 않는다는 뜻.

미디어 쿼리를 위한 파일 만들기

queries.css

  • @meida(max-width: ) 안에 넣어야 숫자의 기준은?
    • 반올림한 em 단위

      /* @media()에서 괄호 안에 rem이나 em의 단위를 넣어도 이는 기존 html{}에 설정해준
      font-size의 영향을 받지 않아, 그대로 1rem = 1em = 16px이다.
      (브라우저 설정해준 font-size가 16px임)
      
      /* rem: root font size
         em: current font size */
      
      /************************************/
      /* BELOW 1344px (Smaller desktops) */
      /**********************************/
      
      /* 1344px 인 이유?
      1350px을 목표로 했다.
      1350px을 em 단위로 변경하려 계산하면, 1350px / 16px = 84.375 em 이다.
      반올림하면 84em이므로 @media() 괄호 안에 84em을 써주고 84em * 16px = 1344px 이다.
      따라서 1344px 이다.*/
      
      @media (max-width: 84em) {
      	...
      }
    • 200~300px의 간격

      → 너무 많은 미디어 쿼리를 만들지 않고 최소 200px 이상의 간격으로 미디어 쿼리를 만드는 게 좋다.

Responding to Small Laptops

/************************************/
/* BELOW 1344px (Smaller desktops) */
/**********************************/
****
@media (max-width: 84em) {
  .hero {
		/* 기존 130rem */  
    max-width: 120rem;
    /* 기존 130rem이므로 container와 width를 맞추기 위해 */
		/* 위처럼 설정하면 hero 부분의 너비가 1300px에서 1200px로 변하는 걸 볼 수 있다. */
    /* 여기서 새로 생기는 문제는, heading-primary 가 3줄에서 4줄이 된다는 것이다. */
  }
  .heading-primary {
    /* 기존 5.2rem */
    font-size: 4.4rem;
    /* 4줄로 늘어났던 제목이 원래의 3줄로 바뀐다. */
  }
  /* 아래로 내려보면 .gallery 부분도 width를 줄일 수록 이상해진다. */
  /* grid 3열을 2열로 줄여서 해결해보자. */
  /* 참고: 모든 스크린 사이즈를 완벽하게 만들 수는 없다. 조금 이상해도 OK*/
  /* 작은 노트북 중 가장 흔한 것이 1200px이니까 그 정도에서 확인하고 괜찮으면 OK */
  .gallery {
    grid-template-columns: repeat(2, 1fr);
  }
}

Responding to Landscape Tablets

/************************************/
/* BELOW 1200px (Landscape Tablets)*/
/**********************************/

/* 1200px / 16px = 75em */
@media (max-width: 75em) {
  /* 우리는 반응형으로 웹을 설계했기 때문에
  모든 것을 줄이고 싶을 때 글꼴 크기를 줄이면 된다. */
  /* 62.5%가 브라우저가 보통 16px이므로 10px을 의미했다면,
  우리는 이제 9px을 쓰고 싶을 때 아래와 같이 하면 된다.
  9px / 16 = 0.5625*/
  html {
    font-size: 56.25%;
  }
  /* container가 120rem으로 1200px이었는데,
  이제는 120rem * 9px = 1080 px로 변경됐다. */

  /* 너비가 좁아지는데 grid gap은 그대로라 gap이 점점 커져 보일 것임. 이를 줄여준다. */
  .grid {
    column-gap: 4.8rem;
    row-gap: 6.4rem;
  }

  .heading-secondary {
    font-size: 3.6rem;
  }
  .heading-teritiary {
    font-size: 2.4rem;
  }
  /* header가 원래 폭이 넓었는데, 화면이 줄어들수록 container보다 폭이 좁아진다.
  container와 폭을 맞춰줄 것이다. */
  .header {
    padding: 0 3.2rem;
  }
  .main-nav-list {
    gap: 3.2rem;
  }
  .hero {
    gap: 4.8rem;
  }
  /* 개발자 도구에서 ipad, landscape 설정해서 중간 점검 해보기 */
  .testimonials-container {
    padding: 4.8rem 3.2rem;
  }
}

Responding to Tablets

/**************************/
/* BELOW 940px (Tablets) */
/************************/

/* 940px / 16 = 58.75 */
@media (max-width: 59em) {
  /* 8px / 16 = 0.5*/
  html {
    font-size: 50%;
  }
  /* 다음 미디어쿼리가 될 영역까지 너비를 줄여보고,
  200~300px이 안됐는데 디자인이 무너진다면 개별 요소를 직접 수정한다. */
  /* 2개의 열을 1개의 열로 수정하기 - 세로 모바일 화면 생각! */
  .hero {
    grid-template-columns: 1fr;
    padding: 0 3.2rem;
    gap: 6.4rem;
  }
  /* hero를 바꾸니 이미지가 너무 커져서 수정 */
  /* 기존: 100% */
  .hero-img {
    width: 60%;
  }
  /* 중앙 정렬 */
  .hero-text-box,
  .hero-img-box {
    text-align: center;
  }
  .delivered-meals {
    /* 해당 블록은 flexbox이다. 따라서 justify-content 사용해서 중앙 정렬 */
    justify-content: center;
    margin-top: 3.2rem;
	  /* visual hierarchy 때문에 delivered-meals는 hero-text-box에 더 붙이고,
	  hero-img-box는 위 둘과 좀 더 간격을 주었다. */
  }

  /* 로고들이 너무 크고 간격도 좁아 보여서 조정한다. */
  .logos img {
    height: 2.4rem;
  }
  .step-number {
    font-size: 7.4rem;
  }

  /* meal-content 카드 제목이 자꾸 2줄이 되어 조정한다. */
  .meal-content {
    padding: 2.4rem 3.2rem 3.2rem 2.4rem;
  }

  /* gallery 부분도 hero처럼 1 column으로 만들기 */
  .section-testimonials {
    grid-template-columns: 1fr;
  }
  /* 이미지 부분이 너무 커지니까 수정해보기 2x6의 행렬로 만들어보자. */
  .gallery {
    grid-template-columns: repeat(6, 1fr);
  }
  
	/* 마지막 폼 부분에 사진이 많이 좁아짐.
  이를 조정하기 위해 form 부분을 한줄로 만들기 */
  .cta {
    /* 기존: 2fr 1fr. 사진의 비율이 33%였음. 이를 40%로 키워보기 */
    grid-template-columns: 3fr 2fr;
  }
  /* form 한줄로 만들기 */
  .cta-form {
    grid-template-columns: 1fr;
  }
  .btn--form {
    /* 입력 칸과 버튼 간의 간격이 좁아보여서 넓혀준다. */
    margin-top: 1.2rem;
  }
}

모바일용 Navigation bar 만들기

  • 기존의 내비게이션에서 이동식 내비게이션으로 변경할 것이다.
  • nav 메뉴가 전부 사라지도록 하고 그 대신에 메뉴 버튼를 추가한다. 버튼을 누르면 메뉴들이 표시되게 만들 것이다.
  • index.html
    • nav-open 클래스가 있으면, nav 메뉴가 열린 상태이다.
    • nav-open 클래사그 없으면, nav 메뉴가 닫힌 상태이다.
    • 이 부분은 js로 해야하므로 여기에서는 디자인만 하고 nav-open 클래스를 지워주었다.
<body>
	<header class="header nav-open">
		...
		<button class="btn-mobile-nav">
      <!-- 메뉴 아이콘 -->
      <ion-icon class="icon-mobile-nav" name="menu-outline"></ion-icon>
      <!-- 메뉴 닫기 아이콘 -->
      <ion-icon class="icon-mobile-nav" name="close-outline"></ion-icon>
    </button>
  </header>
</body>
  • style.css
/* MOBILE */
.btn-mobile-nav {
  border: none;
  background: none;
  /* background-color: none이 아님에 주의! */
  cursor: pointer;

  /* 데스크탑일 때 보여지면 안되는 버튼이므로 display: none 설정 */
  display: none;
}

.icon-mobile-nav {
  height: 4.8rem;
  width: 4.8rem;
  color: #333;
}
/* 
<!-- 메뉴 아이콘 -->
<ion-icon class="icon-mobile-nav" name="menu-outline"></ion-icon>
<!-- 메뉴 닫기 아이콘 -->
<ion-icon class="icon-mobile-nav" name="close-outline"></ion-icon>
*/

/* name 속성으로 아이콘 구분짓기 */
/* .icon-mobile-nav[name="menu-outline"] {
} */
.icon-mobile-nav[name="close-outline"] {
  display: none;
}
  • queries.css
.main-nav {
    background-color: rgba(255, 255, 255, 0.97);
    position: absolute; /*parent에 relatvie를 주어야 함에 주의*/
    top: 0;
    left: 0;
    width: 100%;
    height: 100vh;
    transform: translateX(100%);
    /* translateX(100%) 했을 때 오른쪽에 그만큼 공간이 생긴다.
    이를 해결하기 위해 body에 overflow-x: hidden을 설정한다.*/
    /* header에 realtive를 설정해주어야 한다. */

    display: flex;
    justify-content: center;
    align-items: center;

    transition: all 0.5s ease-in;
    /* Hide navigation */
    /* display: none; 하면 단점 - Allows NO transitions at all */
    /* display: none; */

    /* 1) Hide it visually */
    opacity: 0;

    /* 2) Make it unaccessible to mouse and keyboard. display:none이 아닌 opacity:0을 사용하기 때문에 */
    pointer-events: none;

    /* Hide it from screen readers */
    visibility: hidden;
  }

  /* nav-open: 내비게이션 바가 열렸을 때 */
  .nav-open .main-nav {
    opacity: 1;
    pointer-events: auto;
    visibility: visible;
    transform: translateX(0%);
  }

  .nav-open .icon-mobile-nav[name="close-outline"] {
    display: block;
  }
  .nav-open .icon-mobile-nav[name="menu-outline"] {
    display: none;
  }

  .main-nav-list {
    /* .main-nav-list는 이미 플렉스 박스라 display: flex; 선언해줄 필요 X */
    flex-direction: column;
    gap: 4.8rem;
  }
  .main-nav-link:link,
  .main-nav-link:visited {
    font-size: 3rem;
  }

Responding to Smaller Tablets

/**********************************/
/* BELOW 704px (Smaller Tablets) */
/********************************/

/* 700/16 = 43.75em */
/* 44*16 =  704px*/
@media (max-width: 44em) {
  /* 3,4개의 그리드 열을 전부 2개의 그리드 열로 바꿔준다. */
  .grid--3-cols,
  .grid--4-cols {
    grid-template-columns: repeat(2, 1fr);
  }
  /* 3개 그리드 열에 있던 3개의 요소가 2개 그리드 열로 바뀌면서
  위에 2개, 아래 1개가 되어서 아래 1개를 중앙 정렬하고 싶을 때 */
  .diets {
    grid-column: 1 / -1;
    justify-self: center;
  }

  .heading-secondary {
    margin-bottom: 4.8rem;
  }

  .pricing-plan {
    width: 100%;
  }

  /* footer */
  .grid--footer {
    grid-template-columns: repeat(6, 1fr);
  }

  .nav-col {
    grid-row: 1;
    grid-column: span 2;
  }
  .logo-col,
  .address-col {
    grid-column: span 3;
  }
}

위 아래 요소가 3개, 2개일 때 아래 요소 그리드 중앙 정렬하는 법

  • (trick) 그리드를 3열로만 하면 아래 두 요소를 중앙 정렬 할 수 없으므로, 위 세 요소와 최소공배수인 6열의 그리드를 사용하면 해결할 수 있다.

Responding to Phones

  • 개발자 도구에서 자신이 설정한 미디어 쿼리 눈금을 볼 수 있다.
/************************/
/* BELOW 544px (Phone) */
/**********************/
/* 550px/16px = 34.375em */
/* 34em*16px = 544px*/
@media (max-width: 34em) {
  /* 이제 충분한 공간이 없기 때문에 모든 그리드를 하나의 열에 모을 것이다. */
  .grid {
    row-gap: 4.8rem;
  }
  .grid--2-cols,
  .grid--3-cols,
  .grid--4-cols {
    grid-template-columns: 1fr;
  }
  .section-hero {
    padding: 2.4rem 0 6.4rem 0;
  }
  .btn,
  .btn:link,
  .btn:visited {
    padding: 2.4rem 1.6rem;
  }
  .hero-img {
    width: 80%;
  }
  .logos img {
    height: 1.2rem;
  }
  .step-img-box:nth-child(2) {
    grid-row: 1;
  }
  .step-img-box:nth-child(6) {
    grid-row: 5;
  }
  .step-img-box {
    /* grid gap을 개별적으로 조정할 수 없을 때 쓰는 trick */
    /* 이미지가 해당하는 아래의 글과 더 가깝도록 이미지를 아래로 조정할 때 */
    /* translateY를 사용한다. */
    transform: translateY(2.4rem);
  }
  .testimonials {
    grid-template-columns: 1fr;
  }
  .gallery {
    grid-template-columns: repeat(4, 1fr);
    gap: 1.2rem;
  }
  .cta {
    grid-template-columns: 1fr;
  }
  .cta-text-box {
    padding: 3.2rem;
  }
  /* cta-img-box는 원래 아무것도 없는 요소이고, 다른 것에 의지해 background-image가 보였다.
  다른 것에 의지하지 않을 때는 수동으로 해당 요소에 높이를 부여해야 한다. */
  .cta-img-box {
    grid-row: 1; /* form보다 위에 위치시키기 */
    height: 32rem;
  }
}

0개의 댓글

관련 채용 정보