7.1 header
앞에서 배운 지식과 구현 실습으로 모바일 퍼스트 반응형 웹사이트를 알아보겠습니다.
이 실습 파일은 노션 페이지에 있습니다.폴더의 경로는 이렇습니다
HTML에서 CSS와 Javascript, 이미지 파일을 불러오기 위해서는 파일명과 폴더명을 일치시켜야 합니다. 파일명과 폴더명은 대소문자도 구분하니 일치시켜 주시기를 바랍니다.
reset css
파일명 :
style.css
/* 리셋 css */ *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; } li { list-style: none; } a { text-decoration: none; } a, img, span, table, tbody, button { display: block; } button, input { font: inherit; background: none; border: none; } input { width: 100%; } button { cursor: pointer; } address { font-style: normal; line-height: 1.8; } html { font-family: var(--ff-notosans); font-size: 10px; /* height: 300vh; */ } body { background: var(--white); font-size: 1.6rem; padding-block-start: 90px; }
Chrome , Edge , Firefox , Safari 등 여러 브라우저에는 HTML 태그의 기본 스타일을 적용하는 방식이 다릅니다.
크로스 브라우징을 하여도 우리가 만든 웹페이지가 같은 모습으로 보이도록 하기 위해서
reset.css를 적용하겠습니다.
:root
파일명 :
style.css
:root { /* 색 */ --main_color: rgba(92, 206, 89, 1); --main_color_light: rgba(92, 206, 89, 0.85); --gray: hsl(0, 0%, 74%); --light_gray: hsl(0, 0%, 88%); --toasty_gray: hsl(200, 12%, 95%); --white: hsl(0, 0%, 100%); --black: hsl(0, 0%, 0%); --silver: hsl(0, 0%, 27%); /* 폰트 */ --ff-notosans: "Noto Sans KR", sans-serif; /*폰트사이즈 font-size */ --fs-1: 3rem; --fs-2: 2.6rem; --fs-3: 2.2rem; --fs-4: 2rem; --fs-5: 1.8rem; --fs-6: 1.6rem; --fs-7: 1.4rem; --fs-8: 1.2rem; /* 폰트웨이트 font-weight */ --fw-300: 300; --fw-500: 500; --fw-600: 600; --fw-700: 700; /* 트렌지션 */ --transition-1: 0.25s ease; --transition-2: 0.5s ease; --cubic-out: cubic-bezier(0.51, 0.03, 0.64, 0.28); --cubic-in: cubic-bezier(0.33, 0.85, 0.56, 1.02); /* section 별 padding */ --section-padding: 60px; }
:root에 우리가 공통으로 사용할 색상이나 폰트 등에 미리 속성값을 정해주어 유지보수가 용이하게 만듭니다.
예시로 웹사이트의 대표 색상을 바꿔야 하는 상황이 생긴다면 적용된 태그들의 css를 찾아 하나씩 바꿔주는 것이 아닌 :root에서 속성값 색상을 바꿔주어 한 번에 교체해 줄 수 있는 장점이 있습니다.
/*사용방법 :root에 적혀진 속성의 이름을 var()에 적어줍니다.*/ 예시) h1{ color : var(--main-color) }
Javascript, ionicons
파일명 :
index.html
</footer> </article> </main> <!-- 자바스크립트 --> <script src="./assets/js/script.js"></script> <!-- 아이콘 자바스크립트 ionicon link --> <script type="module" src="https://unpkg.com/ionicons@7.1.0/dist/ionicons/ionicons.esm.js" ></script> <script nomodule src="https://unpkg.com/ionicons@7.1.0/dist/ionicons/ionicons.js" ></script> </body> </html>
body
태그 끝나는 부분 바로 위에 작성합니다.햄버거 메뉴를 클릭했을 때 웹사이트의 좌측에서 사이드 메뉴바가 나올 수 있는 이벤트 핸들러 작성을 자바스크립트와 여러 아이콘을 간단하게 적용하기 위한 ionicons 자바스크립트를 적어줍니다.
파일명 :
index.html
<!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>shop</title> <!-- css와 연결--> <link rel="stylesheet" href="./assets/css/style.css" /> <!-- 구글폰트 --> <link rel="preconnect" href="https://fonts.googleapis.com" /> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght @100;200;300;400;500;600;700;800;900&display=swap" rel="stylesheet" /> </head>
index.html의 head 부분입니다.
css와 연결 주석 부분에서 css를 연결해 주고 있습니다.
구글 폰트를 불러오는 곳은 웹폰트를 이용하여 폰트를 불러오고 있습니다.
파일명 :
index.html
<body id="top"> <!-- 헤더 --> <header class="header"> <div class="container"> <!-- dim = 사이드 메뉴바 외 배경 검게 처리--> <div class="dim"></div> <a href="#" class="logo"> <img src="./assets/images/logo.svg" width="190" height="50" alt="로고" /> </a> <!-- 사이드 메뉴바 오픈 버튼 --> <button class="nav-open-btn"> <ion-icon name="menu-outline"></ion-icon> </button> <nav class="navbar"> <!-- 사이드 메뉴바 클로즈 버튼 --> <button class="nav-close-btn"> <ion-icon name="close-outline"></ion-icon> </button> <a href="#" class="logo"> <img src="./assets/images/logo.svg" alt="로고2" /> </a>
헤더 부분의 로고와 사이드 메뉴바 열기 버튼입니다.
메뉴바는 사용자가 햄버거 메뉴 버튼을 클릭하면 왼쪽에서 사이드 메뉴바가 나오는 방식입니다.
파일명:
script.js
const dim = document.querySelector(".dim"); const navOpenBtn = document.querySelector(".nav-open-btn"); const navBar = document.querySelector(".navbar"); const navCloseBtn = document.querySelector(".nav-close-btn"); const navElems = [dim, navOpenBtn, navCloseBtn]; // 햄버거 메뉴 버튼 클릭 이벤트와 사이드 메뉴바 외 어둡게 처리 for (let i = 0; i < navElems.length; i++) { navElems[i].addEventListener("click", function () { navBar.classList.toggle("active"); dim.classList.toggle("active"); }); }
이 코드는 햄버거 메뉴 버튼을 클릭했을 때 사이드 메뉴바를 화면에 렌더링하기 위한 자바스크립트 코드입니다.
코드의 내용은 사용자가 햄버거 메뉴 버튼을 클릭하면 HTML의
<button class="nav-open-btn">
부분에 <button class="nav-open-btn active">
active 클래스명이 추가되어 active의 css가 적용되어 사이드 메뉴바가 생성되고,
dim에도 active 클래스명이 추가되어 사이드 메뉴바가 아닌 곳을 검게 처리해주는 방식입니다.햄버거 메뉴 버튼을 클릭하여 왼쪽에서 사이드 메뉴바가 나온 모습입니다.
<div class="dim"></div>
앞으로 만들 사이드 메뉴바를 제외한 곳을 어둡게 처리하여 사용자가 사이드 메뉴바에 집중할 수 있도록 한 곳입니다.사용자는 로고 위의 x 아이콘을 클릭하여 사이드 메뉴바를 다시 넣을 수 있습니다.
<main> <article> <section class="section main" style="background-image: url(./assets/images/main-banner.png)" > <div class="container"> <h1 class="h1 main-title"> 안녕하세요<strong>WebTravel 쇼핑몰 입니다.</strong> </h1> <p class="main-text"> Lorem ipsum dolor sit amet consectetur adipisicing elit. Nisivoluptatem veritatis reiciendis. </p> <!-- 메인 더보기 버튼 --> <button class="btn btn-primary"> <span>더보기</span> <!-- 더보기 화살표 아이콘 --> <ion-icon name="arrow-forward-outline" aria-hidden="true" ></ion-icon> </button> </div> </section>

section 태그에 background-image에 url 경로를 정해주어 배경 이미지를 넣어주었습니다.
/*재사용 가능한 스타일*/ .container { padding-inline: 15px; } .section { padding-block: var(--section-padding); } .h1, .h2, .h3, .h4 { color: var(--black); } .h1 { font-size: var(--fs-1); font-weight: var(--fw-300); line-height: 1.5; } .h2 { font-size: var(--fs-2); } .h3 { font-size: var(--fs-4); } .h4 { font-size: var(--fs-5); text-transform: uppercase; }
재사용이 가능하게
container
와 section
클래스의 css 값을 만들어주고 영역을 나눌 때 사용할 수 있게 만들어 주었습니다.h1부터 h4까지 root에서 지정한 크기로 만들어 주었습니다.
/* 더보기 버튼 */ .btn { background: var(--background, var(--main_color)); color: var(--color, var(--white)); font-size: var(--fs-5); display: flex; align-items: center; gap: 5px; padding: 14px 25px; border: 1px solid var(--border-color, var(--main_color)); } /* 더보기 버튼 호버,포커스 시 */ .btn-primary:is(:hover, :focus) { --background: var(--main_color_light); --border-color: var(--main_color_light); }
더보기 버튼의 배치와 마우스가 더보기 버튼의 위에 올라갔을 때, 키보드의 탭으로 포커스가 되었을 때 색상이 연해지도록 설정했습니다.
.header{ background-color: var(--white); position: fixed; top: 0; left: 0; width: 100%; transition: var(--transition-1); z-index: 4; }
헤더의 로고와 햄버거 메뉴 버튼이 웹페이지를 스크롤 하여도 상단에 항상 있도록
fixed
로 고정해주었습니다..header{ box-shadow: 0 2px 10px hsla(0, 0%, 0%, 0.1); } .header .container { display: flex; justify-content: space-between; align-items: center; padding: 20px 15px; }
헤더의 경계를 주기 위하여
box-shadow
로 그림자를 주었습니다..nav-open-btn { font-size: 30px; background: var(--main_color); color: var(--white); padding: 8px; } .nav-open-btn:is(:hover, :focus) { background: var(--main_color_light); }
.nav-open-btn
의 font-size
는 아이콘의 크기를 키우기 위해 사용되었고, 사용자의 커서가 햄버거 버튼의 위에 올라갔을 때, 키보드의 탭으로 포커스가 되었을 때 색상이 연해지도록 설정했습니다..navbar { background: var(--white); position: fixed; top: 0; left: -280px; width: 100%; max-width: 270px; height: 100%; border-right: 3px solid var(--black); font-family: var(--ff-notosans); overflow-y: auto; overscroll-behavior: contain; z-index: 2; visibility: hidden; transition: 0.25s var(--cubic-out); }
사이드 메뉴바의 설정입니다.
visibility
에 hidden
을 주어 숨겨두고 햄버거 버튼을 클릭했을 때 visibility: visible
속성값을 주어 보이도록 하였습니다.<ul class="navbar-list"> <li class="navbar-item"> <a href="#" class="navbar-link">홈</a> </li> <li class="navbar-item"> <a href="#" class="navbar-link">카테고리</a> </li> <li class="navbar-item"> <a href="#" class="navbar-link">베스트</a> </li> <li class="navbar-item"> <a href="#" class="navbar-link">블로그</a> </li> <li class="navbar-item"> <a href="#" class="navbar-link">기획전</a> </li> <li class="navbar-item"> <a href="#" class="navbar-link">빠른배송</a> </li> <ul class="nav-action-list"> <li> <button class="nav-action-btn"> <span class="nav-action-text">로그인</span> </button> </li> <li> <button class="nav-action-btn"> <span class="nav-action-text">장바구니</span> </button> </li> </ul> </ul> </nav> </div> </header>
.navbar.active { visibility: visible; transform: translateX(280px); transition: 0.5s var(--cubic-in); }
햄버거 버튼을 클릭하여 사이드 메뉴바가 왼쪽에서 나올 때의 속성값입니다.
transform
의 translateX(280px)
은 위의 .navbar
의 left:-280px
으로 숨겨둔 사이드 메뉴바를 X축으로 이동시키는 속성입니다. transition
을 0.5s로 주어 사이드 메뉴바가 부드럽게 꺼내지는 모습을 볼 수 있습니다..nav-close-btn { color: var(--black); position: absolute; top: 0; right: 0; padding: 13px; font-size: 25px; transition: var(--transition-1); } .nav-close-btn:is(:hover, :focus) { color: var(--main_color); } .navbar .logo { padding-block: 50px 40px; }
padding-block
속성은 로고 위의 패딩 값을 50px , 로고 아래의 패딩 값을 40px 준 것입니다.이 속성은 padding:50px 0 40px
과 같습니다..navbar .logo img { margin:0 auto; } .navbar-list, .nav-action-list { margin: 30px; } .navbar-list { padding: 20px; border-bottom: 1px solid var(--light_gray); } .navbar-link { color: var(--black); padding-block: 10px; transition: var(--transition-1); } .navbar-link:is(:hover, :focus) { color: var(--main_color); }
:is
는 괄호()안에 있는:hover
나 :focus
가 되면 속성을 바꾸어 주는 가상선택자 입니다./* :is 가상선택자를 설명하기 위한 예시입니다 */ .navbar-link:hover { color: var(--main_color); } .navbar-link:focus { color: var(--main_color); }
:is
는 이 코드를 줄이기 위하여 사용되었습니다..navbar-item{ border-bottom: 1px solid var(--light_gray); } .nav-action-btn { color: var(--black); display: flex; align-items: center; width: 100%; padding-block: 10px; transition: var(--transition-1); } .nav-action-btn:is(:hover, :focus) { color: var(--main_color); }
/* 딤처리 dimmed */ .dim { position: fixed; inset: 0; background: hsla(0, 0%, 0%, 0.6); z-index: 1; opacity: 0; pointer-events: none; transition: var(--transition-1); } .dim.active { opacity: 1; pointer-events: all; }
사이드 메뉴바가
active
되었을 때 사이드 메뉴바 이외의 레이아웃을 어둡게 처리하는 부분입니다.opacity
속성에 0을 주어 사이드 메뉴바가 나오기 전에는 어둡게 처리하지 않고 햄버거 메뉴 버튼을 눌렀을 때 active 클래스 명이 들어가면서 opacity:1
이 적용되어 주변이 어둡게 처리하였습니다.파일명 :
index.html
/* 메인 */ .main { background-repeat: no-repeat; background-size: cover; background-position: left; min-height: 400px; display: flex; align-items: center; } .main-title { margin-block: 10px; } .main-title > strong { display: block; } .main-text { color: var(--silver); font-family: var(--ff-notosans); font-size: var(--fs-8); line-height: 1.8; max-width: 40%; margin-bottom: 25px; }
.main
부분입니다“WebTravel 쇼핑몰입니다.”라는 문구를 “안녕하세요”라는 문구 밑에 넣기 위해서
.main-title > strong
부분에 display: block
을 주었습니다..main
클래스에 display: flex
속성을 사용하여 메인의 background-image
, .main-title
.main-text
를 가로로 나열합니다. justify-content: flex-start
속성을 통해 왼쪽으로 정렬하고, 요소들을 가운데 정렬하기 위해 align-items: center
속성을 사용합니다./* 미디어쿼리를 이용하여 사용자의 너비가 최소 992px 이상이되면 적용됩니다.*/ @media (min-width: 992px) { /* 헤더 */ .nav-open-btn, .nav-close-btn, .navbar .logo, .nav-action-text, .dim { display: none; }
사용자의 웹사이트 화면 너비가 992px 이상으로 커지게 되면 햄버거버튼과 사이드 메뉴등을 보이지 않게 처리한 곳입니다.
.navbar, .navbar-list, .nav-action-list { all: unset; }
사용자의 웹사이트 화면 너비가 커졌으므로 사이드 메뉴바로 표현하지 않고 로고 옆에 메뉴바로 만들어서 표현하는 부분입니다.
.navbar-list{ display: flex; }
메뉴바의 요소들을 수평으로 정렬합니다.
.nav-action-list { gap: 20px; } .navbar { display: flex; align-items: center; flex-grow: 1; } .navbar-list { margin: 0 auto; gap: 35px; } .navbar-link { font-weight: var(--fw-500); } .main { height: 480px; } }
@media (min-width: 992px)
미디어쿼리 적용 이후의 header
입니다.
7.2 main
<!-- 프로덕트 --> <section class="section product"> <div class="container"> <h2 class="h2 section-title">베스트 랭킹</h2> <!-- 프로덕트 필터 리스트 --> <ul class="filter-list"> <li> <button class="filter-btn">All</button> </li> <li> <button class="filter-btn">침대</button> </li> <li> <button class="filter-btn">소파</button> </li> <li> <button class="filter-btn">수납장</button> </li> <li> <button class="filter-btn">의자</button> </li> <li> <button class="filter-btn">책상</button> </li> </ul>
/* 프로덕트 */ .product .section-title { text-align: center; margin-bottom: 25px; } .filter-list { display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: 10px; margin-bottom: 30px; } .filter-btn { color: var(--silver); padding: 10px 16px; font-family: var(--ff-notosans); font-size: var(--fs-7); font-weight: var(--fw-500); border: 1px solid var(--light_gray); border-radius: 30px; }
.filter-list
클래스에 display: flex;
와 flex-wrap: wrap;
속성을 사용해 .filter-btn
을 가로로 나열하고, 작은 화면에서 .filter-btn
이 여러 줄로 나열되도록 합니다. 또한, 버튼을 가운데 정렬하기 위해 justify-content: center;
과 align-items: center;
속성을 사용합니다.
<!-- 프로덕트 리스트 UL 시작 --> <ul class="product-list"> <!-- 상품 리스트 원목 1200 책장 시작 부분--> <li class="product-card"> <div class="product-card"></div> <figure class="card-banner"> <img src="./assets/images/product-1.jpg" alt="" width="312" height="350" alt="원목 1200 책장" class="image-contain" /> <!-- 상품 위 new 뱃지 --> <div class="card-badge">New</div> </figure> <div class="card-content"> <!-- 상품 분류 category--> <div class="card-cat"> <a href="#" class="card-cat-link">가구</a>/ <a href="#" class="card-cat-link">책장</a> </div> <!-- 상품명 --> <h3 class="h3 card-title"> <a href="#">원목 1200 책장</a> </h3> <!-- 가격 --> <data class="card-price" value="56000">56,000원</data> </div> </li>
.product-list { display: grid; gap: 50px 20px; } /* 프로덕트 */ .card-banner { background: var(--toasty_gray); position: relative; height: 350px; overflow: hidden; } .image-contain { width: 100%; height: 100%; object-fit: contain; object-position: center; transition: var(--transition-2); } /* 상품 호버,포커스시 효과 */ .product-card:is(:hover, :focus) .image-contain { transform: scale(1.1); } .card-badge { position: absolute; top: 20px; left: 20px; background: var(--main_color); color: var(--white); padding: 5px 15px; font-family: var(--ff-notosans); font-size: var(--fs-7); border-radius: 25px; } .card-content { padding: 24px 15px 0; text-align: center; } .card-cat { font-family: var(--ff-notosans); font-size: var(--fs-7); margin-bottom: 12px; } .card-cat-link { display: inline-block; color: inherit; transition: var(--transition-1); } .card-cat-link:is(:hover, :focus) { color: var(--main_color); }
.product-list
클래스에 display: grid;
속성을 사용하여 .product-card
컨텐츠를 정렬합니다. 그리고 .product-card
사이의 간격을 일정하게 유지하기 위해 gap: 50px 20px;
속성을 사용해 레이아웃을 깔끔하게 보이도록 합니다..card-banner
는 자식 요소인.image-contain
을 감싸는 역할을 하며, 이 이미지에 대한 위치와 크기를 조정하기 위해 position: relative;
속성을 사용합니다..card-badge
요소에 position: absolute;
속성을 적용하면 .card-banner
요소가.card-badge
의 부모 요소로 설정되어, .card-banner
를 기준으로 .card-badge
의 위치를 조절할 수 있습니다. 이렇게 하면,.card-badge
를 .card-banner
내부의 원하는 위치에 고정시킬 수 있고, 두 요소 간의 상대적인 위치를 조절할 수 있습니다.
<!-- 상품 리스트 컴퓨터 책상 + 의자 세트 시작 부분 --> <li class="product-card"> <div class="product-card"></div> <figure class="card-banner"> <img src="./assets/images/product-2.jpg" alt="" width="312" height="350" alt="컴퓨터 책상 플러스 의자 세트" class="image-contain" /> <div class="card-badge">New</div> </figure> <div class="card-content"> <div class="card-cat"> <a href="#" class="card-cat-link">가구</a>/ <a href="#" class="card-cat-link">책상</a> </div> <h3 class="h3 card-title"> <a href="#">컴퓨터 책상 + 의자 세트</a> </h3> <data class="card-price" value="120000">120,000원</data> </div> </li> <!-- 상품 리스트 원목 거실 테이블 시작 부분--> <li class="product-card"> <div class="product-card"></div> <figure class="card-banner"> <img src="./assets/images/product-3.jpg" alt="" width="312" height="350" alt="원목 거실 테이블" class="image-contain" /> <div class="card-badge">New</div> </figure> <div class="card-content"> <div class="card-cat"> <a href="#" class="card-cat-link">가구</a>/ <a href="#" class="card-cat-link">테이블</a> </div> <h3 class="h3 card-title"> <a href="#">원목 거실 테이블</a> </h3> <data class="card-price" value="69900">69,900원</data> </div> </li> <!-- 상품 리스트 원목 침대 부분 --> <li class="product-card"> <div class="product-card"></div> <figure class="card-banner"> <img src="./assets/images/product-4.jpg" alt="" width="312" height="350" alt="원목 침대" class="image-contain" /> <div class="card-badge">New</div> </figure> <div class="card-content"> <div class="card-cat"> <a href="#" class="card-cat-link">가구</a>/ <a href="#" class="card-cat-link">침대</a> </div> <h3 class="h3 card-title"> <a href="#">원목 침대</a> </h3> <data class="card-price" value="170.85">260,000원</data> </div> </li> <!-- 5번 째 상품 시작 부분 --> <!-- 상품 리스트 원목 전신거울 시작 부분--> <li class="product-card"> <div class="product-card"></div> <figure class="card-banner"> <img src="./assets/images/product-5.jpg" alt="" width="312" height="350" alt="원목 전신 거울" class="image-contain" /> <div class="card-badge">New</div> </figure> <div class="card-content"> <div class="card-cat"> <a href="#" class="card-cat-link">가구</a>/ <a href="#" class="card-cat-link">거울</a> </div> <h3 class="h3 card-title"> <a href="#">원목 전신거울</a> </h3> <data class="card-price" value="43000">43,000원</data> </div> </li> <!-- 상품 리스트 테이블 단스탠드 시작 부분 --> <li class="product-card"> <div class="product-card"></div> <figure class="card-banner"> <img src="./assets/images/product-6.jpg" alt="" width="312" height="350" alt="테이블 단스탠드" class="image-contain" /> <div class="card-badge">New</div> </figure> <div class="card-content"> <div class="card-cat"> <a href="#" class="card-cat-link">가구</a>/ <a href="#" class="card-cat-link">스탠드</a> </div> <h3 class="h3 card-title"> <a href="#">테이블 단스탠드</a> </h3> <data class="card-price" value="48000">48,000원</data> </div> </li> <!-- 상품 리스트 아쿠아텍스 4인 소파 시작 부분--> <li class="product-card"> <div class="product-card"></div> <!-- 피규어 1 --> <figure class="card-banner"> <img src="./assets/images/product-7.jpg" alt="" width="312" height="350" alt="아쿠아텍스 4인 소파" class="image-contain" /> <div class="card-badge">New</div> </figure> <div class="card-content"> <div class="card-cat"> <a href="#" class="card-cat-link">가구</a>/ <a href="#" class="card-cat-link">소파</a> </div> <h3 class="h3 card-title"> <a href="#">아쿠아텍스 4인 소파</a> </h3> <data class="card-price" value="330000">330,000원</data> </div> </li> <!-- 상품 리스트 안락 의자 부분 --> <li class="product-card"> <div class="product-card"></div> <figure class="card-banner"> <img src="./assets/images/product-8.jpg" alt="" width="312" height="350" alt="안락 의자" class="image-contain" /> <div class="card-badge">New</div> </figure> <div class="card-content"> <div class="card-cat"> <a href="#" class="card-cat-link">가구</a>/ <a href="#" class="card-cat-link">의자</a> </div> <h3 class="h3 card-title"> <a href="#">안락 의자</a> </h3> <data class="card-price" value="82000">82,000원 </data> </div> </li> </ul> </div> </article> </main> </section> <!-- 프로덕트 끝 -->
.card-cat-link:is(:hover, :focus) { color: var(--main_color); } .product-card .card-title { margin-bottom: 12px; } .product-card .card-title > a { color: inherit; transition: var(--transition-1); } .product-card .card-title > a:is(:hover, :focus) { color: var(--main_color); } .card-price { color: var(--black); font-family: var(--ff-notosans); font-weight: var(--fw-600); }
7.3 footer

웹의 가장 하단에 있는 풋터입니다.
웹의 로고와 소셜 네트워크 리스트가 있습니다.
<!-- 풋터 시작 --> <footer class="footer"> <div class="footer-top section"> <div class="container"> <div class="footer-brand"> <a href="#" class="logo"> <img src="./assets/images/logo.svg" width="160" height="50" /> </a> <!-- 소셜 네트워크 리스트 시작--> <ul class="social-list"> <!-- 풋터 소셜 페이스북 시작 --> <li> <a href="#" class="social-link"> <ion-icon name="logo-facebook"></ion-icon> </a> </li> <!-- 풋터 소셜 페이스북 끝 --> <!-- 풋터 소셜 트위터 시작 --> <li> <a href="#" class="social-link"> <ion-icon name="logo-twitter"></ion-icon> </a> </li> <!-- 풋터 소셜 트위터 끝 --> <!-- 풋터 소셜 인스타그램 시작 --> <li> <a href="#" class="social-link"> <ion-icon name="logo-instagram"></ion-icon> </a> </li> <!-- 풋터 소셜 인스타그램 끝 --> <!-- 풋터 소셜 유튜브 시작 --> <li> <a href="#" class="social-link"> <ion-icon name="logo-youtube"></ion-icon> </a> </li> <!-- 풋터 소셜 유튜브 끝 --> </ul> <!-- 소셜 네트워크 리스트 끝--> </div>
.social-list
클래스에 display: flex;
를 이용해서 .social-list
를 가로방향으로 배치시킵니다..footer { font-family: var(--ff-notosans); } .footer-top { background: var(--toasty_gray); } .footer-brand { padding-bottom: 50px; border-bottom: 1px solid var(--light_gray); margin-bottom: 50px; } .footer-brand .logo { margin-bottom: 15px; } .social-list { display: flex; align-items: center; gap: 8px; } .social-link { background: var(--light_gray); color: var(--silver); font-size: 20px; padding: 10px; transition: var(--transition-1); } .social-link:is(:hover, :focus) { background: var(--main_color); color: var(--white); }
.social-link:is(:hover, :focus)
는 .social-link
클래스를 가진 요소가 :hover, :focus
상태일때 스타일을 적용합니다. :is()
는 주어진 :hover, :focus
중 하나라도 일치하면 해당하는 스타일을 적용합니다.
<div class="footer-link-box"> <!-- 풋터 주소 번호 이메일 리스트 시작 --> <ul class="footer-list"> <li> <p class="footer-list-title">고객센터</p> </li> <!-- 풋터 주소 시작 --> <li> <address class="footer-link"> <ion-icon name="location"></ion-icon> <span class="footer-link-text">서울 강남구</span> </address> </li> <!-- 풋터 주소 끝 --> <!-- 풋터 전화번호 시작 --> <li> <a href="tel:010-####-####" class="footer-link"> <ion-icon name="call"></ion-icon> <span class="footer-link-text">010-####-####</span> </a> </li> <!-- 풋터 전화번호 끝 --> <!-- 풋터 이메일 시작 --> <li> <a href="mailto:holiday#00@naver.com" class="footer-link"> <ion-icon name="mail"></ion-icon> <span class="footer-link-text">webtravel@naver.com</span> </a> </li> <!-- 풋터 이메일 끝 --> </ul> <!-- 풋터 주소 번호 이메일 리스트 끝 --> <div class="footer-bottom"> <div class="container"> <p class="copyright"> ⓒ Copyright 2023. Webtravel All rights reserved. </p> </div> </div> </div> </div> </div> </footer>
.footer-list-title::after
는 가상 요소 선택자 중 하나인 ::after
를 사용하여 .footer-list-title
클래스를 가진 요소 뒤에 내용이나 스타일을 추가할 때 사용합니다.
가상요소 ::after
는 ‘content’
속성은 필수로 추가해야하며, 빈 문자열이나 특정 문자열 등을 값으로 가질수 있습니다..footer-list-title { position: relative; color: var(--black); font-family: var(--ff-notosans); font-size: var(--fs-3); font-weight: var(--fw-700); margin-bottom: 25px; } .footer-list-title::after { content: ""; display: block; background: var(--main_color); width: 90px; height: 2px; margin-top: 10px; } .footer-link { color: var(--silver); display: flex; align-items: center; gap: 10px; padding-block: 6px; transition: var(--transition-1); } .footer-bottom { padding-block: 20px; } .copyright { color: var(--gray); }
media queries
자 이제, 위에서 생성한 요소 중 화면 크기에 따라 다르게 반응하도록 해야 하는 요소들은 어떤 것이 있을까요? 이를 위해서는 미디어쿼리를 사용할 수 있습니다.
여기서는 화면의 크기를 575px이상, 768px이상, 992p이상, 1200px 이상 이렇게 네가지 범위로 구분하여 요소의 속성을 재설정해 줍니다. 먼저 575px이상일 경우부터 살펴보겠습니다.
/* 미디어쿼리 min width : 575px 시작*/ @media (min-width: 575px) { /* 커스텀 프로퍼티 */ :root { /* 폰트 사이즈 */ --fs-1: 4rem; } /* 첫 표지 */ .main-text { font-size: var(--fs-7); } /* 재사용 가능 스타일*/ .container { max-width: 650px; width: 100%; margin-inline: auto; } /* 프로덕트 */ .product-list { grid-template-columns: 1fr 1fr; } /* 푸터 */ .footer-brand .logo { margin-bottom: 0; } .footer-brand { display: flex; justify-content: space-between; align-items: center; } } /* 미디어쿼리 min width : 575px 끝*/
화면의 최소 너비가 575px일 경우, 즉 화면 크기가 575px 이상일 때
.main-text
클래스의 폰트 크기도 함께 커지도록 하고, .container
에서는 화면의 크기가 달라지더라도 “중앙 정렬 컨테이너"를 구현할 수 있도록 속성을 지정합니다. 이를 통해 .container
는 중앙에 위치하고 양옆에 동일한 margin이 생길 것입니다.
가장 큰 변화는 .product-list
에서 느낄 수 있을 것입니다. 기존에 설정했던 grid 속성에서 상품 리스트가 2열로 나타나도록 grid-template-columns
값을 지정합니다.
.product-list
의 변화는 아래 사진에서 확인합시다!
/* 미디어쿼리 min width : 768px 시작*/ @media (min-width: 768px) { /* 커스텀 프로퍼티 */ :root { /* 폰트 사이즈 */ --fs-2: 3rem; } /* 재사용 가능 스타일*/ .container { max-width: 720px; } .h4 { --fs-5: 2rem; } } /* 미디어쿼리 min width : 768px 끝*/
다음으로는 화면의 최소 너비가 768px일 경우의 미디어쿼리를 알아보겠습니다. 너비가 그 이상이 될 경우,
.container
클래스는 max-width
속성을 사용해 최대 너비를 720px로 제한합니다. .h4
요소는 커스텀 프로퍼티 --fs-5
를 사용해 폰트 사이즈는 2rem만큼, 즉 루트 폰트 크기의 2배로 설정합니다.
@media (min-width: 992px) { /* 커스텀 프로퍼티 */ :root { /* 폰트 사이즈 */ --fs-3: 2.4rem; } /* 재사용 가능 스타일*/ .container { max-width: 970px; } /* 헤더 */ .nav-open-btn, .nav-close-btn, .navbar .logo, .nav-action-text, .dim { display: none; } .navbar, .navbar-list, .nav-action-list { all: unset; } .navbar-item:not(:last-child) { border: none; } .navbar-list { display: flex; } .navbar { display: flex; align-items: center; flex-grow: 1; } .navbar-list { margin: 0 auto; gap: 35px; } .navbar-link { font-weight: var(--fw-500); } /* 첫표지 */ .main { height: 480px; } /* 프로덕트 */ .product-list { grid-template-columns: repeat(3, 1fr); } } /* 미디어쿼리 min width : 992px 끝*/
화면 크기가 922px 이상일 때는 상품의 배치가 또 한 번 달라집니다.
repeat
(3, 1fr)으로 값을 지정해주어 .product-list
의 배치는 3열로 변화합니다.
/* 미디어쿼리 min width : 1200px 시작*/ @media (min-width: 1200px) { /* 커스텀 프로퍼티 */ :root { /* 폰트 사이즈 */ --fs-1: 5rem; --fs-2: 3.6rem; } /* 재사용 가능 스타일*/ .container { max-width: 1280px; } /* 첫표지 */ .main { height: 580px; } .main .container { max-width: 1000px; } .main-text { font-size: var(--fs-6); max-width: 50%; } /* 프로덕트 */ .product-list { grid-template-columns: repeat(4, 1fr); } /* 푸터 */ .footer-link-box { grid-template-columns: 1.5fr 1fr 1fr 1.5fr; gap: 50px; } } /* 미디어쿼리 min width : 1200px 끝*/
화면 크기가 1200px 이상일 때, 폰트 크기가 함께 커지며 상품은 4열로 진열됩니다.
