프로젝트 개요
이 프로젝트는 HTML, CSS를 활용한 레스토랑 웹사이트 제작 실습입니다. "Feane"이라는 레스토랑 테마의 반응형 홈페이지를 구현했습니다.
주요 기술 스택
- HTML5
- CSS3
- Font Awesome 아이콘 라이브러리
핵심 구현 기능
1. 헤더 네비게이션
- 로고 "Feane" 포함
- 메뉴 항목: Home, Menu, About, Book Table
- 아이콘 및 버튼 추가 (장바구니, 검색, 온라인 주문)
2. 슬라이드 배너
- 3개의 슬라이드로 구성
- 라디오 버튼을 이용한 슬라이드 전환
- 각 슬라이드별 다른 텍스트 및 프로모션 메시지
- HTML 구조:
- .head: 최상단 헤더 영역. 로고(h2), 메뉴 목록(ul.menu-bar), 아이콘(i), 버튼(button) 포함.
- .main: 메인 콘텐츠 영역. 슬라이더 배경 이미지(img)와 슬라이더 로직(input, .slidebox, .slidebtn) 포함.
- input[type="radio"]: 슬라이드 상태를 저장하고 CSS에서 선택하기 위한 숨겨진 라디오 버튼. name="slide"로 그룹화하여 하나만 선택되도록 함. id는 label과 연결하기 위함.
- .slidebox > .slides > .wrap: 슬라이드 콘텐츠를 담는 구조. .slides는 모든 슬라이드(wrap)를 가로로 포함하고, CSS transform으로 이 전체가 이동합니다.
- .slidebtn > label: 라디오 버튼과 for 속성으로 연결된 라벨. 사용자가 클릭할 수 있는 동그란 버튼 역할을 합니다.
- CSS 스타일 및 로직:
- 헤더 스타일링: display: flex를 사용하여 메뉴 요소들을 정렬하고, position: absolute와 z-index로 헤더를 배경 이미지 위에 배치합니다.
- 슬라이더 기본 구조: .main에 position: relative를 주어 자식 요소인 .slidebox와 .slidebtn의 position: absolute 기준을 마련합니다. .main에 overflow: hidden을 주어 현재 보이는 슬라이드 외의 나머지 부분을 숨깁니다.
- 슬라이드 배치: .slides의 너비를 300%(슬라이드 3개)로 설정하고 display: flex를 사용해 자식인 .wrap 요소들을 가로로 배열합니다. 각 .wrap은 width: calc(100% / 3) (즉, 100%)로 설정되어 화면 너비만큼 차지합니다.
- 핵심 로직 (Radio Button Hack):
- input[type="radio"] { display: none; }: 실제 라디오 버튼은 화면에서 숨깁니다.
- label: 사용자는 라벨을 클릭합니다. for 속성으로 연결된 라디오 버튼이 체크됩니다.
- :checked 유사 클래스와 ~ (General Sibling Combinator): 체크된 라디오 버튼(:checked)의 형제 요소(~)인 .slidebox .slides와 .slidebtn label에 스타일을 적용합니다.
- transform: translateX(): 체크된 라디오 버튼에 따라 .slides 전체를 왼쪽으로 이동시켜 해당 슬라이드를 보여줍니다. translateX(-100%)는 .slides 너비의 1/3 만큼 (즉, 한 슬라이드 너비만큼) 왼쪽으로 이동합니다.
- 활성화된 버튼 표시: 체크된 라디오 버튼에 연결된 label의 배경색과 크기를 변경하여 현재 어떤 슬라이드가 보이는지 시각적으로 알려줍니다.
- 전환 효과: .slides 요소에 transition: transform 0.8s ease-in-out; 속성을 추가하여 슬라이드가 부드럽게 이동하도록 합니다.
4. 실습 결과
HTML (index.html)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Feane Restaurant Mockup</title>
<!-- CSS 링크 -->
<link rel="stylesheet" href="style.css">
<!-- Font Awesome 아이콘 CDN -->
<script src="https://kit.fontawesome.com/0022f8e9f1.js" crossorigin="anonymous"></script>
</head>
<body>
<!-- 헤더 영역 -->
<div class="container">
<div class="head">
<div class="menu">
<h2>Feane</h2>
<ul class="menu-bar">
<a href="#"><li>Home</li></a>
<a href="#"><li>Menu</li></a>
<a href="#"><li>About</li></a>
<a href="#"><li>Book Table</li></a>
</ul>
<!-- 아이콘 및 주문 버튼 -->
<a href="#"><i class="fa-solid fa-user"></i></a> <!-- 아이콘 변경 예시 -->
<a href="#"><i class="fa-solid fa-cart-shopping"></i></a>
<a href="#"><i class="fa-solid fa-magnifying-glass"></i></a>
<button class="order">Order Online</button>
</div>
</div>
</div> <!-- .container 불필요하게 중복된 닫는 태그 제거 -->
<!-- 메인 영역 (슬라이더 포함) -->
<div class="main">
<!-- 슬라이더 배경 이미지 -->
<img src="https://themewagon.github.io/feane/images/hero-bg.jpg" alt="Restaurant Background">
<!-- 슬라이드 컨트롤용 라디오 버튼 (숨김 처리됨) -->
<input type="radio" name="slide" id="s01" checked> <!-- 첫 슬라이드 기본 선택 -->
<input type="radio" name="slide" id="s02">
<input type="radio" name="slide" id="s03">
<!-- 슬라이드 콘텐츠 영역 -->
<div class="slidebox">
<div class="slides">
<!-- 슬라이드 1 -->
<div class="wrap">
<div class="slide">
<div class="textbox">
<h1 class="text1">Sale 20% Off On Everything</h1>
<p class="text2">Doloremque, itaque aperiam facilis rerum, commodi, temporibus sapiente ad mollitia laborum quam quisquam esse error unde. Tempora ex doloremque, labore, sunt repellat dolore, iste magni quos nihil ducimus libero ipsam.</p>
<button class="text3">Order Now</button>
</div>
</div>
</div>
<!-- 슬라이드 2 -->
<div class="wrap">
<div class="slide">
<div class="textbox">
<h1 class="text1">Fast Food Restaurant</h1>
<p class="text2">It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters...</p>
<button class="text3">Order Now</button>
</div>
</div>
</div>
<!-- 슬라이드 3 -->
<div class="wrap">
<div class="slide">
<div class="textbox">
<h1 class="text1">Why Shop With Us</h1>
<p class="text2">There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable...</p>
<button class="text3">Order Now</button>
</div>
</div>
</div>
</div> <!-- .slides 닫는 태그 -->
</div> <!-- .slidebox 닫는 태그 -->
<!-- 슬라이드 버튼 (라디오 버튼과 연결된 라벨) -->
<div class="slidebtn">
<label for="s01" class="left"><i class="fa-solid fa-circle"></i></label>
<label for="s02" class="mid"><i class="fa-solid fa-circle"></i></label>
<label for="s03" class="right"><i class="fa-solid fa-circle"></i></label>
</div>
</div> <!-- .main 닫는 태그 -->
<!-- </body> 바깥에 있던 불필요한 </div> 제거 -->
</body>
</html>
CSS (style.css)
/* 기본 리셋 및 공통 스타일 */
* {
margin: 0; /* auto 제거 -> 요소별로 필요시 지정 */
padding: 0;
list-style: none;
text-decoration: none;
box-sizing: border-box; /* 너비 계산을 더 쉽게 하기 위해 추가 */
}
body {
font-family: sans-serif; /* 기본 폰트 지정 */
}
/* 헤더 스타일 */
.head {
width: 100%;
position: absolute; /* 배경 이미지 위에 위치 */
top: 0;
left: 0;
color: aliceblue;
padding: 20px 5%; /* 좌우 여백 추가 */
z-index: 99; /* 다른 요소들 위에 오도록 설정 */
}
.menu {
display: flex; /* Flexbox 레이아웃 사용 */
justify-content: space-between; /* 요소들을 양쪽 끝으로 분산 */
align-items: center; /* 세로 중앙 정렬 */
max-width: 1200px; /* 최대 너비 지정 (선택적) */
margin: 0 auto; /* 가운데 정렬 */
}
h2 {
font-size: 40px;
font-family: cursive;
}
.menu-bar li {
display: inline-block; /* 메뉴 항목 가로 배치 */
margin: 0 15px; /* 메뉴 간격 조정 */
}
.menu-bar a li { /* 링크 안의 li에 직접 스타일 */
color: white;
transition: color 0.3s ease; /* 부드러운 색상 변화 */
}
.menu-bar a:hover li { /* 호버 시 색상 변경 */
color: orange;
}
.menu > a { /* 헤더의 직접적인 자식 a 태그 (아이콘) */
margin: 0 10px; /* 아이콘 간격 */
color: white;
font-size: 1.2rem; /* 아이콘 크기 */
}
.order { /* 버튼 스타일 */
border: 1px solid orange;
background-color: orange;
color: white;
font-size: 16px; /* 폰트 크기 조정 */
padding: 10px 20px; /* 패딩 조정 */
width: auto; /* 내용에 맞게 너비 자동 조절 */
border-radius: 30px;
cursor: pointer; /* 마우스 커서 변경 */
transition: background-color 0.3s ease; /* 부드러운 배경색 변화 */
}
.order:hover {
background-color: darkorange;
}
/* 메인 영역 (슬라이더) 스타일 */
.main {
width: 100%;
position: relative; /* 자식 요소 absolute positioning의 기준점 */
overflow: hidden; /* 슬라이드 영역 밖의 콘텐츠 숨기기 */
}
.main img {
width: 100%;
display: block; /* 이미지 하단 여백 제거 */
min-height: 70vh; /* 최소 높이 지정 (뷰포트 높이 기준) */
object-fit: cover; /* 이미지가 비율 유지하며 꽉 차도록 */
}
/* 슬라이드 박스 (슬라이드들을 감싸는 컨테이너) */
.slidebox {
width: 100%;
height: 100%; /* 부모 높이만큼 채움 */
position: absolute;
top: 0; /* top: 50%, transform 방식 대신 사용 */
left: 0;
display: flex; /* 내부 .slides 정렬 위해 */
align-items: center; /* 세로 중앙 정렬 */
}
/* 슬라이드 전체 (3개의 슬라이드를 가로로 나열) */
.slides {
width: 300%; /* 슬라이드 개수(3개) * 100% */
height: auto; /* 내용에 맞게 높이 자동 */
display: flex; /* 슬라이드(wrap)들을 가로로 배치 */
}
/* 개별 슬라이드 래퍼 */
.wrap {
width: calc(100% / 3); /* 전체 너비(300%)를 슬라이드 개수(3)로 나눔 -> 100% */
/* transition: transform 1s ease-in-out; */ /* 부드러운 이동 효과 -> .slides 에 적용 */
/* padding: 0 15%; */ /* 패딩은 textbox 로 이동 */
flex-shrink: 0; /* 슬라이드가 줄어들지 않도록 함 */
}
/* 슬라이드 내 텍스트 박스 */
.textbox {
color: white;
width: 50%; /* 텍스트 영역 너비 */
/* margin: 0; 제거 */
padding: 0 10%; /* 좌우 패딩으로 텍스트 위치 조정 */
margin-left: 5%; /* 왼쪽 여백 추가 */
}
.textbox h1 { /* h1 태그 직접 선택 */
font-family: cursive;
font-size: 50px;
margin-bottom: 20px; /* 제목 아래 여백 */
}
.textbox p { /* p 태그 직접 선택 */
font-size: 18px; /* 폰트 크기 조정 */
line-height: 1.6; /* 줄 간격 */
margin-bottom: 30px;
}
.textbox button { /* text3 대신 button 직접 선택 */
border: 1px solid orange;
background-color: orange;
color: white;
font-size: 17px;
padding: 12px 25px;
border-radius: 30px;
cursor: pointer;
transition: background-color 0.3s ease;
}
.textbox button:hover {
background-color: darkorange;
}
/* 슬라이드 버튼 (라벨) 스타일 */
.slidebtn {
position: absolute;
bottom: 10%; /* 위치 조정 */
left: 50%; /* 가운데 정렬 위해 */
transform: translateX(-50%); /* 정확히 가운데 오도록 */
z-index: 10; /* 슬라이드 위에 오도록 */
}
.slidebtn label {
display: inline-block; /* 가로 배치 */
width: 12px;
height: 12px;
background-color: rgba(255, 255, 255, 0.5); /* 반투명 흰색 */
border-radius: 50%; /* 원 모양 */
margin: 0 5px;
cursor: pointer;
transition: all 0.3s ease; /* 부드러운 변화 */
}
.slidebtn label i {
display: none; /* Font Awesome 아이콘 숨김 (CSS로 원 모양 직접 그림) */
}
/* 라디오 버튼 숨기기 */
input[type="radio"] {
display: none;
}
/* 슬라이드 이동 및 버튼 활성화 로직 (핵심!) */
#s01:checked ~ .slidebox .slides {
transform: translateX(0%); /* 첫 번째 슬라이드 위치 */
}
#s02:checked ~ .slidebox .slides {
transform: translateX(-100%); /* 두 번째 슬라이드 위치 (wrap 너비만큼 왼쪽으로) */
}
#s03:checked ~ .slidebox .slides {
transform: translateX(-200%); /* 세 번째 슬라이드 위치 */
}
/* 체크된 라디오 버튼에 해당하는 라벨 스타일 변경 */
#s01:checked ~ .slidebtn label[for="s01"],
#s02:checked ~ .slidebtn label[for="s02"],
#s03:checked ~ .slidebtn label[for="s03"] {
background-color: yellow; /* 활성화된 버튼 색상 */
transform: scale(1.2); /* 약간 크게 */
}
/* 슬라이드 전환 효과 추가 */
.slides {
transition: transform 0.8s ease-in-out; /* .slides 전체에 transition 적용 */
}
위 코드를 적용하면 다음과 같은 결과를 얻을 수 있습니다.
- 화면 상단에 로고, 메뉴, 아이콘, 버튼이 있는 헤더가 표시됩니다. (배경 이미지 위에 표시됨)
- 메인 영역에는 배경 이미지가 있고, 그 위에 텍스트 콘텐츠가 있는 슬라이드가 보입니다.
- 하단 중앙 부근에 동그란 버튼 3개가 있습니다.
- 동그란 버튼을 클릭하면 해당 슬라이드로 부드럽게 이동하며, 클릭된 버튼의 색상이 노란색으로 변경됩니다.
