반응형 웹을 위한 CSS Media Query & Container Query

반응형 웹을 위한 CSS Media Query & Container Query

Tag
CSS
프론트엔드 개발을 하다보면, @media 를 이용해서 반응형 개발을 진행해본 경험들이 다들 있을 것이다.
그런데 그럼에도 불구하고 같은 컴포넌트가 화면에 따라 배열이 달라진다든지 등과 같이 화면의 크기가 아닌 해당 컴포넌트를 래핑하고 있는 컨테이너의 크기에 따라도 달라지는 경우가 있다.
이럴때는 보통 나의 경우는 flex를 이용해서 자주 해결하였는데, 이 방법이 아니라 정말 섹시하게 해결할 수 있는 방법을 알게 되어 정리해보려 한다.
바로 컨테이너 쿼리 ( Container Query ) 이다.
컨테이너 쿼리를 알아보기 이전에, 미디어 쿼리부터 간단하게 정리해보자.
 

Media Query

@media {media-type}? and ( {condition} ) { @content }
보통 사용하는 방식은 위와 같다.
 

media type

media-type의 경우 사용하지 않아도 상관이 없으며,
  • all ( 기본값 )
    • 모든 미디어 유형에 대해 적용
  • print
    • 인쇄 미디어에 대한 스타일을 지정
  • screen
    • 컴퓨터 화면에 대한 스타일을 지정
  • speech
    • 음성 합성기에 의해 읽어지는 미디어 유형에 대한 스타일을 지정
위와 같이 총 4가지 종류가 있다.
 
보통 화면 상에서 적용할 일이 많을 테니, screen을 사용하곤 한다.
 

condition

조건으로 사용할 수 있는 것은 크게 세가지 키워드가 있다.
  • min-width
    • 해당 조건보다 viewport ( 화면 크기 ) 보다 크거나 작을 때 적용
    • ex) min-width: 480px ⇒ 화면의 너비가 480px 이상일 때
  • max-width
    • 해당 조건보다 viewport ( 화면 크기 )가 작을 경우에만 적용
    • ex) max-width: 480px ⇒ 화면의 너비가 480px 보다 작을 때
  • width
    • 해당 조건이 viewport ( 화면 크기 )일 때 적용
    • ex) width: 480px ⇒ 화면의 너비가 480px일 때
 
media-type과 condition을 이용해서 여러 복잡한 짓들을 할 수 있지만, 보통 그렇게까지 안하기 때문에 이정도까지만 알고 넘어가자.
 

Example

// Card.tsx import React from 'react'; import styles from './card.module.scss'; const Card: React.FC = () => { return ( <article className={styles.wrapper}> <img src="https://dummyimage.com/200x200/000/fff" /> <div className={styles.contentWrapper}> <h1 className={styles.title}>TITLE</h1> <p className={styles.desc}>Lorem Ipsum is simply dummy text of the printing and typesetting industry.</p> </div> </article> ); }; export default Card;
 
// card.module.scss .wrapper { display: flex; flex-direction: column; gap: 12px; align-items: center; width: 240px; padding: 12px; background-color: rgba(0, 0, 0, 12%); border-radius: 12px; .contentWrapper { text-align: center; .title { font-size: 24px; font-weight: 600; color: #18181a; } .desc { font-size: 16px; font-weight: 400; color: #353536; } } } // viewport의 너비가 768px 이상일 경우 @media screen and (min-width: 768px) { .wrapper { .contentWrapper { .title { font-size: 40px; color: red; } .desc { font-size: 24px; color: blue; } } } }
 
notion image
화면의 크기가 1200px 일 때
 
notion image
화면의 크기가 700px 일 때

Container Query

그럼 이 글을 쓰게 된 목적인, 컨테이너 쿼리를 알아보자.
컨테이너 쿼리의 경우 뷰포트를 기준으로 하는 것이 아닌, 말 그대로 컨테이너의 크기에 따라 분기처리 할 수 있는 것이다.
 

Container단

컨테이너 { container-name: 이름; // container-name: 이름1 이름2 이름3; (여러개도 가능) container-type: size | inline-size | normal; // 필수 값 container: {container-name} / {container-type}; }
해당 컨테이너 내부를 추후에 변경하기 위해서는 container-type과 container-name property를 추가해줘야 한다.
css의 특성과 같이 container query를 쓸 때는 해당 영역에서 가장 가까운 조상 컨테이너를 기반으로 스타일을 적용하게 되는데, container-name을 적용하면 해당 container를 직접 선택하여 스타일을 적용해줄 수 있다는 장점이 있다.
container-type의 경우 코드를 적은 것과 같이 3가지 종류가 있다.
  • inline-size: width값에 따라 container query를 적용 ( 인라인 측면 )
  • size: height값이 조건에 해당할 때에도 container query를 적용 ( 인라인 + 블록 측면 )
  • normal: 해당 값이 부여된 html element를 container에서 제외
 

container query

@container {container-name}? ( {condition} ) { @content }
사용 법은 media query와 다르지 않다.
container-name을 가지고 있는 컨테이너라면 container-name를 적어주면 되고,
media query 처럼 condition을 동일하게 적어주면 된다.
 

cqw, cqh

보면 vw, vh 와 비슷하게 생겨보인다.
vw ⇒ viewport width, vh ⇒ viewport height 인 것 처럼,
cqw ⇒ container query width, vqh ⇒ container query height 라고 생각하면 된다.
컨테이너 기준으로 width, height를 잡을 수 있다고 생각하면 되겠다.
 
container-type이 inline size에서는 cqw만, size에서는 cqw, cqh를 적용할 수 있다. 라고 생각하면 좋겠다.
 

Example

// Card.tsx import React from 'react'; import styles from './card.module.scss'; const Card: React.FC = () => { return ( <article className={styles.wrapper}> <img src="https://dummyimage.com/200x200/000/fff" /> <div className={styles.contentWrapper}> <h1 className={styles.title}>TITLE</h1> <p className={styles.desc}>Lorem Ipsum is simply dummy text of the printing and typesetting industry.</p> </div> </article> ); }; export default Card;
 
// card.module.scss .wrapper { display: flex; flex-direction: column; gap: 12px; align-items: center; width: 240px; padding: 12px; overflow: auto; resize: both; background-color: rgba(0, 0, 0, 12%); border-radius: 12px; container-name: card; container-type: inline-size; .contentWrapper { text-align: center; .title { font-size: 24px; font-weight: 600; color: #18181a; } .desc { font-size: 16px; font-weight: 400; color: #353536; } } } @container card (min-width: 768px) { .wrapper { .contentWrapper { .title { font-size: 40px; color: red; } .desc { font-size: 24px; color: blue; } } } }
 
 
notion image
.wrapper의 크기가 240px 일 때
 
notion image
.wrapper의 크기가 800px 일 때
 
테스트를 해보면서 간단한 예시를 적어 보았지만,
보통 container query의 존재를 모를 때는 같은 기능에 비슷한 디자인이더라도 컴포넌트를 두 가지 만들어야 하는 경우가 꽤나 자주 있었다.
그런 경우에 container query를 이용해서 처리하는 것이 매우 좋은 방법이라고 생각된다.
 
추가로 container query를 찾아보다 보니 css의 contain 이라는 속성의 존재를 알게 되었는데, 이 부분 또한 찾아봐야 할 듯 하다.
 
notion image
나온지 별로 안되어 많은 사람들이 알지 못하는 css 기능으로 생각되는데, 우리가 사용하는 대부분의 브라우저에서 지원하는 css 기능으로서 잘 사용했으면 좋겠당 :)