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. Essayez la démonstration et consultez 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 la 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, de l'écran 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 également les boutons clair et sombre fournis par le navigateur.

Il existe également différents types de boutons, chacun illustré dans l'intégration de 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. Étant donné qu'elles sont destructrices, les boutons de réinitialisation seront accompagnés d'une couleur d'avertissement. Les boutons d'envoi seront quant à eux accentués en bleu et paraîtront donc un peu plus mis en avant 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, à des fins de test, chaque bouton est placé dans un formulaire. Je peux ainsi 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 d'envoi. J'ai également changé la stratégie d'icône, en passant du SVG intégré à un SVG masqué, pour m'assurer que les deux fonctionnent de la même manière.

<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 réduit la bague de mise au point à la taille du bouton lorsque l'utilisateur appuie dessus.

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

Assurer un contraste des couleurs satisfaisant

Il existe au moins quatre combinaisons de couleurs différentes en clair et en foncé qui doivent tenir compte du contraste des couleurs: bouton, bouton d'envoi, bouton de réinitialisation et bouton désactivé. VisBug est utilisé ici pour inspecter et afficher tous leurs scores à la fois:

Masquer les icônes des personnes qui ne peuvent pas 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>
 
<svgaria-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 aria-hidden est défini sur "true" (vrai)

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.

Stratégie de propriétés 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 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 déchargées dans 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.

Se préparer à la cohérence de 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 les icônes d’envoi 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, ce qui 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 s'asseoir au-dessus du bouton, ce qui améliore la lisibilité et ajoute une belle couche de raffinement de la présentation.

--_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 de thème clair sont plus efficaces lorsqu'elles sont subtiles et teintées par rapport à 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 points forts qui s'adaptent, je peux assembler deux profondeurs d'ombre:

--_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 un aspect 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

: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'objet statique -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"] n'est pas la partie bouton de l'entrée, contrairement au pseudo-élément ::file-selector-button. 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 des commandes tactiles

Tout d'abord, j'applique le style du curseur au style pointer, ce qui aide le bouton à indiquer aux utilisateurs de souris qu'il est interactif. J'ajoute ensuite touch-action: manipulation pour que les clics n'aient pas à attendre et à 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, ainsi que les couleurs de l'arrière-plan, du texte et des bordures à l'aide de certaines des propriétés personnalisées adaptatives établies 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é, créant une apparence subtile et agréable du texte du bouton sur l'arrière-plan. Trois ombres sont attribuées à box-shadow. La première, --_shadow-2, est une ombre 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, en particulier une mise en page inline-flex adaptée à son contenu. Ensuite, je centre le texte, puis j'aligne verticalement et horizontalement les enfants sur le centre. Cela aidera les icônes et autres éléments de bouton à s'aligner correctement.

: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, s'applique à 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.

Je m'aperçois qu'il ne s'agit généralement pas de l'expérience utilisateur avec les boutons des applications intégrées. Je la désactive donc en définissant user-select sur "aucun". Les couleurs de mise en surbrillance (-webkit-tap-highlight-color) et les menus contextuels du système d'exploitation (-webkit-touch-callout) sont d'autres fonctionnalités de boutons très axées sur le Web qui ne correspondent pas aux attentes générales des utilisateurs de 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 élément drop-shadow correspondant à l'élément 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 aient un aspect légèrement mis en avant. Pour ce faire, j'ai choisi la couleur d'accentuation pour les boutons:

: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-ci avec le 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 jeu de couleurs et je me suis assuré qu'il répondait correctement, en encourageant la valeur de luminosité HSL jusqu'à ce que le score soit transmis 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 légèrement le conteneur d'entrée ainsi que le bouton imbriqué, mais pas le segment. 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 m'amuser 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 vif

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. Les marges intérieures et les autres éléments d'espace sont définis par rapport à cette taille, en s'adaptant de manière proportionnelle à 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 des SVG intégrés.

[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 feriez-vous ? 😃

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

Créez une démo, envoyez-moi des tweets via des liens et je l'ajouterai à la section "Remix de la communauté" ci-dessous.

Remix de la communauté

Aucun élément à afficher pour le moment.

Ressources