<header>
<div class="inner">
<a href="/" class="logo">
<img src="./images/starbucks_logo.png" alt="STARBUCKS" />
</a>
</div>
</header>
우선 로고를 넣기위해 header라는 태그를 사용하여 img를 사용한 로고를 추가하는 코드를 작성했습니다. 여기서 header라는 태그는 시멘틱 태그라고 하는데 시멘틱 태그가 무엇일까요?
시멘틱 태그 = 시멘틱 태그란 HTML에서 태그 이름 자체가 그 역할과 의미를 나타내는태그로, 페이지 구조를 명확히 보여줍니다.
ex) header,footer,article,section 등이 있습니다.
이러한 시맨틱 태그를 사용하는 이유에서 가장 중요한 것이 "SEO(검색 엔진 최적화)"에 유리한데, 검색 엔진이 페이지 구조를 더 잘 이해하고, 관련성있는 콘텐츠로 인식하여 검색 순위에 긍정적인 영향을 줄 수 있기 때문입니다.
여기서 우리는 header태그를 사용했는데 말그대로, 문서의 머리말 부분을 정의하는데 사용되고, 페이지의 로고, 네비게이션 메뉴, 제목 등을 포함할 수 있기에 사용합니다.
그다음 inner라는 클래스 이름을 가진 div태그로 a태그를 감싸준 이유는, 레이아웃을 제어하고, 콘텐츠를 구조화하기 위해서 인데, 다음과 같은 목적이 있습니다.
레이아웃 조정: .inner 클래스는 콘텐츠의 가로 너비나 중앙 정렬 같은 스타일을 적용하기 위해 사용됩니다. 예를 들어, 화면 크기에 상관없이 로고나 콘텐츠를 페이지 중앙에 위치시키거나, 특정 최대 너비를 설정하는 데 유용합니다.
구조화: HTML 코드를 더 깔끔하고 읽기 쉽게 만들어 콘텐츠 그룹을 나눠 구조를 명확하게 보여줍니다. 이렇게 하면 유지보수와 확장이 더 쉬워집니다.
스타일 적용: 특정 영역에만 스타일을 적용하거나, 여백을 조정하기 위해 inner 같은 클래스가 사용됩니다. 이를 통해 디자인적인 일관성을 유지할 수 있습니다.
img {
display: block;
}
/*HEADER*/
hedaer { // 임의적으로 넣은 배경색
background-color: royalblue;
}
header .inner {
width: 1100px;
height: 120px;
margin: 0 auto;
background-color: orange;
position: relative;
}
header .logo {
position: absolute;
top: 0;
bottom: 0;
left: 0;
margin: auto;
height: 75px;
}
.inner의 css코드를 살펴보면 높이와 너비의 크기를 주고, margin을 통해 상하 여백을 0으로, 그리고 좌우 여백을 auto로 설정함에 따라 가로축 중앙에 배치되도록 만들 수 있습니다.
.logo의 css코드를 살펴보면 position: absolute를 통해 부모요소 inner의 css의 position: relative를 통해 부모요소를 기준으로 절대 위치에 배치됩니다.
absolute는 부모요소를 기준으로 위치를 정할 수 있기 때문에 이를 통해 logo의 위치를 정할 수 있습니다. 그래서 아까 위의 코드에서 inner의 역할이 레이아웃을 설정하기 위함인 것을 이해할 수 있을겁니다.
다음 top,bottom,left를 0으로 설정함으로써, 부모 요소의 좌측 모서리부터 배치되도록 하고, margin의 값을 auto로 줌으로써 상하좌우 여백을 top,bottom이 0임으로 수직정렬이 되고, left가 0으로 설정해줌으로써 이는 좌측으로 수직정렬이 된것을 확인할 수 있습니다.
마지막으로 확인할 것은 img를 css에서 display : block으로 작성해주어서 inline요소인 img태그를 block요소로 바꾸어 준것을 확인할 수 있는데 이렇게 한 이유는 레이아웃 제어와 디자인 최적화를 위해서 입니다.
기본적으로 inline요소인 상태로 img를 사용하면 하단의 여백이 생기는데 이 여백은 이미지가 텍스트 라인에 맞춰 정렬되면서 생기는데, 이를 없애기 위해 block으로 바꾸어 주면 여백이 사라집니다.
또한 이미지를 한 줄에 배치하며, 레이아웃을 더 정확히 제어할 수 있게 됩니다.
우선 기본적인 뼈대부터 작성해서 시작해보겠습니다.
<header>
<div class="inner">
<a href="/">
<img class="logo" src="./images/starbucks_logo.png" alt="STARBUCKS" />
</a>
<div class="sub-menu">
<ul class="menu">
<li>
<a href="./singin">Sign In</a>
</li>
<li>
<a href="#">My Starbucks</a>
</li>
<li>
<a href="javascript:void(0)">Customer Service & ideas</a>
</li>
<li>
<a href="javascript:void(0)">Find a Store</a>
</li>
</ul>
<div class="search">
<input type="text" />
<div class="material-icons">search</div>
</div>
</div>
</div>
</header>
sub-menu라는 div요소를 inner안에 만들어주고, 그 안에, ul태그를 통해 4가지의 li요소를 만들어 주고 a태그로 이동할 수 있는 페이지들을 작성해주고, 검색기능을 위한 search요소를 만들어줍니다.
여기서 a태그안에 #과 javascript:void(0)이라고 작성이 되어있는데, 이 내용은 링크를 클릭하여도, 페이지로 리로드되거나, 이동하지 않도록 하기 위해서입니다.
/* HEADER */
header {
background-color: #f6f5f0;
border-bottom: 1px solid #c8c8c8;
}
header .inner {
width: 1100px;
height: 120px;
margin: 0 auto;
position: relative;
}
header .logo {
height: 75px;
position: absolute;
top: 0;
bottom: 0;
left: 0;
margin: auto;
}
header .sub-menu {
display: flex;
justify-content: flex-end;
}
header .sub-menu ul.menu {
font-family: Arial, sans-serif;
display: flex;
}
header .sub-menu ul.menu li {
position: relative;
}
header .sub-menu ul.menu li::before {
content: "";
width: 1px;
height: 12px;
background-color: #e5e5e5;
position: absolute;
top: 0;
bottom: 0;
margin: auto 0;
}
header .sub-menu ul.menu li:first-child:before {
display: none;
}
header .sub-menu ul.menu li a {
font-size: 16px;
padding: 11px 16px;
display: block;
color: #656565;
cursor: pointer;
}
header .sub-menu ul.menu li a:hover {
color: #000;
}
header .sub-menu .search {
height: 34px;
position: relative;
}
header .sub-menu .search input {
width: 36px;
height: 34px;
padding: 4px 10px;
border: 1px solid #ccc;
box-sizing: border-box;
border-radius: 5px;
outline: none;
background-color: #fff;
color: #777;
transition: width 0.5s;
font-size: 12px;
}
header .sub-menu .search input:focus {
border-color: #669900;
width: 190px;
}
header .sub-menu .search .material-icons {
position: absolute;
top: 0;
bottom: 0;
margin: auto 0;
height: 24px;
right: 5px;
cursor: pointer;
}
header .sub-menu .search.focused .material-icons {
opacity: 0;
}
header .sub-menu {
display: flex;
justify-content: flex-end;
}
모든 서브 메뉴들의 정렬을 수평으로 정렬되게끔 하고, 서브 메뉴의 위치는 우측에 위치하고 있기 때문에 flex-end를 사용하여 우측에 배치되도록 하였습니다.
header .sub-menu ul.menu {
font-family: Arial, sans-serif;
display: flex;
}
마찬가지로 서브 메뉴내부에 4가지 li요소들도 수평으로 정렬되게끔 하기위해 사용하였습니다.
header .sub-menu ul.menu li::before {
content: "";
width: 1px;
height: 12px;
background-color: #e5e5e5;
position: absolute;
top: 0;
bottom: 0;
margin: auto 0;
}
서브 메뉴들 4가지 li요소들 사이사이에 작은 구분선이 있는데 이를 사용하기 위해 사용하였습니다.
header .sub-menu ul.menu li:first-child:before {
display: none;
}
위의 방식으로 사용하면 첫번째 li요소 앞에도 작은 구분선이 존재하는데 이는 필요하지 않기 때문에 이를 사용하였습니다.
const searchEl = document.querySelector(".search");
const searchInputEl = searchEl.querySelector("input");
searchEl.addEventListener("click", function () {
searchInputEl.focus();
});
searchInputEl.addEventListener("focus", function () {
searchEl.classList.add("focused");
searchInputEl.setAttribute("placeholder", "통합검색");
});
searchInputEl.addEventListener("blur", function () {
searchEl.classList.remove("focused");
searchInputEl.setAttribute("placeholder", "");
});
querySelector을 통해 search div와 input요소를 찾아 변수에 담고, 클릭 이벤트를 통해 search 내부의 아이콘을 클릭을 하더라도 input요소가 focus되도록 추가해주었습니다.
그리고, focus 되었을때, focused라는 클래스를 추가해주고, 통합검색이라는 안내문구를 작성해주고, blur 되었을경우에는 클래스를 없애주고, 빈문자열로 초기화 해줍니다.
<ul class="main-menu">
<li class="item">
<div class="item__name">COFFEE</div>
<div class="item__contents">
<div class="contents__menu">
<ul class="inner">
<li>
<h4>커피</h4>
<ul>
<li>스타벅스 원두</li>
<li>스타벅스 비아</li>
<li>스타벅스 오리가미</li>
</ul>
</li>
<li>
<h4>에스프레소 음료</h4>
<ul>
<li>도피오</li>
<li>에스프레소 마키아또</li>
<li>아메리카노</li>
<li>마키아또</li>
<li>카푸치노</li>
<li>라떼</li>
<li>모카</li>
<li>리스트레또 비안코</li>
</ul>
</li>
<li>
<h4>커피 이야기</h4>
<ul>
<li>스타벅스 로스트 스팩트럼</li>
<li>최상의 아라비카 원두</li>
<li>한 잔의 커피가 완성되기까지</li>
<li>클로버® 커피 추출 시스템</li>
</ul>
</li>
<li>
<h4>최상의 커피를 즐기는 법</h4>
<ul>
<li>커피 프레스</li>
<li>푸어 오버</li>
<li>아이스 푸어 오버</li>
<li>커피 메이커</li>
<li>리저브를 매장에서 다양하게 즐기는 법</li>
</ul>
</li>
</ul>
</div>
<div class="contents__texture">
<div class="inner">
<h4>나와 어울리는 커피 찾기</h4>
<p>스타벅스가 여러분에게 어울리는 커피를 찾아드립니다.</p>
<h4>최상의 커피를 즐기는 법</h4>
<p>여러가지 방법을 통해 다양한 풍미의 커피를 즐겨보세요.</p>
</div>
</div>
</div>
</li>
<li class="item">
<div class="item__name">MENU</div>
<div class="item__contents">
<div class="contents__menu">
<ul class="inner">
<li>
<h4>음료</h4>
<ul>
<li>콜드 브루</li>
<li>브루드 커피</li>
<li>에스프레소</li>
<li>프라푸치노</li>
<li>블렌디드 음료</li>
<li>스타벅스 피지오</li>
<li>티(티바나)</li>
<li>기타 제조 음료</li>
<li>스타벅스 주스(병음료)</li>
</ul>
</li>
<li>
<h4>푸드</h4>
<ul>
<li>베이커리</li>
<li>케익</li>
<li>샌드위치 & 샐러드</li>
<li>따뜻한 푸드</li>
<li>과일 & 요거트</li>
<li>스낵 & 미니 디저트</li>
<li>아이스크림</li>
</ul>
</li>
<li>
<h4>상품</h4>
<ul>
<li>머그</li>
<li>글라스</li>
<li>플라스틱 텀블러</li>
<li>스테인리스 텀블러</li>
<li>보온병</li>
<li>액세서리</li>
<li>커피 용품</li>
<li>패키지 티(티바나)</li>
</ul>
</li>
<li>
<h4>카드</h4>
<ul>
<li>실물카드</li>
<li>e-Gift 카드</li>
</ul>
</li>
<li>
<h4>메뉴 이야기</h4>
<ul>
<li>콜드 브루</li>
<li>스타벅스 티바나</li>
</ul>
</li>
</ul>
</div>
<div class="contents__texture">
<div class="inner">
<h4 class="new">스타벅스 티바나</h4>
<p>다양한 찻잎과 향신료 등 개성있는 재료로 새로운 맛과 향의 티를 선보입니다.</p>
</div>
</div>
</div>
</li>
<li class="item">
<div class="item__name">STORE</div>
<div class="item__contents">
<div class="contents__menu">
<ul class="inner">
<li>
<h4>매장 찾기</h4>
<ul>
<li>퀵 검색</li>
<li>지역 검색</li>
<li>My 매장</li>
</ul>
</li>
<li>
<h4>매장 이야기</h4>
<ul>
<li>청담스타</li>
<li>티바나 인스파이어드 매장</li>
<li>파미에파크</li>
</ul>
</li>
</ul>
</div>
<div class="contents__texture">
<div class="inner">
<h4>매장 찾기</h4>
<p>보다 빠르게 매장을 찾아보세요.</p>
<h4 class="new">청담스타</h4>
<p>스타벅스 1,000호점인 청담스타점을 만나보세요.</p>
</div>
</div>
</div>
</li>
<li class="item">
<div class="item__name">RESPONSIBILITY</div>
<div class="item__contents">
<div class="contents__menu">
<ul class="inner">
<li>
<h4>지역 사회 참여 활동</h4>
<ul>
<li>회망배달 캠페인</li>
<li>재능기부 카페 소식</li>
<li>커뮤니티 스토어</li>
<li>청년인재 양성</li>
<li>우리 농산물 사랑 캠페인</li>
<li>우리 문화 지키기</li>
</ul>
</li>
<li>
<h4>환경보호 활동</h4>
<ul>
<li>환경 발자국 줄이기</li>
<li>일회용 컵 없는 매장</li>
<li>커피 원두 재활용</li>
</ul>
</li>
<li>
<h4>윤리 구매</h4>
<ul>
<li>윤리적 원두 구매</li>
<li>공정무역 인증</li>
<li>커피 농가 지원 활동</li>
</ul>
</li>
<li>
<h4>글로벌 사회 공헌</h4>
<ul>
<li>윤리경영 보고서</li>
<li>스타벅스 재단</li>
<li>지구촌 봉사의 달</li>
</ul>
</li>
</ul>
</div>
<div class="contents__texture">
<div class="inner">
<h4>커피원두 재활용</h4>
<p>스타벅스 커피 원두를 재활용 해보세요.</p>
</div>
</div>
</div>
</li>
<li class="item">
<div class="item__name">MY STARBUCKS REWARDS</div>
<div class="item__contents">
<div class="contents__menu">
<ul class="inner">
<li>
<h4>마이 스타벅스 리워드</h4>
<ul>
<li>마이 스타벅스 리워드 소개</li>
<li>등급 및 혜택</li>
<li>스타벅스 별</li>
<li>자주하는 질문</li>
</ul>
</li>
<li>
<h4>스타벅스 카드</h4>
<ul>
<li>스타벅스 카드 소개</li>
<li>스타벅스 카드 갤러리</li>
<li>등록 및 조회</li>
<li>충전 및 이용안내</li>
<li>분실신고/환불신청</li>
<li>자주하는 질문</li>
</ul>
</li>
<li>
<h4>스타벅스 카드 e-Gift</h4>
<ul>
<li>스타벅스 카드 e-Gift 소개</li>
<li>이용안내</li>
<li>선물하기</li>
<li>자주하는 질문</li>
</ul>
</li>
</ul>
</div>
<div class="contents__texture">
<div class="inner">
<h4>스타벅스 카드 등록하기</h4>
<p>카드 등록 후 리워드 서비스를 누리고 사용내역도 조회해보세요.</p>
</div>
</div>
</div>
</li>
<li class="item">
<div class="item__name">WHAT'S NEW</div>
<div class="item__contents">
<div class="contents__menu">
<ul class="inner">
<li>
<h4>프로모션 & 이벤트</h4>
<ul>
<li>전체</li>
<li>스타벅스 카드</li>
<li>마이 스타벅스 리워드</li>
<li>온라인</li>
<li>2017 스타벅스 플래너</li>
</ul>
</li>
<li>
<h4>새소식</h4>
<ul>
<li>전체</li>
<li>상품 출시</li>
<li>스타벅스의 문화</li>
<li>스타벅스 사회공헌</li>
<li>스타벅스 카드출시</li>
</ul>
</li>
<li>
<h4>매장별 이벤트</h4>
<ul>
<li>일반 매장</li>
<li>신규 매장</li>
</ul>
</li>
</ul>
</div>
<div class="contents__texture">
<div class="inner">
<h4>매장별 이벤트</h4>
<p>스타벅스의 매장 이벤트 정보를 확인 하실 수 있습니다.</p>
<h4>소셜 스타벅스</h4>
<p>다양한 스타벅스 SNS 채널을 통해 스타벅스를 만나보세요!</p>
</div>
</div>
</div>
</li>
</ul>
메인메뉴는 우선 전체 메인메뉴의 리스트를 담고 있는 최상위 ul요소로 부터 시작하여, li요소의 item이라는 클래스 이름으로 각각의 메뉴 항목을 나타냅니다.
예를들어, 'COFFEE','MENU','STORE'등이 메뉴 이름으로 표시됩니다.
이러한 각 메뉴의 이름을 나타내는 요소는 div 요소의 item__name 클래스 이름을 가진 요소로 메뉴 이름을 표시합니다.
다음으로 해당 메뉴의 세부 항목인 하위 콘텐츠들을 담고 있는 div 요소인 item__contents로 항목을 담고, 2가지로 나누어 세부 내용들을 표시합니다.
div 요소의 contents__menu 클래스는 세부 메뉴를 나타내는 부분으로, 각 카테고리에 포함된 메뉴 항목들을 리스트로 표시합니다.
div 요소의 contents__texture 클래스는 각 메뉴와 관련된 설명이나 추가 정보를 표시하는 부분입니다.
이렇게 모든 부분을 작성해주면 각 카테고리에는 여러 세부 메뉴와 관련 설명이 함께 들어있는 메인 메뉴를 만들어 줄 수 있게 됩니다.
header .main-menu {
position: absolute;
bottom: 0;
right: 0;
z-index: 1;
display: flex;
}
header .main-menu .item .item__name {
padding: 10px 20px 34px 20px;
font-family: Arial, sans-serif;
font-size: 13px;
}
header .main-menu .item:hover .item__name {
background-color: #2C2A29;
color: #669900;
border-radius: 6px 6px 0 0;
}
header .main-menu .item .item__contents {
width: 100%;
position: fixed;
left: 0;
display: none;
}
header .main-menu .item:hover .item__contents {
display: block;
}
header .main-menu .item .item__contents .contents__menu {
background-color: #2C2A29;
}
header .main-menu .item .item__contents .contents__menu > ul {
padding: 20px 0;
display: flex;
}
header .main-menu .item .item__contents .contents__menu > ul > li {
width: 220px;
}
header .main-menu .item .item__contents .contents__menu > ul > li h4 {
padding: 3px 0 12px 0;
font-size: 14px;
color: #fff;
}
header .main-menu .item .item__contents .contents__menu > ul > li ul li {
padding: 5px 0;
font-size: 12px;
color: #999;
cursor: pointer;
}
header .main-menu .item .item__contents .contents__menu > ul > li ul li:hover {
color: #669900;
}
header .main-menu .item .item__contents .contents__texture {
padding: 26px 0;
font-size: 12px;
background-image: url("../images/main_menu_pattern.jpg");
}
header .main-menu .item .item__contents .contents__texture h4 {
color: #999;
font-weight: 900;
}
header .main-menu .item .item__contents .contents__texture p {
color: #64a70b;
margin: 4px 0 14px;
}
스타일의 경우 메뉴의 위치는 position : absolute로 설정하고 bottom,right를 0으로 설정하여 헤더부분에서 오른쪽 아래에 고정되게 설정해줍니다.
또한 배경색은 어두운 회색 #2c2a29로 설정해주고, radius를 상단 좌우만 둥글게 만들어주면서 아래 내용이 hover 된 경우 일치한 느낌을 주기위해 설정해줍니다.
서브메뉴의 경우 fixed를 사용하고 left : 0 을 설정하여 화면에 고정되어 왼쪽에 배치되도록 해주었습니다.
또한 hover을 사용하여 메인메뉴 항목을 hover 하지 않았을경우 세부 메뉴의 내용들은 보이지 않아야 하므로, display: none으로 설정해두고, hover 되었을 경우 block으로 설정하여 보이도록 설정하였습니다.
즉 이 CSS는 메인 메뉴와 서브 메뉴가 마우스를 올렸을 때 표시되도록 하며, 각 항목과 세부 항목의 레이아웃, 색상 변화, 텍스트 스타일을 세부적으로 정의하여 디자인을 깔끔하게 보이도록 만듭니다.
<div class="badges">
<div class="badge">
<img src="./images/badge1.jpg" alt="Badge" />
</div>
<div class="badge">
<img src="./images/badge2.jpg" alt="Badge" />
</div>
</div>
html 작성은 간단합니다. badges라는 div요소를 만들고, 그안의 badge라는 div요소 안에 img를 넣어주면 됩니다.
header .badges {
position: absolute;
top: 132px;
right: 12px;
}
header .badges .badge {
border-radius: 10px;
overflow: hidden;
margin-bottom: 12px;
box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.15);
cursor: pointer;
}
css도 간단한데 우선 bages의 css를 보면 absolute를 사용했는데 이 경우 상위요소의 부모요소가 필요한데 inner가 없기 때문에 badges는 header를 바라봅니다. 그렇기 때문에 header의 스타일요소에 fixed를 추가해줍니다.
relative가 아닌 fixed인 이유는 header는 화면에 고정되어 있어야 하기때문에 굳이 relative를 넣어줄 필요 없이 fixed를 사용해주면 됩니다.
다음으로 badge의 경우 overflow는 radius로 화면을 둥글게 만들어 주었는데 이경우 짤리게 되는 이미지를 overflow : hidden을 통해 화면 크기에 맞게 조절해주기 때문에 사용하였습니다.
이제 html,css 작성은 마무리 했습니다. 하지만 badge의 경우 스크롤을 일정 부분까지 내릴경우 사라지게 만들어 줄것이기 때문에 해당 역할을 할 수 있도록 js를 작성해 보겠습니다.
const badgeEl = document.querySelector('header .badges')
// 스크롤이 지나치게 자주 발생하는 것을 조절(throttle, 일부러 부하를 줌)
window.addEventListener('scroll', _.throttle(function () {
// 페이지 스크롤 위치가 500px이 넘으면.
if (window.scrollY > 500) {
// Badge 요소 숨기기!
gsap.to(badgeEl, .6, {
opacity: 0,
display: 'none'
})
// 페이지 스크롤 위치가 500px이 넘지 않으면.
} else {
// Badge 요소 보이기!
gsap.to(badgeEl, .6, {
opacity: 1,
display: 'block'
})
}
}, 300))
코드를 보면 lodash와 gsap이라는 라이브러리를 사용했습니다.
lodash의 경우 스크롤을 올렸다가 내렸다가 하는경우 지나치게 자주 발생하게 되는데 이러한 경우 부하가 올 수 있고, 성능이 안좋아지기 때문에 최적화를 위해 자주 발생하는 부분을 일부러 부하를 주어서 조절을 하게 만드는 역할을 합니다.
'_.throttle()'가 lodash의 쓰로틀링 함수로, 스크롤이 지나치게 많이 발생하는 것을 조절하는 역할을 해줍니다.
그리고 스크롤 위치가 500px을 넘는지 확인 후 넘는다면 gsap을 이용하여 화면에 보였다가 안보였다가를 해주는 역할을 수행하는데
gsap은 gsap.to(요소, 지속시간, 옵션) 등을 사용하여 이용하는데, 요소의 경우는 badgeEl을 넣어주고, 시간은 스르륵 사라지고, 스르륵 보이게 하는 효과를 위해 .6s를 넣어줍니다. 그리고 마지막으로 옵션의 경우는 opacity로 안보이게 또는 보이게 만들어주는데 이것만 넣게되면 해당 위치에 마우스를 올려보면 커서가 잡히는걸 볼 수 있습니다.
이러한 문제를 방지하기 위해 display : none,block을 이용하여 확실하게 안보이도록 수행했습니다.
즉, 사용자가 페이지를 스크롤할 때 500px을 넘으면 배지 요소가 사라지고, 500px 이하일 때는 다시 나타납니다.
Lodash의 _.throttle()을 사용해 스크롤 이벤트가 지나치게 자주 실행되지 않도록 제어하고, GSAP를 통해 배지가 부드럽게 사라지고 나타나는 애니메이션을 적용했습니다