플로팅 작업 버튼 (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 문서에 따라 포커스가 이동합니다. 시각적 순서를 반전하면 시력이 정상인 사용자와 키보드 사용자의 환경을 통합할 수 있습니다. 기본 작업의 스타일을 미니 버튼보다 크게 설정하면 정상 사용자에게 해당 작업이 기본 작업임을 알 수 있고 키보드 사용자는 이 작업의 첫 번째 항목으로 초점을 맞출 수 있습니다.

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

.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 {
  --_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 미디어 쿼리를 사용하여 전환 값을 전체 모션으로 바꿉니다.

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

세 가지 맞춤 속성은 --_motion-reduced, --_motion-ok, --_transition CSS에서 생성됩니다. 처음 두 개는 사용자의 환경설정을 고려하여 적절한 전환을 유지하고 마지막 변수 --_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);
    }
  }
}

결론

이제 제가 어떻게 했는지 알았으니 어떻게 되세요?‽ 🙂

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

데모를 만들고 링크를 트윗해 주세요. 그러면 아래의 커뮤니티 리믹스 섹션에 추가하겠습니다.

커뮤니티 리믹스

표시할 항목 없음

자료