분할 버튼 구성요소 빌드

액세스 가능한 분할 버튼 구성요소를 빌드하는 방법에 관한 기본 개요

이 게시물에서는 분할 버튼을 만드는 방법에 대한 생각을 공유하고자 합니다 . 데모 사용해 보기

<ph type="x-smartling-placeholder">
</ph> <ph type="x-smartling-placeholder">
</ph> 데모

동영상을 선호한다면 이 게시물의 YouTube 버전을 참고하세요.

개요

분할 버튼은 버튼입니다. 기본 버튼과 추가 버튼 목록을 숨기는 버튼도 있습니다. 유용함 자주 사용되지 않는 보조 중첩을 중첩하고 공통 작업을 노출하기 위해 실행할 수 있습니다 분할 버튼은 복잡한 디자인에 매우 중요할 수 있습니다. 만들 수 있습니다. 고급 분할 버튼은 마지막 사용자 작업을 기억할 수도 있습니다. 기본 게재순위에 게재합니다.

일반적인 분할 버튼은 이메일 애플리케이션에 있습니다. 기본 액션 나중에 전송하거나 초안을 저장할 수도 있습니다.

이메일 애플리케이션에 표시된 분할 버튼의 예

공유 작업 영역은 사용자가 주변을 둘러볼 필요가 없기 때문에 좋습니다. 그들은 분할 버튼에 필수 이메일 작업이 포함되어 있습니다.

부품

먼저 분할 버튼의 필수 부분을 전반적인 조정 및 최종 사용자 경험을 제공합니다. VisBug의 접근성 검사 도구는 요소의 매크로 보기를 표시하는 데 사용되며 각 주요 부분에 대한 HTML, 스타일 및 접근성 측면의 측면을 다룹니다.

분할 버튼을 구성하는 HTML 요소입니다.

최상위 분할 버튼 컨테이너

가장 높은 수준의 구성요소는 다음 클래스가 포함된 인라인 Flexbox입니다. gui-split-button - 기본 작업 포함 및 .gui-popup-button.

검사된 gui-split-button 클래스가 이 클래스에서 사용된 CSS 속성을 보여줍니다.

기본 작업 버튼

처음에 표시되고 포커스 가능한 <button>는 컨테이너 내에 들어갑니다. 두 개의 일치하는 모서리 모양 focus 마우스 오버활성 상호작용을 사용하여 .gui-split-button에 포함된 것처럼 보입니다.

버튼 요소의 CSS 규칙을 보여주는 검사기

팝업 전환 버튼

'팝업 버튼' 지원 요소는 보조 버튼. <button>가 아니며 포커스를 맞출 수 없습니다. 하지만 사용된 .gui-popup의 배치 앵커 및 :focus-within의 호스트입니다. 를 사용하여 팝업을 표시합니다.

gui-popup-button 클래스의 CSS 규칙을 보여주는 검사기

팝업 카드

앵커의 플로팅 카드 하위 요소 .gui-popup-button, 배치된 절댓값 및 버튼 목록을 의미론적으로 래핑합니다.

gui-popup 클래스의 CSS 규칙을 보여주는 검사기

보조 액션

기본 글꼴보다 글꼴 크기가 작은 포커스 가능 <button> 작업 버튼에는 아이콘과 무료 스타일을 기본 버튼에 추가합니다.

버튼 요소의 CSS 규칙을 보여주는 검사기

맞춤 속성

다음 변수는 색상의 조화를 만들고 색깔의 조화를 이루는 값을 수정할 수 있습니다.

@custom-media --motionOK (prefers-reduced-motion: no-preference);
@custom-media --dark (prefers-color-scheme: dark);
@custom-media --light (prefers-color-scheme: light);

.gui-split-button {
  --theme:             hsl(220 75% 50%);
  --theme-hover:  hsl(220 75% 45%);
  --theme-active:  hsl(220 75% 40%);
  --theme-text:      hsl(220 75% 25%);
  --theme-border: hsl(220 50% 75%);
  --ontheme:         hsl(220 90% 98%);
  --popupbg:         hsl(220 0% 100%);

  --border: 1px solid var(--theme-border);
  --radius: 6px;
  --in-speed: 50ms;
  --out-speed: 300ms;

  @media (--dark) {
    --theme:             hsl(220 50% 60%);
    --theme-hover:  hsl(220 50% 65%);
    --theme-active:  hsl(220 75% 70%);
    --theme-text:      hsl(220 10% 85%);
    --theme-border: hsl(220 20% 70%);
    --ontheme:         hsl(220 90% 5%);
    --popupbg:         hsl(220 10% 30%);
  }
}
드림

레이아웃 및 색상

마크업

요소는 맞춤 클래스 이름이 있는 <div>로 시작합니다.

<div class="gui-split-button"></div>

기본 버튼과 .gui-popup-button 요소를 추가합니다.

<div class="gui-split-button">
  <button>Send</button>
  <span class="gui-popup-button" aria-haspopup="true" aria-expanded="false" title="Open for more actions"></span>
</div>

ARIA 속성 aria-haspopuparia-expanded에 주목하세요. 이러한 신호는 스크린 리더가 분할의 기능과 상태를 인식하는 데 매우 중요합니다. 기능을 제공합니다. title 속성은 모든 사용자에게 유용합니다.

<svg> 아이콘과 .gui-popup 컨테이너 요소를 추가합니다.

<div class="gui-split-button">
  <button>Send</button>
  <span class="gui-popup-button" aria-haspopup="true" aria-expanded="false" title="Open for more actions">
    <svg aria-hidden="true" viewBox="0 0 20 20">
      <path d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" />
    </svg>
    <ul class="gui-popup"></ul>
  </span>
</div>

간단한 팝업 배치를 위해 .gui-popup는 펼칩니다. 이 전략에서 유일하게 주목해야 할 점은 .gui-split-button입니다. 컨테이너는 팝업을 자르기 때문에 overflow: hidden를 사용할 수 없습니다. 볼 수 있습니다.

<li><button> 콘텐츠로 채워진 <ul>가 스스로를 '버튼'이라고 알립니다. 목록' 스크린 리더에 구현되어 있으며, 이는 정확히 표시되는 인터페이스입니다.

<div class="gui-split-button">
  <button>Send</button>
  <span class="gui-popup-button" aria-haspopup="true" aria-expanded="false" title="Open for more actions">
    <svg aria-hidden="true" viewBox="0 0 20 20">
      <path d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" />
    </svg>
    <ul class="gui-popup">
      <li>
        <button>Schedule for later</button>
      </li>
      <li>
        <button>Delete</button>
      </li>
      <li>
        <button>Save draft</button>
      </li>
    </ul>
  </span>
</div>

장식을 하고 색상을 다듬기 위해 보조 버튼에 아이콘을 추가했습니다. 출처: https://heroicons.com 아이콘은 둘 다 선택사항입니다. 기본 버튼과 보조 버튼을 사용합니다

<div class="gui-split-button">
  <button>Send</button>
  <span class="gui-popup-button" aria-haspopup="true" aria-expanded="false" title="Open for more actions">
    <svg aria-hidden="true" viewBox="0 0 20 20">
      <path d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" />
    </svg>
    <ul class="gui-popup">
      <li><button>
        <svg aria-hidden="true" viewBox="0 0 24 24">
          <path d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
        </svg>
        Schedule for later
      </button></li>
      <li><button>
        <svg aria-hidden="true" viewBox="0 0 24 24">
          <path d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
        </svg>
        Delete
      </button></li>
      <li><button>
        <svg aria-hidden="true" viewBox="0 0 24 24">
          <path d="M5 5a2 2 0 012-2h10a2 2 0 012 2v16l-7-3.5L5 21V5z" />
        </svg>
        Save draft
      </button></li>
    </ul>
  </span>
</div>

스타일

HTML과 콘텐츠가 있으면 스타일에서 색상과 레이아웃을 제공할 수 있습니다.

분할 버튼 컨테이너의 스타일 지정

inline-flex 디스플레이 유형은 이 래핑 구성요소에 적합합니다. 다른 분할 버튼, 작업 또는 요소와 일렬로 배치되어야 합니다.

.gui-split-button {
  display: inline-flex;
  border-radius: var(--radius);
  background: var(--theme);
  color: var(--ontheme);
  fill: var(--ontheme);

  touch-action: manipulation;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
}

분할 버튼입니다.

<button> 스타일 지정

버튼은 필요한 코드 양을 아주 잘 위장합니다. 다음과 같은 작업이 필요할 수 있습니다. 브라우저 기본 스타일을 실행취소하거나 교체할 수 있지만 일부 스타일은 상호작용 상태를 추가하고, 다양한 사용자 환경설정과 입력 유형 버튼 스타일은 빠르게 추가됩니다.

이러한 버튼은 배경을 공유한다는 점에서 일반 버튼과 다릅니다. 을 포함해야 합니다. 일반적으로 버튼은 배경 및 텍스트 색상을 소유합니다. 하지만 이를 공유하고 상호작용 시 자체 배경만 적용합니다.

.gui-split-button button {
  cursor: pointer;
  appearance: none;
  background: none;
  border: none;

  display: inline-flex;
  align-items: center;
  gap: 1ch;
  white-space: nowrap;

  font-family: inherit;
  font-size: inherit;
  font-weight: 500;

  padding-block: 1.25ch;
  padding-inline: 2.5ch;

  color: var(--ontheme);
  outline-color: var(--theme);
  outline-offset: -5px;
}

일부 CSS로 상호작용 상태 추가 의사 클래스 및 일치의 사용 상태에 대한 커스텀 속성을 제공합니다.

.gui-split-button button {
  

  &:is(:hover, :focus-visible) {
    background: var(--theme-hover);
    color: var(--ontheme);

    & > svg {
      stroke: currentColor;
      fill: none;
    }
  }

  &:active {
    background: var(--theme-active);
  }
}

디자인 효과를 완성하려면 기본 버튼에 몇 가지 특수한 스타일이 필요합니다.

.gui-split-button > button {
  border-end-start-radius: var(--radius);
  border-start-start-radius: var(--radius);

  & > svg {
    fill: none;
    stroke: var(--ontheme);
  }
}

마지막으로 밝은 테마 버튼과 아이콘은 그림자:

.gui-split-button {
  @media (--light) {
    & > button,
    & button:is(:focus-visible, :hover) {
      text-shadow: 0 1px 0 var(--theme-active);
    }
    & > .gui-popup-button > svg,
    & button:is(:focus-visible, :hover) > svg {
      filter: drop-shadow(0 1px 0 var(--theme-active));
    }
  }
}

마이크로 상호작용과 작은 디테일을 강조한 버튼이 훌륭한 버튼입니다.

:focus-visible 관련 참고사항

버튼 스타일이 :focus 대신 :focus-visible를 사용하는 방식을 확인합니다. :focus 접근성이 뛰어난 사용자 인터페이스를 만드는 데 중요한 요소이지만 다운 함: 사용자가 봐야 하는지 또는 사용자가 광고를 볼 필요가 있는지에 대해 모든 포커스에 적용됩니다.

아래 동영상은 이러한 마이크로 상호작용을 세분화하여 :focus-visible은(는) 지능형 대안입니다.

팝업 버튼 스타일 지정

아이콘을 중앙에 배치하고 팝업 버튼 목록을 고정하는 4ch Flexbox 좋아요 기본 버튼은 다른 방법으로 마우스를 가져가거나 상호작용할 때까지 투명합니다. 늘어져 채워질 수 있습니다.

팝업을 트리거하는 데 사용되는 분할 버튼의 화살표 부분입니다.

.gui-popup-button {
  inline-size: 4ch;
  cursor: pointer;
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-inline-start: var(--border);
  border-start-end-radius: var(--radius);
  border-end-end-radius: var(--radius);
}

CSS를 사용하여 마우스 오버, 포커스, 활성 상태의 레이어 중첩:is() 기능 선택기:

.gui-popup-button {
  

  &:is(:hover,:focus-within) {
    background: var(--theme-hover);
  }

  /* fixes iOS trying to be helpful */
  &:focus {
    outline: none;
  }

  &:active {
    background: var(--theme-active);
  }
}

이 스타일은 팝업을 표시하고 숨기는 기본 후크입니다..gui-popup-button의 하위 요소에 focus가 있습니다. opacity로 설정됨, 위치는 아이콘과 팝업에서 pointer-events가 생성됩니다.

.gui-popup-button {
  

  &:focus-within {
    & > svg {
      transition-duration: var(--in-speed);
      transform: rotateZ(.5turn);
    }
    & > .gui-popup {
      transition-duration: var(--in-speed);
      opacity: 1;
      transform: translateY(0);
      pointer-events: auto;
    }
  }
}

안쪽과 바깥쪽 스타일이 완성되면 마지막 부분은 조건부 사용자의 모션 환경설정에 따라 다음과 같이 변환합니다.

.gui-popup-button {
  

  @media (--motionOK) {
    & > svg {
      transition: transform var(--out-speed) ease;
    }
    & > .gui-popup {
      transform: translateY(5px);

      transition:
        opacity var(--out-speed) ease,
        transform var(--out-speed) ease;
    }
  }
}

코드를 면밀히 살펴보면 사용자의 불투명도가 계속 전환되는 것을 확인할 수 있습니다. 움직임이 적은 것을 선호합니다.

팝업 스타일 지정

.gui-popup 요소는 맞춤 속성을 사용하는 플로팅 카드 버튼 목록입니다. 상대 단위를 미묘하게 더 작게 만들어 기본 광고 단위와 상호작용하여 색상을 사용하여 브랜드에 적용할 수 있습니다 아이콘은 대비가 낮고 색이 더 얇고 어두운 부분의 파란색 색조가 보입니다. 버튼과 마찬가지로 이렇게 작은 디테일이 쌓인 덕분에 뛰어난 UI와 UX가 만들어집니다.

플로팅 카드 요소

.gui-popup {
  --shadow: 220 70% 15%;
  --shadow-strength: 1%;

  opacity: 0;
  pointer-events: none;

  position: absolute;
  bottom: 80%;
  left: -1.5ch;

  list-style-type: none;
  background: var(--popupbg);
  color: var(--theme-text);
  padding-inline: 0;
  padding-block: .5ch;
  border-radius: var(--radius);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  font-size: .9em;
  transition: opacity var(--out-speed) ease;

  box-shadow:
    0 -2px 5px 0 hsl(var(--shadow) / calc(var(--shadow-strength) + 5%)),
    0 1px 1px -2px hsl(var(--shadow) / calc(var(--shadow-strength) + 10%)),
    0 2px 2px -2px hsl(var(--shadow) / calc(var(--shadow-strength) + 12%)),
    0 5px 5px -2px hsl(var(--shadow) / calc(var(--shadow-strength) + 13%)),
    0 9px 9px -2px hsl(var(--shadow) / calc(var(--shadow-strength) + 14%)),
    0 16px 16px -2px hsl(var(--shadow) / calc(var(--shadow-strength) + 20%))
  ;
}

아이콘과 버튼은 어두운 테마 속에서도 잘 어울리도록 브랜드 색상으로 꾸며져 있습니다. 밝은 테마 카드:

결제, Quick Pay, 나중을 위해 저장하기 위한 링크 및 아이콘입니다.

.gui-popup {
  

  & svg {
    fill: var(--popupbg);
    stroke: var(--theme);

    @media (prefers-color-scheme: dark) {
      stroke: var(--theme-border);
    }
  }

  & button {
    color: var(--theme-text);
    width: 100%;
  }
}

어두운 테마 팝업에는 텍스트와 아이콘 그림자가 추가되며 강렬한 상자 그림자:

어두운 테마의 팝업

.gui-popup {
  

  @media (--dark) {
    --shadow-strength: 5%;
    --shadow: 220 3% 2%;

    & button:not(:focus-visible, :hover) {
      text-shadow: 0 1px 0 var(--ontheme);
    }

    & button:not(:focus-visible, :hover) > svg {
      filter: drop-shadow(0 1px 0 var(--ontheme));
    }
  }
}

일반 <svg> 아이콘 스타일

모든 아이콘은 버튼에 비해 상대적으로 크기가 작으며 font-size ch 단위를 inline-size입니다. 각각은 아이콘을 부드럽고 윤곽선으로 표현하는 데 도움이 되는 스타일이 제공됩니다. 부드럽게 처리하세요.

.gui-split-button svg {
  inline-size: 2ch;
  box-sizing: content-box;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 2px;
}

오른쪽에서 왼쪽 레이아웃

논리적 속성은 복잡한 작업을 모두 실행합니다. 다음은 사용되는 논리적 속성의 목록입니다. - display: inline-flex는 인라인 flex 요소를 만듭니다. - padding 대신 padding-blockpadding-inline를 쌍으로 지정 논리 면을 패딩하는 이점을 얻게 됩니다. - border-end-start-radius친구가 모서리를 둥글게 만들 수 있습니다. - width가 아닌 inline-size는 크기가 실제 크기와 연결되지 않도록 합니다. - border-inline-start는 시작 부분에 테두리를 추가합니다. 테두리는 스크립트 방향에 따라 오른쪽 또는 왼쪽에 있습니다.

자바스크립트

다음 JavaScript는 거의 모두 접근성을 높이기 위한 것입니다. 2개 도우미 라이브러리를 사용하면 작업을 좀 더 쉽게 할 수 있습니다. 간결한 표현에 BlingBlingJS가 사용됨 DOM 쿼리 및 간편한 이벤트 리스너 설정 외에도 Roving-ux는 접근성이 뛰어나고 팝업의 키보드 및 게임패드 상호작용을 지원합니다.

import $ from 'blingblingjs'
import {rovingIndex} from 'roving-ux'

const splitButtons = $('.gui-split-button')
const popupButtons = $('.gui-popup-button')

위의 라이브러리를 가져오고 요소를 선택하여 변수, 환경 업그레이드는 몇 가지 기능만 거치면 완료됩니다.

이동 색인

키보드나 스크린 리더가 .gui-popup-button에 포커스를 맞추면 화면의 첫 번째 (또는 최근에 포커스가 설정된) 버튼으로 포커스를 앞으로 옮깁니다. .gui-popup 라이브러리는 elementtarget로 이 작업을 하는 데 도움이 됩니다. 매개변수입니다.

popupButtons.forEach(element =>
  rovingIndex({
    element,
    target: 'button',
  }))

이제 요소가 타겟 <button> 하위 요소에 포커스를 전달하고 표준 화살표 키 탐색으로 옵션을 탐색하세요.

aria-expanded 전환 중

팝업이 표시되고 숨겨지는 것을 시각적으로 분명히 알 수 있지만 스크린 리더에는 시각적 신호 그 이상이 필요합니다. 여기서 JavaScript는 스크린 리더의 적절한 속성을 전환하여 CSS 기반 :focus-within 상호작용을 보완하는 데 사용됩니다.

popupButtons.on('focusin', e => {
  e.currentTarget.setAttribute('aria-expanded', true)
})

popupButtons.on('focusout', e => {
  e.currentTarget.setAttribute('aria-expanded', false)
})

Escape 키 사용 설정

사용자의 포커스가 의도적으로 트랩으로 전송되었으므로 떠날 방법을 제공합니다. 가장 일반적인 방법은 Escape 키 사용을 허용하는 것입니다. 이렇게 하려면 모든 키보드 이벤트가 작동하므로 팝업 버튼의 키 누름을 하위 항목이 이 상위 항목까지 표시됩니다.

popupButtons.on('keyup', e => {
  if (e.code === 'Escape')
    e.target.blur()
})

팝업 버튼에 Escape 키가 눌리는 것이 감지되면 해당 버튼에서 포커스가 삭제됩니다. 다음 코드로 교체합니다. blur()

분할 버튼 클릭수

마지막으로, 사용자가 클릭하거나 탭하거나 키보드가 버튼과 상호작용하면 적절한 작업을 수행해야 합니다. 이벤트 도움말 풍선이 사용됨 이번에도 .gui-split-button 컨테이너에서 버튼을 포착하기 위해 하위 팝업 또는 기본 작업 클릭

splitButtons.on('click', event => {
  if (event.target.nodeName !== 'BUTTON') return
  console.info(event.target.innerText)
})

결론

이제 제가 어떻게 했는지 알게 되셨으니 어떻게 하면 좋을까요?‽ 보상

접근방식을 다각화하고 웹에서 빌드하는 방법을 모두 알아보겠습니다. 데모를 만들고 링크를 트윗하여 추가하면 됩니다. 아래 커뮤니티 리믹스 섹션을 공유해 주세요

커뮤니티 리믹스