<div id="header-72345-16168" class="header">
    <div class="header__inner">
        <a class="header__logo" href="#" title="zplane">
            <img width="164" height="50" src="/assets/images/logo.7e7ebc0793.svg" alt="zplane" class="header__logo-img">
        </a>

        <div class="header__contents">
            <div class="header__icon-buttons">

                <button class="button" type="button" title="Open menu" aria-expanded="false" aria-controls="header-72345-16168-menu" data-action="open">
                    <span class="button__icon">
                        <svg class="icon icon--bars" viewBox="0 0 200 200" aria-hidden="true">
                            <use xlink:href="/assets/icons/icons.dcaca1c147.svg#bars"></use>
                        </svg> </span>
                </button>

                <div class="header__icon-button">

                    <a class="button" title="Partner Area" href="#">
                        <span class="button__icon">
                            <svg class="icon icon--lock-keyhole" viewBox="0 0 200 200" aria-hidden="true">
                                <use xlink:href="/assets/icons/icons.dcaca1c147.svg#lock-keyhole"></use>
                            </svg> </span>
                    </a>
                </div>
            </div>

            <div class="header__menu" id="header-72345-16168-menu" hidden>
                <div class="header__menu-close">

                    <button class="button" type="button" title="Close menu" data-action="close">
                        <span class="button__icon">
                            <svg class="icon icon--xmark" viewBox="0 0 200 200" aria-hidden="true">
                                <use xlink:href="/assets/icons/icons.dcaca1c147.svg#xmark"></use>
                            </svg> </span>
                    </button>
                </div>

                <ul class="header__navigation">
                    <li class="header__navigation-item" data-floating-root>

                        <span class="header__navigation-link">

                            <button class="link" type="button" data-link-block data-link-type="internal" aria-expanded="false" aria-haspopup="true" aria-controls="header-72345-16168-navigation-menu-1"><span class="link__text u-underline">Company</span><span class="link__icon"><svg class="icon icon--caret-down-solid" viewBox="0 0 200 200" aria-hidden="true">
                                        <use xlink:href="/assets/icons/icons.dcaca1c147.svg#caret-down-solid"></use>
                                    </svg></span></button>
                        </span>

                        <ul class="header__navigation-menu" id="header-72345-16168-navigation-menu-1" data-floating-placements="bottom" data-floating-offset="16" role="menu" hidden>
                            <li class="header__navigation-menu-item" role="presentation">

                                <a class="link" data-link-type="internal" href="#" role="menuitem"><span class="link__text u-underline">Overview</span></a>
                            </li>

                            <li class="header__navigation-menu-item" role="presentation">

                                <a class="link" data-link-type="internal" href="#" role="menuitem"><span class="link__text u-underline">Pressroom</span></a>
                            </li>
                            <li class="header__navigation-menu-item" role="presentation">

                                <a class="link" data-link-type="internal" href="#" role="menuitem"><span class="link__text u-underline">Blog &amp; News</span></a>
                            </li>
                        </ul>
                    </li>
                    <li class="header__navigation-item" data-floating-root>

                        <span class="header__navigation-link">

                            <a class="link" data-link-block data-link-type="internal" href="#" aria-current="true"><span class="link__text u-underline">Technology</span></a>
                        </span>

                    </li>
                    <li class="header__navigation-item" data-floating-root>

                        <span class="header__navigation-link">

                            <a class="link" data-link-block data-link-type="internal" href="#"><span class="link__text u-underline">Licensing</span></a>
                        </span>

                    </li>
                    <li class="header__navigation-item" data-floating-root>

                        <span class="header__navigation-link">

                            <a class="link" data-link-block data-link-type="internal" href="#"><span class="link__text u-underline">Partner</span></a>
                        </span>

                    </li>
                    <li class="header__navigation-item" data-floating-root>

                        <span class="header__navigation-link">

                            <a class="link" data-link-block data-link-type="internal" href="#"><span class="link__text u-underline">Contact</span></a>
                        </span>

                    </li>
                </ul>

                <div class="header__navigation-highlight"></div>
            </div>

            <div class="header__login-button">

                <a class="button" href="#">
                    <span class="button__icon">
                        <svg class="icon icon--lock-keyhole" viewBox="0 0 200 200" aria-hidden="true">
                            <use xlink:href="/assets/icons/icons.dcaca1c147.svg#lock-keyhole"></use>
                        </svg> </span>
                    <span class="button__text u-underline">Partner Area</span>
                </a>
            </div>
        </div>
    </div>
</div>
{% set id = id ??? html_id('header') %}

<div {{ html_attributes({
  id,
  class: 'header',
}, attrs ?? {}) }}>
  <div class="header__inner">
    {% if logo|default %}
      <a class="header__logo" href="{{ logo.link }}" title="{{ logo.text }}">
        <img {{ html_attributes({
          width: 164,
          height: 50,
          src: asset('images/logo.svg'),
          alt: logo.text,
          class: 'header__logo-img',
        }) }}>
      </a>
    {% endif %}

    <div class="header__contents">
      {% if navigation|default %}
        <div class="header__icon-buttons">
          {% include '@button' with {
            icon: 'bars',
            title: 'Open menu' | t('site'),
            attrs: {
              'aria-expanded': 'false',
              'aria-controls': 'menu' | namespaceInputId(id),
              'data-action': 'open',
            },
          } only %}

          {% for iconButton in iconButtons|default([]) %}
            <div {{ html_attributes({ class: 'header__icon-button' }, iconButton.containerAttrs ?? {}) }}>
              {% include '@button' with iconButton only %}
            </div>
          {% endfor %}
        </div>

        <div {{ html_attributes({
          class: 'header__menu',
          id: 'menu' | namespaceInputId(id),
          hidden: true,
        }, menuAttrs ?? {}) }}>
          <div class="header__menu-close">
            {% include '@button' with {
              icon: 'xmark',
              title: 'Close menu' | t('site'),
              attrs: {
                'data-action': 'close',
              },
            } only %}
          </div>

          {% if navigation|default %}
            <ul class="header__navigation">
              {% for item in navigation %}
                <li class="header__navigation-item" data-floating-root>
                  {% set hasChildren = item.children|default and item.children|length > 0 %}
                  {% set isActiveOrChildren = item.active|default or item.children|default([])|filter(child => child.active|default) %}

                  <span class="header__navigation-link">
                    {% if item.link|default or hasChildren %}
                      {% include '@link' with {
                        text: item.text,
                        link: not hasChildren ? item.link,
                        icon: hasChildren ? 'caret-down-solid',
                        iconPosition: 'after',
                        block: true,
                        attrs: {
                          'aria-expanded': hasChildren ? 'false',
                          'aria-haspopup': hasChildren ? 'true',
                          'aria-controls': hasChildren ? "navigation-menu-#{loop.index}" | namespaceInputId(id),
                          'aria-current': isActiveOrChildren ? 'true',
                        },
                      } only %}
                    {% else %}
                      {{- item.text -}}
                    {% endif %}
                  </span>

                  {% if hasChildren %}
                    <ul {{ html_attributes({
                      class: 'header__navigation-menu',
                      id: "navigation-menu-#{loop.index}" | namespaceInputId(id),
                      'data-floating-placements': 'bottom',
                      'data-floating-offset': '16',
                      role: 'menu',
                      hidden: true,
                    }) }}>
                      {% if item.link|default %}
                        <li class="header__navigation-menu-item" role="presentation">
                          {% include '@link' with {
                            text: 'Overview'|t('site'),
                            link: item.link,
                            attrs: {
                              role: 'menuitem',
                              'aria-current': item.active|default ? 'true',
                            },
                          } only %}
                        </li>
                      {% endif %}

                      {% for child in item.children %}
                        <li class="header__navigation-menu-item" role="presentation">
                          {% if child.link|default %}
                            {% include '@link' with {
                              text: child.text,
                              link: child.link,
                              attrs: {
                                role: 'menuitem',
                                'aria-current': child.active|default ? 'true',
                              },
                            } only %}
                          {% else %}
                            <span {{ html_attributes({
                              role: 'menuitem',
                              'aria-current': child.active|default ? 'true',
                            }) }}>
                              {{- item.text -}}
                            </span>
                          {% endif %}
                        </li>
                      {% endfor %}
                    </ul>
                  {% endif %}
                </li>
              {% endfor %}
            </ul>
          {% endif %}

          <div class="header__navigation-highlight"></div>
        </div>
      {% endif %}

      {% if loginButton|default %}
        <div class="header__login-button">
          {% include '@button' with loginButton only %}
        </div>
      {% endif %}
    </div>
  </div>
</div>
{
  "logo": {
    "text": "zplane",
    "link": "#"
  },
  "navigation": [
    {
      "text": "Company",
      "link": "#",
      "children": [
        {
          "text": "Pressroom",
          "link": "#"
        },
        {
          "text": "Blog & News",
          "link": "#"
        }
      ]
    },
    {
      "text": "Technology",
      "link": "#",
      "active": true
    },
    {
      "text": "Licensing",
      "link": "#"
    },
    {
      "text": "Partner",
      "link": "#"
    },
    {
      "text": "Contact",
      "link": "#"
    }
  ],
  "iconButtons": [
    {
      "link": "#",
      "title": "Partner Area",
      "icon": "lock-keyhole"
    }
  ],
  "loginButton": {
    "link": "#",
    "text": "Partner Area",
    "icon": "lock-keyhole"
  }
}
  • Content:
    .header {
      container-name: header;
      container-type: inline-size;
    }
    
    .header__inner {
      align-items: center;
      display: flex;
      justify-content: space-between;
      position: relative;
    
      @container header (inline-size >= 85rem) {
        column-gap: 1.6rem;
        flex-direction: column;
        row-gap: 2.4rem;
      }
    
      @container header (inline-size >= 110rem) {
        flex-direction: row;
      }
    }
    
    .header__logo {
      flex-shrink: 0;
      inline-size: 40%;
      line-height: 0;
      max-inline-size: 17rem;
      min-inline-size: 8rem;
      position: relative;
      translate: 0 -6%;
    }
    
    .header__contents {
      column-gap: 1.6rem;
      display: flex;
      justify-content: center;
    
      @container header (inline-size >= 110rem) {
        display: contents;
      }
    }
    
    .header__icon-buttons {
      column-gap: 0.8rem;
      display: flex;
    
      @container header (inline-size >= 85rem) {
        display: none;
      }
    }
    
    .header__menu {
      --_header-menu-padding: 4rem;
    
      display: flex;
      font-weight: var(--font-weight-bold);
      line-height: var(--line-height-wide);
    
      @container header (inline-size < 85rem) {
        background-color: var(--color-gray-100);
        color: var(--text-color);
        flex-direction: column;
        font-size: var(--font-size-large);
        inset: 0;
        overflow-y: auto;
        overscroll-behavior: contain;
        padding-block: var(--_header-menu-padding);
        padding-inline-end: calc(4.4rem + var(--_header-menu-padding) + 3.2rem);
        padding-inline-start: var(--_header-menu-padding);
        position: fixed;
        z-index: 2;
    
        &:is([hidden]) {
          display: none;
        }
      }
    
      @container header (inline-size >= 85rem) {
        background-color: var(--color-white);
        block-size: 4.2rem;
        border: 1px solid var(--color-gray-200);
        border-radius: 99999px;
        position: relative;
      }
    }
    
    .header__menu-close {
      inset-block-start: var(--_header-menu-padding);
      inset-inline-end: var(--_header-menu-padding);
      position: fixed;
    
      @container header (inline-size >= 85rem) {
        display: none;
      }
    }
    
    .header__navigation {
      --link-color: var(--color-steel-950);
      --link-underline-color: transparent;
      --link-color--engaged: var(--color-steel-950);
      --link-underline-color--engaged: currentColor;
      --link-display: block;
    
      display: flex;
      flex-direction: column;
      row-gap: 0.8rem;
    
      @container header (inline-size >= 85rem) {
        align-items: center;
        flex-direction: row;
        padding-inline: 0.4rem;
        position: relative;
        z-index: 2;
      }
    }
    
    .header__navigation-item {
      position: relative;
    }
    
    .header__navigation-link {
      @container header (inline-size >= 85rem) {
        --link-padding-inline: 1.6rem;
        --link-padding-block: calc((4.2rem - 1lh) / 2);
        --link-underline-color--engaged: transparent;
      }
    }
    
    .header__navigation-highlight {
      background-color: var(--color-orange-300);
      block-size: 4.2rem;
      border-radius: 99999px;
      opacity: 0;
      position: absolute;
      transition-property: inline-size, inset-inline-start, opacity;
      transition-timing-function: ease-in-out;
      translate: -1px -1px;
      will-change: inline-size, inset-inline-start, opacity;
      z-index: 1;
    
      @container header (inline-size < 85rem) {
        display: none;
      }
    }
    
    .header__navigation-menu {
      display: grid;
      grid-template-columns: 100%;
      margin-block-end: 2.4rem;
      margin-block-start: 1.6rem;
      padding-inline: 2.4rem;
      row-gap: 0.8rem;
    
      &:is([hidden]) {
        display: none;
      }
    
      @container header (inline-size >= 85rem) {
        background-color: var(--color-white);
        border: 1px solid var(--color-gray-200);
        border-radius: var(--border-radius-large);
        box-shadow: var(--box-shadow-xxl);
        color: var(--text-color);
        inline-size: 25rem;
        inset-block-start: 6.4rem;
        inset-inline-start: 50%;
        margin: 0;
        opacity: 1;
        padding: 0;
        position: absolute;
        row-gap: 0;
        transition: opacity var(--duration-long), translate var(--duration-xx-long);
        translate: -50% 0;
    
        &::before {
          background-color: var(--color-gray-200);
          block-size: 1rem;
          clip-path: path('M11.8.7a2 2 0 0 0-3 0L.3 10h20L11.8.7Z');
          content: '';
          inline-size: 2rem;
          inset-block-start: -1rem;
          inset-inline-start: calc(50% - 1rem);
          position: absolute;
        }
    
        &::after {
          background-color: var(--color-white);
          block-size: 1rem;
          clip-path: path('M11.8.7a2 2 0 0 0-3 0L.3 10h20L11.8.7Z');
          content: '';
          inline-size: 2rem;
          inset-block-start: calc(-1rem + 0.1rem);
          inset-inline-start: calc(50% - 1rem);
          position: absolute;
        }
    
        @starting-style {
          opacity: 0;
          translate: -50% -0.6rem;
        }
      }
    }
    
    .header__navigation-menu-item {
      font-size: var(--font-size-medium);
    
      @container header (inline-size >= 85rem) {
        --link-padding-inline: 1.6rem;
        --link-padding-block: 0.8rem;
    
        font-size: var(--font-size-default);
    
        & + & {
          border-block-start: 1px solid var(--color-gray-200);
        }
      }
    }
    
    .header__login-button {
      @container header (inline-size < 85rem) {
        display: none;
      }
    }
    
  • URL: /components/raw/header/header.scss
  • Filesystem Path: src/components/4-organisms/header/header.scss
  • Size: 5 KB
  • Content:
    import { on } from 'delegated-events';
    import { createFocusTrap, type FocusTrap } from 'focus-trap';
    import collapse from '../../../javascripts/utils/collapse';
    import moveFocus from '../../../javascripts/utils/moveFocus';
    import onLostFocus from '../../../javascripts/utils/onLostFocus';
    import onLoad from '../../../javascripts/utils/onLoad';
    import abort from '../../../javascripts/utils/abort';
    
    let $currentOpenLink: HTMLElement | null = null;
    let currentLostFocusHandler: CallableFunction | null = null;
    let focusTrap: FocusTrap | null = null;
    let returnHighlight: CallableFunction | null = null;
    
    on('click', '.header__navigation-item [aria-controls]', async (event) => {
      const { currentTarget: $trigger } = event;
    
      event.preventDefault();
    
      const { isOpen, $target } = await collapse($trigger);
    
      if (isOpen) {
        if (currentLostFocusHandler) {
          currentLostFocusHandler();
        }
    
        moveFocus($target);
    
        currentLostFocusHandler = onLostFocus($target, () => {
          collapse($trigger, false);
          returnHighlight?.();
          $currentOpenLink = null;
        });
    
        $currentOpenLink = $trigger.closest('.header__navigation-item')
          ?.querySelector<HTMLElement>('.header__navigation-link') ?? null;
      } else if (currentLostFocusHandler) {
        currentLostFocusHandler();
        returnHighlight?.();
    
        currentLostFocusHandler = null;
        $currentOpenLink = null;
      }
    });
    
    on('click', '.header [data-action="open"]', async (event) => {
      const { currentTarget: $trigger } = event;
    
      event.preventDefault();
    
      const { $target } = await collapse($trigger, true);
    
      focusTrap = createFocusTrap($target, {
        initialFocus: '.header__navigation-item a, .header__navigation-item button',
        clickOutsideDeactivates: false,
        returnFocusOnDeactivate: true,
        async onDeactivate() {
          await collapse($trigger, false);
        },
        onPostDeactivate() {
          focusTrap = null;
        },
      });
    
      focusTrap.activate();
    });
    
    on('click', '.header [data-action="close"]', async (event) => {
      event.preventDefault();
      focusTrap?.deactivate();
    });
    
    onLoad<HTMLElement>('.header', ($header) => {
      const resizeObserver = new ResizeObserver(() => {
        focusTrap?.deactivate();
      });
    
      resizeObserver.observe($header);
    
      const $highlight = $header.querySelector<HTMLElement>('.header__navigation-highlight') ?? abort();
      const $menu = $header.querySelector('.header__menu') ?? abort();
      const $currentActiveLink = $header.querySelector<HTMLElement>('.header__navigation-link [aria-current=true]');
    
      let $currentHighlight: HTMLElement | null = $currentActiveLink;
    
      const moveHighlight = ($target: HTMLElement | null) => {
        if (!$target) {
          $highlight.style.opacity = '0';
        } else {
          const { offsetWidth: inlineSize } = $target;
          const { left: targetInlineStart } = $target.getBoundingClientRect();
          const { left: menuInlineStart } = $menu.getBoundingClientRect();
    
          $highlight.style.opacity = '1';
          $highlight.style.insetInlineStart = `${targetInlineStart - menuInlineStart - 5}px`;
          $highlight.style.inlineSize = `${inlineSize + 10}px`;
        }
      };
    
      const highlightResizeObserver = new ResizeObserver(() => {
        moveHighlight($currentHighlight);
      });
    
      moveHighlight($currentHighlight);
    
      highlightResizeObserver.observe($menu);
    
      returnHighlight = () => moveHighlight($currentActiveLink);
    
      $header.querySelectorAll<HTMLElement>('.header__navigation-link').forEach(($link) => {
        $link.addEventListener('mouseenter', () => {
          $currentHighlight = $link;
          moveHighlight($link);
        });
    
        $link.addEventListener('mouseleave', () => {
          $currentHighlight = $currentActiveLink;
          moveHighlight($currentOpenLink ?? $currentActiveLink);
        });
      });
    });
    
  • URL: /components/raw/header/header.ts
  • Filesystem Path: src/components/4-organisms/header/header.ts
  • Size: 3.7 KB

No notes defined.