버튼 구성요소 빌드

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

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

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

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

개요

브라우저 지원

  • 1
  • 12
  • 1
  • 4 이하

소스

<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 단위를 다시 사용하므로 두 문자의 크기입니다. 따라서 아이콘이 버튼 텍스트에 비례하여 크기를 조정할 수 있습니다. 아이콘 색상은 --_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 상자 그림자를 사용하면 착시 효과가 발생합니다.

--_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); }

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

어두운 테마 조정

어두운 테마 속성이 설정되면 -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);
}

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

레이아웃

버튼에 flexbox 레이아웃, 특히 콘텐츠에 맞는 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 변수는 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));
}

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

결론

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

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

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

커뮤니티 리믹스

표시할 항목 없음

자료