Présentation de base de la création de composants <button> adaptatifs aux couleurs, responsifs et accessibles.
Dans cet article, je souhaite partager mes réflexions sur la façon de créer un élément <button> adaptatif aux couleurs, responsif et accessible.
Essayer la démo et afficher la source
Si vous préférez les vidéos, voici une version YouTube de cet article :
Présentation
L'élément <button> est conçu pour l'interaction utilisateur. Son événement click se déclenche à partir du clavier, de la souris, du toucher, de la voix, etc., avec des règles intelligentes concernant son 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 étant présenté dans l'intégration Codepen précédente. Un <button> sans type s'adaptera à un <form> et deviendra un 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-ci, chaque bouton recevra des styles pour aider à différencier visuellement son intention. Les boutons de réinitialisation auront des couleurs d'avertissement, car ils sont destructifs, et les boutons d'envoi auront un texte bleu accentué pour qu'ils soient légèrement plus mis en avant que les boutons normaux.
Les boutons disposent également de pseudo-classes que le CSS peut utiliser pour la mise en forme. Ces classes fournissent des hooks CSS pour personnaliser l'apparence du bouton : :hover lorsque la souris pointe sur le bouton, :active lorsqu'un clic de souris ou une pression sur le clavier est effectué, et :focus ou :focus-visible pour faciliter la mise en forme des technologies d'assistance.
button:hover {}
button:active {}
button:focus {}
button:focus-visible {}
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. Cela me permet de m'assurer que les styles sont correctement mis à jour pour le bouton par défaut, qui se comporte comme un bouton d'envoi. Je modifie également la stratégie d'icônes, en passant du SVG intégré à un SVG masqué, pour m'assurer que les deux fonctionnent aussi bien.
<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 de combinaisons est assez impressionnante à ce stade. Entre les types de boutons, les pseudo-classes et le fait d'être dans un formulaire ou non, il existe plus de 20 combinaisons de boutons. Heureusement que les CSS peuvent nous aider à les exprimer clairement !
Accessibilité
Les éléments de bouton sont naturellement accessibles, mais il existe quelques améliorations courantes.
Survoler et sélectionner
J'aimerais regrouper :hover et :focus avec le pseudo-sélecteur fonctionnel :is(). Cela permet de garantir que mes interfaces tiennent toujours compte des styles de clavier et de technologie d'assistance.
button:is(:hover, :focus) {
…
}
Bague de mise au point interactive
J'aime animer l'anneau de sélection pour les utilisateurs du clavier et des technologies d'assistance. Pour ce faire, j'anime le contour en l'éloignant du bouton de 5 px, mais uniquement lorsque le bouton n'est pas actif. Cela crée un effet qui fait revenir l'anneau de sélection à la taille du bouton lorsqu'il est enfoncé.
:where(button, input):where(:not(:active)):focus-visible {
outline-offset: 5px;
}
Garantir un contraste des couleurs suffisant
Il existe au moins quatre combinaisons de couleurs différentes (claires et sombres) pour lesquelles le contraste des couleurs doit être pris en compte : bouton, bouton d'envoi, bouton de réinitialisation et bouton désactivé. VisBug est utilisé ici pour inspecter et afficher tous les scores à la fois :
Masquer les icônes pour les personnes qui ne peuvent pas les voir
Lorsque vous créez un bouton d'icône, l'icône doit fournir un support visuel au texte du bouton. Cela signifie également que l'icône n'a aucune valeur pour une personne souffrant d'une perte de vision. Heureusement, le navigateur permet de masquer les éléments de la technologie de lecteur d'écran afin que les personnes malvoyantes ne soient pas dérangées par les images décoratives des boutons :
<button>
<svg … aria-hidden="true">...</svg>
Icon Button
</button>
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. Grâce à ces propriétés personnalisées, je peux commencer à sélectionner des éléments et à personnaliser leur apparence.
Une stratégie de propriétés personnalisées adaptative
La stratégie de propriété personnalisée utilisée dans ce défi d'interface utilisateur est très semblable à celle utilisée pour créer un jeu de couleurs. Pour un système de couleurs adaptatif clair et sombre, une propriété personnalisée est définie pour chaque thème 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, la propriété personnalisée unique peut être mise à jour avec une autre valeur, puis le style du bouton est mis à jour.
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 le fait 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.
Préparer la cohérence de la conception
Sélecteur partagé
Le sélecteur suivant est utilisé pour cibler tous les types de boutons. Il peut sembler un peu complexe au premier abord. :where() est utilisé pour 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() permet de faciliter la tâche. Dans :where(), chaque type de bouton est sélectionné, y compris ::file-selector-button, qui ne peut pas être utilisé dans :is() ni :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 incluses dans ce sélecteur. Il est temps d'examiner toutes les propriétés personnalisées. Ce bouton utilise de nombreuses propriétés personnalisées. Je vais décrire chaque groupe au fur et à mesure, puis partager les contextes de faible luminosité et de réduction du mouvement à la fin de la section.
Couleur d'accentuation du bouton
Les boutons d'envoi et les icônes sont un excellent moyen d'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, mais des versions plus sombres ou plus claires de --_accent utilisant hsl() et 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, qui 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);
Arrière-plan du bouton
Cette couleur d'arrière-plan permet de faire apparaître une surface derrière d'autres surfaces. Elle est utile pour l'arrière-plan de l'entrée 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 de grands boutons peuvent simplement augmenter le font-size et que les boutons sont mis à l'échelle de manière proportionnelle :
--_padding-inline: 1.75ch;
--_padding-block: .75ch;
Bordure du bouton
Le rayon de bordure du bouton est stocké dans une propriété personnalisée afin que l'entrée de fichier puisse correspondre aux autres boutons. Les couleurs de bordure suivent le système de couleurs adaptatives établi :
--_border-radius: .5ch;
--_border-light: hsl(210 14% 89%);
--_border-dark: var(--_bg-dark);
--_border: var(--_border-light);
Effet de surbrillance au survol des boutons
Ces propriétés établissent une propriété de taille pour la transition lors de l'interaction, et la couleur de mise en surbrillance suit le système de couleurs adaptatif. Nous verrons comment ces éléments interagissent plus loin dans cet article, mais 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 présente un style d'ombre de texte subtil. Cela permet au texte de se trouver au-dessus du bouton, ce qui améliore la lisibilité et ajoute une touche 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 à l'icône de s'adapter proportionnellement au texte du bouton. La couleur de l'icône s'appuie sur --_accent-color pour une couleur adaptative et conforme au thème.
--_icon-size: 2ch;
--_icon-color: var(--_accent);
Ombre du bouton
Pour que les ombres s'adaptent correctement aux modes clair et sombre, elles doivent modifier à la fois leur couleur et leur opacité. Les ombres du thème clair sont plus efficaces lorsqu'elles sont subtiles et teintées en fonction de la couleur de la surface qu'elles recouvrent. Les ombres du thème sombre doivent être plus sombres et plus saturées pour 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);
Avec les couleurs et les 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 1px box-shadow
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 bouton
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); }

Adaptations du thème sombre
La valeur du modèle de propriétés statiques -light et -dark devient claire lorsque les propriétés du thème sombre sont définies :
@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 cela se lit bien, mais les consommateurs de ces boutons personnalisés peuvent utiliser les propriétés nues en toute confiance, car ils s'adapteront de manière appropriée aux préférences de l'utilisateur.
Adaptations pour les mouvements réduits
Si le mouvement est autorisé pour cet utilisateur invité, 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 boutons et les champs de saisie doivent avoir une police définie sur inherit pour correspondre au reste des polices de la page. Sinon, ils seront stylisés par le navigateur. Cela s'applique également à letter-spacing. Définir line-height sur 1.5 définit la taille de la boîte aux lettres pour donner de l'espace au texte au-dessus et en dessous :
: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);
}

Mise en forme des boutons
Ajustement du sélecteur
Le sélecteur input[type="file"] n'est pas la partie bouton de l'entrée, mais le 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 du toucher
Je commence par définir le style du curseur sur pointer, ce qui aide le bouton à indiquer aux utilisateurs de la souris qu'il est interactif. J'ajoute ensuite touch-action: manipulation pour que les clics n'aient pas à attendre et à observer un éventuel double-clic, 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
Ensuite, je personnalise la taille de la police, ainsi que les couleurs de l'arrière-plan, du texte et de la bordure, en utilisant 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);
}

Ombres
De superbes techniques ont été appliquées aux boutons. L'text-shadow s'adapte aux thèmes clairs et sombres, ce qui permet au texte du bouton de s'afficher de manière 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 classique.
La deuxième ombre est une illusion d'optique qui donne l'impression que le bouton est légèrement biseauté. La dernière ombre est celle de la mise en surbrillance au survol, initialement de taille 0. Elle recevra une taille ultérieurement et sera transitionnée pour donner l'impression de grandir à 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);
}

Disposition
J'ai attribué au bouton une mise en page flexbox, plus précisément une mise en page inline-flex qui s'adaptera à son contenu. Je centre ensuite le texte et aligne les enfants verticalement et horizontalement sur le centre. Cela permettra aux icônes et aux autres éléments de bouton de 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;
}

Espacement
Pour l'espacement des boutons, j'ai utilisé gap pour éviter que les éléments frères ne se touchent et les 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);
}

Expérience utilisateur tactile et à la souris
La section suivante s'adresse principalement aux utilisateurs d'appareils mobiles avec écran tactile. La première propriété, user-select, s'applique à tous les utilisateurs. Elle empêche la mise en surbrillance du texte du bouton. Cela est surtout visible sur les appareils tactiles lorsqu'un bouton est appuyé de manière prolongée et que le système d'exploitation met en surbrillance le texte du bouton.
J'ai généralement constaté que ce n'était pas l'expérience utilisateur avec les boutons dans les applications intégrées. Je la désactive donc en définissant user-select sur "none". 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 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 agréable qui semble se développer à l'intérieur du bouton :
:where(
button,
input[type="button"],
input[type="submit"],
input[type="reset"]
):where(:not(:active):hover) {
--_highlight-size: .5rem;
}
Lors de la sélection, 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 se développer à partir de l'intérieur du bouton :
:where(button, input):where(:not(:active)):focus-visible {
outline-offset: 5px;
}
Icônes
Pour gérer les icônes, le sélecteur comporte un sélecteur :where() supplémentaire 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 des propriétés logiques en ligne et de bloc. La couleur du trait est définie, ainsi qu'un drop-shadow pour correspondre à text-shadow. flex-shrink est défini sur 0, de sorte que l'icône n'est jamais écrasée. Enfin, je sélectionne des icônes linéaires et j'attribue ces styles ici avec des extrémités et des jointures 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;
}

Personnaliser les boutons d'envoi
Je voulais que les boutons d'envoi soient légèrement mis 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);
}

Personnaliser les boutons de réinitialisation
Je voulais que les boutons de réinitialisation comportent des signaux d'avertissement 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 celui du thème sombre. La personnalisation s'effectue en modifiant la couleur claire ou foncée sous-jacente 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%);
}
J'ai également pensé qu'il serait agréable que la couleur du contour de sélection corresponde à l'accent rouge. La couleur du texte passe du rouge foncé au rouge clair. Je fais correspondre la couleur du contour à celle-ci avec le mot clé currentColor :
:where([type="reset"]):focus-visible {
outline-color: currentColor;
}

Personnaliser les boutons désactivés
Il est trop fréquent que les boutons désactivés présentent un mauvais contraste de couleurs lorsqu'on tente de les atténuer pour qu'ils paraissent moins actifs. J'ai testé chaque ensemble de couleurs et me suis assuré qu'ils étaient conformes, en ajustant la valeur de luminosité HSL jusqu'à ce que le score soit conforme dans les outils de développement 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);
}

Personnaliser les boutons de saisie de fichier
Le bouton de saisie de fichier est un conteneur pour une étendue et un bouton. Le CSS peut styliser un peu le conteneur d'entrée, ainsi que le bouton imbriqué, mais pas la balise span. Le conteneur reçoit max-inline-size afin de ne pas dépasser la taille nécessaire, tandis que inline-size: 100% se réduit pour s'adapter aux conteneurs plus petits. La couleur d'arrière-plan est définie sur une couleur adaptative plus foncée que les autres surfaces, de sorte qu'elle se trouve derrière le bouton du sélecteur de fichiers.
:where(input[type="file"]) {
inline-size: 100%;
max-inline-size: max-content;
background-color: var(--_input-well);
}
Les boutons de sélection de fichier et de saisie sont spécifiquement définis sur appearance: none pour supprimer tous les styles fournis par le navigateur qui n'ont pas été remplacés par les autres styles de bouton.
:where(input[type="button"]),
:where(input[type="file"])::file-selector-button {
appearance: none;
}
Enfin, une marge est ajoutée au inline-end du bouton pour éloigner le texte de la span du bouton, créant ainsi un espace.
:where(input[type="file"])::file-selector-button {
margin-inline-end: var(--_padding-inline);
}

Exceptions spéciales pour le thème sombre
J'ai donné aux boutons d'action principaux un arrière-plan plus sombre pour un texte plus contrasté, ce qui leur donne une apparence légèrement plus mise en avant.
@media (prefers-color-scheme: dark) {
:where(
[type="submit"],
[type="reset"],
[disabled],
form button:not([type="button"])
) {
--_bg: var(--_input-well);
}
}

Créer des variantes
Pour le plaisir et parce que c'est pratique, j'ai choisi de vous montrer comment créer quelques variantes. Une variante est très vive, comme le sont souvent les boutons principaux. Une autre variante est de grande taille. La dernière variante comporte une icône remplie d'un dégradé.
Bouton dynamique
Pour obtenir ce style de bouton, j'ai directement remplacé les props de base par des couleurs bleues. Bien que cette méthode soit rapide et facile, elle supprime les accessoires adaptatifs et le rendu est le même 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%);
}

Gros bouton
Ce style de bouton est obtenu en modifiant la propriété personnalisée --_size.
La marge intérieure 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;
}

Bouton d'icône
Cet effet d'icône n'a rien à voir avec nos styles de boutons, mais il montre comment l'obtenir avec quelques propriétés CSS et comment le bouton gère les icônes qui ne sont pas au format 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));
}
![]()
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, tweetez-moi les liens et je l'ajouterai à la section des remix de la communauté ci-dessous !
Remix de la communauté
Aucun élément à afficher pour le moment.
Ressources
- Code source sur GitHub