리본 달린 카드 테이블 만들기

Kim, Hyeonseo·2021년 7월 5일
0

최근 들어 포트폴리오 사이트 디자인 하면서 이것저것 조금씩 만들어 보는 것들이 있는데, 처음 만들어 보면서 배우는 것이 많아 정리해보려고 합니다.

이번에 만든 건 기술 스택을 표현할 카드와, 그 카드에 부가 설명을 달아줄 효과와 리본, 그리고 그 것들을 Grid System을 이용해서 정렬한 것을 다룹니다. 그리고 비네팅 효과도 구현해 볼 거예요.

1. 사전 준비

<!DOCTYPE html>
<head>
  <title>portfolio-cards-table</title>
  <link rel="stylesheet" type="text/css" href="/main.css" />
</head>
<body>
  <div id="root">
    <div class="table-wrapper">
      <h1>Technical Skills</h1>
      <div class="table-cell-wrapper">
        <div class="card tech-title">Frontend</div>
        <div class="card tech react">
          <div class="ribbon-wrapper">
            <div class="ribbon advanced"></div>
            <div class="ribbon-label">3</div>
          </div>
          <span class="ribbon-tooltips">능숙한<br />수준</span>
        </div>
  </div>
</body>
@import url("https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@100;300;400;500;700;900&display=swap");

#root {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: #505050;

  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  font-family: "Noto Sans KR", sans-serif;
}

.table-wrapper {
  width: 1000px;
  height: 500px;

  background: #f0f0f0;

  box-shadow: 4px 8px 16px 0 rgba(0, 0, 0, 0.1);
  display: flex;
  flex-direction: column;
  align-items: center;
}

h1 {
  text-align: center;
  font-weight: 900;
}

.table-cell-wrapper {
  width: 95%;
  display: grid;
  grid-template-columns: repeat(5, 150px);
  grid-template-rows: repeat(2, 150px);
  gap: 50px 50px;
  place-items: start center;
}

<!DOCTYPE html> 선언, <head />, <body />, 전체를 담아줄 #root, 테이블을 담아줄 래퍼와 제목, 그리고 일단 레이아웃을 만들기 전에 만들 2개의 카드 종류, 리본과 리본에 달릴 라벨, 감쌀 래퍼, 툴팁을 만들어 줍니다.

2. 비네팅 효과(Vignetting)

비네팅 효과는 화면 가장자리로 갈수록 채도와 명도가 떨어지는 현상을 의미합니다. 여러가지 원인이 있지만, 가장 주요한 원인은 렌즈 가장자리로 갈수록 들어오는 빛의 양이 줄어서 나타납니다. 화면 전체가 균일하게 보여야 할 때에는 방해가 되는 현상이지만, 자연스럽게 화면 중앙에 집중 시킬 수 있는 방법이기에 상황에 따라 쓰이는 방법입니다.

비네팅 효과는 시멘틱한 의미가 있지 않고, 시각적인 요소에만 해당하기 때문에 실제 태그를 만들어서 넣기보다는, 의사 클래스(::before)를 만들어 전체를 덧씌우는 것으로 구현하였습니다.

https://stackoverflow.com/questions/9603337/how-to-make-a-vignette-effect-in-html

비네팅은 다양한 CSS로 구현할 수 있는데, 저는 box-shadow를 이용하여 구현했습니다.

#root::before {
  position: fixed;
  width: 100%;
  height: 100%;
  content: "";
  box-shadow: 0px 0px 220px black inset;

  pointer-events: none;

pointer-events: none; 은 마우스와 상호작용을 막아주는 CSS 요소로서, #root::before 때문에 #root를 선택하지 못하는 것을 방지합니다.

content는 아무 내용이 없으면 렌더링이 아예 안되길래 추가하였습니다.

3. 카드 만들기

카드는 카테고리를 나타내는 tech-title 카드와 각 요소를 표현한 tech 카드가 있습니다. tech 카드는 각각 요소별로 배경이 다릅니다. 이 둘은 기본적으로 카드이기 때문에 card 라는 클래스로 묶여 있고요.

.card {
  width: 150px;
  height: 150px;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 4px 8px 16px 0 rgba(0, 0, 0, 0.1);
  position: relative;
}

.react {
  background-image: url(assets/react.png);
  background-position: center center;
  background-size: 100% auto;
  background-repeat: no-repeat;
  background-color: #fff;
}

.tech-title {
  font-size: 1.5rem;
  font-weight: 600;
  color: #f0f0f0;
  background-color: #505050;
}

기본적인 카드가 만들어 졌습니다. 이 카드 중 .tech:hover를 넣어 특정 내용이 드러나게 해봅시다.

:hover 시 CSS 속성들을 편집해서 원하는 것이 나타나게 하는 방법도 있지만, 그 내용이 복잡할 경우, 다른 <div> 요소를 집어넣거나, ::before같은 의사 선택자를 활용하여 만든 것을 :hover와 다른 선택자를 조합해서 사용하는 방법이 있습니다.

여기서는 ::before를 사용해서 그 위에 덮어 씌워지듯이 나타나게 했습니다. 주의할 점은, .tech::before에 호버가 되는 것이 아니라, .tech:hover 될때 나타나는 것이므로, 의사 선택자는 다음 .tech:hover::before가 됩니다.

.tech::before {
  align-items: center;
  background-color: rgba(0, 0, 0, 0.8);
  content: "React";
  font-size: 2rem;
  font-weight: 400;
  color: #f0f0f0;
  display: flex;
  height: 100%;
  justify-content: center;
  opacity: 0%;
  transition: opacity 0.5s;
  width: 100%;
}

.tech:hover::before {
  opacity: 100%;
}

4. 리본 만들기

제일 시간을 많이 쓴 코너에 리본을 만드는 방법입니다. 현실에서 종이 문서에 구석에 리본을 달때, 우리는 리본을 기울여서 종이에 고정한 다음, 필요 없는 부분을 자르거나 종이 뒤쪽에서 고정해서 만듭니다. CSS에서도 비슷한 발상으로 작업합니다. 리본에 해당하는 영역을 만든 뒤, 그것을 회전시키고 필요없는 부분을 잘라야 합니다. 자르기 위해서, 저희는 영역 밖에 넘치는 부분을 감춰버리는 overflow:hidden을 사용하겠습니다.

.tech .ribbon-wrapper {
  width: 33%;
  height: 33%;
  overflow: hidden;
  position: absolute;
  top: -3px;
  right: -3px;
}

.tech .ribbon-wrapper .ribbon {
  position: relative;
  right: 0px;
  top: 0px;
  color: #333;
  transform-origin: 0% 0%;
  transform: rotate(-45deg);
  height: 71px;
  width: 35px;
}

.advanced {
  /* 리본 색 지정 */
  background-color: hsl(41, 100%, 60%);
}

이 리본은 카드의 오른쪽에 배치되는 것을 기준으로 하였기 때문에, 다른 방향에 하고 싶으면 회전 방향과 기준점, 위치를 변경해 주세요.

transform-origin: 0% 0%; 은 회전의 기준점을 왼쪽 위 모서리로 만들어 주는 것인데, 여유 영역 계산할때 좌표 기준을 여기로 두는 것이 제일 편해서 이 곳으로 하였습니다.

리본이 직각 이등변 삼각형이므로, 그 안에 접하게 사각형을 그려서 그 안에서 리본에 라벨을 달아주었습니다. 리본이랑 영역이 충돌나지 않게 positionabsolute로 주었습니다.

.tech .ribbon-wrapper .ribbon-label {
  width: 50%;
  height: 50%;
  color: #ffffff;
  position: absolute;
  top: 0px;
  right: 0px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 500;
  font-size: 22px;
  pointer-events: none;
}

5. 리본에 툴팁 달아주기

위에서는 opacity 가지고 사라지고 나타나는 것을 구현했지만, 다른 방법도 있습니다. visibility를 사용하는 것인데요. 툴팁은 visibility를 사용해서 구현하겠습니다.

.ribbon-tooltips {
  visibility: hidden;
  width: 120px;
  background-color: #2d2d2d;
  color: #f0f0f0;
  text-align: center;
  padding: 5px 0;
  border-radius: 6px;

  /* 툴팁의 위치 조정: 기본적으로 다른 요소보다 위에 보여야 합니다. */
  position: absolute;
  left: 160px;
  top: -3px;
  z-index: 2;
}

.ribbon-tooltips::after {
  /* 툴팁 말풍선의 꼬리입니다. 디자인적인 요소고, 영향을 미치지 않으므로 ::after로 구현합니다. */
  content: " ";
  position: absolute;
  top: 30%;
  right: 100%; /* To the left of the tooltip */
  margin-top: -5px;
  border-width: 5px;
  border-style: solid;
  border-color: transparent #2d2d2d transparent transparent;
}

.ribbon-wrapper:hover ~ .ribbon-tooltips {
  /* ~는 같은 계층에 존재하는 요소들을 연결하는 선택자입니다.*/
  visibility: visible;
}

6. 배열하기

제가 이때까지 자주 썼던 flex 시스템은 한 축에 대해서 정렬하기에는 편안하지만, 정렬하는 축을 늘릴때마다 사용하는 컨테이너가 하나씩 더 필요했습니다. Grid를 활용하면, 2개의 축과 그 사이 갭을 이용해서 배치를 할 수 있습니다.

https://studiomeal.com/archives/533

이 글을 보고 CSS 그리드를 맞춰주었습니다.

.table-cell-wrapper {
  width: 95%;
  display: grid;
  grid-template-columns: repeat(5, 150px);
  grid-template-rows: repeat(2, 150px);
  gap: 50px 50px;
  place-items: start center;
}

.card:nth-child(1) {
  grid-column: 1 / 1;
  grid-row: 1 / 3;
}
profile
초보 학생입니다. 개인 블로그는 독서, 음악, 여행 관련 내용도 올라옵니다.

0개의 댓글