시작하기에 앞서...

처음에는 반응형으로 만들어서 모든 디바이스에 대응하도록 하고 싶었지만, 시간이 부족할 것 같습니다. 여건이 되는대로 개선하는 것을 목표로 해보겠습니다 ㅠㅠ

header 작업 과정

line-height ?


개발자 도구로 네비게이션 메뉴들에 대해 확인하던 중 "여기서 line-height를 적용한다고?"라고 생각되는 부분이 있었습니다. 이와 관련해서 생각해본 결과 "사용자의 편의성을 위해 li 요소의 글자가 아닌 상하좌우 영역을 클릭하더라도 링크가 동작할 수 있도록 하고자 한다. 그리고, 가운데로 정렬하여 디자인적인 요소를 스타일링한다" 라는 결론을 내리게 되었습니다. 실제 카카오 사이트의 네비게이션 메뉴글씨 상하단 영역으로 마우스를 올리더라도 클릭시 해당 링크로 이동할 수 있으며 글자는 li의 가운데 정렬 되있습니다.

일종의 css trick이라고 생각이 되며,
작업 중간에 flex로 개선할 수도 있지만, line-height를 적용해서 진행해보겠습니다.

:hover

당차게 navigation 영역을 작성하다가 첫번째 난관에 부딪혔습니다.
hover시 어떻게 처리할 것인가에 대한 문제이며 아래와 같은 고민을 하였습니다.

  1. (javascript) mouse 이벤트를 통해 클래스를 삽입하고 지워서 스타일을 적용한다.
  2. (css) css변수를 사용하고, javascript로 제어해야되나?
  3. (css) css만 가지고 사용할 수는 없을까?

가능한 css만으로 구현하고 싶어서 생각해보았고, 그 결과 구현을 할 수 있었습니다.
제가 구현한 방법은 아래와 같습니다.

  • navigation menu item 중 if(kakao) 2022은 id를 부여해서 우선순위를 더 높게 준 뒤, 해당 item만이 가져야 되는 스타일을 정의해준다.
  • navigation menu list를 hover할 때 if(kakao) 2022를 제외한 모든 item의 글자색을 원하는 색으로 지정한다(#888)
  • menu item을 hover할 경우 글자색을 부모의 글자색으로 강제상속하여 원래의 글자색이 되도록 스타일을 덮어쓴다.
/* gnb(nav) */
.gnb {
  &_list {
    display: flex;
    align-items: center;
    font-family: sans-serif;
    color: $fontColor;
    margin-left: 74px;
    &:hover > li > a:not(#link_if_kakao) {
      color: #888;
    }
  }
  .list_sub {
    position: absolute;
    top: 60px;
    left: 4px;
  }
  &_item {
    position: relative;
    color: $fontColor;

    .link_menu {
      display: block;
      color: $fontColor;
      flex-shrink: 0;
      font-weight: 700;
      padding: 0 28px;
      line-height: 72px;
      &:hover:not(#link_if_kakao) {
        color: inherit;
      }
    }
    #link_if_kakao {
      height: 42px;
      line-height: 42px;
      padding: 0 20px;
      color: #fff;
      background-color: #000;
      border-radius: 21px;
      position: relative;
      &::after {
        content: "";
        position: absolute;
        top: 0;
        right: -6px;
        width: 6px;
        height: 6px;
        background-color: #ff4024;
        border-radius: 50%;
      }
    }
  }
}

결과

click -> expand menu

클릭할 때의 구현을 위해서는 js를 이용해야 될 것 같다는 생각이 들었습니다. css 가상 선택자 중에 클릭했을 때의 요소에 대한 것은 없다고 알고 있기 때문입니다. focus가 대안이 될수는 있겠지만 완벽한 대안이라고 하기에는 말하기 어려울 것 같습니다.

js를 활용해 구현하기 위해 생각한 방법은 아래와 같습니다.

  • 클릭시 상위 메뉴의 글자색이 유지될수 있도록 하기 위해 .on클래스를 활용한 스타일을 적용
  • 관련된 sublist를 li의 자식요소로 삽입(position:absolute 로 배치하기 위해서)
  • 상위 메뉴 클릭시 .on클래스를 토글하여 스타일을 적용
  • 메뉴 이외의 영역을 클릭할 경우 active된 메뉴의 .on클래스 제거
    • window 클릭 캡쳐 이벤트
    • 버블링 이벤트로 할 경우 메뉴 on -> window 에서 on클래스 제거 -> 메뉴가 보이지 않는다(클릭해도 변화가 없다)

결과

button - search, language, darkmode

  • 기한 내 제출이 어려울 수 있기 때문에 생략하고 진행
  • 기한 내 제출이 가능할 경우 추가적으로 작업

기한 내 제출이 어려울 수도 있기 때문에 먼저는 생략하고 진행하지만, 만약 시간적 여유가 있다면 작업해보는 것으로 하겠습니다 ㅠㅠ
단, button에 대해 focus 가능하도록 하기 위해 요소에 tabindex를 설정해주고, focus시 outline이 생기도록 css 스타일링 하겠습니다.

아쉬웠던 점

  • 카카오 공식 사이트에서는 kakaobig이라는 자체 글꼴을 사용하고 있지만, 제가 그 글꼴을 사용할 방법은 없기 때문에, 최대한 유사한 NotoSansKR (sans-serif, 고딕체 계열) 구글 웹폰트를 사용했습니다.
  • 글자를 두껍게 적용할 경우 이로 인해 sub_menu_listwidth가 미세하게 증가해서 화면에 출력됨
    • max-width:352px로 설정하여 미세하게 width가 증가하지 않도록 함
  • desktop만 고려하여 작업하고 있음 (시간적 여유가 있을 경우 개선 예정)

코드 결과

html

<!DOCTYPE html>
<html lang="ko">
  <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" />
    <meta property="og:type" content="website" />
    <meta property="og:site_name" content="kakaocorp.com" />
    <meta property="og:url" content="https://www.kakaocorp.com/page/" />
    <meta property="og:title" content="Kakao" />
    <meta
      property="og:image"
      content="https://t1.kakaocdn.net/kakaocorp/corp_thumbnail/Kakao.png"
    />
    <meta
      property="og:description"
      content="기술과 사람으로 더 나은 세상을 만듭니다"
    />
    <meta
      name="description"
      content="기술과 사람으로 더 나은 세상을 만듭니다"
    />
    <title>카카오</title>
    <link rel="shortcut icon" href="/favicon.ico" />
    <link rel="preconnect" href="https://fonts.googleapis.com" />
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
    <link
      href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@400;500;700&display=swap"
      rel="stylesheet"
    />
    <link rel="stylesheet" href="./styles/main.scss" />
    <script defer src="./src/index.js"></script>
  </head>
  <body>
    <header class="landing_header">
      <div class="landing_header__wrapper">
        <!-- logo -->
        <a class="logo_link" href="https://www.kakaocorp.com/page/">
          <svg
            xmlns="http://www.w3.org/2000/svg"
            xmlns:xlink="http://www.w3.org/1999/xlink"
            viewBox="0 0 75 25"
            class="ico_logo"
          >
            <defs>
              <path
                data-v-37d948ec=""
                id="os5cgsl0ta"
                d="M0.011 0.205L11.948 0.205 11.948 22.203 0.011 22.203z"
              ></path>
              <path
                data-v-37d948ec=""
                id="oanpyfjipc"
                d="M0.264 0.004L13.566 0.004 13.566 15.487 0.264 15.487z"
              ></path>
            </defs>
            <g fill="#000" fill-rule="evenodd">
              <g>
                <path
                  d="M18.91 20.05c.344 0 .7-.046 1.071-.137.371-.09.742-.209 1.113-.354.371-.146.72-.323 1.045-.532.327-.21.616-.432.87-.668V14.87h-2.607c-1.32 0-2.284.227-2.89.681-.606.455-.91 1.173-.91 2.154 0 1.562.769 2.344 2.308 2.344m-4.706-2.235c0-1.508.503-2.658 1.513-3.448 1.008-.79 2.476-1.186 4.401-1.186h2.89v-.954c0-2.308-1.018-3.461-3.053-3.461-.653 0-1.34.09-2.057.272-.719.182-1.377.409-1.977.681l-.736-1.771c.745-.418 1.55-.74 2.413-.968.862-.227 1.704-.341 2.52-.341 3.526 0 5.288 1.88 5.288 5.642v9.54h-1.852l-.3-1.635c-.745.6-1.54 1.063-2.385 1.39-.845.328-1.649.49-2.414.49-1.325 0-2.365-.376-3.12-1.13-.754-.754-1.131-1.794-1.131-3.12"
                  transform="translate(-151 -168) translate(79.5 145) translate(72 24) translate(.956 .112)"
                  class="path"
                ></path>
                <g
                  transform="translate(-151 -168) translate(79.5 145) translate(72 24) translate(.956 .112) translate(29.859)"
                >
                  <mask id="5ym1s98mqb" fill="#fff">
                    <use xlink:href="#os5cgsl0ta"></use>
                  </mask>
                  <path
                    d="M9.222 6.53l1.963 1.416-4.823 6.052 5.586 6.705-1.934 1.5-6.596-8.07L9.222 6.53zM2.518 21.82H.011V.75L2.518.206V21.82z"
                    mask="url(#5ym1s98mqb)"
                    class="path"
                  ></path>
                </g>
                <path
                  d="M48.735 20.05c.343 0 .701-.046 1.072-.137.371-.09.742-.209 1.113-.354.37-.146.718-.323 1.045-.532.324-.21.614-.432.868-.668V14.87h-2.606c-1.322 0-2.285.227-2.89.681-.607.455-.909 1.173-.909 2.154 0 1.562.768 2.344 2.307 2.344m-4.706-2.235c0-1.508.504-2.658 1.512-3.448 1.01-.79 2.475-1.186 4.403-1.186h2.889v-.954c0-2.308-1.017-3.461-3.053-3.461-.655 0-1.34.09-2.058.272-.719.182-1.377.409-1.975.681l-.737-1.771c.746-.418 1.55-.74 2.412-.968.862-.227 1.703-.341 2.522-.341 3.524 0 5.288 1.88 5.288 5.642v9.54h-1.855l-.3-1.635c-.745.6-1.538 1.063-2.385 1.39-.844.328-1.648.49-2.411.49-1.327 0-2.368-.376-3.121-1.13-.755-.754-1.131-1.794-1.131-3.12"
                  transform="translate(-151 -168) translate(79.5 145) translate(72 24) translate(.956 .112)"
                  class="path"
                ></path>
                <g
                  transform="translate(-151 -168) translate(79.5 145) translate(72 24) translate(.956 .112) translate(58.405 6.66)"
                >
                  <mask id="e5668wah2d" fill="#fff">
                    <use xlink:href="#oanpyfjipc"></use>
                  </mask>
                  <path
                    d="M6.915 2.021c-1.308 0-2.312.491-3.011 1.472-.701.982-1.05 2.417-1.05 4.307 0 1.872.349 3.285 1.05 4.239.699.954 1.703 1.431 3.011 1.431 1.326 0 2.34-.477 3.04-1.431.7-.954 1.049-2.367 1.049-4.239 0-1.89-.35-3.325-1.049-4.307-.7-.981-1.714-1.472-3.04-1.472m0-2.017c2.071 0 3.699.673 4.878 2.017 1.182 1.346 1.773 3.272 1.773 5.78 0 2.47-.585 4.37-1.758 5.697-1.172 1.325-2.804 1.989-4.893 1.989-2.07 0-3.698-.664-4.878-1.99C.855 12.172.264 10.272.264 7.8c0-2.507.594-4.433 1.785-5.779C3.24.677 4.862.004 6.915.004"
                    mask="url(#e5668wah2d)"
                    class="path"
                  ></path>
                </g>
                <path
                  d="M2.552.205L.044.75v21.07h2.508V.204zm.9 13.929l6.595 8.069 1.937-1.5-5.589-6.705 4.825-6.051-1.962-1.418-5.807 7.605z"
                  transform="translate(-151 -168) translate(79.5 145) translate(72 24) translate(.956 .112)"
                  class="path"
                ></path>
              </g>
            </g>
          </svg>
        </a>
        <!-- nav -->
        <nav class="gnb">
          <ul class="gnb_list">
            <li class="gnb_item flex-container--not-shrink">
              <a class="menu_link" href="javascript:void(0)">카카오</a>
              <ul class="gnb_sub_list">
                <li class="gnb_sub_list_item flex-container--not-shrink">
                  <a
                    class="sub_menu_link"
                    href="https://www.kakaocorp.com/page/kakao/kakaoCulture"
                    >카카오문화</a
                  >
                </li>
                <li class="gnb_sub_list_item flex-container--not-shrink">
                  <a
                    class="sub_menu_link"
                    href="https://www.kakaocorp.com/page/kakao/subsidiaryCompany"
                    >공동체</a
                  >
                </li>
                <li class="gnb_sub_list_item flex-container--not-shrink">
                  <a
                    class="sub_menu_link"
                    href="https://www.kakaocorp.com/page/kakao/history"
                    >히스토리</a
                  >
                </li>
              </ul>
            </li>
            <li class="gnb_item flex-container--not-shrink">
              <a class="menu_link" href="https://www.kakaocorp.com/page/news"
                >뉴스</a
              >
            </li>
            <li class="gnb_item flex-container--not-shrink">
              <a class="menu_link" href="javascript:void(0)">기술과 서비스</a>
              <ul class="gnb_sub_list">
                <li class="gnb_sub_list_item flex-container--not-shrink">
                  <a
                    class="sub_menu_link"
                    href="https://www.kakaocorp.com/page/service/tech"
                    >기술</a
                  >
                </li>
                <li class="gnb_sub_list_item flex-container--not-shrink">
                  <a
                    class="sub_menu_link"
                    href="https://www.kakaocorp.com/page/service/service"
                    >서비스</a
                  >
                </li>
              </ul>
            </li>
            <li class="gnb_item flex-container--not-shrink">
              <a class="menu_link" href="javascript:void(0)">약속과 책임</a>
              <ul class="gnb_sub_list">
                <li class="gnb_sub_list_item flex-container--not-shrink">
                  <a
                    class="sub_menu_link"
                    href="https://www.kakaocorp.com/page/responsible/esg"
                    >ESG</a
                  >
                </li>
                <li class="gnb_sub_list_item flex-container--not-shrink">
                  <a
                    class="sub_menu_link"
                    href="https://www.kakaocorp.com/page/responsible/social"
                    >소셜임팩트</a
                  >
                </li>
                <li class="gnb_sub_list_item flex-container--not-shrink">
                  <a
                    class="sub_menu_link"
                    href="https://www.kakaocorp.com/page/responsible/digital"
                    >디지털 권리</a
                  >
                </li>
                <li class="gnb_sub_list_item flex-container--not-shrink">
                  <a
                    class="sub_menu_link"
                    href="https://www.kakaocorp.com/page/responsible/aiEthics"
                    >AI 윤리</a
                  >
                </li>
              </ul>
            </li>
            <li class="gnb_item flex-container--not-shrink">
              <a
                class="menu_link"
                id="link_if_kakao"
                href="https://if.kakao.com/"
                target="_blank"
                >if(Kakao) 2022</a
              >
            </li>
          </ul>
        </nav>
        <!-- util-area -->
        <div class="area-util flex-container--not-shrink">
          <!-- search-button -->
          <button class="btn btn_search" tabindex="0">
            <svg
              xmlns="http://www.w3.org/2000/svg"
              viewBox="0 0 28 28"
              class="ico ico_search"
            >
              <g fill="none" fill-rule="evenodd">
                <g stroke-width="1.6">
                  <g transform="translate(-308 -16) translate(312 20)">
                    <circle cx="8.944" cy="8.944" r="8.944"></circle>
                    <path d="M14.987 14.987L21.017 21.017"></path>
                  </g>
                </g>
              </g>
            </svg>
          </button>
          <!-- language-button -->
          <button class="btn btn_language" tabindex="0">
            <svg
              xmlns="http://www.w3.org/2000/svg"
              viewBox="0 0 24 24"
              class="ico ico_language"
            >
              <g fill="none" fill-rule="evenodd">
                <g stroke-width="1.35">
                  <path
                    d="M19.353 9.914c0 5.213-4.226 9.438-9.438 9.438-5.213 0-9.438-4.225-9.438-9.438C.477 4.702 4.702.477 9.915.477c5.212 0 9.438 4.225 9.438 9.437z"
                    transform="translate(-1402 -23) translate(1358 23) translate(44) translate(2 2)"
                  ></path>
                  <path
                    stroke-linejoin="round"
                    d="M13.662 9.914c0 5.213-3.748 9.438-3.748 9.438s-3.747-4.225-3.747-9.438c0-5.212 3.747-9.437 3.747-9.437s3.748 4.225 3.748 9.437z"
                    transform="translate(-1402 -23) translate(1358 23) translate(44) translate(2 2)"
                  ></path>
                  <path
                    d="M.876 7.018L18.952 7.018M.876 12.811L18.952 12.811"
                    transform="translate(-1402 -23) translate(1358 23) translate(44) translate(2 2)"
                  ></path>
                </g>
              </g>
            </svg>
          </button>
          <!-- theme-button(darkMode) -->
          <button class="btn btn_theme" tabindex="0">
            <svg
              xmlns="http://www.w3.org/2000/svg"
              viewBox="0 0 24 24"
              class="ico ico_theme"
            >
              <g fill="none" fill-rule="evenodd" stroke-linejoin="round">
                <g stroke-width="1.5">
                  <path
                    d="M16.086 13.417c-5.013 0-9.076-4.04-9.076-9.023 0-1.596.42-3.093 1.152-4.394C3.58.456 0 4.3 0 8.977 0 13.961 4.064 18 9.076 18c3.407 0 6.372-1.868 7.924-4.628-.3.03-.605.045-.914.045z"
                    transform="translate(-1344 -24) translate(1248 24) translate(96) translate(4 3)"
                  ></path>
                </g>
              </g>
            </svg>
          </button>
        </div>
      </div>
    </header>
  </body>
</html>

scss (css)

/* ./styles/main.scss */

@import url("./_reset.scss");
@import url("https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@400;500;700&display=swap");

/* global */
$fontColor: #000;
$baseForeground: #000;
$buttonBaseBackground: transparent;
$buttonBaseHoverColor: #eee;

body {
  font-family: "Noto Sans KR", sans-serif;
}

a {
  text-decoration: none;
}

.btn {
  appearance: none;
  outline: none;
  border: none;
  margin: 0;
  padding: 0;
  border-radius: 1.125em;
  background-color: $buttonBaseBackground;
  font-weight: 400;
  color: #333;
  cursor: pointer;
  &:hover {
    background-color: $buttonBaseHoverColor;
  }
}

/* icon */
.ico {
  width: 24px;
  height: 24px;
  vertical-align: top;
}

/* custom-common-styles (for deduplication) */
.flex-container--not-shrink {
  display: flex;
  flex-shrink: 0;
}

/* header */
.landing_header {
  position: fixed;
  width: 100%;
  top: 0;
  left: 0;

  &__wrapper {
    display: flex;
    align-items: center;
    min-width: 952px;
    max-width: 1296px;
    margin: 0 auto;
    height: 72px;
    justify-content: space-between;

    a.logo_link {
      display: block;
      text-decoration: none;
      width: 74px;
      height: 24px;
      .ico_logo {
        width: 100%;
        height: 100%;
      }
    }
  }
}

/* gnb(nav) */
.gnb {
  &_list {
    display: flex;
    align-items: center;
    color: $fontColor;
    margin-left: 74px;
    &:hover > li > a:not(#link_if_kakao) {
      color: #888;
    }
    &:hover > li.on > a:not(#link_if_kakao) {
      color: inherit;
    }
  }

  &_item {
    position: relative;
    color: $fontColor;
    .menu_link {
      display: block;
      color: $fontColor;
      flex-shrink: 0;
      font-weight: 700;
      padding: 0 28px;
      line-height: 72px;
      &:hover:not(#link_if_kakao) {
        color: inherit;
      }
    }
    &:not(.on) {
      .gnb_sub_list {
        display: none;
      }
    }
    &.on {
      .gnb_sub_list {
        position: absolute;
        display: flex;
        gap: 24px;
        width: max-content;
        max-width: 352px;
        border-radius: 24px;
        top: 60px;
        left: 4px;
        padding: 0 24.5px;
        background-color: $baseForeground;
        box-sizing: border-box;

        &_item {
          .sub_menu_link {
            display: block;
            color: #eee;
            font-weight: 500;
            flex-shrink: 0;
            padding: 11px 0 13px;
            line-height: 24px;
            z-index: 99;
            &:hover {
              font-weight: 700;
              color: #eee;
            }
          }
        }
      }
    }
    #link_if_kakao {
      height: 42px;
      line-height: 42px;
      padding: 0 20px;
      color: #fff;
      background-color: $baseForeground;
      border-radius: 21px;
      position: relative;
      &::after {
        content: "";
        position: absolute;
        top: 0;
        right: -6px;
        width: 6px;
        height: 6px;
        background-color: #ff4024;
        border-radius: 50%;
      }
    }
  }
}

/* util-area */
.area-util {
  gap: 12px;

  .btn {
    width: 36px;
    height: 36px;
    flex-shrink: 0;
    &:focus {
      outline: 2px solid;
      border-radius: 50%;
    }

    &_search {
      &:hover {
        border-radius: 50%;
      }
      .ico_search {
        stroke: $baseForeground;
      }
    }

    &_language {
      &:hover {
        border-radius: 50%;
      }
      .ico_language {
        stroke: $baseForeground;
      }
    }

    &_theme {
      &:hover {
        border-radius: 100%;
      }

      .ico_theme {
        stroke: $baseForeground;
      }
    }
  }
}

js (javascript)

// ./src/index.js
const attachNavMenuEvent = () => {
  const $nav_menu_items = document.querySelectorAll(
    ".gnb_list > li:not(:last-child)"
  );

  const notExpandMenuTitles = ["뉴스"];

  $nav_menu_items.forEach((menu_item) => {
    menu_item.addEventListener("click", (e) => {
      const title = e.currentTarget.querySelector("a").textContent;

      if (notExpandMenuTitles.includes(title)) {
        return;
      }

      const [activedMenu] = Array.from($nav_menu_items).filter((menu_item) =>
        menu_item.classList.contains("on")
      );
      if (activedMenu) activedMenu.classList.remove("on");

      e.currentTarget.classList.toggle("on");
    });
  });

  window.addEventListener(
    "click",
    (e) => {
      const [activedMenu] = Array.from($nav_menu_items).filter((menu_item) =>
        menu_item.classList.contains("on")
      );
      if (!activedMenu) return;
      if (e.target !== activedMenu) activedMenu.classList.remove("on");
    },
    true
  );
};

attachNavMenuEvent();
profile
$ npm run dev:ryan

0개의 댓글