버튼 구성요소 빌드

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

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

밝은 테마와 어두운 테마에서 키보드와 마우스를 통해 버튼과 상호작용할 수 있습니다.

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

개요

브라우저 지원

  • Chrome: 1.
  • Edge: 12.
  • Firefox: 1.
  • Safari: 1.

소스

<button> 요소는 사용자 상호작용을 위해 빌드됩니다. click 이벤트는 키보드, 마우스, 터치, 음성 등에서 타이밍에 관한 스마트 규칙을 사용하여 트리거됩니다. 또한 각 브라우저에 기본 스타일이 제공되므로 맞춤설정 없이 바로 사용할 수 있습니다. color-scheme를 사용하여 브라우저에서 제공하는 밝은색 및 어두운색 버튼도 선택할 수 있습니다.

또한 다양한 유형의 버튼이 있으며, 이는 이전 Codepen 삽입에 각각 나와 있습니다. 유형이 없는 <button><form> 내에 있는 것에 적응하여 제출 유형으로 변경됩니다.

<!-- buttons -->
<button></button>
<button type="submit"></button>
<button type="button"></button>
<button type="reset"></button>

<!-- button state -->
<button disabled></button>

<!-- input buttons -->
<input type="button" />
<input type="file">

이번 달의 GUI 챌린지에서는 의도를 시각적으로 차별화하는 데 도움이 되는 스타일을 각 버튼에 적용합니다. 재설정 버튼은 파괴적이므로 경고 색상이 사용되고 제출 버튼은 파란색 강조 텍스트가 사용되어 일반 버튼보다 약간 더 강조된 것처럼 보입니다.

양식이 아닌 양식으로 표시된 모든 버튼 유형의 최종 세트의 미리보기로, 아이콘 버튼과 맞춤설정한 버튼이 보기 좋게 추가되었습니다.
양식이 아닌 양식에 표시되는 모든 버튼 유형의 최종 세트 미리보기로, 아이콘 버튼과 맞춤설정된 버튼이 추가되었습니다.

버튼에는 CSS가 스타일 지정에 사용할 수 있는 의사 클래스도 있습니다. 다음 클래스는 버튼의 느낌을 맞춤설정하는 CSS 후크를 제공합니다. 마우스가 버튼 위에 있을 때는 :hover, 마우스 또는 키보드가 눌릴 때는 :active, 보조 기술 스타일 지정을 지원하는 경우 :focus 또는 :focus-visible를 사용합니다.

button:hover {}
button:active {}
button:focus {}
button:focus-visible {}
어두운 테마의 모든 버튼 유형 최종 세트 미리보기
어두운 테마의 모든 버튼 유형의 최종 세트 미리보기

마크업

HTML 사양에서 제공하는 버튼 유형 외에도 아이콘이 있는 버튼과 맞춤 클래스 btn-custom가 있는 버튼을 추가했습니다.

<button>Default</button>
<input type="button" value="<input>"/>
<button>
  <svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true">
    <path d="..." />
  </svg>
  Icon
</button>
<button type="submit">Submit</button>
<button type="button">Type Button</button>
<button type="reset">Reset</button>
<button disabled>Disabled</button>
<button class="btn-custom">Custom</button>
<input type="file">

그런 다음 테스트를 위해 각 버튼을 양식 내에 배치합니다. 이렇게 하면 제출 버튼으로 작동하는 기본 버튼에 맞게 스타일이 적절하게 업데이트됩니다. 또한 두 가지 모두 동일하게 작동하도록 아이콘 전략을 인라인 SVG에서 마스크 처리된 SVG로 전환합니다.

<form>
  <button>Default</button>
  <input type="button" value="<input>"/>
  <button>Icon <span data-icon="cloud"></span></button>
  <button type="submit">Submit</button>
  <button type="button">Type Button</button>
  <button type="reset">Reset</button>
  <button disabled>Disabled</button>
  <button class="btn-custom btn-large" type="button">Large Custom</button>
  <input type="file">
</form>

이 시점에서는 조합 매트릭스가 상당히 압도적입니다. 버튼 유형, 가상 클래스, 양식 내부 또는 외부 등 20가지가 넘는 버튼 조합이 있습니다. CSS를 사용하면 각각의 요소를 명확하게 표현할 수 있습니다.

접근성

버튼 요소는 자연스럽게 액세스할 수 있지만 몇 가지 일반적인 개선사항이 있습니다.

함께 마우스 오버 및 포커스 설정

:hover:focus:is() 함수 가상 선택자와 함께 그룹화하고 싶습니다. 이렇게 하면 인터페이스에서 항상 키보드 및 보조 기술 스타일을 고려할 수 있습니다.

button:is(:hover, :focus) {
  
}
데모를 사용해 보세요.

대화형 초점 링

키보드 및 보조 기술 사용자를 위해 포커스 링에 애니메이션을 적용하는 것을 좋아합니다. 이를 위해 버튼이 활성화되지 않은 경우에만 윤곽선을 버튼에서 5픽셀 멀리 애니메이션 처리합니다. 이렇게 하면 누르면 포커스 링이 버튼 크기로 다시 축소되는 효과가 만들어집니다.

:where(button, input):where(:not(:active)):focus-visible {
  outline-offset: 5px;
}

색상 대비 통과 확인

밝은 색상과 어두운 색상에는 버튼, 제출 버튼, 재설정 버튼, 사용 중지된 버튼 등 최소 4가지 색상 조합이 있으며 색상 대비를 고려해야 합니다. VisBug는 여기에서 모든 점수를 한 번에 검사하고 표시하는 데 사용됩니다.

볼 수 없는 사용자에게 아이콘 숨기기

아이콘 버튼을 생성할 때 아이콘은 버튼 텍스트를 시각적으로 지원해야 합니다. 또한 이 아이콘은 시각 장애인에게는 유용하지 않습니다. 다행히 브라우저에서는 시각 장애가 있는 사용자가 장식용 버튼 이미지로 인해 방해받지 않도록 스크린 리더 기술에서 항목을 숨기는 방법을 제공합니다.

<button>
  <svg … aria-hidden="true">...</svg>
  Icon Button
</button>
버튼의 접근성 트리가 표시된 Chrome DevTools 트리는 aria-hidden이 true로 설정되어 있으므로 버튼 이미지를 무시합니다.
버튼의 접근성 트리를 보여주는 Chrome DevTools aria-hidden이 true로 설정되어 있으므로 트리에서 버튼 이미지를 무시합니다.

스타일

다음 섹션에서는 먼저 버튼의 적응형 스타일을 관리하기 위한 맞춤 속성 시스템을 설정합니다. 이러한 맞춤 속성을 사용하여 요소를 선택하고 모양을 맞춤설정할 수 있습니다.

적응형 맞춤 속성 전략

이 GUI 챌린지에서 사용된 맞춤 속성 전략은 색 구성표 빌드에 사용된 전략과 매우 유사합니다. 밝은 색상과 어두운 색상 자동 조절 시스템의 경우 각 테마의 맞춤 속성이 정의되고 적절하게 이름이 지정됩니다. 그런 다음 단일 맞춤 속성이 테마의 현재 값을 보유하는 데 사용되며 CSS 속성에 할당됩니다. 나중에 단일 맞춤 속성을 다른 값으로 업데이트한 후 버튼 스타일을 업데이트할 수 있습니다.

button {
  --_bg-light: white;
  --_bg-dark: black;
  --_bg: var(--_bg-light);

  background-color: var(--_bg);
}

@media (prefers-color-scheme: dark) {
  button {
    --_bg: var(--_bg-dark);
  }
}

밝은 테마와 어두운 테마가 선언적이고 명확하다는 점이 마음에 듭니다. 간접과 추상화는 이제 유일한 '반응형' 속성인 --_bg 맞춤 속성으로 오프로드됩니다. --_bg-light--_bg-dark는 정적입니다. 또한 밝은 테마가 기본 테마이고 어두운 테마는 조건부로만 적용된다는 것도 명확하게 알 수 있습니다.

디자인 일관성 준비

공유 선택기

다음 선택기는 다양한 유형의 버튼을 모두 타겟팅하는 데 사용되며 처음에는 다소 복잡해 보일 수 있습니다. :where()가 사용되므로 버튼을 맞춤설정할 때는 구체적인 사항이 필요하지 않습니다. 버튼은 대체 시나리오에 맞게 조정되는 경우가 많으며 :where() 선택기를 사용하면 이 작업이 간편해집니다. :where() 내에서 :is() 또는 :where() 내에서 사용할 수 없는 ::file-selector-button를 포함하여 각 버튼 유형이 선택됩니다.

:where(
  button,
  input[type="button"],
  input[type="submit"],
  input[type="reset"],
  input[type="file"]
),
:where(input[type="file"])::file-selector-button {
  
}

모든 맞춤 속성의 범위는 이 선택기 내에 있습니다. 이제 모든 커스텀 속성을 검토하세요. 이 버튼에는 사용되는 맞춤 속성이 꽤 많습니다. 각 그룹을 설명하면서 진행하면서 어둡고 모션이 줄어든 컨텍스트에 관해서도 섹션 끝에서 설명하겠습니다.

버튼 강조 색상

제출 버튼과 아이콘은 색상을 강조하는 데 적합합니다.

--_accent-light: hsl(210 100% 40%);
--_accent-dark: hsl(210 50% 70%);
--_accent: var(--_accent-light);

버튼 텍스트 색상

버튼 텍스트 색상은 흰색이나 검은색이 아니며 hsl()를 사용하고 210 색조를 유지하는 --_accent의 어둡거나 밝은 버전입니다.

--_text-light: hsl(210 10% 30%);
--_text-dark: hsl(210 5% 95%);
--_text: var(--_text-light);

버튼 배경 색상

버튼 배경은 동일한 hsl() 패턴을 따릅니다. 단, 밝은 테마 버튼은 흰색으로 설정되어 표면이 사용자 가까이 또는 다른 노출 영역 앞에 표시됩니다.

--_bg-light: hsl(0 0% 100%);
--_bg-dark: hsl(210 9% 31%);
--_bg: var(--_bg-light);

버튼 배경

이 배경 색상은 노출 영역이 다른 노출 영역 뒤에 표시되도록 하기 위한 것이며 파일 입력의 배경에 유용합니다.

--_input-well-light: hsl(210 16% 87%);
--_input-well-dark: hsl(204 10% 10%);
--_input-well: var(--_input-well-light);

버튼 패딩

버튼의 텍스트 주위의 간격은 글꼴 크기와 상대적인 길이인 ch 단위를 사용합니다. 큰 버튼이 font-size 및 버튼 크기를 비례하여 간단히 올릴 수 있는 경우 이는 중요합니다.

--_padding-inline: 1.75ch;
--_padding-block: .75ch;

버튼 테두리

버튼 테두리 반경은 맞춤 속성에 저장되므로 파일 입력이 다른 버튼과 일치할 수 있습니다. 테두리 색상은 기존의 적응형 색상 시스템을 따릅니다.

--_border-radius: .5ch;

--_border-light: hsl(210 14% 89%);
--_border-dark: var(--_bg-dark);
--_border: var(--_border-light);

버튼 마우스 오버 강조 표시 효과

이러한 속성은 상호작용 시 전환을 위한 크기 속성을 설정하며 강조 표시 색상은 적응형 색상 시스템을 따릅니다. 이 게시물의 뒷부분에서 이러한 요소가 상호작용하는 방식을 설명하겠지만, 최종적으로는 box-shadow 효과에 사용됩니다.

--_highlight-size: 0;

--_highlight-light: hsl(210 10% 71% / 25%);
--_highlight-dark: hsl(210 10% 5% / 25%);
--_highlight: var(--_highlight-light);

버튼 텍스트 음영

각 버튼에는 은근한 텍스트 그림자 스타일이 적용되어 있습니다. 이렇게 하면 텍스트가 버튼 위에 배치되어 가독성을 개선하고 세련된 프레젠테이션을 다듬을 수 있습니다.

--_ink-shadow-light: 0 1px 0 var(--_border-light);
--_ink-shadow-dark: 0 1px 0 hsl(210 11% 15%);
--_ink-shadow: var(--_ink-shadow-light);

버튼 아이콘

아이콘은 상대 길이 ch 단위를 사용하여 다시 2자 크기가 되므로 아이콘이 버튼 텍스트에 비례하여 크기 조절됩니다. 아이콘 색상은 적응형 테마 내 색상을 위해 --_accent-color를 사용합니다.

--_icon-size: 2ch;
--_icon-color: var(--_accent);

버튼 그림자

그림자가 밝은 부분과 어두운 부분에 적절하게 조정되려면 색상과 불투명도를 모두 변경해야 합니다. 밝은 테마 그림자는 섬세하고 겹쳐지는 노출 영역 색상으로 색조가 지정된 것이 가장 좋습니다. 어두운 테마 그림자는 더 어둡고 채도가 높아야 더 어두운 노출 영역 색상을 오버레이할 수 있습니다.

--_shadow-color-light: 220 3% 15%;
--_shadow-color-dark: 220 40% 2%;
--_shadow-color: var(--_shadow-color-light);

--_shadow-strength-light: 1%;
--_shadow-strength-dark: 25%;
--_shadow-strength: var(--_shadow-strength-light);

적응형 색상과 강도를 사용하면 두 가지 그림자 깊이를 조합할 수 있습니다.

--_shadow-1: 0 1px 2px -1px hsl(var(--_shadow-color)/calc(var(--_shadow-strength) + 9%));

--_shadow-2: 
  0 3px 5px -2px hsl(var(--_shadow-color)/calc(var(--_shadow-strength) + 3%)),
  0 7px 14px -5px hsl(var(--_shadow-color)/calc(var(--_shadow-strength) + 5%));

또한 버튼에 약간의 3D 모양을 주기 위해 1px box-shadow를 사용하여 착시 효과를 줍니다.

--_shadow-depth-light: 0 1px var(--_border-light);
--_shadow-depth-dark: 0 1px var(--_bg-dark);
--_shadow-depth: var(--_shadow-depth-light);

버튼 전환

적응형 색상 패턴에 따라 디자인 시스템 옵션을 포함할 두 개의 정적 속성을 만듭니다.

--_transition-motion-reduce: ;
--_transition-motion-ok:
  box-shadow 145ms ease,
  outline-offset 145ms ease
;
--_transition: var(--_transition-motion-reduce);

선택기에 있는 모든 속성

:where(
  button,
  input[type="button"],
  input[type="submit"],
  input[type="reset"],
  input[type="file"]
),
:where(input[type="file"])::file-selector-button {
  --_accent-light: hsl(210 100% 40%);
  --_accent-dark: hsl(210 50% 70%);
  --_accent: var(--_accent-light);

--_text-light: hsl(210 10% 30%); --_text-dark: hsl(210 5% 95%); --_text: var(--_text-light);

--_bg-light: hsl(0 0% 100%); --_bg-dark: hsl(210 9% 31%); --_bg: var(--_bg-light);

--_input-well-light: hsl(210 16% 87%); --_input-well-dark: hsl(204 10% 10%); --_input-well: var(--_input-well-light);

--_padding-inline: 1.75ch; --_padding-block: .75ch;

--_border-radius: .5ch; --_border-light: hsl(210 14% 89%); --_border-dark: var(--_bg-dark); --_border: var(--_border-light);

--_highlight-size: 0; --_highlight-light: hsl(210 10% 71% / 25%); --_highlight-dark: hsl(210 10% 5% / 25%); --_highlight: var(--_highlight-light);

--_ink-shadow-light: 0 1px 0 hsl(210 14% 89%); --_ink-shadow-dark: 0 1px 0 hsl(210 11% 15%); --_ink-shadow: var(--_ink-shadow-light);

--_icon-size: 2ch; --_icon-color-light: var(--_accent-light); --_icon-color-dark: var(--_accent-dark); --_icon-color: var(--accent, var(--_icon-color-light));

--_shadow-color-light: 220 3% 15%; --_shadow-color-dark: 220 40% 2%; --_shadow-color: var(--_shadow-color-light); --_shadow-strength-light: 1%; --_shadow-strength-dark: 25%; --_shadow-strength: var(--_shadow-strength-light); --_shadow-1: 0 1px 2px -1px hsl(var(--_shadow-color)/calc(var(--_shadow-strength) + 9%)); --_shadow-2: 0 3px 5px -2px hsl(var(--_shadow-color)/calc(var(--_shadow-strength) + 3%)), 0 7px 14px -5px hsl(var(--_shadow-color)/calc(var(--_shadow-strength) + 5%)) ;

--_shadow-depth-light: hsl(210 14% 89%); --_shadow-depth-dark: var(--_bg-dark); --_shadow-depth: var(--_shadow-depth-light);

--_transition-motion-reduce: ; --_transition-motion-ok: box-shadow 145ms ease, outline-offset 145ms ease ; --_transition: var(--_transition-motion-reduce); }

기본 버튼이 밝은 테마와 어두운 테마로 나란히 표시됩니다.

어두운 테마 조정

어두운 테마 props가 설정되면 -light-dark 정적 props 패턴의 값이 명확해집니다.

@media (prefers-color-scheme: dark) {
  :where(
    button,
    input[type="button"],
    input[type="submit"],
    input[type="reset"],
    input[type="file"]
  ),
  :where(input[type="file"])::file-selector-button {
    --_bg: var(--_bg-dark);
    --_text: var(--_text-dark);
    --_border: var(--_border-dark);
    --_accent: var(--_accent-dark);
    --_highlight: var(--_highlight-dark);
    --_input-well: var(--_input-well-dark);
    --_ink-shadow: var(--_ink-shadow-dark);
    --_shadow-depth: var(--_shadow-depth-dark);
    --_shadow-color: var(--_shadow-color-dark);
    --_shadow-strength: var(--_shadow-strength-dark);
  }
}

이렇게 하면 읽기 쉬울 뿐만 아니라 이러한 맞춤 버튼의 소비자는 사용자 환경설정에 적절하게 조정될 것이라는 확신을 가지고 빈 속성을 사용할 수 있습니다.

모션 적응 감소

방문한 사용자가 모션을 허용하는 경우 --_transitionvar(--_transition-motion-ok)에 할당합니다.

@media (prefers-reduced-motion: no-preference) {
  :where(
    button,
    input[type="button"],
    input[type="submit"],
    input[type="reset"],
    input[type="file"]
  ),
  :where(input[type="file"])::file-selector-button {
    --_transition: var(--_transition-motion-ok);
  }
}

공유된 스타일 몇 개

버튼과 입력란의 글꼴은 페이지의 나머지 글꼴과 일치하도록 inherit로 설정해야 합니다. 그러지 않으면 브라우저에서 스타일을 지정합니다. 이는 letter-spacing에도 적용됩니다. line-height1.5로 설정하면 문자 상자 크기가 위아래에 여백이 있도록 설정됩니다.

:where(
  button,
  input[type="button"],
  input[type="submit"],
  input[type="reset"],
  input[type="file"]
),
:where(input[type="file"])::file-selector-button {
  /* …CSS variables */

  font: inherit;
  letter-spacing: inherit;
  line-height: 1.5;
  border-radius: var(--_border-radius);
}

위의 스타일을 적용한 후의 버튼을 보여주는 스크린샷

버튼 스타일 지정

선택기 조정

선택기 input[type="file"]는 입력의 버튼 부분이 아니며 가상 요소 ::file-selector-button가 그러므로 목록에서 input[type="file"]를 삭제했습니다.

:where(
  button,
  input[type="button"],
  input[type="submit"],
  input[type="reset"],
  input[type="file"]
),
:where(input[type="file"])::file-selector-button {
  
}

커서 및 터치 조정

먼저 커서 스타일을 pointer 스타일로 지정합니다. 이렇게 하면 버튼이 마우스 사용자에게 양방향임을 나타낼 수 있습니다. 그런 다음 touch-action: manipulation를 추가하여 클릭을 기다릴 필요가 없도록 하고 잠재적인 더블클릭을 관찰하여 버튼의 클릭 속도를 높입니다.

:where(
  button,
  input[type="button"],
  input[type="submit"],
  input[type="reset"]
),
:where(input[type="file"])::file-selector-button {
  cursor: pointer;
  touch-action: manipulation;
}

색상 및 테두리

그런 다음 앞에서 설정한 적응형 맞춤 속성 중 일부를 사용하여 글꼴 크기, 배경, 텍스트, 테두리 색상을 맞춤설정합니다.

:where(
  button,
  input[type="button"],
  input[type="submit"],
  input[type="reset"]
),
:where(input[type="file"])::file-selector-button {
  

  font-size: var(--_size, 1rem);
  font-weight: 700;
  background: var(--_bg);
  color: var(--_text);
  border: 2px solid var(--_border);
}

위의 스타일을 적용한 후 버튼을 보여주는 스크린샷

그림자

버튼에 몇 가지 멋진 기법이 적용되어 있습니다. text-shadow는 밝은 색상과 어두운 색상에 맞게 조정되므로 배경 위에 멋지게 배치된 버튼 텍스트가 눈에 잘 띄지 않고도 눈에 띄게 표시됩니다. box-shadow의 경우 세 개의 그림자가 할당됩니다. 첫 번째(--_shadow-2)는 일반 박스 그림자입니다. 두 번째 그림자는 버튼이 약간 경사져 있는 것처럼 보이게 하는 시각적 효과입니다. 마지막 그림자는 마우스 오버 강조 표시를 위한 것으로 처음에는 크기가 0이지만 나중에 크기가 지정되고 전환되어 버튼에서 커진 것처럼 보입니다.

:where(
  button,
  input[type="button"],
  input[type="submit"],
  input[type="reset"]
),
:where(input[type="file"])::file-selector-button {
  

  box-shadow: 
    var(--_shadow-2),
    var(--_shadow-depth),
    0 0 0 var(--_highlight-size) var(--_highlight)
  ;
  text-shadow: var(--_ink-shadow);
}

위의 스타일을 적용한 후 버튼을 보여주는 스크린샷

레이아웃

버튼에 플렉스박스 레이아웃, 특히 콘텐츠에 맞는 inline-flex 레이아웃을 적용했습니다. 그런 다음 텍스트를 중앙에 배치하고 하위 요소를 가운데에 세로 및 가로로 정렬합니다. 이렇게 하면 아이콘과 기타 버튼 요소가 올바르게 정렬됩니다.

:where(
  button,
  input[type="button"],
  input[type="submit"],
  input[type="reset"]
),
:where(input[type="file"])::file-selector-button {
  

  display: inline-flex;
  justify-content: center;
  align-items: center;
  text-align: center;
}

위의 스타일을 적용한 후의 버튼을 보여주는 스크린샷

간격

버튼 간격의 경우 gap를 사용하여 상위 요소가 서로 닿지 않도록 하고 패딩에 논리적 속성을 사용하여 버튼 간격이 모든 텍스트 레이아웃에 적용되도록 했습니다.

:where(
  button,
  input[type="button"],
  input[type="submit"],
  input[type="reset"]
),
:where(input[type="file"])::file-selector-button {
  

  gap: 1ch;
  padding-block: var(--_padding-block);
  padding-inline: var(--_padding-inline);
}

위의 스타일을 적용한 후 버튼을 보여주는 스크린샷

터치 및 마우스 UX

다음 섹션은 주로 휴대기기의 터치 사용자를 대상으로 합니다. 첫 번째 속성인 user-select는 모든 사용자에게 적용되며, 버튼 텍스트를 강조 표시하는 텍스트를 방지합니다. 이는 주로 터치 기기에서 버튼을 길게 탭하고 운영체제에서 버튼 텍스트를 강조 표시할 때 눈에 띕니다.

일반적으로 이는 내장 앱의 버튼과는 다른 사용자 환경이므로 user-select를 none으로 설정하여 사용 중지합니다. 탭 강조 표시 색상(-webkit-tap-highlight-color) 및 운영체제 컨텍스트 메뉴(-webkit-touch-callout)는 일반적인 버튼 사용자의 기대치에 부합하지 않는 매우 웹 중심의 버튼 기능이므로 이를 삭제합니다.

:where(
  button,
  input[type="button"],
  input[type="submit"],
  input[type="reset"]
),
:where(input[type="file"])::file-selector-button {
  

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

화면전환

적응형 --_transition 변수가 전환 속성에 할당됩니다.

:where(
  button,
  input[type="button"],
  input[type="submit"],
  input[type="reset"]
),
:where(input[type="file"])::file-selector-button {
  

  transition: var(--_transition);
}

사용자가 실제로 누르지 않은 상태에서 마우스를 가져가면 버튼 내부에서 커지는 듯한 멋진 포커스 모양을 만들기 위해 그림자 강조 표시 크기를 조정합니다.

:where(
  button,
  input[type="button"],
  input[type="submit"],
  input[type="reset"]
):where(:not(:active):hover) {
  --_highlight-size: .5rem;
}

포커스가 있을 때 버튼에서 포커스 윤곽선 오프셋을 늘리면서 버튼 안에서 커져가는 것처럼 멋진 포커스 모양도 만듭니다.

:where(button, input):where(:not(:active)):focus-visible {
  outline-offset: 5px;
}

아이콘

아이콘을 처리하기 위해 선택기에는 직접 SVG 하위 요소 또는 맞춤 속성 data-icon가 있는 요소를 위한 :where() 선택기가 추가되었습니다. 아이콘 크기는 인라인 및 블록 로직 속성을 사용하여 맞춤 속성으로 설정됩니다. 획 색상과 text-shadow에 맞는 drop-shadow가 설정됩니다. flex-shrink0로 설정되어 아이콘이 압축되지 않습니다. 마지막으로 선이 그어진 아이콘을 선택하고 여기에 fill: noneround 선 끝과 선 접합을 사용하여 이러한 스타일을 할당합니다.

:where(
  button,
  input[type="button"],
  input[type="submit"],
  input[type="reset"]
) > :where(svg, [data-icon]) {
  block-size: var(--_icon-size);
  inline-size: var(--_icon-size);
  stroke: var(--_icon-color);
  filter: drop-shadow(var(--_ink-shadow));

  flex-shrink: 0;
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
}

이전 스타일이 적용된 후의 버튼을 보여주는 스크린샷

제출 버튼 맞춤설정

제출 버튼이 약간 강조된 모양을 갖도록 하기 위해 버튼의 텍스트 색상을 강조 색상으로 지정했습니다.

:where(
  [type="submit"], 
  form button:not([type],[disabled])
) {
  --_text: var(--_accent);
}

위의 스타일을 적용한 후의 버튼을 보여주는 스크린샷

재설정 버튼 맞춤설정

사용자에게 잠재적으로 파괴적인 동작을 알리는 경고 기호가 내장되어 있습니다. 또한 밝은 테마 버튼의 스타일을 어두운 테마보다 빨간색 강조 표시를 더 많이 사용하도록 선택했습니다. 적절한 밝은 색상 또는 어두운 색상의 기본 색상을 변경하여 맞춤설정할 수 있으며, 버튼을 누르면 스타일이 업데이트됩니다.

:where([type="reset"]) {
  --_border-light: hsl(0 100% 83%);
  --_highlight-light: hsl(0 100% 89% / 20%);
  --_text-light: hsl(0 80% 50%);
  --_text-dark: hsl(0 100% 89%);
}

포커스 윤곽선 색상이 빨간색 강조 색상과 일치하는 것도 좋을 것 같습니다. 텍스트 색상이 어두운 빨간색에서 밝은 빨간색으로 조정됩니다. 윤곽선 색상을 currentColor 키워드와 일치시킵니다.

:where([type="reset"]):focus-visible {
  outline-color: currentColor;
}

위의 스타일을 적용한 후의 버튼을 보여주는 스크린샷

사용 중지된 버튼 맞춤설정

사용 중지된 버튼이 덜 활성 상태로 표시되도록 사용 중지된 버튼의 색상을 낮추려고 시도하는 동안 색상 대비가 낮아지는 경우가 너무나 흔한 일입니다. 각 색상 세트를 테스트하고 통과하는지 확인했으며, DevTools 또는 VisBug에서 점수가 통과할 때까지 HSL 밝기 값을 조정했습니다.

:where(
  button,
  input[type="button"],
  input[type="submit"],
  input[type="reset"]
)[disabled] {
  --_bg: none;
  --_text-light: hsl(210 7% 40%);
  --_text-dark: hsl(210 11% 71%);

  cursor: not-allowed;
  box-shadow: var(--_shadow-1);
}

위의 스타일을 적용한 후 버튼을 보여주는 스크린샷

파일 입력 버튼 맞춤설정

파일 입력 버튼은 스팬과 버튼의 컨테이너입니다. CSS는 중첩된 버튼뿐만 아니라 입력 컨테이너에도 약간의 스타일을 지정할 수 있지만 스팬에는 지정할 수 없습니다. 컨테이너에 max-inline-size가 주어져 필요한 것보다 더 커지지 않도록 하는 반면 inline-size: 100%는 자체적으로 컨테이너를 실제보다 작게 축소하고 맞출 수 있도록 합니다. 배경 색상은 다른 표면보다 어두운 자동 조절 색상으로 설정되므로 파일 선택기 버튼 뒤에 표시됩니다.

:where(input[type="file"]) {
  inline-size: 100%;
  max-inline-size: max-content;
  background-color: var(--_input-well);
}

파일 선택기 버튼과 입력 유형 버튼에는 다른 버튼 스타일로 덮어쓰지 않은 브라우저 제공 스타일을 삭제하기 위해 appearance: none가 특별히 지정됩니다.

:where(input[type="button"]),
:where(input[type="file"])::file-selector-button {
  appearance: none;
}

마지막으로 버튼의 inline-end에 여백을 추가하여 스팬 텍스트를 버튼에서 멀리 밀어내고 공백을 만듭니다.

:where(input[type="file"])::file-selector-button {
  margin-inline-end: var(--_padding-inline);
}

이전 스타일이 적용된 후의 버튼을 보여주는 스크린샷

특별한 어두운 테마 예외

기본 작업 버튼의 배경을 더 어둡게 설정하여 텍스트와의 대비를 높여 약간 더 강조된 느낌을 주었습니다.

@media (prefers-color-scheme: dark) {
  :where(
    [type="submit"],
    [type="reset"],
    [disabled],
    form button:not([type="button"])
  ) {
    --_bg: var(--_input-well);
  }
}

위의 스타일을 적용한 후 버튼을 보여주는 스크린샷

대안 만들기

재미와 실용성을 위해 몇 가지 변형을 만드는 방법을 보여드리기로 했습니다. 한 가지 변형은 기본 버튼의 모양과 유사하게 매우 선명합니다. 다른 변형은 큽니다. 마지막 변형에는 그라데이션이 채워진 아이콘이 있습니다.

생생한 버튼

이 버튼 스타일을 구현하기 위해 기본 속성을 파란색으로 직접 덮어썼습니다. 빠르고 쉬웠지만 적응형 소품을 삭제하고 밝은 테마와 어두운 테마에서 모두 동일하게 보입니다.

.btn-custom {
  --_bg: linear-gradient(hsl(228 94% 67%), hsl(228 81% 59%));
  --_border: hsl(228 89% 63%);
  --_text: hsl(228 89% 100%);
  --_ink-shadow: 0 1px 0 hsl(228 57% 50%);
  --_highlight: hsl(228 94% 67% / 20%);
}

맞춤 버튼이 밝은 색상과 어두운 색상으로 표시됩니다. 일반적인 기본 작업 버튼과 마찬가지로 매우 선명한 파란색입니다.

큰 버튼

이 버튼 스타일은 --_size 맞춤 속성을 수정하여 얻을 수 있습니다. 패딩 및 기타 공간 요소는 이 크기를 기준으로 하며 새 크기에 비례하여 크기가 조정됩니다.

.btn-large {
  --_size: 1.5rem;
}

큰 버튼은 맞춤 버튼 옆에 표시되어 약 150배 더 큰 크기입니다.

아이콘 버튼

이 아이콘 효과는 버튼 스타일과는 아무런 관련이 없지만, 몇 가지 CSS 속성으로 이 효과를 얻는 방법과 버튼이 인라인 SVG가 아닌 아이콘을 얼마나 잘 처리하는지 보여줍니다.

[data-icon="cloud"] {
  --icon-cloud: url("https://api.iconify.design/mdi:apple-icloud.svg") center / contain no-repeat;

  -webkit-mask: var(--icon-cloud);
  mask: var(--icon-cloud);
  background: linear-gradient(to bottom, var(--_accent-dark), var(--_accent-light));
}

아이콘이 있는 버튼이 밝은 테마와 어두운 테마에 표시됩니다.

결론

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

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

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

커뮤니티 리믹스

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

리소스