모바일·데스크톱 레이아웃 가이드

Tailwind로 한 화면을 모바일과 데스크톱 양쪽에 맞출 때 쓰는 판단 기준을 모았어요. 작은 것에서 큰 것 순서로 읽어요. 한 줄 배치(1부), 섹션 구조(2부), 배치 말고 달라지는 것(3부) 순이에요.

데모 보는 법

1부한 줄 배치하기: flex와 grid

요소 몇 개를 한 줄에 놓을 때 flex와 grid 중 무엇을 쓸지 정해요.

flex와 grid의 차이: 누가 폭을 정하나

flex — 자식이 정해요각 요소가 자기 내용만큼 폭을 차지하고, 남는 공간을 나눠 가져요. 한 줄로 나열할 때 써요.
grid — 부모가 정해요부모가 열을 먼저 선언하고 요소는 그 칸에 들어가요. 같은 열의 칸은 폭이 같아요. 여러 행의 열을 맞출 때 써요.

어느 쪽이 더 좋은 게 아니에요. 아래 두 데모에서 한 번은 grid가, 한 번은 flex가 맞아요.

grid가 맞는 경우: 여러 행의 열을 맞출 때

랜딩 페이지의 반응 지수 막대와 같은 구조예요. 행마다 라벨 · 막대 · 숫자가 반복돼요.

해 보기버튼을 왼쪽부터 차례로 누르면서 막대의 시작점과 끝점을 보세요. 첫 라벨도 길게 바꿔 보세요.

행마다 실제 폭


    
HTML 코드 보기

클래스를 누르면 꺼지고, 다시 누르면 켜져요. 눌러도 모형이 그대로면 그 이유가 코드 아래에 나와요.

grid-cols-[6rem_1fr_3rem] 읽는 법

대괄호 안의 _는 공백이에요. CSS로는 grid-template-columns: 6rem 1fr 3rem, 곧 열 세 개의 폭이에요.

값뜻정하는 법
6rem라벨 열. 고정 폭 (보통 96px)가장 긴 라벨이 들어가고 조금 남는 폭
1fr막대 열. 남는 폭 전부정하지 않아요. 부모 폭에서 나머지를 뺀 값이에요
3rem숫자 열. 고정 폭 (보통 48px)가장 긴 숫자 "100%"가 들어가는 폭

부모가 320px이고 간격이 12px이면 막대 열은 320 − 96 − 48 − 12 × 2 = 152px이에요. 위 데모에서 "grid (폭 고정)"을 고르고 부모 폭을 움직이면, 라벨과 숫자는 그대로고 막대 폭만 바뀌어요.

흔한 방식행마다 grid-cols-[6rem_1fr_3rem]를 반복해요. 폭을 직접 정해야 하고 더 긴 이름이 들어오면 다시 정해야 해요. 행이 서너 개고 데이터가 정해져 있으면 충분해요.
더 나은 방식부모에 grid-cols-[auto_1fr_auto]를 한 번 선언하고 각 행은 grid-cols-subgrid로 물려받아요. 가장 긴 라벨에 맞춰 열 폭이 스스로 정해져요. 데모의 네 번째 버튼이에요.

flex가 맞는 경우: 폭이 내용마다 달라야 할 때

태그, 버튼 묶음, 내비게이션 링크가 여기에 해당해요.

해 보기버튼을 바꿔 가며 부모 폭을 줄여 보세요.

HTML 코드 보기

클래스를 누르면 꺼지고, 다시 누르면 켜져요. 눌러도 모형이 그대로면 그 이유가 코드 아래에 나와요.

고르는 기준

질문은 하나예요. 여러 행이나 칸의 폭을 서로 맞춰야 하나? 맞춰야 하면 grid, 내용 길이에 맡겨도 되면 flex예요.

상황선택Tailwind 예
여러 행에서 열 폭이 같아야 함gridgrid grid-cols-[6rem_1fr_3rem]
모바일 1열 → 데스크톱 2열gridgrid lg:grid-cols-2
한 줄 나열, 폭은 내용대로flexflex flex-wrap gap-2
양끝으로 밀기 (로고 ↔ 메뉴)flexflex justify-between
아이콘 + 글자 한 쌍flexflex items-center gap-2
세로로 쌓기만 함둘 다 됨grid gap-4 또는 flex flex-col gap-4

둘은 섞어 써요. 큰 틀은 grid로 나누고 그 안의 작은 나열은 flex로 놓는 경우가 많아요. 지금 dashboard.tsx도 2단 배치는 grid, 푸터의 양끝 정렬은 flex예요.

2부구조 잡기: 묶음

한 화면을 모바일과 데스크톱 양쪽에 맞추려면, 무엇을 무엇과 묶을지가 먼저 정해져야 해요.

묶음이 왜 필요한가

묶음은 요소 몇 개를 감싸는 부모 요소예요. 래퍼(wrapper)라고도 불러요. div일 때가 많고, 의미가 있으면 section, ul, figure를 써요.

묶음이 필요한 이유는 grid와 flex가 바로 아래 자식만 배치하기 때문이에요. "제목·설명·버튼은 왼쪽, 카드는 오른쪽"을 시키려면 제목·설명·버튼이 먼저 한 덩어리로 묶여 있어야 해요.

배치가 바뀔 때 같이 움직이는 것끼리 한 부모에 넣어요.모바일에서 세로로 쌓이다가 데스크톱에서 좌우로 갈라진다면, 갈라지는 두 덩어리가 각각 묶음이에요. 그 둘의 공통 부모가 lg:grid-cols-2를 받는 자리예요.

해 보기"묶음 없음"인 채로 부모 폭을 줄였다 넓혀 보세요. 그다음 "묶음 있음"으로 바꿔서 다시 해 보세요.

HTML 코드 보기

클래스를 누르면 꺼지고, 다시 누르면 켜져요. 눌러도 모형이 그대로면 그 이유가 코드 아래에 나와요.

묶음을 찾는 질문

묶음은 감각으로 정하는 게 아니에요. 대부분 시안에서 확인할 수 있는 질문 세 개로 정해져요. 감각처럼 보이는 건 이 질문에 여러 번 답해 본 사람이 빨리 답하는 거예요.

질문 1. 간격이 어디서 넓어지나

가장 기계적인 방법이에요. 요소 사이 간격을 재서 주변보다 눈에 띄게 넓은 곳이 묶음의 경계예요. 디자이너가 간격으로 이미 묶음을 표시해 둔 거예요.

해 보기숫자를 눌러 간격을 바꿔 보세요. 전부 같게도 만들어 보세요.

    질문 2. 두 요소가 같이 다니나

    요소 두 개를 골라 모바일 시안과 데스크톱 시안에서 비교해요.

    두 요소가…그러면
    모바일에서는 위아래로, 데스크톱에서는 좌우로 놓인다 (또는 그 반대)방향을 바꾸는 부모 아래에 둘을 둬요. 예: grid lg:grid-cols-2
    붙은 채로 함께 움직여서 다른 덩어리의 옆으로 간다둘을 감싸는 묶음을 만들어요. 그 묶음이 통째로 움직여요.
    배경, 테두리, 최대 폭을 같이 쓴다그 배경이나 테두리를 가진 상자로 감싸요.
    합쳐서 한 단어로 부를 수 있다 (카드, 내비게이션, 목록)의미가 있는 요소로 감싸요. 예: figure, nav, ul
    어느 것도 아니다묶지 않아요. 형제로 둬요.

    질문 3. 이 div를 지우면 뭐가 깨지나

    반대 방향의 확인이에요. 이미 있는 div가 정말 필요한지 의심될 때 써요. 지워도 깨지는 게 없으면 묶음이 아니니 지워요.

    해 보기경우마다 "div 지우기"를 눌러 보세요.

    HTML 코드 보기

    클래스를 누르면 꺼지고, 다시 누르면 켜져요. 눌러도 모형이 그대로면 그 이유가 코드 아래에 나와요.

    질문으로 안 정해질 때

    상황정하는 방법
    같은 결과를 내는 구조가 여러 개HTML이 덜 깊은 쪽을 골라요.
    간격이 비슷하거나 데스크톱 시안이 없음의미로 묶어요. "제목 + 설명 + 버튼", "사진 + 설명글", "라벨 + 입력칸"처럼 한 가지를 설명하는 요소들은 데스크톱에서도 거의 항상 같이 움직여요.
    나중에 요소가 늘어날 것 같음기획을 아는 만큼만 맞힐 수 있어요. 모르면 지금 시안대로 해요.

    여기서 틀려도 비용은 작아요. 간격을 부모가 갖게 해 두면() 묶음을 바꾸는 건 div 하나를 넣고 빼는 일이에요.

    작업 순서: 구조는 먼저, 스타일은 모바일부터

    모바일 화면을 다 만든 뒤 데스크톱에서 묶으려 하면 어려워요. 이미 맞춰 놓은 모바일 간격을 다시 손봐야 하기 때문이에요.

    "모바일 우선"은 CSS를 쓰는 순서에 대한 규칙이에요. HTML 구조는 두 시안을 모두 보고 먼저 정해요.

    무엇을 보고 정하나
    HTML 구조 (묶음)모바일과 데스크톱 시안 둘 다
    CSS (클래스)모바일 먼저, 그다음 lg:

    해 보기1단계부터 차례로 눌러 보세요.

    모바일
    데스크톱

    5단계에서 접두사가 없는 클래스는 모든 폭에 적용되니 모바일 스타일이 돼요. lg:가 붙은 클래스는 화면 폭 1024px 이상에서 그걸 덮어써요. 그래서 모바일을 먼저 완성하고 달라지는 것만 lg:로 더해요.

    묶음을 코드로 쓸 때 지킬 세 가지

    ① 묶음 사이 간격은 부모가 갖기

    묶음과 묶음 사이 간격을 자식의 margin으로 주면, 배치가 바뀔 때 그 여백이 엉뚱한 곳에 남아요. 부모의 gap으로 주면 세로로 쌓일 때는 위아래 간격이, 가로로 놓일 때는 좌우 간격이 돼요.

    해 보기두 방식을 바꿔 가며 부모 폭을 줄였다 넓혀 보세요. 넓을 때 파란 점선 상자의 아래쪽을 보세요.

    HTML 코드 보기

    클래스를 누르면 꺼지고, 다시 누르면 켜져요. 눌러도 모형이 그대로면 그 이유가 코드 아래에 나와요.

    묶음 안에서 요소마다 간격이 달라 gap 하나로 안 될 때는 뒤따르는 요소에 mt-*를 줘요. 방향은 위쪽 하나로만 쓰고 묶음의 마지막 요소 아래에는 여백을 두지 않아요. 지금 dashboard.tsx가 이 방식이에요. 글 묶음과 카드 사이는 gap-8 lg:gap-20, 글 묶음 안은 mt-5와 mt-1이에요.

    ② 배경은 바깥 요소에, 폭 제한과 배치는 안쪽 요소에

    배경은 화면 끝까지 가야 하고 내용은 최대 폭 안에 있어야 해요. 한 요소가 둘을 다 할 수 없으니 나눠요.

    <section class="bg-paper">                  ← 배경: 화면 끝까지
      <div class="container-page                ← 폭 제한: 최대 폭과 좌우 여백
                  grid gap-8
                  lg:grid-cols-2 lg:gap-20">    ← 배치가 바뀌는 지점
        <div>...</div>                          ← 묶음 1: 제목 · 설명 · 버튼
        <figure>...</figure>                    ← 묶음 2: 카드
      </div>
    </section>

    dashboard.tsx 맨 위 주석 "섹션은 전체 폭, 내용만 container-page"가 이 규칙이에요.

    ③ HTML 순서는 모바일에서 읽는 순서로

    스크린리더와 탭 키는 화면에 보이는 위치가 아니라 HTML 순서를 따라요. 데스크톱은 CSS로 배치만 바꾸고 순서는 건드리지 않아요.

    상자가 두 시안에서 안 맞을 때

    모바일과 데스크톱의 모양이 가로·세로 전환보다 더 많이 다를 때예요. 방법이 세 가지 있어요. 위에서부터 시도해서 되는 첫 번째에서 멈춰요. 아래로 갈수록 관리할 것이 늘어나기 때문이에요.

    방법맞는 경우주의할 점
    1. CSS로 자리만 옮기기요소는 같고 위치만 달라요.화면에 보이는 순서와 HTML 순서가 달라져요. 읽는 순서가 뒤바뀔 정도면 쓰지 않아요.
    2. 둘 다 두고 하나만 보이기작은 조각의 모양이 달라요. 예: 햄버거 버튼 ↔ 가로 메뉴큰 덩어리에 쓰면 같은 내용을 두 번 관리해요.
    3. JS로 하나만 그리기모양보다 동작이 달라요. 예: 카드 목록 ↔ 표, 하단 시트 ↔ 팝오버서버에서 화면을 미리 그리는 구조면 처음에 다른 쪽이 잠깐 보일 수 있어요.

    해 보기방법마다 부모 폭을 줄였다 넓혀 보세요.

    HTML 코드 보기

    클래스를 누르면 꺼지고, 다시 누르면 켜져요. 눌러도 모형이 그대로면 그 이유가 코드 아래에 나와요.

    방법을 고른 뒤: 두 벌이 되는 범위 정하기

    방법 2와 3은 같은 내용을 두 가지 모양으로 만들어요. 그래서 방법을 고른 다음에는 어디까지 두 벌로 만들지를 따로 정해요. 아래 두 방식은 방법 2·3 중 하나를 가리키는 게 아니라, 둘 모두에 해당하는 기준이에요.

    흔한 방식섹션이나 페이지 전체를 두 벌 만들어요. 방법 2라면 hidden lg:block / lg:hidden, 방법 3이라면 isDesktop ? <DesktopPage /> : <MobilePage />예요. 빠르지만 고칠 때마다 두 곳을 고쳐야 해요.
    더 나은 방식데이터와 로직은 하나로 두고, 모양이 다른 조각의 보여 주는 컴포넌트만 나눠요. 방법 2라도 <ReactionCards rows={rows} />와 <ReactionTable rows={rows} />를 같이 두고 하나만 보이면 rows는 하나예요.

    방법 3이 방법 2보다 나은 것도 아니에요. 방법 2는 처음부터 맞는 쪽이 보이고 JS가 필요 없어요. 방법 3은 둘을 HTML에 같이 둘 수 없을 때만 써요. 포커스 관리가 서로 다르거나, 같은 id나 폼 필드가 두 번 들어가거나, 두 번 그리기에 무거울 때예요.

    "아예 다른 디자인"으로 보여도, 두 시안에서 같은 요소에 같은 색을 칠해 보면 정말 다른 부분은 메뉴나 표 같은 한두 조각인 경우가 많아요. 그 조각에만 방법 2나 3을 쓰고 나머지는 그대로 둬요.

    익숙해지는 방법

    기본형 이름으로 부르기

    레이아웃은 몇 가지 기본형의 조합이에요. 아래는 Every Layout이 붙인 이름 중 자주 쓰는 다섯 개예요.

    해 보기이름마다 있는 부모 폭을 줄였다 넓혀 보세요. 그 이름의 모형들이 같이 바뀌어요. "HTML 코드 보기"를 펼쳐서 클래스를 누르면 그 클래스가 꺼지고 위 모형에 바로 반영돼요. 다시 누르면 켜져요. 눌러도 모형이 그대로일 때는 그 이유가 코드 아래에 나와요. Sidebar에서는 눌러서 달라진 것도 무엇이 왜 달라졌는지 나와요.

    기본형하는 일flex와 grid 중 고르는 기준
    Stack세로로 쌓고 일정한 간격결과가 같아요. 더 자주 보이는 건 flex flex-col이고, 한 단어 짧은 건 grid예요.

    grid grid gap-2 자식이 한 열에 쌓여요.

    HTML 코드 보기

    클래스를 누르면 꺼지고, 다시 누르면 켜져요. 눌러도 모형이 그대로면 그 이유가 코드 아래에 나와요.

    flex flex flex-col gap-2 방향을 세로로 바꾼 flex예요. 보이는 결과는 grid와 같아요.

    HTML 코드 보기

    클래스를 누르면 꺼지고, 다시 누르면 켜져요. 눌러도 모형이 그대로면 그 이유가 코드 아래에 나와요.

    Cluster가로 나열, 넘치면 줄바꿈flex예요. grid로 하면 칸 폭이 모두 같아져서 다른 모양이 돼요. ()

    flex flex flex-wrap gap-2 칸 폭이 내용만큼이에요.

    HTML 코드 보기

    클래스를 누르면 꺼지고, 다시 누르면 켜져요. 눌러도 모형이 그대로면 그 이유가 코드 아래에 나와요.

    grid grid grid-cols-[repeat(auto-fit,minmax(7rem,1fr))] gap-2 줄바꿈은 되지만 칸 폭이 모두 같아져요.

    HTML 코드 보기

    클래스를 누르면 꺼지고, 다시 누르면 켜져요. 눌러도 모형이 그대로면 그 이유가 코드 아래에 나와요.

    Switcher좁으면 세로, 넓으면 가로grid예요. 부모에 클래스 하나만 더하면 되고 자식은 건드리지 않아요.

    grid grid gap-2 lg:grid-cols-2 부모에 lg:grid-cols-2만 더해요.

    HTML 코드 보기

    클래스를 누르면 꺼지고, 다시 누르면 켜져요. 눌러도 모형이 그대로면 그 이유가 코드 아래에 나와요.

    flex flex flex-col gap-2 lg:flex-row 부모는 방향을 바꾸고, 자식마다 lg:flex-1로 폭을 나눠 가져요.

    HTML 코드 보기

    클래스를 누르면 꺼지고, 다시 누르면 켜져요. 눌러도 모형이 그대로면 그 이유가 코드 아래에 나와요.

    Center최대 폭을 두고 가운데 정렬flex도 grid도 아니고 mx-auto예요. 부모를 건드리지 않고 자기 클래스만으로 끝나요.

    margin mx-auto max-w-80 px-4 기본이에요. 좌우 margin이 남는 폭을 반씩 가져요.

    HTML 코드 보기

    클래스를 누르면 꺼지고, 다시 누르면 켜져요. 눌러도 모형이 그대로면 그 이유가 코드 아래에 나와요.

    flex flex justify-center 부모가 자식을 가운데로 모아요. 자식에 w-full이 없으면 내용 폭으로 줄어들어요.

    HTML 코드 보기

    클래스를 누르면 꺼지고, 다시 누르면 켜져요. 눌러도 모형이 그대로면 그 이유가 코드 아래에 나와요.

    grid grid justify-items-center flex와 같은 구조고, justify-center 자리에 justify-items-center를 써요. Chrome처럼 justify-items-center를 grid가 아닌 보통 블록 요소에도 적용하는 브라우저에서는, 코드에서 grid를 꺼도 자식이 가운데에 그대로 있어요. 최근에 생긴 동작이라 다른 브라우저에서는 grid를 끄면 가운데 정렬이 풀릴 수 있으니, 실제 코드에서는 grid와 같이 써요.

    HTML 코드 보기

    클래스를 누르면 꺼지고, 다시 누르면 켜져요. 눌러도 모형이 그대로면 그 이유가 코드 아래에 나와요.

    block h-32 content-center 세로 가운데도 flex나 grid 없이 돼요. content-center(align-content: center)는 2024년부터 주요 브라우저가 보통 블록 요소에서도 지원해요. 남는 높이가 있어야 하니 부모에 높이를 줬고, 가로 가운데는 안쪽 요소의 mx-auto가 그대로 맡아요.

    HTML 코드 보기

    클래스를 누르면 꺼지고, 다시 누르면 켜져요. 눌러도 모형이 그대로면 그 이유가 코드 아래에 나와요.

    Sidebar한쪽 고정 폭, 나머지 전부폭이 정해진 2열 뼈대는 grid예요. 사이드바를 접었다 펴거나 폭을 내용에 맡기면 flex예요.

    grid grid grid-cols-[8rem_minmax(0,1fr)] gap-2 두 열의 폭이 부모 한 줄에 다 있어요. 둘째 열을 1fr로만 쓰면 띄어쓰기 없는 긴 낱말보다 좁아지지 못해서 부모 밖으로 넘쳐요. 그래서 minmax(0,1fr)로 써요. 상자가 좁아졌을 때 줄바꿈할 곳이 없는 긴 낱말이 상자 밖으로 삐져나오는 건 wrap-break-word가 막아요.

    HTML 코드 보기

    클래스를 누르면 꺼지고, 다시 누르면 켜져요. 눌러도 모형이 그대로면 그 이유가 코드 아래에 나와요.

    flex flex gap-2 폭을 자식이 정해요. 부모 폭을 끝까지 줄인 뒤 클래스를 꺼 보세요. min-w-0이 없으면 본문 상자가 띄어쓰기 없는 긴 낱말보다 좁아지지 못해서 부모 밖으로 넘쳐요. 그때 shrink-0까지 없으면 사이드바도 눌려요. min-w-0은 상자만 줄여요. 줄어든 상자보다 긴 낱말이 상자 밖으로 삐져나오는 건 wrap-break-word가 막아요. 꺼 보면 삐져나오는 게 보여요.

    HTML 코드 보기

    클래스를 누르면 꺼지고, 다시 누르면 켜져요. 눌러도 모형이 그대로면 그 이유가 코드 아래에 나와요.

    코드의 값은 모형과 같아요. 다만 모형은 lg: 대신 부모 폭 520px에서 바뀌고, 상자의 색과 안쪽 여백은 코드에서 뺐어요. Center의 점선은 px-4가 만드는 좌우 여백까지 포함한 상자예요.

    데모의 화면을 이 이름으로 읽으면 이래요.

    Center            mx-auto max-w-5xl px-4
    └ Switcher        grid gap-8 lg:grid-cols-2
       ├ Stack        grid gap-4          ← 제목 · 설명 · 버튼
       └ 카드

    화면을 볼 때 이렇게 이름이 붙으면 묶음이 정해진 거예요. 이름이 안 붙으면 구조가 아직 애매한 거예요.

    연습 방법

    1. 잘 만든 사이트를 개발자 도구로 열어요.폭을 줄였다 넓혔다 하면서 어느 요소에서 배치가 바뀌는지 찾아요. 그 요소가 묶음들의 부모예요.
    2. 코드를 쓰기 전에 시안 위에 상자를 그려요.종이든 Figma든 상관없어요. 의 질문에 답하는 과정이 눈에 남아요.
    3. 그린 상자를 기본형 이름으로 불러 봐요.몇 화면만 이렇게 해 보면 시안을 볼 때 상자가 먼저 보이기 시작해요.

    3부배치 너머: 상호작용과 우선순위

    여기까지는 요소를 어디에 놓느냐만 다뤘어요. 모바일과 데스크톱은 그 밖에도 달라요.

    배치만 바꾸면 충분한가

    화면 종류에 따라 달라요.

    읽는 화면 — 대체로 충분랜딩, 글, 소개 화면이에요. 위에서 아래로 읽고 버튼 하나 누르는 일은 장치가 달라도 같아요. 재배치에 상호작용()만 더하면 돼요.
    일하는 화면 — 부족함대시보드, 편집기, 관리 화면이에요. 한 손으로 잠깐 쓰는 상황과 책상에서 오래 쓰는 상황이 달라서 우선순위와 이동 방식()부터 달라져요.
    화면마다 던질 질문"이 화면에서 사용자가 하려는 일이, 폰을 들고 있을 때와 책상에 앉아 있을 때 같은가?" 같으면 읽는 화면처럼, 다르면 일하는 화면처럼 다뤄요.

    무엇이 같고 무엇이 다른가

    층모바일과 데스크톱다루는 곳
    내용, 기능같음바로 아래
    배치다름1부, 2부
    상호작용다름
    우선순위, 이동, 입력다름

    내용과 기능은 줄이지 않아요. "모바일 사용자는 바쁘니까 기능을 빼자"는 접근은 권하지 않아요. 모바일로만 접속하는 사람에게는 뺀 기능이 곧 못 쓰는 기능이 돼요. 구글 검색도 모바일 화면의 내용을 기준으로 색인해요.

    상호작용: 호버와 터치

    데스크톱에서는 호버 효과가, 모바일에서는 터치 동작이 경험을 좋게 만들어요. 이걸 설계할 때 전제가 두 가지 있어요.

    기준은 화면 폭이 아니라 입력 장치아이패드는 넓지만 터치고, 터치스크린 노트북은 둘 다 돼요. lg:로 나누지 않고 입력 장치 조건으로 나눠요.
    달라지는 건 상호작용뿐레이아웃과 내용은 같아요. 같은 요소에 "호버일 때는 이것, 터치일 때는 저것"을 붙여요. 화면을 두 벌 만들지 않아요.

    입력 장치 조건

    CSS 조건뜻Tailwind v4
    (hover: hover)호버가 되는 장치hover: (이 조건이 자동으로 붙어요)
    (pointer: fine)마우스처럼 정밀한 포인터pointer-fine:
    (pointer: coarse)손가락처럼 뭉툭한 포인터pointer-coarse:

    해 보기버튼에 마우스를 올리거나, 탭 키로 이동하거나, 눌러 보세요. 폰에서 열면 결과가 달라져요.

    지금 이 기기 →

    마우스는 올리면 떠오르고, 키보드는 초점이 가면 같은 효과가 나고, 터치는 누르는 동안 작아져요. 손가락 기기에서는 버튼도 더 커요. HTML은 하나예요.
    HTML 코드 보기

    클래스를 누르면 꺼지고, 다시 누르면 켜져요. 눌러도 모형이 그대로면 그 이유가 코드 아래에 나와요.

    호버를 터치로 옮기기

    호버 효과를 설계할 때마다 "터치에서는 이 목적을 무엇으로 채우나"를 같이 정해요. 아래가 그 짝이에요.

    호버로 하던 것터치에서는
    올리면 미리보기스크롤로 화면에 들어올 때 재생, 또는 탭해서 열기
    올리면 버튼이 나타남항상 보이게 하거나, 옆으로 밀기, 길게 누르기
    커서를 따라다니는 효과스크롤에 연동된 움직임
    툴팁글자로 항상 표시하거나, 탭하면 열리는 말풍선
    올리면 펼쳐지는 메뉴탭하면 열리는 서랍·아코디언
    화살표 눌러 넘기기좌우로 밀기

    잘 알려진 예시

    서비스마우스터치
    YouTube썸네일에 올리면 미리보기 재생스크롤하다 화면에 들어오면 자동 재생
    Netflix카드에 올리면 커지면서 예고편 표시탭하면 상세 시트가 올라옴
    Instagram그리드 사진에 올리면 좋아요 수 표시두 번 탭해서 좋아요
    Pinterest핀에 올리면 저장 버튼 표시길게 누르면 메뉴가 펼쳐짐
    Airbnb카드에 올리면 사진 넘김 화살표 표시사진을 좌우로 밀어서 넘김

    공통점은 같은 목적을 장치에 맞는 동작으로 바꿨다는 거예요. YouTube는 양쪽 다 "누르기 전에 미리 보기"가 목적이에요. 서비스 화면은 수시로 바뀌니 지금은 다를 수 있어요.

    CSS만으로 되는 좌우 넘김

    손가락으로 밀거나 트랙패드로 넘기면 카드 단위로 멈춰요. 부모의 flex overflow-x-auto snap-x snap-mandatory와 자식의 snap-start면 돼요. 하단 시트나 밀어서 삭제처럼 손가락을 따라가는 동작은 보통 라이브러리를 써요.
    HTML 코드 보기

    클래스를 누르면 꺼지고, 다시 누르면 켜져요. 눌러도 모형이 그대로면 그 이유가 코드 아래에 나와요.

    흔한 방식lg:를 기준으로 데스크톱에만 호버 효과를 넣고 모바일은 비워 둬요. 모바일이 밋밋해지고 아이패드에서 어긋나요.
    더 나은 방식입력 장치 조건으로 나누고 호버 효과마다 터치에서의 짝을 같이 정해요.
    지켜야 할 한 가지호버로만 볼 수 있는 정보나 기능은 만들지 않아요. 호버는 더 즐겁게 하는 층이고 없어도 쓸 수 있어야 해요. 키보드 사용자도 호버를 못 하니 focus-visible:을 같이 붙여요.

    우선순위와 이동: 일하는 화면

    폰은 한 손으로 잠깐씩 쓰고 데스크톱은 책상에서 두 손으로 오래 써요. 그래서 조작을 반복하는 화면은 배치 말고도 아래가 달라져요.

    달라지는 것모바일데스크톱
    우선순위한 화면에 한 가지 일. 나머지는 접어요.목록과 상세를 나란히 펼쳐요.
    이동 방식엄지가 닿는 아래쪽 탭위쪽이나 옆쪽 메뉴
    입력입력 칸을 줄이고 선택지나 자동 완성으로타이핑, 단축키
    장치 기능카메라, 공유, 위치끌어다 놓기, 여러 개 한꺼번에 처리

    가장 흔한 예가 목록과 상세예요. 넓으면 둘을 나란히 보여 주고 좁으면 한 번에 하나만 보여 줘요.

    해 보기넓은 상태에서 항목을 눌러 보고 부모 폭을 줄인 뒤 다시 눌러 보세요.

      왼쪽에서 하나를 고르세요.

      내용과 기능은 양쪽이 같아요. 달라진 건 "한 번에 무엇을 보여 주나"와 "어떻게 이동하나"예요. 좁을 때만 뒤로 가기 버튼이 생겨요.

      이런 차이는 의 방법 2나 3으로 만들어요. 위 데모는 방법 2예요.

      모바일을 먼저 설계하라는 조언(Luke Wroblewski, 『Mobile First』)도 같은 맥락이에요. 좁은 화면에서 먼저 정하면 무엇이 중요한지 골라야 하고 데스크톱은 거기에 여유를 더하는 작업이 돼요. 반대로 하면 데스크톱 화면을 좁은 화면에 구겨 넣게 돼요.

      마무리정리

      읽은 순서와 실제로 작업하는 순서는 달라요. 화면 하나를 만들 때는 아래 순서로 해요.

      한 화면을 만드는 순서

      1. 두 시안을 나란히 놓고 상자를 그려요.간격이 넓어지는 곳과 좌우로 갈라지는 곳이 경계예요. → ,
      2. 상자대로 HTML 구조를 써요.순서는 모바일에서 읽는 순서로 쓰고 배경과 폭 제한은 다른 요소에 둬요. →
      3. 묶음마다 flex인지 grid인지 골라요.폭을 서로 맞춰야 하면 grid, 내용 길이에 맡기면 flex예요. →
      4. 모바일 스타일을 쓰고, 달라지는 것만 lg:로 더해요.묶음 사이 간격은 부모의 gap으로 줘요. → ,
      5. 상자가 안 맞는 조각만 따로 처리해요.CSS로 자리 옮기기, 숨기기, JS 순서로 시도해요. →
      6. 호버 효과를 넣을 때마다 터치에서의 짝을 정해요.호버로만 쓸 수 있는 기능은 만들지 않아요. →
      7. 일하는 화면이면 우선순위와 이동 방식을 따로 정해요.내용과 기능은 줄이지 않아요. → ,

      더 읽을거리

      이름내용
      Every Layout레이아웃을 기본형으로 나누는 방법이에요. 의 이름이 여기서 왔어요.
      Intrinsic Web DesignJen Simmons가 붙인 이름이에요. 브레이크포인트를 늘리기보다 fr, minmax, auto-fit으로 스스로 맞춰지게 해요.
      Atomic DesignBrad Frost의 방법이에요. 작은 부품 → 묶음 → 섹션 순으로 쌓아요. 컴포넌트를 어디서 자를지 정할 때 써요.
      Mobile FirstLuke Wroblewski의 책이에요. 좁은 화면에서 먼저 정해야 하는 이유를 다뤄요.
      Tailwind 반응형 문서모바일 우선 규칙과 컨테이너 쿼리(@container) 사용법이 있어요.