Buttons

This pattern shows how to style the different buttons types of the web.

Full article · Video on YouTube · Source on Github

<section>
  <h2>9 button types</h2>
  <p>Unified modern style, visual differences reinforce purpose.</p>
</section>

<button>Default</button>
<input type="button" value="<input>"/>
<button>
  <svg viewBox="0 0 24 24" stroke="currentColor" width="24" height="24" aria-hidden="true">
    <path d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
  </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">

        :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 var(--_border-light);
  --_ink-shadow-dark: 0 1px 0 hsl(210 11% 15%);
  --_ink-shadow: var(--_ink-shadow-light);
  
  --_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%));
  
  --_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);

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

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

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

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

  font-size: var(--_size, 1rem);
  background: var(--_bg);
  color: var(--_text);
  border: 2px solid var(--_border);
  box-shadow: 
    var(--_shadow-2),
    var(--_shadow-depth),
    0 0 0 var(--_highlight-size) var(--_highlight)
  ;
  text-shadow: var(--_ink-shadow);
  transition: var(--_transition);
  
  display: inline-flex;
  justify-content: center;
  align-items: center;
  text-align: center;
  gap: 1ch;
  
  font-weight: 700;
  padding-block: var(--_padding-block);
  padding-inline: var(--_padding-inline);

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

/* icons */
: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;
}

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

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

/* disabled */
: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);
}

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

/* red reset */
: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%);
}

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

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

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

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

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