Créer un composant de bouton

Présentation de base sur la création de composants <button> adaptatifs aux couleurs, responsifs et accessibles.

Dans cet article, je souhaite partager mes réflexions sur la création d'un élément <button> adaptatif aux couleurs, responsif et accessible. Essayer la démo et consulter la source

Les boutons sont utilisés via le clavier et la souris dans les thèmes clair et sombre.

Si vous préférez regarder une vidéo, voici une version YouTube de cet article:

Présentation

Navigateurs pris en charge

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

Source

L'élément <button> est conçu pour l'interaction utilisateur. Ses déclencheurs d'événements click sont issus du clavier, de la souris, du tactile, de la voix, etc., avec des règles intelligentes sur le timing. Il est également fourni avec des styles par défaut dans chaque navigateur, ce qui vous permet de les utiliser directement sans aucune personnalisation. Utilisez color-scheme pour activer les boutons clairs et sombres fournis par le navigateur.

Il existe également différents types de boutons, chacun étant illustré dans l'intégration Codepen précédente. Un <button> sans type s'adaptera à un <form> et deviendra le type d'envoi.

<!-- 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">

Dans le défi d'interface utilisateur de ce mois, chaque bouton recevra des styles pour aider à différencier visuellement leur intention. Les boutons de réinitialisation sont associés à des couleurs d'avertissement, car ils sont destructeurs. Les boutons de validation sont associés à un texte d'accentuation bleu, ce qui les rend légèrement plus visibles que les boutons standards.

Aperçu de l&#39;ensemble final de tous les types de boutons, affichés dans un formulaire et en dehors d&#39;un formulaire, avec de belles additions pour les boutons avec icône et les boutons personnalisés.
Aperçu de l'ensemble final de tous les types de boutons, affichés dans un formulaire et en dehors d'un formulaire, avec de belles additions pour les boutons avec icône et les boutons personnalisés

Les boutons disposent également de pseudo-classes que le CSS peut utiliser pour le style. Ces classes fournissent des crochets CSS pour personnaliser la sensation du bouton: :hover pour lorsque la souris est sur le bouton, :active pour lorsque la souris ou le clavier est enfoncé, et :focus ou :focus-visible pour faciliter le style des technologies d'assistance.

button:hover {}
button:active {}
button:focus {}
button:focus-visible {}
Aperçu de l&#39;ensemble final de tous les types de boutons dans le thème sombre.
Aperçu de l'ensemble final de tous les types de boutons dans le thème sombre

Annoter

En plus des types de boutons fournis par la spécification HTML, j'ai ajouté un bouton avec une icône et un bouton avec une classe personnalisée 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">

Ensuite, pour les tests, chaque bouton est placé dans un formulaire. De cette façon, je peux m'assurer que les styles sont mis à jour de manière appropriée pour le bouton par défaut, qui se comporte comme un bouton de soumission. Je change également la stratégie d'icône, du SVG intégré au SVG masqué, pour m'assurer que les deux fonctionnent de manière égale.

<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>

La matrice des combinaisons est assez écrasante à ce stade. Entre les types de boutons, les pseudo-classes et la présence ou l'absence de bouton dans un formulaire, il existe plus de 20 combinaisons de boutons. Heureusement, le CSS peut nous aider à articuler chacun d'eux clairement.

Accessibilité

Les éléments de bouton sont naturellement accessibles, mais il existe quelques améliorations courantes.

Pointer et mettre au point ensemble

J'aime regrouper :hover et :focus avec le pseudo-sélecteur fonctionnel :is(). Cela permet de m'assurer que mes interfaces prennent toujours en compte les styles de clavier et d'assistance technologique.

button:is(:hover, :focus) {
  
}
Essayez une démo !

Anneau de mise au point interactif

J'aime animer le cercle de sélection pour les utilisateurs de clavier et de technologies d'assistance. Pour ce faire, j'anime le contour en le déplaçant de 5 pixels du bouton, mais uniquement lorsque le bouton n'est pas actif. Cela crée un effet qui fait que l'anneau de mise au point se réduit à la taille du bouton lorsqu'il est enfoncé.

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

S'assurer que le contraste des couleurs est satisfaisant

Il existe au moins quatre combinaisons de couleurs différentes entre les couleurs claires et sombres qui doivent être prises en compte pour le contraste des couleurs: bouton, bouton de validation, bouton de réinitialisation et bouton désactivé. VisBug est utilisé ici pour inspecter et afficher tous ses scores en même temps:

Masquer les icônes des personnes qui ne peuvent pas les voir

Lorsque vous créez un bouton avec icône, l'icône doit renforcer visuellement le texte du bouton. Cela signifie également que l'icône n'est pas utile pour une personne ayant une perte de vision. Heureusement, le navigateur permet de masquer des éléments à la technologie de lecteur d'écran afin que les personnes ayant une déficience visuelle ne soient pas gênées par les images de boutons décoratives:

<button>
  <svg … aria-hidden="true">...</svg>
  Icon Button
</button>
Outils pour les développeurs Chrome affichant l&#39;arborescence d&#39;accessibilité du bouton. L&#39;arborescence ignore l&#39;image du bouton, car la valeur aria-hidden est définie sur &quot;true&quot;.
Les outils pour les développeurs Chrome affichant l'arborescence d'accessibilité du bouton. L'arborescence ignore l'image du bouton, car l'attribut aria-hidden est défini sur "true".

Styles

Dans la section suivante, je vais d'abord établir un système de propriétés personnalisées pour gérer les styles adaptatifs du bouton. Avec ces propriétés personnalisées, je peux commencer à sélectionner des éléments et à personnaliser leur apparence.

Une stratégie de propriété personnalisée adaptative

La stratégie de propriété personnalisée utilisée dans ce défi d'IUG est très semblable à celle utilisée pour créer un jeu de couleurs. Pour un système de couleurs clair et sombre adaptatif, une propriété personnalisée pour chaque thème est définie et nommée en conséquence. Une seule propriété personnalisée est ensuite utilisée pour contenir la valeur actuelle du thème et est attribuée à une propriété CSS. Par la suite, vous pouvez remplacer la valeur de la propriété personnalisée unique par une autre, puis mettre à jour le style du bouton.

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

J'apprécie que les thèmes clair et sombre soient déclaratifs et clairs. L'indirection et l'abstraction sont transférées vers la propriété personnalisée --_bg, qui est désormais la seule propriété "réactive". --_bg-light et --_bg-dark sont statiques. Il est également clair que le thème clair est le thème par défaut et que le thème sombre n'est appliqué que de manière conditionnelle.

Préparer la cohérence de la conception

Sélecteur de partage

Le sélecteur suivant permet de cibler tous les différents types de boutons. Il peut sembler un peu intimidant au premier abord. :where() est utilisé, de sorte que la personnalisation du bouton ne nécessite aucune spécificité. Les boutons sont souvent adaptés à d'autres scénarios, et le sélecteur :where() garantit que la tâche est facile. Dans :where(), chaque type de bouton est sélectionné, y compris ::file-selector-button, qui ne peut pas être utilisé dans :is() ou :where().

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

Toutes les propriétés personnalisées seront définies dans ce sélecteur. Il est temps d'examiner toutes les propriétés personnalisées. De nombreuses propriétés personnalisées sont utilisées dans ce bouton. Je vais décrire chaque groupe au fur et à mesure, puis partager les contextes sombres et à mouvement réduit à la fin de la section.

Couleur d'accentuation du bouton

Les boutons et icônes de soumission sont un excellent endroit pour ajouter une touche de couleur:

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

Couleur du texte du bouton

Les couleurs du texte des boutons ne sont pas blanches ni noires. Il s'agit de versions plus sombres ou plus claires de --_accent à l'aide de hsl() et en respectant la teinte 210:

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

Couleur de l'arrière-plan du bouton

Les arrière-plans des boutons suivent le même modèle hsl(), à l'exception des boutons du thème clair. Ceux-ci sont définis sur blanc afin que leur surface les fasse apparaître près de l'utilisateur ou devant d'autres surfaces:

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

Puits d'arrière-plan du bouton

Cette couleur d'arrière-plan permet d'afficher une surface derrière d'autres surfaces. Elle est utile pour l'arrière-plan de la saisie de fichier:

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

Marge intérieure des boutons

L'espacement autour du texte du bouton est effectué à l'aide de l'unité ch, une longueur relative à la taille de la police. Cela devient essentiel lorsque les boutons de grande taille peuvent simplement augmenter les échelles font-size et des boutons de manière proportionnelle:

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

Bordure du bouton

Le rayon de la bordure du bouton est stocké dans une propriété personnalisée afin que la saisie de fichier puisse correspondre aux autres boutons. Les couleurs de bordure suivent le système de couleurs adaptatif établi:

--_border-radius: .5ch;

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

Effet de surlignage du bouton au survol

Ces propriétés établissent une propriété de taille pour la transition lors de l'interaction, et la couleur de surlignage suit le système de couleurs adaptatif. Nous verrons plus tard dans cet article comment ces éléments interagissent, mais en fin de compte, ils sont utilisés pour un effet box-shadow:

--_highlight-size: 0;

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

Ombre du texte du bouton

Chaque bouton est associé à un style d'ombre de texte subtil. Cela permet au texte de se placer au-dessus du bouton, ce qui améliore la lisibilité et ajoute une belle couche de présentation soignée.

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

Icône de bouton

Les icônes ont la taille de deux caractères grâce à l'unité de longueur relative ch, ce qui permet de les redimensionner proportionnellement au texte du bouton. La couleur de l'icône s'appuie sur --_accent-color pour une couleur adaptative et dans le thème.

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

Ombre du bouton

Pour que les ombres s'adaptent correctement à la lumière et à l'obscurité, elles doivent modifier à la fois leur couleur et leur opacité. Les ombres du thème clair sont optimales lorsqu'elles sont subtiles et teintées vers la couleur de la surface qu'elles superposent. Les ombres du thème sombre doivent être plus sombres et plus saturées afin de pouvoir se superposer aux couleurs de surface plus sombres.

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

Grâce aux couleurs et aux intensités adaptatives, je peux assembler deux profondeurs d'ombres:

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

De plus, pour donner aux boutons une apparence légèrement 3D, une ombre de boîte 1px crée l'illusion:

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

Transitions de boutons

En suivant le modèle des couleurs adaptatives, je crée deux propriétés statiques pour contenir les options du système de conception:

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

Toutes les propriétés ensemble dans le sélecteur

Toutes les propriétés personnalisées d'un sélecteur

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

Les boutons par défaut s&#39;affichent côte à côte dans les thèmes clair et sombre.

Adaptations du thème sombre

La valeur du modèle d'accessoires statiques -light et -dark devient claire lorsque les accessoires du thème sombre sont définis:

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

Non seulement la lecture est fluide, mais les utilisateurs de ces boutons personnalisés peuvent utiliser les accessoires bruts en toute confiance, car ils s'adapteront de manière appropriée aux préférences des utilisateurs.

Adaptations de mouvement réduites

Si l'utilisateur invité accepte le mouvement, attribuez --_transition à var(--_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);
  }
}

Quelques styles partagés

Les polices des boutons et des entrées doivent être définies sur inherit pour qu'elles correspondent au reste des polices de la page. Sinon, elles seront stylisées par le navigateur. Cela s'applique également à letter-spacing. Définir line-height sur 1.5 définit la taille de la zone de texte pour laisser un espace au-dessus et en dessous du texte:

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

Capture d&#39;écran montrant les boutons après l&#39;application des styles précédents.

Mettre en forme des boutons

Ajustement du sélecteur

Le sélecteur input[type="file"] ne correspond pas à la partie du bouton de la saisie. C'est le pseudo-élément ::file-selector-button qui le fait. J'ai donc supprimé input[type="file"] de la liste:

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

Ajustements du curseur et de la saisie tactile

Je commence par définir le style du curseur sur le style pointer, ce qui permet au bouton d'indiquer aux utilisateurs de la souris qu'il est interactif. J'ajoute ensuite touch-action: manipulation pour que les clics n'aient pas besoin d'attendre et d'observer un double-clic potentiel, ce qui rend les boutons plus rapides:

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

Couleurs et bordures

Je personnalise ensuite la taille de la police, l'arrière-plan, le texte et les couleurs de la bordure à l'aide de certaines des propriétés personnalisées adaptatives définies précédemment:

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

Capture d&#39;écran montrant les boutons après l&#39;application des styles précédents.

Ombres

Des techniques très intéressantes ont été appliquées aux boutons. text-shadow s'adapte à la lumière et à l'obscurité, ce qui donne au texte du bouton une apparence subtile et agréable sur l'arrière-plan. Pour box-shadow, trois ombres sont attribuées. La première, --_shadow-2, est une ombre portée de boîte standard. La deuxième ombre est un trompe-l'œil qui donne l'impression que le bouton est légèrement biseauté vers le haut. La dernière ombre est destinée à l'accentuation en survol. Elle a initialement une taille de 0, mais elle en recevra une plus tard et sera mise en transition pour qu'elle semble croître à partir du bouton.

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

Capture d&#39;écran montrant les boutons après l&#39;application des styles précédents.

Mise en page

J'ai attribué au bouton une mise en page flexbox, plus précisément une mise en page inline-flex qui s'adapte à son contenu. Je centre ensuite le texte, puis j'aligne verticalement et horizontalement les enfants au centre. Cela permet d'aligner correctement les icônes et les autres éléments de bouton.

: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;
}

Capture d&#39;écran montrant les boutons après l&#39;application des styles précédents.

Espacement

Pour l'espacement des boutons, j'ai utilisé gap pour éviter que les frères et sœurs ne se touchent et des propriétés logiques pour la marge intérieure afin que l'espacement des boutons fonctionne pour toutes les mises en page de texte.

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

Capture d&#39;écran montrant les boutons après l&#39;application des styles précédents.

Expérience utilisateur avec les commandes tactiles et la souris

La section suivante s'adresse principalement aux utilisateurs d'appareils mobiles à écran tactile. La première propriété, user-select, est destinée à tous les utilisateurs. Elle empêche le texte de mettre en surbrillance le texte du bouton. Cela est particulièrement visible sur les appareils tactiles lorsqu'un bouton est enfoncé de manière prolongée et que le système d'exploitation met en surbrillance le texte du bouton.

En règle générale, ce n'est pas l'expérience utilisateur avec les boutons dans les applications intégrées. Je le désactive donc en définissant user-select sur "none". Les couleurs de surbrillance des boutons tactiles (-webkit-tap-highlight-color) et les menus contextuels de l'OS (-webkit-touch-callout) sont d'autres fonctionnalités de bouton très axées sur le Web qui ne correspondent pas aux attentes générales des utilisateurs concernant les boutons. Je les supprime donc également.

: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;
}

Transitions

La variable --_transition adaptative est attribuée à la propriété transition:

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

  transition: var(--_transition);
}

Lorsque l'utilisateur pointe sur le bouton, sans appuyer dessus, ajustez la taille de l'ombre pour lui donner un aspect de mise au point qui semble croître depuis le bouton:

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

Lorsque l'utilisateur est en mode "Focus", augmentez le décalage du contour de sélection par rapport au bouton, ce qui lui donne une belle apparence de sélection qui semble croître à partir du bouton:

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

Icônes

Pour gérer les icônes, le sélecteur ajoute un sélecteur :where() pour les enfants SVG directs ou les éléments avec l'attribut personnalisé data-icon. La taille de l'icône est définie avec la propriété personnalisée à l'aide de propriétés logiques en ligne et en bloc. La couleur du trait est définie, ainsi qu'un drop-shadow correspondant à text-shadow. flex-shrink est défini sur 0, de sorte que l'icône ne soit jamais écrasée. Enfin, je sélectionne les icônes avec des traits et j'attribue ces styles ici avec les extrémités et les raccordements de ligne fill: none et round:

: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;
}

Capture d&#39;écran montrant les boutons après l&#39;application des styles précédents.

Personnaliser les boutons d'envoi

Je voulais que les boutons d'envoi aient une apparence légèrement mise en avant. Pour ce faire, j'ai défini la couleur du texte des boutons sur la couleur d'accentuation:

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

Capture d&#39;écran montrant les boutons après l&#39;application des styles précédents.

Personnaliser les boutons de réinitialisation

Je voulais que les boutons de réinitialisation comportent des avertissements intégrés pour alerter les utilisateurs de leur comportement potentiellement destructeur. J'ai également choisi de styliser le bouton du thème clair avec plus d'accents rouges que le thème sombre. La personnalisation se fait en modifiant la couleur sous-jacente claire ou sombre appropriée, et le bouton met à jour le style:

: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%);
}

Je pense également que la couleur du contour de mise au point devrait correspondre à l'accentuation rouge. La couleur du texte passe d'un rouge foncé à un rouge clair. Je fais correspondre la couleur du contour à celle du mot clé currentColor:

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

Capture d&#39;écran montrant les boutons après l&#39;application des styles précédents.

Personnaliser les boutons désactivés

Il est très courant que les boutons désactivés présentent un contraste de couleur faible lors de la tentative de les atténuer pour qu'ils paraissent moins actifs. J'ai testé chaque ensemble de couleurs et m'assurer qu'ils ont réussi, en ajustant la valeur de luminosité HSL jusqu'à ce que le score soit accepté dans DevTools ou VisBug.

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

Capture d&#39;écran montrant les boutons après l&#39;application des styles précédents.

Personnaliser les boutons de saisie de fichiers

Le bouton d'importation de fichier est un conteneur d'une span et d'un bouton. Le CSS peut styliser un peu le conteneur d'entrée, ainsi que le bouton imbriqué, mais pas la span. Le conteneur est défini sur max-inline-size afin qu'il ne devienne pas plus grand que nécessaire, tandis que inline-size: 100% lui permet de se réduire et de s'adapter aux conteneurs plus petits que lui. La couleur d'arrière-plan est définie sur une couleur adaptative plus sombre que les autres surfaces, de sorte qu'elle se trouve derrière le bouton de sélecteur de fichiers.

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

Le bouton de sélecteur de fichiers et les boutons de type de saisie sont spécifiquement définis sur appearance: none pour supprimer tous les styles fournis par le navigateur qui n'ont pas été écrasés par les autres styles de boutons.

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

Enfin, une marge est ajoutée à l'inline-end du bouton pour éloigner le texte span du bouton, créant ainsi un espace.

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

Capture d&#39;écran montrant les boutons après l&#39;application des styles précédents.

Exceptions spéciales pour le thème sombre

J'ai donné aux boutons d'action principaux un arrière-plan plus sombre pour un contraste plus élevé du texte, ce qui leur donne un aspect légèrement plus mis en avant.

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

Capture d&#39;écran montrant les boutons après l&#39;application des styles précédents.

Créer des variantes

Pour le plaisir et parce que c'est pratique, j'ai choisi de montrer comment créer quelques variantes. L'une des variantes est très vive, comme le sont souvent les boutons principaux. Une autre variante est volumineuse. La dernière variante comporte une icône remplie de dégradé.

Bouton lumineux

Pour obtenir ce style de bouton, j'ai écrasé les propriétés de base directement avec des couleurs bleues. Bien que cette méthode soit rapide et facile, elle supprime les accessoires adaptatifs et présente le même aspect dans les thèmes clair et sombre.

.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%);
}

Le bouton personnalisé s&#39;affiche en clair et en foncé. Il est bleu très vif, comme le sont généralement les boutons d&#39;action principaux.

Bouton de grande taille

Pour obtenir ce style de bouton, modifiez la propriété personnalisée --_size. Le rembourrage et les autres éléments d'espace sont relatifs à cette taille, et sont mis à l'échelle proportionnellement à la nouvelle taille.

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

Le bouton grand est affiché à côté du bouton personnalisé, environ 150 fois plus grand.

Bouton avec icône

Cet effet d'icône n'a rien à voir avec nos styles de boutons, mais il montre comment l'obtenir avec seulement quelques propriétés CSS et comment le bouton gère les icônes qui ne sont pas en SVG intégré.

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

Un bouton avec une icône s&#39;affiche dans les thèmes clair et sombre.

Conclusion

Maintenant que vous savez comment j'ai fait, comment procéderiez-vous ? 🙂

Diversifions nos approches et découvrons toutes les façons de créer sur le Web.

Créez une démo, tweetez-moi des liens et je les ajouterai à la section "Remix de la communauté" ci-dessous.

Remix de la communauté

Aucun élément à afficher pour le moment.

Ressources