ocean project/clone
반응형 개인 포트폴리오 사이트 Clone Coding(1)
dankthedust
2021. 9. 6. 01:35
Youtube [Bedimcode : Responsive Personal Portfolio Website Using HTML CSS And JavaScript | Dark/Light Mode] 를 그대로 클론 코딩하면서 작성한 글임을 밝힙니다.
https://www.youtube.com/watch?v=27JtRAI3QO8&list=PLMHwCGy6OXi5GoC_S9L75Q2UkIJwxgddb&index=13&t=408s
유튜브 알고리즘에서 뜬 코딩 영상이었다. 퀄리티가 생각한 것보다 좋아 보였고 차후에 포트폴리오 사이트를 제작하는데, 디자인적이나 코드의 짜임새, 그리고 기초적인 html,css,js에 대한 공부가 될 것 같아서 클론코딩을 해보면서 공부해보기로 했다.
영상에는 각 섹션이 나누어져있는데 오늘은 Header와 Nav Menu까지를 클론 했다.
우선, 당연히 해당 영상을 찍기 전에 계획과 완성을 미리 하였을 것이라 생각하고는 있지만 느낀 점은 css 선택자와 가상의 전역 속성에 대한 계획이 중요하다는 것을 느꼈다.
전역 속성이 얼마나 잘 짜임새 있게 계획된 후에 코딩하는 것이 완성된 이후에도 얼마나 쉽게 수정할 수 있는 지를 결정하는 중요한 요인이라 느꼈다.
첫번째로 html의 기본 틀을 작성하였다. 아이콘은 iconscout 을 통해서 사용하였다. fabicon 과 같은 사용 방법을 이용하며, 사용에 간편했다.
HEADER
<!--=============== HEADER ===============-->
<header class="header" id="header">
<nav class="nav container">
<a href="#" class="nav__logo">8wallDawn</a>
<!--네비게이션 메뉴 -->
<div class="nav__menu" id="nav-menu">
<ul class="nav__list grid">
<!-- 홈 메뉴 -->
<li class="nav__item">
<a href="#home" class="nav__link">
<i class="uil uil-estate nav__icon"></i> Home
</a>
</li>
<!-- About 메뉴 -->
<li class="nav__item">
<a href="#about" class="nav__link">
<i class="uil uil-user nav__icon"></i> About
</a>
</li>
<!-- 가능한 기술 명시 메뉴 -->
<li class="nav__item">
<a href="#skills" class="nav__link">
<i class="uil uil-file-alt nav__icon"></i> Skills
</a>
</li>
<!-- 이용 가능한 서비스 메뉴 -->
<li class="nav__item">
<a href="#sevices" class="nav__link">
<i class="uil uil-briefcase-alt nav__icon"></i> Services
</a>
</li>
<li class="nav__item">
<a href="#portfolio" class="nav__link">
<i class="uil uil-scenery nav__icon"></i> Portfolio
</a>
</li>
<!-- 컨택트 메뉴 -->
<li class="nav__item">
<a href="#contact" class="nav__link">
<i class="uil uil-comment-alt-message nav__icon"></i> Contactme
</a>
</li>
<!-- 메뉴 닫기 아이콘 -->
<i class="uil uil-times nav__close" id="nav-close"></i>
</ul>
</div>
<!-- 햄버거 메뉴 -->
<div class="nav__btns">
<div class="nav__toggle" id="nav-toggle">
<i class="uil uil-draggabledots"></i>
</div>
</div>
</nav>
</header>
해당 채널은 class와 id 속성을 중복으로 줄 때와 아닌 때로 구분이 되는데 그 이유를 생각해봤을 때에는 js에는 id선택자를 사용하고 css에는 class 선택자를 명시하고 사용하고 있는 것 같다라는 생각이 들었다. 앞으로의 코딩을 더욱 클론해 봐야 명확해 지겠지만 현재는 그렇게 구분지어 나름대로 가독성을 높힌 것이 아닐까라는 생각이 들었다.
CSS
- 스타일을 적용하기 전에 설정한 전역 속성들로 색상, 폰트 크기, 폰트 굵기, 외부여백과 내부여백 등이 명시되어 있다.
/*=============== VARIABLES CSS ===============*/
:root {
/* :root는 가상 선택자로 최상위 엘리먼트를 의미하며, 사용 이유는 우선순위 html 선택자는 1점, :root는 클래스 선택자로 10점으로 간주되기 때문에 사용한다.
또한, 각 전역에서 사용하기 위한 가상의 속성과 속성값을 지정하는 전역변수라고 생각하면 편하다.
하이픈(-) 두개를 쓰고 변수명을 쓰며 명시한다.
*/
--header-height: 3rem;
/*=============== COLORS =============== */
/* 테마색을 바꿀 때 용이하도록 메인 색상을 변수로 지정 */
--hue-color: 255;
/* HSL color made */
--first-color: hsl(var(--hue-color), 57%, 61%);
--first-color-second: hsl(var(--hue-color), 69%, 61%);
--first-color-alt: hsl(var(--hue-color), 57%, 53%);
--first-color--lighter: hsl(var(--hue-color), 92%, 85%);
--title-color: hsl(var(--hue-color), 8%, 15%);
--text-color: hsl(var(--hue-color), 8%, 45%);
--text-color-light: hsl(var(--hue-color), 8%, 65%);
--input-color: hsl(var(--hue-color), 70%, 96%);
--body-color: hsl(var(--hue-color), 60%, 99%);
--container-color: #fff;
/*=============== Font and typography ===============*/
/* 폰트 스타일과 사이즈별 전역 속성 설정 */
--body-font: 'Nanum Myeongjo', serif;
--big-font-size: 2rem;
--h1-font-size: 1.5rem;
--h2-font-size: 1.25rem;
--h3-font-size: 1.123rem;
--normal-font-size: .938rem;
--small-font-size: .813rem;
--smaller-font-size: .75rem;
/*=============== Font weight ===============*/
--font-medium: 700;
--font-semi-bold: 800;
/*=============== Margin Bottom ===============*/
/* .25rem = 4px, .5rem = 8px, .75rem = 12px ... */
--mb-0-25: .25rem;
--mb-0-5: .5rem;
--mb-0-75: .75rem;
--mb-1: 1.5rem;
--mb-1-5: 1.5rem;
--mb-2: 2rem;
--mb-2-5: 2.5rem;
--mb-3: 3rem;
/*=============== Z index ===============*/
--z-tooltip: 10;
--z-fixed: 100; /*위치가 고정되어 보여져야 할 중요도*/
--z-modal: 1000; /*팝업 창과 비슷한 개념으로 기본레이아웃보다 중요도가 높아야 하는 요소*/
}
/* Font size for Large devices
중간(태블릿)이상의 매체일 때에 대한 글자 크기에 대한 전역 속성 */
@media screen and (min-width:968px) {
:root {
--big-font-size: 3rem;
--h1-font-size: 2.25rem;
--h2-font-size: 1.5rem;
--h3-font-size: 1.25rem;
--normal-font-size: 1rem;
--small-font-size: .875rem;
--smaller-font-size: .813rem;
}
}
/*=============== BASE ===============*/
*{
box-sizing:border-box;
padding: 0;
margin: 0;
}
html{
/* 스크롤을 부드럽게 해주는 기능이다 */
scroll-behavior: smooth;
}
body{
margin: 0 0 var(--header-height) 0;
font-family: var(--body-font);
font-size: var(--normal-font-size);
background-color: var(--body-color);
color: var(--text-color);
}
h1,h2,h3,h4{
color: var(--title-color);
font-weight: var(--font-semi-bold);
}
ul{
list-style: none;
}
a{
text-decoration: none;
}
img{
max-width: 100%;
height: auto;
}
- header와 nav
/*=============== LAYOUT ===============*/
.container{
max-width: 768px;
margin-left: var(--mb-1-5);
margin-right: var(--mb-1-5);
}
.grid{
display: grid;
/* 행과 열 사이의 범위 값 */
gap: 1.5rem;
}
.header{
width: 100%;
position: fixed;
bottom: 0;
left: 0;
z-index: var(--z-fixed);
background-color: var(--body-color);
}
/*=============== NAV ===============*/
.nav{
max-width: 968px;
height: var(--header-height);
display: flex;
justify-content: space-between;
align-items: center;
}
.nav__logo,
.nav__toggle{
color: var(--title-color);
font-weight: var(--font-medium);
}
.nav__logo:hover{
color: var(--first-color);
}
.nav__toggle{
font-size: 1.1rem;
cursor: pointer;
}
.nav__toggle:hover{
color: var(--first-color);
}
@media screen and (max-width: 767px) {
.nav__menu{
position: fixed;
bottom: -100%;
left: 0;
width: 100%;
background-color: var(--body-color);
padding: 2rem 1.5rem 4rem;
box-shadow: 0 -1px 4px rgba(0,0,0, .15);
border-radius: 1.5rem 1.5rem 0 0;
transition: .3s;
}
}
.nav__list{
/* nav__list 의 컨텐츠가 3개씩 한열에 보이게 반복한다 */
grid-template-columns: repeat(3, 1fr);
gap: 2rem;
}
.nav__link{
display: flex;
flex-direction: column;
align-items: center;
font-size: var(--small-font-size);
color: var(--title-color);
font-weight: var(--font-medium);
}
.nav__link:hover{
color: var(--first-color);
}
.nav__icon{
font-size: 1.2rem;
}
.nav__close{
position: absolute;
right: 1.3rem;
bottom: .5rem;
font-size: 1.5rem;
cursor: pointer;
color: var(--first-color);
}
.nav__close:hover{
color: var(--first-color-alt);
}
/* show menu */
/* js파일에서 menu를 show 할 때 쓰이는 css 스타일 */
/* .nav-menu의 기본값을 bottom:-100%로 했기 때문에 bottom: 0이 새롭게 적용되면서 나타나게 됨.*/
.show-menu{
bottom: 0;
}
JavaScript
- toggle 버튼과 close 버튼을 눌렀을 때에 대한 메뉴의 숨기기와 나타나기
- 각 아이콘을 눌렀을 때 메뉴 숨기기
/*=============== MENU SHOW & HIDDEN Elements ===============*/
const navMenu = document.getElementById('nav-menu'),
navToggle = document.getElementById('nav-toggle'),
navClose = document.getElementById('nav-close');
/*=============== MENU SHOW ===============*/
// nav-toggle 버튼을 눌렀을 때 show-menu가 class로 추가되면서 css가 구동되게 함.
if(navToggle){
navToggle.addEventListener('click', () => {
navMenu.classList.add('show-menu')
});
}
/*=============== MENU HIDDEN ===============*/
// nav-close 버튼을 눌렀을 때 추가되었던 show-menu가 클래스에서 제거되면서 적용되었던 css가 적용되지 않게 함.
if(navClose){
navClose.addEventListener('click', () => {
navMenu.classList.remove('show-menu')
});
}
/*=============== REMOVE MENU MOBILE ===============*/
// menu의 icon을 선택하였을 때 menu 가 hidden 되도록 하는 js
const navLink = document.querySelectorAll('.nav__link'); /*nav내에 있는 icon들을 모두 묶는 변수*/
function linkAction(){
// When we click on each nav__link, we remove the show-menu class
navMenu.classList.remove('show-menu')
};
navLink.forEach(n => n.addEventListener('click', linkAction));