📒

7. 구현실습

 

7.1 header

앞에서 배운 지식과 구현 실습으로 모바일 퍼스트 반응형 웹사이트를 알아보겠습니다.
이 실습 파일은 노션 페이지에 있습니다.폴더의 경로는 이렇습니다
 
notion imagenotion image
 
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>
notion imagenotion image
헤더 부분의 로고와 사이드 메뉴바 열기 버튼입니다.
메뉴바는 사용자가 햄버거 메뉴 버튼을 클릭하면 왼쪽에서 사이드 메뉴바가 나오는 방식입니다.
파일명: 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 클래스명이 추가되어 사이드 메뉴바가 아닌 곳을 검게 처리해주는 방식입니다.
 
notion imagenotion image
 
햄버거 메뉴 버튼을 클릭하여 왼쪽에서 사이드 메뉴바가 나온 모습입니다.
<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>
 
notion imagenotion image
 
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; }
재사용이 가능하게 containersection클래스의 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-btnfont-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); }
 
사이드 메뉴바의 설정입니다. visibilityhidden을 주어 숨겨두고 햄버거 버튼을 클릭했을 때 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); }
 
햄버거 버튼을 클릭하여 사이드 메뉴바가 왼쪽에서 나올 때의 속성값입니다. transformtranslateX(280px)은 위의 .navbarleft:-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; }
 
notion imagenotion image
 
.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입니다.
notion imagenotion image

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; 속성을 사용합니다.
 
notion imagenotion image
 
<!-- 프로덕트 리스트 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 내부의 원하는 위치에 고정시킬 수 있고, 두 요소 간의 상대적인 위치를 조절할 수 있습니다.
 
notion imagenotion image
 
<!-- 상품 리스트 컴퓨터 책상 + 의자 세트 시작 부분 --> <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

 
 
notion imagenotion image
 
웹의 가장 하단에 있는 풋터입니다.
웹의 로고와 소셜 네트워크 리스트가 있습니다.
 
<!-- 풋터 시작 --> <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 중 하나라도 일치하면 해당하는 스타일을 적용합니다.
 
notion imagenotion image
 
<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의 변화는 아래 사진에서 확인합시다!
 
notion imagenotion image
 
/* 미디어쿼리 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배로 설정합니다.
 
notion imagenotion image
 
@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열로 변화합니다.
 
notion imagenotion image
 
/* 미디어쿼리 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열로 진열됩니다.
 
notion imagenotion image