JavaScript 기초/실습_포트폴리오 웹페이지 만들기

MINJOO·2022년 6월 17일
0

"나를 보여주는 포트폴리오 만들기"

너무 해보고싶었던 작업인데 이렇게 할수 있어서 너무 좋다!!

<!DOCTYPE html>
<html>
<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>Final Project</title>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css" integrity="sha512-KfkfwYDsLkIlwQp6LFnl8zNdLGxu9YAA1QvwINks4PhcElQSvqcyVLLD9aMhXd13uQjoXtEKNosOWaZqXgel0g==" crossorigin="anonymous" referrerpolicy="no-referrer" />
  <!--아래 스타일은 style.css만 적용하거나 style.css만 빼거나 해서 적용하면됩니다.  -->
  <!--link rel="stylesheet" href="./css/init.css">
  <link rel="stylesheet" href="./css/header.css">
  <link rel="stylesheet" href="./css/main.css">
  <link rel="stylesheet" href="./css/about_me.css">
  <link rel="stylesheet" href="./css/what_i_do.css">
  <link rel="stylesheet" href="./css/background.css">
  <link rel="stylesheet" href="./css/portfolio.css">
  <link rel="stylesheet" href="./css/contact_with_me.css"-->
  <link rel="stylesheet" href="./css/style.css">  
</head>
<body>
  <!-- header -->
  <header>
    <div class="container">
      <h1>
        <!-- <button>LOGO</button> -->
        <button data-animation-scroll="true" data-target="#main">LOGO</button>
      </h1>
      <nav>
        <!-- <ul>
          <li>
            <button>About</button>
          </li>
          <li>
            <button>Features</button>
          </li>
          <li>
            <button>Portfolio</button>
          </li>
          <li>
            <button>Contact</button>
          </li>
        </ul> -->
        <!-- behavior: 'smooth' 적용할 때 아래 바꾸기 -->
        <!-- <ul>
          <li>
            <button data-animation-scroll="true" data-target="#about">About</button>
          </li>
          <li>
            <button data-animation-scroll="true" data-target="#features">Features</button>
          </li>
          <li>
            <button data-animation-scroll="true" data-target="#portfolio">Portfolio</button>
          </li>
          <li>
            <button data-animation-scroll="true" data-target="#contact">Contact</button>
          </li>
        </ul> -->
        <ul>
          <li>
            <button data-animation-scroll="true" data-target="#about">About</button>
          </li>
          <li>
            <button data-animation-scroll="true" data-target="#features">Features</button>
          </li>
          <li>
            <button data-animation-scroll="true" data-target="#portfolio">Portfolio</button>
          </li>
          <li>
            <button data-animation-scroll="true" data-target="#contact">Contact</button>
          </li>
        </ul>
      </nav>
    </div>
  </header>
  <!-- //end header -->
   <!-- main -->
   <main id="main">
    <div class="container">
      <h4>Welcome</h4>
      <!-- 13.10.1에서 span 태그의 내용이 비워집니다. -->
      <h2>I`M A <span>Front-End Developer</span></h2>
      <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Praesentium dolor quas nulla unde ea officiis?</p>
      <button class="download">DOWNLOAD CV</button>
      <button class="mouse"><i class="fa-solid fa-computer-mouse"></i></button>
    </div>
  </main>
  <!-- //end Main -->
  
  <!-- About Me -->
  <section id="about" class="about">
    <div class="container">
      <div class="title">
        <h4>Who Am I</h4>
        <h2>About Me</h2>
      </div>
      <div class="about-self">
        <div class="left">
          <img src="./images/me_alone.jpg" alt="">
        </div>
        <div class="right">
          <h3>Hello, <strong>I`m Sucoding</strong></h3>
          <p>I`m Web Publisher And Web Front-End Developer.</p>
          <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Saepe veritatis aperiam accusantium.</p>
          <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Sit praesentium doloremque quos quis est officiis.</p>
          <p>Lorem, ipsum dolor sit amet consectetur adipisicing elit. Autem, omnis quibusdam.</p>
          <div class="social">
            <a href="#">
              <i class="fa-brands fa-facebook"></i>
            </a>
            <a href="#">
              <i class="fa-brands fa-instagram"></i>
            </a>
            <a href="#">
              <i class="fa-brands fa-twitch"></i>
            </a>
            <a href="#">
              <i class="fa-brands fa-youtube"></i>
            </a>
          </div>
        </div>
      </div>
    </div>
  </section>
  <!-- end About Me -->
  
  <!-- What I Do -->
  <section id="features" class="do">
    <div class="container">
      <div class="title">    <!-- title 영역 앞이랑 유사 -->
        <h4>Features</h4>
        <h2>What I Do</h2>
      </div>
      <!-- 사각형 모양으로 3단 분리된 본문 -->
      <div class="do-me">                
        <div class="do-inner">  <!-- 하나의 사각형을 나타내는 do-inner 클래스-->
          <div class="icon">
            <i class="fa-brands fa-html5"></i>
          </div>
          <div class="content">
            <h3>HTML5</h3>
            <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Illo culpa magni laboriosam sit excepturi quibusdam adipisci, vero debitis?</p>
          </div>
        </div>        
        <div class="do-inner">  <!-- 하나의 사각형을 나타내는 do-inner 클래스-->
          <div class="icon">
            <i class="fa-brands fa-css3-alt"></i>        
          </div>
          <div class="content">
            <h3>CSS3</h3>
            <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Illo culpa magni laboriosam sit excepturi quibusdam adipisci, vero debitis?</p>
          </div>
        </div>
        <div class="do-inner">  <!-- 하나의 사각형을 나타내는 do-inner 클래스-->
          <div class="icon">
            <i class="fa-brands fa-bootstrap"></i>
          </div>
          <div class="content">
            <h3>BootStrap v5.0</h3>
            <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Illo culpa magni laboriosam sit excepturi quibusdam adipisci, vero debitis?</p>
          </div>
        </div>
      </div>
    </div>  
  </section>
  <!-- //end What I Do -->
  <!-- Background -->
  <div class="bg"></div>
  <!-- //end Background-->
  <!-- PortFolio -->
  <!-- what_i_do 영역과 유사  -->
  <section id="portfolio" class="portfolio">
    <div class="container">
      <div class="title">
        <h4>PORTFOLIOBACK</h4>
        <h2>PortFolio</h2>
      </div>
      <div class="portfolio-me">
        <div class="portfolio-inner">
          <img src="images/mock1.png" alt="">
          <strong>BRANDING</strong>
          <h3>Package Design</h3>
        </div>
        <div class="portfolio-inner">
          <img src="images/mock2.png" alt="">
          <strong>DEVELOPMENT</strong>
          <h3>Tablet App Dev</h3>
        </div>
        <div class="portfolio-inner">
          <img src="images/mock3.png" alt="">
          <strong>MARKETING</strong>
          <h3>Coka Cola </h3>
        </div>
        <div class="portfolio-inner">
          <img src="images/mock4.png" alt="">
          <strong>APP</strong>
          <h3>FaceBook Clone</h3>
        </div>
        <div class="portfolio-inner">
          <img src="images/mock5.png" alt="">
          <strong>APP</strong>
          <h3>Netflix Clone</h3>
        </div>
        <div class="portfolio-inner">
          <img src="images/mock6.png" alt="">
          <strong>WEB</strong>
          <h3>FirmBee Web</h3>
        </div>
      </div>
    </div>
  </section>
  <!-- //end PortFolio -->
  <!-- Contact With Me -->
  <section id="contact" class="contact">
    <div class="container">
      <div class="title">
        <h4>CONTACT</h4>
        <h2>Contact With Me</h2>
      </div>

      <!-- Contact With Me 영역의 본문은 크게 왼쪽(phone, email, address)과 오른쪽(입력 양식 폼)으로 나눌 수 있습니다. 그래서 다음과 같이 contact-me 클래스를 가지는 div 태그 안에 left 클래스를 가지는 div 태그와 right 클래스를 가지는 div 태그로 영역을 구분합니다. -->
      <div class="contact-me">
        <div class="left">
          <div class="card">
            <div class="icon">
              <i class="fa-solid fa-phone-volume"></i>
            </div>
            <div class="info-text">
              <h3>phone</h3>
              <p>010-2222-1111</p>
            </div>
          </div>
          <div class="card">
            <div class="icon">
              <i class="fa-solid fa-envelope-open-text"></i>
            </div>
            <div class="info-text">
              <h3>email</h3>
              <p>sucoding@naver.com</p>
            </div>
          </div>
          <div class="card">
            <div class="icon">
              <i class="fa-solid fa-location-crosshairs"></i>
            </div>
            <div class="info-text">
              <h3>address</h3>
              <p>Samseong-ro, Gangnam-gu, Seoul, Republic of Korea</p>
            </div>
          </div>
        </div>
        <div class="right">
          <form action="#">
            <div class="form-group">
              <label for="name">name</label>
              <input type="text" id="name">
            </div>
            <div class="form-group">
              <label for="email">email</label>
              <input type="text" id="email">
            </div>
            <div class="form-group">
              <label for="msg">message</label>
              <textarea id="msg"></textarea>
            </div>
            <button>send</button>
          </form>
        </div>
      </div>
    </div>
  </section>
  <!-- end Contact With Me -->
  <script src="./js/script.js"></script>
</body>
</html>

메뉴나 내용은 개별적으로 추가하거나 수정가능하다.

JS 코드

// 첫 번째는 메인 영역의 텍스트 타이핑 효과입니다. 화면에 보여 줄 텍스트 데이터를 배열로 저장하고 일정 시간마다 반복하면 타이핑하듯이 화면에 출력합니다.

// 두 번째는 헤더 영역의 디자인 변경 효과입니다. 웹 브라우저를 스크롤하면 헤더 영역에 새로운 클래스를 추가해 디자인을 변경합니다.

// 세 번째는 스크롤 이동 효과입니다. 헤더 메뉴를 클릭하면 페이지 내부의 다른 영역으로 부드럽게 스크롤이 이동합니다.

// 텍스트 타이핑 효과
// 먼저 메인 영역의 h2 태그 안에 있는 span 태그의 텍스트를 지웁니다.
// span 요소 노드 가져오기
const spanEl = document.querySelector("main h2 span");
// 화면에 표시할 문장 배열
const txtArr = ['Web Publisher', 'Front-End Developer', 'Web UI Designer', 'UX Designer', 'Back-End Developer'];
// 배열의 인덱스 초깃값
let index = 0;
// 화면에 표시할 문장 배열에서 요소를 하나 가져온 뒤, 배열로 만들기
let currentTxt = txtArr[index].split("");
// 실행결과  ['W', 'e', 'b', ' ', 'P', 'u', 'b', 'l', 'i', 's', 'h', 'e', 'r']
// 텍스트가 입력되는 효과의 핵심은 currentTxt 변수에 할당된 배열 요소를 앞에서부터 한 개씩 출력하는 것입니다. 그러면 마치 텍스트가 한 글자씩 작성되는 것처럼 보이게 됩니다. 이를 위해 다음처럼 writeTxt() 함수를 만들어 배열 요소를 한 개씩 출력하게 합니다.

function writeTxt(){
    spanEl.textContent += currentTxt.shift(); // ①
    if(currentTxt.length !== 0){ // ②
       setTimeout(writeTxt, Math.floor(Math.random() * 100));
    }else{ // ③
      currentTxt = spanEl.textContent.split("");
      setTimeout(deleteTxt, 3000);
    }
  }
  writeTxt();
// ① 배열의 요소를 앞에서부터 한 개씩 출력해야 합니다. 이럴 때 사용하는 메서드가 Array 객체의 shift()입니다. shift() 메서드는 파괴적 메서드로, 배열에서 맨 앞의 요소를 추출하고 추출한 요소를 원본 배열에서 삭제합니다. 만약 currentTxt 변수에 ['W', 'e', 'b', ' ', 'P', 'u', 'b', 'l', 'i', 's', 'h', 'e', 'r'] 배열이 할당된 상태라면 배열의 첫 번째 요소인 W를 배열에서 추출하고, 배열에서 W를 삭제합니다.
// ② if 문으로 currentTxt 변수에 할당된 배열의 길이가 0인지 확인합니다. 확인하는 이유는 배열의 길이가 0이 아니라면 아직 출력해야 하는 단어가 남아 있다는 뜻이어서 배열 요소를 모두 출력할 때까지 writeTxt() 함수를 반복 호출하기 위해서입니다. 그래서 if 문의 코드를 보면 setTimeout() 메서드로 일정 시간이 흐른 뒤에 writeTxt() 함수를 다시 호출합니다. 여기서 setTimeout() 메서드의 두 번째 인자는 시간(밀리초)을 의미합니다. 이 값은 호출할 때마다 Math 객체의 random() 메서드로 0부터 100 사이의 숫자가 무작위로 구해져, 작성되는 글자 속도를 매번 달라지게 합니다.
// ③ else 문이 실행된다는 건 결국 currentTxt 배열이 비었다는 뜻입니다. 다르게 말하면 배열 안의 모든 텍스트가 전부 화면에 출력됐다는 의미죠. 그러면 텍스트 작성 함수를 끝내기 전에 텍스트를 지우기 위해 화면에 표시된 텍스트를 가져와서 split() 메서드로 다시 단어 단위로 분리해 배열에 할당합니다. 그리고 3초 뒤에 텍스트를 지우는 함수인 deleteTxt() 함수를 호출하는 것까지가 텍스트를 입력하는 writeTxt() 함수의 역할입니다.


// 텍스트 삭제는 입력 과정과 비슷합니다. 텍스트를 입력할 때는 배열의 앞에서부터 요소를 추출해 한 글자씩 출력했는데, 텍스트를 삭제할 때는 뒤에서부터 요소를 추출해 한 글자씩 줄어드는 것처럼 표현
function deleteTxt(){
    currentTxt.pop(); // ①
    spanEl.textContent = currentTxt.join("");// ②
    if(currentTxt.length !== 0){ // ③
      setTimeout(deleteTxt, Math.floor(Math.random() * 100));
    }else{ // ④
      index = (index + 1) % txtArr.length;
      currentTxt = txtArr[index].split("");
      writeTxt();
    }
  }
//   ① 가장 먼저 currentTxt 변수에서 pop() 메서드를 실행합니다. 현재 currentTxt 변수에는 deleteTxt() 함수를 실행하기 전에 화면에 표시된 텍스트를 가져와서 split() 메서드로 분리한 배열이 할당되어 있습니다. 지금 작성하는 코드를 기준으로 첫 번째 문장인 ‘Web Publisher’를 출력했으니 현재 currentTxt 변수에는 ['W', 'e', 'b', ' ', 'P', 'u', 'b', 'l', 'i', 's', 'h', 'e', 'r']이 할당되어 있습니다.

// 이 상태에서 Array 객체의 pop() 메서드로 배열 요소를 끝에서부터 한 개씩 삭제합니다. pop() 메서드는 파괴적 메서드라서 원본 배열에서 요소가 삭제됩니다. 그래서 변수에 할당된 배열은 끝에서 요소가 하나 삭제되고 변수에는 ['W', 'e', 'b', ' ', 'P', 'u', 'b', 'l', 'i', 's', 'h', 'e']가 할당된 상태가 됩니다.

// ② 다음으로 Array 객체의 join() 메서드로 현재 배열에 있는 요소를 하나의 문자열로 합칩니다. 그러면 ‘Web Publishe’라는 문자열이 span 요소의 텍스트로 할당됩니다. 따라서 사용자 눈에는 한 글자가 삭제된 것처럼 보입니다.

// ③ 그러고 나서 writeTxt() 함수처럼 if 문으로 currentTxt 변수에 할당된 배열이 비었는지 확인합니다. 만약 값이 남아 있으면 다시 deleteTxt() 함수를 호출합니다. 이때 호출되는 시간은 0에서 0.1초(0~100) 사이에서 무작위로 설정합니다.

// ④ 모든 배열이 pop() 메서드에 의해 삭제되면 else 문이 실행됩니다. else 문에서는 다음 문장을 출력하기 위해 배열에 다시 접근합니다. 이때 index 숫자를 1 증가시키는데, 그 이유는 index 숫자가 문장이 담긴 배열(txtArr)의 길이를 넘지 않게 하기 위해서입니다. index 숫자를 1 증가시키고 나면 문장 배열에 접근해 새로운 문장을 가져옵니다. 작성된 순서대로라면 ‘Front-End Developer’에 접근합니다. 이를 split() 메서드를 사용해 배열로 만드므로 currentTxt 변수에는 ['F', 'r', 'o', 'n', 't', '-', 'E', 'n', 'd', ' ', 'D', 'e', 'v', 'e', 'l', 'o', 'p', 'e', 'r'] 배열이 할당됩니다. 그러면 다시 할당된 currentTxt 변수의 배열로 writeTxt() 메서드를 호출해 지금까지 한 과정을 무한히 반복합니다.

// ********************************************************
// 웹 브라우저의 수직 스크롤 위치는 window 객체의 pageYOffset 속성으로 참조할 수 있습니다. 
// 속성값이 0보다 크면 스크롤됐다고 볼 수 있으므로 이를 조건으로 처리해서 if 문으로 active 클래스를 추가하거나 삭제하면 됩니다.
// pageYOffset : 스크롤했을 때 화면이 수직으로 이동하는 픽셀 수

const headerEl = document.querySelector("header");
window.addEventListener('scroll', function(){
 const browerScrollY = window.pageYOffset;
  if(browerScrollY > 0){
    headerEl.classList.add("active");   //명시된 클래스를 추가하는 메서드
  }else{
    headerEl.classList.remove("active");  //명시된 클래스를 제거하는 메서드
  }
});


// ********************************************************
// 이번에는 헤더 영역의 메뉴를 클릭하면 메뉴 영역으로 스크롤이 부드럽게 이동하는 효과를 자바스크립트로 작성해 보겠습니다.

// Window 객체의 scrollTo() 메서드에서 behavior 속성을 사용하면 애니메이션 효과를 적용해 스크롤을 부드럽게 이동할 수 있습니다. 단, IE나 iOS 모바일에서는 제대로 동작하지 않습니다. 이 외의 웹 브라우저에서는 정상적으로 작동합니다.

// 일단 이동할 대상 요소를 가리키는 선택자(selector)를 매개변수에 전달받아 이동하려는 대상의 현재 위칫값을 구하는 코드를 작성합니다.
/* 애니메이션 스크롤 이동 */
const animationMove = function(selector){
    // ① selector 매개변수로 이동할 대상 요소 노드 가져오기
    const targetEl = document.querySelector(selector);
    // ② 현재 웹 브라우저의 스크롤 정보(y 값)
    const browserScrollY = window.pageYOffset;
    // ③ 이동할 대상의 위치(y 값) https://developer.mozilla.org/ko/docs/Web/API/Element/getBoundingClientRect
    //getBoundingClientRect(): 윈도우(window)룰 기준으로 특정 엘리먼트의 위치 값을 구하는 방법
    const targetScorllY = targetEl.getBoundingClientRect().top + browserScrollY;
    // ④ 스크롤 이동 https://salgum1114.github.io/css/2019-04-28-scroll-behavior-smooth/
    window.scrollTo({ top: targetScorllY, behavior: 'smooth' });
  };

//   코드가 조금 복잡해 보일 수 있지만, 부드러운 스크롤 이동 효과를 구현하는 데 꼭 필요한 값들입니다. 웹 브라우저의 스크롤 이동을 처리하려면 이동할 대상의 스크롤 위치(y 값)를 당연히 알아야 합니다. 그러려면 이동할 대상의 노드를 가져올 수 있어야 하고(①), 현재 웹 브라우저의 스크롤 위치를 구해야 합니다(②). 그래야 가져온 요소 노드로 구하는 위치의 정확한 y 값을 구할 수 있습니다(③). 그리고 window 객체의 scrollTo() 메서드를 사용해 해당 위치로 이동합니다(④).

// 이제 기존 헤더 영역에서 메뉴에 해당하는 button 태그에 클릭 이벤트를 연결해 앞에서 만든 animationMove() 함수를 실행하겠습니다. 헤더 영역의 메뉴에 사용된 button 태그에 click 이벤트를 연결하기 위해 다음처럼 코드를 수정합니다.
// https://developer.mozilla.org/ko/docs/Learn/HTML/Howto/Use_data_attributes
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dataset

// 스크롤 이벤트 연결하기
const scollMoveEl = document.querySelectorAll("[data-animation-scroll='true']"); 
for(let i = 0; i < scollMoveEl.length; i++){
  scollMoveEl[i].addEventListener('click', function(e){
    const target = this.dataset.target;
    animationMove(target);
  });
}

css 코드

@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;700&family=Varela+Round&display=swap');
/* header.css */
*{
  margin:0;
  padding:0;
  box-sizing:border-box;
}
a, a:link, a:visited{
  color:inherit;
  text-decoration:none;
}
li{
  list-style:none;
}
.container{
  width:1140px;
  margin:0 auto;
}
header{
  position:fixed;
  color:white;
  top:0;
  z-index:1;
  width:100%;
  padding:1rem;
}
header .container{
  display:flex;
  justify-content:space-between;
  align-items:center;
  width:100%;
}
header nav ul{
  display:flex;
}
header nav ul li{
  padding:10px;
}
header button{
  background: transparent;
  border:0;
  cursor: pointer;
  color:white; 
}
header h1 button{
  font-size: 2rem;
  font-weight: bold;
}
header nav ul li button{
  font-size: 1.2rem;
}
/* End header.css */
/* main.css */
main{
  width:100%;
  height:100vh;
  color:white;
  background:linear-gradient(rgba(0,0,0,0.8), rgba(0,0,0,0.8)), url('../images/me.jpg') center center;
  background-size:cover;
  display:flex;
  justify-content:center;
  align-items:center;
  text-align:center;
}
main h4{
  font-size:2rem;
}
main h2{
  font-size:3.5rem;
  margin:2rem 0;
  letter-spacing:3px;
  font-family:'Varela Round', sans-serif; /* 웹 폰트 추가 */
}
main p{
  max-width:500px;
  margin:0 auto;
  font-size:1.25rem;
}
main button.download{
  background-color:transparent;
  border:3px solid white;
  border-radius:20px;
  padding:1rem 2rem;
  margin-top:3rem;
  color:white;
  font-weight:bold;
  cursor:pointer;
}
main button.mouse{
  background-color:transparent;
  border:none;
  color:white;
  font-size:2rem;
  position:absolute;
  bottom:1rem;
  left:50%;
  transform:translateX(-50%);
  animation:upDown 1s ease-in-out infinite;
  cursor:pointer;
}
@keyframes upDown{
  0%{
    bottom:1rem;
  }
  50%{
    bottom:1.5rem;
  }
  100%{
    bottom:1rem;
  }
}
main h2 span::after{
  content:"";
  height:40px;
  width:3px;
  background-color:#fff;
  display:inline-block;
  animation: blink .7s ease-in-out infinite;
}
@keyframes blink {
  0%{
    opacity: 1;
  }
  100%{
    opacity: 0;
  }
}

/* about_me.css */
section{
  font-family:'Poppins', sans-serif;
  padding:5rem 0;
}
section:nth-child(2n){
  background-color:#f8f8f8;
}
section .title{
  margin-bottom:3rem;
}
section .title h4{
  font-size:1.35rem;
  color:#ed4848;
  position:relative;
}
section .title h2{
  font-size:3.5rem; 
}
section .title p{
  font-size:1.15rem;
}
/* float 속성 해제 */
section .about-self::after{
  content:"";
  clear:both;
  display:block;
}
/* 본문 너비 절반 지정 & 왼쪽 배치 */
section .about-self .left{
  width:50%;
  float:left;
}
/* 이미지 크기가 부모 영역을 넘지 않도록 부모 영역의 최대 크기로 지정 */
section .about-self .left img{
  max-width:100%;
}
/* 본문 너비 절반 지정 & 오른쪽 배치 */
section .about-self .right{
  width:50%;
  float:left;
  padding:0 2rem;
}
/* 본문 오른쪽 h3 태그의 글자 크기와 여백 지정 */
section .about-self .right h3{
  font-size:2.25rem;
  margin-bottom:1rem;
}
/* 본문 오른쪽 h3 태그의 strong 태그 색상 강조 */
section .about-self .right h3 strong{
  color:#ed4848;
}
/* 본문 오른쪽 p 태그의 크기와 여백 지정 */
section .about-self .right p{
  font-size:1.15rem;
  margin:1rem 0;
}
/* 본문 오른쪽의 아이콘 폰트 크기와 여백 지정 */
section .about-self .right .social a{
  font-size:2.5rem;
  margin-right:0.2rem;
}
/* End about_me.css */
/* what_i_do.css */
/* float 속성 해제*/
section .do-me::after{
  content:"";
  display:block;
  clear:both;
}
/* 사각형 크기와 간격, 내부 여백 설정 */
section .do-me .do-inner{
  background-color:#fff;
  width:30%;
  padding:2rem; 
  float:left;
  margin-right:5%;
  cursor:pointer;
}
/* 마지막 사각형의 외부 여백 설정 */
section .do-me .do-inner:last-child{
  margin-right:0;
}
/* 아이콘 폰트 크기와 색상 */
section .do-me .do-inner .icon i{
  font-size:2.5rem;
  color:#ff6a6a;
}
/* HTML5, CSS3, BootStrap v5.0 텍스트 크기와 간격 */
section .do-me .do-inner .content h3{
  font-size:2rem;
  margin:1rem 0;
}
/* 사각형 텍스트 크기 */
section .do-me .do-inner .content p{
  font-size:1.15rem;
}
/* do-inner 클래스에 마우스를 올리면 배경색과 텍스트 색상 변경 */
section .do-me .do-inner:hover{
  background-color:lightcoral;
  color:white;
}
/* do-inner 클래스에 마우스를 올리면 아이콘 폰트 색상 변경 */
section .do-me .do-inner:hover i{
  color:white;
}
/* End what_i_do.css */
/* background.css */
.bg{
  background:url('../images/background.jpg') center center;
  background-size:cover;
  background-attachment:fixed;
  height:650px;
}
/* End background.css */
/* portfolio.css */
/* clear 속성으로 float 속성값 해제*/
section.portfolio::after{
  content:"";
  display:block;
  clear:both;
}
/* portfolio-inner 사각형 꾸미기 */
section.portfolio .portfolio-inner{
  width:30%;
  margin-right:5%;
  padding:1rem 1rem 1.5rem 1rem;
  float:left;
  background-color:#f8f8f8;
  border:1px solid #ccc;
  margin-bottom:3rem;
}
/* 3번째마다 margin-right 0 적용 */
section.portfolio .portfolio-inner:nth-child(3n){
   margin-right:0;
}
/* 이미지의 크기가 부모 요소를 넘지 않도록 100%로 지정 */
section.portfolio .portfolio-inner img{
  width:100%;
  display:block;
}
/* strong 태그 색상과 간격 */
section.portfolio .portfolio-inner strong{
  color:#ff6a6a;
  margin:0.5rem 0;
  display:block;
}
/* h3 태그 색상과 간격 */
section.portfolio .portfolio-inner h3{
  font-size:1.75rem;
}
/* End portfolio.css */
/* contact_with_me.css */

section.contact .contact-me::after{
  content:"";
  display:block;
  clear:both;
}
section.contact .contact-me .left{
  width:30%;
  float:left;
}
section.contact .contact-me .right{
  float:left;
  width:65%;
  margin-left:5%;
}
section.contact .contact-me .left .card{
  border:1px solid #ccc;
  padding:1rem;
  display:flex;
  align-items:center;
  margin-bottom:1.25rem;
}
section.contact .contact-me .left .card .icon i{
  font-size:2rem;
  margin-right:15px;
}
section.contact .contact-me .right{ 
  float:left;
  width:65%;
  margin-left:5%;
  margin-bottom:2rem;
  border:1px solid #ccc;
  padding:1rem;
}
/* form-group 사이 간격 지정 */
section.contact .contact-me .right .form-group{
  margin-bottom:1.25rem;
}
/* label 태그가 인라인 성격이어서 외부 여백을 적용하기 위해 block으로 변경 */
section.contact .contact-me .right .form-group label{
  display:block;
  margin-bottom:0.85rem;
}
/* input 요소 꾸미기 */
section.contact .contact-me .right .form-group input{
  padding:0.625rem;
  width:100%;
  outline:none;
  border:1px solid #ccc;
  border-radius:10px;
}
/* :focus 가상 클래스 선택자로 입력 요소에 커서가 활성화되면 파란색 테두리와 그림자 효과 추가 */ 
section.contact .contact-me .right .form-group input:focus{
  border:1px solid #719ECE;
  box-shadow:0 0 10px #719ECE;
}
/* textarea 요소 꾸미기 */
section.contact .contact-me .right .form-group textarea{
  height:300px;
  width:100%;
  resize:none;
  border:1px solid #ccc;
  border-radius:10px;
}
/* textarea 요소에 커서 활성화가 되면 파란색 테두리와 그림자 효과 추가 */
section.contact .contact-me .right .form-group textarea:focus{
  outline:none;
  border:1px solid #719ECE;
  box-shadow:0 0 10px #719ECE;
}
/* 버튼 요소 꾸미기 */
section.contact .contact-me .right button{
  width:100%;
  padding:1rem;
  background-color:#f78b00;
  border:none;
  color:white;
}

/* End contact_with_me.css */
/* media.css */
@media screen and (max-width: 1140px){

  /* 메인 container 기준 1140 -> 992px */
  main .container{
    width: 992px;
  }
  
  /* 섹션 container 기준 1140 -> 600px */
  section .container{
    width:600px;
  }

  /* About me 영역 왼쪽을 너비를 50% -> 100% 변경 */
  section .about-self .left{
    width:100%;
    margin-bottom: 1.5rem;
  }
  /* About me 영역 오른쪽 너비를 50% -> 100% 변경 */
  section  .about-self .right{
    width:100%;
    padding:0;
  }

  /* What I Do 영역의 콘텐츠 박스의 너비를 30% -> 48% 변경 */
  section .do-me .do-inner{
    width:48%;
    margin-bottom: 1.5rem;
    margin-right: 0;
  }

  section .do-me .do-inner:nth-child(2n+1){
    margin-right:4%; /* 1, 3, 5...번째 본문 사각형에 margin-right 4% 적용 */
  }
  
  /* PortFolio 영역의 콘텐츠 박스 너비를 30% ->  48% 변경 */
  section .portfolio-me .portfolio-inner{
    width:48%;
    margin-right: 0;
  }

  section .portfolio-me .portfolio-inner:nth-child(2n+1){
    margin-right:4%;
  }

  /* Contact With Me 영역 */
  section.contact .contact-me .left{
    width:100%; /* 너비 변경 30% -> 100% */
  }
  
  section.contact .contact-me .right{
    width:100%;/* 너비 변경 65% -> 100% */
    margin-left: 0; /* margin 초기화 */
    
  }
}

@media (max-width: 992px){
  html{
    font-size: 14px;
  }

  /* 메인 영역 container 기준 너비 변경 */
  main .container{
    width: 768px; /* 992px -> 768px */
  }

  /* PortFolio 영역 */
  section .portfolio-me .portfolio-inner{
    width:100%; /* 48% -> 100% 변경 */
  } 
}

@media (max-width: 768px){
  html{
    font-size: 13px;
  }
  
  /* 메인 영역 container 기준 너비 변경 */
  main .container{
    width: 576px; /* 768px -> 576px */
  }

  section .container{
    width:400px; /* 600px -> 400px */
  }

  section .do-me .do-inner{
    width:100%; /* 48% -> 100% */
    margin-right: 0; /* margin 초기화 */
  }
}

@media (max-width: 576px){
  
  html{
    font-size: 12px;
  }

  main .container{
    width: 400px; /* 576px -> 400px */
  }

  section .container{
    width:360px; /* 400px -> 360px */
  }
}

@media (max-width: 400px){
  
  html{
    font-size: 11px;
  }
  main .container{
    width: 320px;/* 400px -> 320px */
  }

  main h4{
    font-size: 1.5rem;
  }
  section .container{
    width: 320px;/* 360px -> 320px */
  }
  section .title h2{
    font-size: 3rem; /* 3.5rem -> 3rem */
  }
}
/* End media.css */
header.active{
  background-color:rgba(0,0,0);
  animation:fadeIn 0.5s ease-in-out;
}
@keyframes fadeIn{
  0%{
    opacity:0;
  }
  100%{
    opacity:1;
  }
}

결과물






느낀점

신기하다!! 얼른 내 포트폴리오 페이지도 만들어야겠다!

profile
코딩 신생아

0개의 댓글