Una descripción general fundamental de cómo compilar componentes <button>
accesibles, responsivos y adaptables al color.
En esta publicación, quiero compartir mis ideas sobre cómo crear un elemento <button>
accesible, responsivo y adaptable al color.
Prueba la demostración y consulta la fuente.
Si prefieres ver un video, aquí tienes una versión de YouTube de esta publicación:
Descripción general
El elemento <button>
se diseñó para la interacción del usuario. Sus activadores de eventos click
se activan desde el teclado, el mouse, la función táctil, la voz y mucho más, con reglas inteligentes sobre los tiempos. También incluye algunos estilos predeterminados en cada navegador, por lo que puedes usarlos directamente sin ninguna personalización. Usa color-scheme
para habilitar los botones claros y oscuros que proporciona el navegador.
También hay diferentes tipos de botones, cada uno de los cuales se muestra en la incorporación de Codepen anterior. Un <button>
sin un tipo se adaptará a estar dentro de un <form>
y cambiará al tipo de envío.
<!-- 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">
En el Desafío de GUI de este mes, cada botón tendrá estilos para ayudar a diferenciar visualmente su intención. Los botones Restablecer tendrán colores de advertencia, ya que son destructivos, y los botones Enviar tendrán texto de acento azul para que aparezcan un poco más destacados que los botones normales.
Los botones también tienen pseudoclases que CSS puede usar para definir el diseño. Estas clases proporcionan hooks de CSS para personalizar la apariencia del botón: :hover
para cuando el mouse está sobre el botón, :active
para cuando se presiona el mouse o el teclado, y :focus
o :focus-visible
para ayudar en el diseño de la tecnología de accesibilidad.
button:hover {}
button:active {}
button:focus {}
button:focus-visible {}
Marca
Además de los tipos de botones que proporciona la especificación HTML, agregué un botón con un ícono y un botón con una clase personalizada 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">
Luego, para las pruebas, cada botón se coloca dentro de un formulario. De esta manera, puedo garantizar que los diseños se actualicen de forma adecuada para el botón predeterminado, que se comporta como un botón de enviar. También cambié la estrategia de íconos, de SVG intercalado a SVG enmascarado, para asegurarme de que ambos funcionen igual de 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 matriz de combinaciones es bastante abrumadora en este punto. Entre los tipos de botones, las pseudoclases y la ubicación dentro o fuera de un formulario, hay más de 20 combinaciones de botones. Es bueno que CSS pueda ayudarnos a articular cada uno de ellos con claridad.
Accesibilidad
Se puede acceder a los elementos de botón de forma natural, pero hay algunas mejoras comunes.
Coloca el cursor y enfoca juntos
Me gusta agrupar :hover
y :focus
junto con el seudoselector funcional :is()
. Esto ayuda a garantizar que mis interfaces siempre tengan en cuenta los estilos del teclado y la tecnología de accesibilidad.
button:is(:hover, :focus) {
…
}
Anillo de enfoque interactivo
Me gusta animar el anillo de enfoque para los usuarios de teclado y tecnología de accesibilidad. Para lograr esto, animé el contorno para que se aleje del botón 5 px, pero solo cuando el botón no está activo. Esto crea un efecto que hace que el anillo de enfoque se reduzca al tamaño del botón cuando se presiona.
:where(button, input):where(:not(:active)):focus-visible {
outline-offset: 5px;
}
Cómo garantizar que se apruebe el contraste de color
Hay al menos cuatro combinaciones de colores diferentes entre claro y oscuro que deben tener en cuenta el contraste de color: botón, botón de envío, botón de restablecimiento y botón inhabilitado. VisBug se usa aquí para inspeccionar y mostrar todas sus puntuaciones a la vez:
Ocultar iconos de las personas que no pueden ver
Cuando crees un botón de ícono, este debe brindar compatibilidad visual con el texto del botón. Esto también significa que el ícono no es valioso para una persona con pérdida de visión. Por suerte, el navegador proporciona una forma de ocultar elementos de la tecnología de lectores de pantalla para que las personas con pérdida de visión no se vean afectadas por las imágenes decorativas de los botones:
<button>
<svg … aria-hidden="true">...</svg>
Icon Button
</button>
Estilos
En la siguiente sección, primero estableceré un sistema de propiedades personalizadas para administrar los estilos adaptables del botón. Con esas propiedades personalizadas, puedo comenzar a seleccionar elementos y personalizar su aspecto.
Una estrategia de propiedad personalizada adaptativa
La estrategia de propiedades personalizadas que se usa en este desafío de la GUI es muy similar a la que se usa para compilar un esquema de colores. En el caso de un sistema adaptable de colores claros y oscuros, se define una propiedad personalizada para cada tema y se le asigna el nombre correspondiente. Luego, se usa una sola propiedad personalizada para contener el valor actual del tema y se asigna a una propiedad CSS. Más adelante, se puede actualizar la única propiedad personalizada a un valor diferente y, luego, actualizar el estilo del botón.
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);
}
}
Me gusta que los temas claro y oscuro sean declarativos y claros. La indirección y la abstracción se descargan en la propiedad personalizada --_bg
, que ahora es la única propiedad "reactiva"; --_bg-light
y --_bg-dark
son estáticos. También se indica claramente que el tema claro es el predeterminado y que el tema oscuro solo se aplica de forma condicional.
Preparación para la coherencia del diseño
El selector compartido
El siguiente selector se usa para apuntar a todos los diferentes tipos de botones y es un poco abrumador al principio. Se usa :where()
para que la personalización del botón no requiera especificidad. Los botones a menudo se adaptan a situaciones alternativas, y el selector :where()
garantiza que la tarea sea fácil. Dentro de :where()
, se selecciona cada tipo de botón, incluido el ::file-selector-button
, que no se puede usar dentro de :is()
ni :where()
.
:where(
button,
input[type="button"],
input[type="submit"],
input[type="reset"],
input[type="file"]
),
:where(input[type="file"])::file-selector-button {
…
}
Todas las propiedades personalizadas tendrán alcance dentro de este selector. Es hora de revisar todas las propiedades personalizadas. Hay bastantes propiedades personalizadas que se usan en este botón. Describiré cada grupo a medida que avanzamos y luego compartiré los contextos oscuro y de movimiento reducido al final de la sección.
Color de los elementos destacados del botón
Los botones y los íconos de envío son un excelente lugar para agregar un toque de color:
--_accent-light: hsl(210 100% 40%);
--_accent-dark: hsl(210 50% 70%);
--_accent: var(--_accent-light);
Color del texto del botón
Los colores de texto de los botones no son blancos ni negros, son versiones oscurecidas o aclaradas
de --_accent
con hsl()
y
se adhieren al tono 210
:
--_text-light: hsl(210 10% 30%);
--_text-dark: hsl(210 5% 95%);
--_text: var(--_text-light);
Color del botón de la parte inferior
Los fondos de los botones siguen el mismo patrón hsl()
, excepto los botones del tema claro, que se establecen en blanco para que su superficie los haga aparecer cerca del usuario o frente a otras superficies:
--_bg-light: hsl(0 0% 100%);
--_bg-dark: hsl(210 9% 31%);
--_bg: var(--_bg-light);
Bien el fondo del botón
Este color de fondo sirve para hacer que una superficie aparezca detrás de otras, lo que es útil para el fondo de la entrada de archivo:
--_input-well-light: hsl(210 16% 87%);
--_input-well-dark: hsl(204 10% 10%);
--_input-well: var(--_input-well-light);
Relleno del botón
El espaciado alrededor del texto en el botón se realiza con la unidad ch
, una longitud relativa al tamaño de la fuente. Esto es fundamental cuando los botones grandes pueden aumentar el font-size
y los botones se escalan de forma proporcional:
--_padding-inline: 1.75ch;
--_padding-block: .75ch;
Borde del botón
El radio del borde del botón se oculta en una propiedad personalizada para que la entrada de archivo pueda coincidir con los otros botones. Los colores de los bordes siguen el sistema de colores adaptativos establecido:
--_border-radius: .5ch;
--_border-light: hsl(210 14% 89%);
--_border-dark: var(--_bg-dark);
--_border: var(--_border-light);
Efecto de resaltado cuando se coloca el cursor sobre un botón
Estas propiedades establecen una propiedad de tamaño para la transición durante la interacción, y el color de resaltado sigue el sistema de colores adaptables. Más adelante en esta publicación, explicaremos cómo interactúan, pero, en última instancia, se usan para un efecto box-shadow
:
--_highlight-size: 0;
--_highlight-light: hsl(210 10% 71% / 25%);
--_highlight-dark: hsl(210 10% 5% / 25%);
--_highlight: var(--_highlight-light);
Sombra del texto del botón
Cada botón tiene un estilo de sombra de texto sutil. Esto ayuda a que el texto se ubique sobre el botón, lo que mejora la legibilidad y agrega una buena capa de perfeccionamiento de la presentación.
--_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);
Ícono de botón
Los íconos tienen el tamaño de dos caracteres gracias a la unidad ch
de longitud relativa, lo que ayudará a que el ícono se ajuste de forma proporcional al texto del botón. El color del ícono se basa en el --_accent-color
para brindar un color adaptable y dentro del tema.
--_icon-size: 2ch;
--_icon-color: var(--_accent);
Sombra del botón
Para que las sombras se adapten correctamente a la luz y la oscuridad, deben cambiar su color y opacidad. Las sombras del tema claro son mejores cuando son sutiles y se tiñen hacia el color de la superficie que se superponen. Las sombras del tema oscuro deben ser más oscuras y más saturadas para que puedan superponerse con colores de superficie más oscuros.
--_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);
Con colores y fortalezas adaptables, puedo ensamblar dos profundidades de sombras:
--_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%));
Además, para darles a los botones un aspecto ligeramente en 3D, una sombra de cuadro 1px
crea la ilusión:
--_shadow-depth-light: 0 1px var(--_border-light);
--_shadow-depth-dark: 0 1px var(--_bg-dark);
--_shadow-depth: var(--_shadow-depth-light);
Transiciones de botones
Siguiendo el patrón de colores adaptables, creo dos propiedades estáticas para contener las opciones del sistema de diseño:
--_transition-motion-reduce: ;
--_transition-motion-ok:
box-shadow 145ms ease,
outline-offset 145ms ease
;
--_transition: var(--_transition-motion-reduce);
Todas las propiedades juntas en el selector
: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); }
Adaptaciones del tema oscuro
El valor del patrón de elementos estáticos -light
y -dark
se aclara cuando se configuran los elementos del tema oscuro:
@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);
}
}
Esto no solo lee bien, sino que los consumidores de estos botones personalizados pueden usar los accesorios básicos con la confianza de que se adaptarán de manera adecuada a las preferencias del usuario.
Adaptaciones de movimiento reducidos
Si el movimiento es aceptable para este usuario visitante, asigna --_transition
a 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);
}
}
Algunos estilos compartidos
Las fuentes y los botones deben tener el valor inherit
para que coincidan con el resto de las fuentes de la página; de lo contrario, el navegador les aplicará un estilo. Esto también se aplica a letter-spacing
. Si estableces line-height
en 1.5
, se establece el tamaño del cuadro de letras para darle al texto un poco de espacio arriba y abajo:
: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);
}
Cómo aplicar diseño a los botones
Ajuste del selector
El selector input[type="file"]
no es la parte del botón de la entrada, el seudoelemento ::file-selector-button
sí, por lo que quité input[type="file"]
de la lista:
:where(
button,
input[type="button"],
input[type="submit"],
input[type="reset"],
input[type="file"]
),
:where(input[type="file"])::file-selector-button {
}
Ajustes táctiles y del cursor
Primero, cambio el estilo del cursor al estilo pointer
, que ayuda al botón a indicar a los usuarios del mouse que es interactivo. Luego, agrego touch-action: manipulation
para que los clics no deban esperar y observar un posible doble clic, lo que hace que los botones se sientan más rápidos:
:where(
button,
input[type="button"],
input[type="submit"],
input[type="reset"]
),
:where(input[type="file"])::file-selector-button {
cursor: pointer;
touch-action: manipulation;
}
Colores y bordes
A continuación, personalizo el tamaño de la fuente, el fondo, el texto y los colores del borde con algunas de las propiedades personalizadas adaptables establecidas anteriormente:
: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);
}
Sombras
Se aplican algunas técnicas excelentes a los botones. text-shadow
se adapta a la luz y la oscuridad, lo que crea una apariencia sutil y agradable del texto del botón que se ubica bien sobre el fondo. Para el box-shadow
, se asignan tres sombras. El primero, --_shadow-2
, es una sombra de cuadro normal.
La segunda sombra es un truco visual que hace que el botón parezca biselado un poco hacia arriba. La última sombra es para el resaltado de desplazamiento, que inicialmente tiene un tamaño de 0, pero se le asignará un tamaño más adelante y se le hará una transición para que parezca que crece desde el botón.
: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);
}
Diseño
Le di al botón un diseño flexbox, específicamente un diseño inline-flex
que se ajustará a su contenido. Luego, centro
el texto y alineo los elementos secundarios de forma vertical y horizontal en el
centro. Esto ayudará a que los íconos y otros elementos de botones se alineen correctamente.
: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;
}
Espaciado
Para el espaciado de botones, usé gap
para evitar que los elementos del mismo nivel toquen, y las propiedades lógicas para el padding, de modo que el espaciado de botones funcione para todos los diseños de texto.
:where(
button,
input[type="button"],
input[type="submit"],
input[type="reset"]
),
:where(input[type="file"])::file-selector-button {
…
gap: 1ch;
padding-block: var(--_padding-block);
padding-inline: var(--_padding-inline);
}
UX táctil y de mouse
La siguiente sección está destinada principalmente a los usuarios de dispositivos móviles con pantalla táctil. La primera
propiedad,
user-select
,
es para todos los usuarios y evita que el texto destaque el texto del botón. Esto se nota principalmente en dispositivos táctiles cuando se presiona y se mantiene presionado un botón, y el sistema operativo destaca el texto del botón.
En general, he descubierto que esta no es la experiencia del usuario con los botones en las apps integradas, por lo que lo inhabilito configurando user-select
como ninguno. Los colores de resaltado (-webkit-tap-highlight-color
) y los menús contextuales del sistema operativo (-webkit-touch-callout
) son otras funciones de botones muy centradas en la Web que no están alineadas con las expectativas generales de los usuarios de botones, por lo que también los quité.
: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;
}
Transiciones
La variable --_transition
adaptable se asigna a la propiedad transition:
:where(
button,
input[type="button"],
input[type="submit"],
input[type="reset"]
),
:where(input[type="file"])::file-selector-button {
…
transition: var(--_transition);
}
Cuando el usuario coloca el cursor sobre el elemento, mientras no lo presiona de forma activa, ajusta el tamaño del resaltado de sombra para darle un aspecto de enfoque agradable que parezca crecer desde el interior del botón:
:where(
button,
input[type="button"],
input[type="submit"],
input[type="reset"]
):where(:not(:active):hover) {
--_highlight-size: .5rem;
}
Cuando se enfoque, aumenta el desplazamiento del contorno de enfoque desde el botón y dale un aspecto de enfoque agradable que parezca crecer desde el interior del botón:
:where(button, input):where(:not(:active)):focus-visible {
outline-offset: 5px;
}
Íconos
Para controlar los íconos, el selector tiene un selector :where()
agregado para elementos secundarios SVG directos o elementos con el atributo personalizado data-icon
. El tamaño del ícono se establece con la propiedad personalizada mediante propiedades lógicas intercaladas y de bloque. Se establece el color del trazo, así como un drop-shadow
para que coincida con el text-shadow
. flex-shrink
se configura como 0
para que el ícono nunca se aplaste. Por último, selecciono los íconos con líneas y les asigno esos estilos aquí con las líneas de unión y los finales de línea fill: none
y 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;
}
Personaliza los botones de envío
Quería que los botones de envío tuvieran una apariencia ligeramente destacada, y lo logré mediante el color de texto de los botones como color de contraste:
:where(
[type="submit"],
form button:not([type],[disabled])
) {
--_text: var(--_accent);
}
Personaliza los botones de restablecimiento
Quería que los botones de restablecimiento tuvieran algunas señales de advertencia integradas para alertar a los usuarios sobre su comportamiento potencialmente destructivo. También decidí aplicar diseño al botón del tema claro con más acentos rojos que el tema oscuro. Para realizar la personalización, se cambia el color subyacente claro u oscuro adecuado, y el botón actualizará el diseño:
: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%);
}
También pensé que sería bueno que el color del contorno del enfoque coincidiera con el acento del rojo. El color del texto pasa de rojo oscuro a rojo claro. Hago que el color del contorno coincida con la palabra clave currentColor
:
:where([type="reset"]):focus-visible {
outline-color: currentColor;
}
Personalizar botones inhabilitados
Es muy común que los botones inhabilitados tengan un contraste de color deficiente cuando se intenta suavizar el botón inhabilitado para que parezca menos activo. Probé cada conjunto de colores y me aseguré de que aprobaran, ajustando el valor de luminosidad de HSL hasta que la puntuación se aprobó en DevTools o 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);
}
Cómo personalizar los botones de entrada de archivos
El botón de entrada de archivos es el contenedor de un intervalo y un botón. CSS puede darle un poco de estilo al contenedor de entrada, así como al botón anidado, pero no al intervalo. Se le asigna max-inline-size
al contenedor para que no crezca más de lo necesario, mientras que inline-size: 100%
se contraerá y se ajustará a contenedores más pequeños que él. El color de fondo se establece en un color adaptativo que es más oscuro que otras superficies, por lo que se ve detrás del botón del selector de archivos.
:where(input[type="file"]) {
inline-size: 100%;
max-inline-size: max-content;
background-color: var(--_input-well);
}
El botón del selector de archivos y los botones de tipo de entrada reciben específicamente appearance: none
para quitar cualquier diseño proporcionado por el navegador que no haya sido reemplazado por los otros estilos de botones.
:where(input[type="button"]),
:where(input[type="file"])::file-selector-button {
appearance: none;
}
Por último, se agrega el margen al inline-end
del botón para alejar el texto del intervalo del botón, lo que crea algo de espacio.
:where(input[type="file"])::file-selector-button {
margin-inline-end: var(--_padding-inline);
}
Excepciones especiales del tema oscuro
Les di a los botones de acción principales un fondo más oscuro para lograr un contraste de texto más alto, lo que les da una apariencia un poco más destacada.
@media (prefers-color-scheme: dark) {
:where(
[type="submit"],
[type="reset"],
[disabled],
form button:not([type="button"])
) {
--_bg: var(--_input-well);
}
}
Cómo crear variantes
Por diversión y porque es práctico, decidí mostrar cómo crear algunas variantes. Una variante es muy vibrante, similar a la apariencia que suelen tener los botones principales. Otra variante es grande. La última variante tiene un ícono relleno con gradiente.
Botón vibrante
Para lograr este estilo de botón, reemplacé los atributos base directamente con colores azules. Si bien esto fue rápido y sencillo, quita los elementos de diseño adaptables y se ve igual en los temas claro y oscuro.
.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%);
}
Botón grande
Este estilo de botón se logra modificando la propiedad personalizada --_size
.
El padding y otros elementos de espacio son relativos a este tamaño y se ajustan proporcionalmente con el nuevo tamaño.
.btn-large {
--_size: 1.5rem;
}
Botón de ícono
Este efecto de ícono no tiene nada que ver con nuestros estilos de botones, pero muestra cómo lograrlo con solo algunas propiedades de CSS y qué tan bien el botón maneja los íconos que no son SVG intercalados.
[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));
}
Conclusión
Ahora que sabes cómo lo hice, ¿cómo lo harías?‽ 🙂
Diversifiquemos nuestros enfoques y aprendamos todas las formas de compilar en la Web.
Crea una demo, twittea los vínculos y los agregaré a la sección de remixes de la comunidad a continuación.
Remixes de la comunidad
Aún no hay nada que ver.
Recursos
- Código fuente en GitHub