플로팅 작업 버튼 (FAB) 구성요소 빌드

색상 적응형, 반응형, 접근성 있는 FAB 구성요소를 빌드하는 방법에 관한 기본 개요입니다.

이 게시물에서는 색상 적응형, 반응형, 접근성 있는 FAB 구성요소를 빌드하는 방법에 관한 생각을 공유하고자 합니다. 데모를 사용해 보고 소스를 확인해 보세요.

동영상을 선호하는 경우 이 게시물의 YouTube 버전을 참고하세요.

개요

FAB는 데스크톱보다 모바일에서 더 일반적이지만 두 시나리오 모두에서 널리 사용됩니다. 기본 작업을 계속 표시하여 편리하고 항상 사용할 수 있습니다. 이 사용자 환경 스타일은 Material UI에서 유명해졌으며 사용 및 배치에 관한 제안은 여기에서 확인할 수 있습니다.

요소 및 스타일

이러한 컨트롤의 HTML에는 컨테이너 요소와 하나 이상의 버튼 세트가 포함됩니다. 컨테이너는 FAB를 표시 영역 내에 배치하고 버튼 간의 간격을 관리합니다. 버튼은 미니 버튼 또는 기본 버튼으로 설정할 수 있으므로 기본 작업과 보조 작업 간에 다양한 버튼을 사용할 수 있습니다.

FAB 컨테이너

이 요소는 일반 <div>일 수 있지만, 시각 장애가 있는 사용자를 위해 이 컨테이너의 목적과 콘텐츠를 설명하는 유용한 속성으로 태그를 지정하세요.

FAB 마크업

CSS가 스타일을 연결할 수 있는 .fabs 클래스로 시작하고 role="group"aria-label를 추가하여 일반 컨테이너가 아닌 이름이 지정되고 목적이 있는 컨테이너로 만듭니다.

<div class="fabs" role="group" aria-label="Floating action buttons">
  <!-- buttons will go here -->
</div>

FAB 스타일

FAB가 편리하도록 항상 표시 영역 내에 고정됩니다. 이는 위치fixed의 훌륭한 사용 사례입니다. 이 뷰포트 위치 내에서 위치가 오른쪽에서 왼쪽 또는 왼쪽에서 오른쪽과 같은 사용자의 문서 모드를 보완할 수 있도록 inset-blockinset-inline를 사용하기로 선택했습니다. 맞춤 속성은 반복을 방지하고 뷰포트의 하단 및 측면 가장자리에서 동일한 거리를 유지하는 데도 사용됩니다.

.fabs {
  --_viewport-margin: 2.5vmin;

  position: fixed;
  z-index: var(--layer-1);

  inset-block: auto var(--_viewport-margin);
  inset-inline: auto var(--_viewport-margin);
}

다음으로 컨테이너 디스플레이에 flex를 지정하고 레이아웃 방향을 column-reverse로 변경합니다. 이렇게 하면 하위 요소가 서로 겹쳐서 배열되고 (열) 시각적 순서도 반전됩니다. 이렇게 하면 포커스가 HTML 문서에 따라 일반적으로 이동하는 상단이 아닌 하단 요소가 포커스를 받을 수 있는 첫 번째 요소가 됩니다. 시각적 순서를 반대로 하면 시각 장애가 있는 사용자와 키보드 사용자의 환경이 통합됩니다. 기본 작업의 스타일이 미니 버튼보다 커서 시각 장애가 있는 사용자에게 기본 작업임을 나타내고 키보드 사용자는 소스의 첫 번째 항목으로 포커스를 맞춥니다.

DevTools가 그리드 레이아웃을 오버레이하여 표시된 두 개의 FAB 버튼 줄무늬 패턴으로 간격을 표시하고 계산된 높이와 너비도 표시합니다.

.fabs {
  

  display: flex;
  flex-direction: column-reverse;
  place-items: center;
  gap: var(--_viewport-margin);
}

가운데 정렬은 place-items로 처리되며 gap는 컨테이너에 배치된 모든 FAB 버튼 사이에 공간을 추가합니다.

FAB 버튼

이제 버튼이 모든 항목 위에 떠 있는 것처럼 보이도록 스타일을 지정할 차례입니다.

기본 FAB

스타일을 지정할 첫 번째 버튼은 기본 버튼입니다. 이 뷰는 모든 FAB 버튼의 기반이 됩니다. 나중에 이러한 기본 스타일을 최대한 적게 수정하면서 대체 모양을 구현하는 대안을 만들겠습니다.

FAB 마크업

<button> 요소가 올바른 선택입니다. 훌륭한 마우스, 터치, 키보드 사용자 환경을 제공하므로 이를 기반으로 시작합니다. 이 마크업의 가장 중요한 측면은 aria-hidden="true"를 사용하여 스크린 리더 사용자에게 아이콘을 숨기고 <button> 마크업 자체에 필요한 라벨 텍스트를 추가하는 것입니다. 이러한 경우에 라벨을 추가할 때 마우스 사용자가 아이콘이 전달하려는 내용에 관한 정보를 얻을 수 있도록 title도 추가하는 것이 좋습니다.

<button data-icon="plus" class="fab" title="Add new action" aria-label="Add new action">
  <svg aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">...</svg>
</button>

FAB 스타일

먼저 버튼을 패딩이 적용되고 그림자가 강한 원형 버튼으로 변환해 보겠습니다. 이는 버튼의 첫 번째 정의 기능입니다.

.fab {
  --_size: 2rem;

  padding: calc(var(--_size) / 2);
  border-radius: var(--radius-round);
  aspect-ratio: 1;
  box-shadow: var(--shadow-4);
}

다음으로 색상을 추가해 보겠습니다. 이전에 GUI 챌린지에서 사용한 전략을 사용합니다. 밝은 색상과 어두운 색상을 정적으로 유지하는 명확한 이름의 맞춤 속성 세트를 만든 다음 사용자의 색상 시스템 환경설정에 따라 밝은 변수 또는 어두운 변수로 설정되는 적응형 맞춤 속성을 만듭니다.

.fab {
  

  /* light button and button hover */
  --_light-bg: var(--pink-6);
  --_light-bg-hover: var(--pink-7);

  /* dark button and button hover */
  --_dark-bg: var(--pink-4);
  --_dark-bg-hover: var(--pink-3);

  /* adaptive variables set to light by default */
  --_bg: var(--_light-bg);

  /* static icon colors set to the adaptive foreground variable */
  --_light-fg: white;
  --_dark-fg: black;
  --_fg: var(--_light-fg);

  /* use the adaptive properties on some styles */
  background: var(--_bg);
  color: var(--_fg);

  &:is(:active, :hover, :focus-visible) {
    --_bg: var(--_light-bg-hover);

    @media (prefers-color-scheme: dark) {
      --_bg: var(--_dark-bg-hover);
    }
  }

  /* if users prefers dark, set adaptive props to dark */
  @media (prefers-color-scheme: dark) {
    --_bg: var(--_dark-bg);
    --_fg: var(--_dark-fg);
  }
}

그런 다음 SVG 아이콘이 공간에 맞게 표시되도록 스타일을 추가합니다.

.fab {
  

  & > svg {
    inline-size: var(--_size);
    block-size: var(--_size);
    stroke-width: 3px;
  }
}

마지막으로 상호작용에 자체 시각적 의견을 추가했으므로 버튼에서 탭 강조 표시를 삭제합니다.

.fab {
  -webkit-tap-highlight-color: transparent;
}

미니 FAB

이 섹션의 목표는 FAB 버튼의 변형을 만드는 것입니다. 일부 FAB를 기본 작업보다 작게 만들면 사용자가 가장 자주 실행하는 작업을 홍보할 수 있습니다.

미니 FAB 마크업

HTML은 FAB와 동일하지만 CSS에 대안을 연결할 수 있는 '.mini' 클래스를 추가합니다.

<button data-icon="heart" class="fab mini" title="Like action" aria-label="Like action">
  <svg aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">...</svg>
</button>
미니 FAB 스타일

맞춤 속성을 사용하면 --_size 변수만 조정하면 됩니다.

.fab.mini {
  --_size: 1.25rem;
}

두 개의 FAB 버튼이 쌓여 있고 상단 버튼이 하단 버튼보다 작은 스크린샷

접근성

FAB의 접근성에서 가장 중요한 부분은 페이지의 키보드 흐름 내에서의 배치입니다. 이 데모에는 FAB만 있으며 키보드 순서와 흐름 측면에서 경쟁할 만한 항목이 없습니다. 즉, 의미 있는 키보드 흐름을 보여주는 기회가 없습니다. 포커스를 놓고 경쟁하는 요소가 있는 시나리오에서는 사용자가 FAB 버튼 흐름으로 진입하는 위치를 깊이 생각해 보시기 바랍니다.

키보드 상호작용 데모

사용자가 FAB 컨테이너에 포커스를 맞추면 화면 리더 사용자에게 포커스를 맞춘 콘텐츠에 관한 정보를 제공하는 role="group"aria-label="floating action buttons"가 이미 추가되었습니다. 사용자가 기본 작업을 먼저 찾을 수 있도록 전략적으로 기본 FAB를 먼저 배치했습니다. 그런 다음 flex-direction: column-reverse;를 사용하여 사용자가 쉽게 액세스할 수 있도록 하단의 기본 버튼을 사용자의 손가락 가까이에 시각적으로 정렬합니다. 기본 버튼이 시각적으로 눈에 띄고 키보드 사용자에게 먼저 표시되어 매우 유사한 환경을 제공하므로 이는 좋은 점입니다.

마지막으로 스크린 리더 사용자에게 아이콘을 숨기고 버튼의 라벨을 제공하여 버튼의 기능을 알 수 있도록 하세요. 이는 이미 HTML에서 <svg>aria-hidden="true"<button>aria-label="Some action"를 사용하여 실행되었습니다.

애니메이션

사용자 환경을 개선하기 위해 다양한 유형의 애니메이션을 추가할 수 있습니다. 다른 GUI 챌린지와 마찬가지로, 감소된 모션 환경과 전체 모션 환경의 인텐트를 보유할 몇 가지 맞춤 속성을 설정합니다. 기본적으로 스타일은 사용자가 모션 감소를 원하는 것으로 가정하고 prefers-reduced-motion 미디어 쿼리를 사용하여 전환 값을 전체 모션으로 전환합니다.

맞춤 속성이 있는 모션 감소 전략

다음 CSS에서 세 가지 맞춤 속성(--_motion-reduced, --_motion-ok, --_transition)이 생성됩니다. 처음 두 개는 사용자의 환경설정에 따라 적절한 전환을 보유하고 있으며 마지막 변수 --_transition는 각각 --_motion-reduced 또는 --_motion-ok로 설정됩니다.

.fab {
  /* box-shadow and background-color can safely be transitioned for reduced motion users */
  --_motion-reduced:
    box-shadow .2s var(--ease-3),
    background-color .3s var(--ease-3);

  /* add transform and outline-offset for users ok with motion */
  --_motion-ok:
    var(--_motion-reduced),
    transform .2s var(--ease-3),
    outline-offset 145ms var(--ease-2);

  /* default the transition styles to reduced motion */
  --_transition: var(--_motion-reduced);

  /* set the transition to our adaptive transition custom property*/
  transition: var(--_transition);

  /* if motion is ok, update the adaptive prop to the respective transition prop */
  @media (prefers-reduced-motion: no-preference) {
    --_transition: var(--_motion-ok);
  }
}

위의 작업을 완료하면 box-shadow, background-color, transform, outline-offset의 변경사항을 전환하여 사용자에게 상호작용이 수신되었음을 알리는 멋진 UI 피드백을 제공할 수 있습니다.

그런 다음 translateY를 약간 조정하여 :active 상태에 약간의 스타일을 더합니다. 이렇게 하면 버튼에 눌림 효과가 생깁니다.

.fab {
  

  &:active {
    @media (prefers-reduced-motion: no-preference) {
      transform: translateY(2%);
    }
  }
}

마지막으로 버튼의 SVG 아이콘을 변경합니다.

.fab {
  

  &[data-icon="plus"]:hover > svg {
    transform: rotateZ(.25turn);
  }

  & > svg {
    @media (prefers-reduced-motion: no-preference) {
      will-change: transform;
      transition: transform .5s var(--ease-squish-3);
    }
  }
}

결론

이제 제가 어떻게 했는지 알았으니 어떻게 하시겠어요? 🙂

접근 방식을 다양화하고 웹에서 빌드하는 모든 방법을 알아보겠습니다.

데모를 만들어 트윗해 주시면 아래의 커뮤니티 리믹스 섹션에 추가해 드리겠습니다.

커뮤니티 리믹스

아직 표시할 내용이 없습니다.

리소스