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 esta publicación en YouTube:
Descripción general
El elemento <button>
está diseñado 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 su tiempo. 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 de restablecimiento tendrán colores de advertencia, ya que son destructivos, y los botones de envío tendrán texto de acento azul para que aparezcan un poco más destacados que los botones normales.
Los botones también tienen pseudoclases para que CSS las use para aplicar estilos. 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 asegurarme de que los estilos se actualicen de forma adecuada para el botón predeterminado, que se comporta como un botón de envío. 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 pseudoselector 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. Aquí se usa VisBug para inspeccionar y mostrar todas sus puntuaciones a la vez:
Cómo ocultar íconos para las personas que no pueden verlos
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 establezco 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 apariencia.
Una estrategia de propiedad personalizada adaptativa
La estrategia de propiedades personalizadas que se usa en este desafío de GUI es muy similar a la que se usa para crear un esquema de colores. Para un sistema de colores claros y oscuros adaptables, se define una propiedad personalizada para cada tema y se le asigna un nombre según corresponda. 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 segmentar todos los tipos de botones y puede ser 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 ::file-selector-button
, que no se puede usar dentro de :is()
o :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 se incluirán en 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 avancemos y, luego, compartiré los contextos de movimiento oscuro y reducido al final de la sección.
Color de los elementos destacados del botón
Los íconos y botones 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 de 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);
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 de botones
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 se vuelve fundamental cuando los botones grandes pueden aumentar el font-size
y las escalas de botones de manera 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 del botón cuando se coloca el cursor sobre él
Estas propiedades establecen una propiedad de tamaño para la transición en la interacción, y el color de resaltado sigue el sistema de colores adaptable. Más adelante en esta publicación, hablaremos sobre 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 de longitud relativa ch
, que ayudará a que el ícono se ajuste proporcionalmente al texto del botón. El color del ícono se basa en --_accent-color
para obtener 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 tienen un tono similar al color de la superficie sobre la 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 se lee bien, sino que los consumidores de estos botones personalizados pueden usar los elementos sin procesar con la confianza de que se adaptarán de forma adecuada a las preferencias del usuario.
Adaptaciones de movimiento reducidas
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 de los botones y las entradas deben establecerse en inherit
para que coincidan con el resto de las fuentes de la página. De lo contrario, el navegador les aplicará un diseño. 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 pseudoelemento ::file-selector-button
es, 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 del cursor y del tacto
Primero, le doy estilo al cursor con el estilo pointer
, lo 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
Los botones tienen algunas técnicas excelentes aplicadas. 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 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 de flexbox, específicamente un diseño inline-flex
que se ajuste 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 los botones, usé gap
para evitar que los elementos hermanos se toquen y propiedades lógicas para el padding, de modo que el espaciado de los 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 los dispositivos táctiles cuando se presiona y 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 establece en 0
para que el ícono nunca se reduzca. 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é convirtiéndolos en el color de los botones de texto:
: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 personalizarlo, cambia el color subyacente claro o oscuro adecuado, y el botón actualizará el estilo:
: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 de enfoque coincida con el acento de
rojo. El color del texto pasa de rojo oscuro a rojo claro. Hago que el color del contorno coincida con esto con la palabra clave currentColor
:
:where([type="reset"]):focus-visible {
outline-color: currentColor;
}
Cómo personalizar los botones inhabilitados
Es muy común que los botones inhabilitados tengan un contraste de color deficiente durante el intento de atenuar 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 archivo es un contenedor para un elemento span 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. 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 se asignan específicamente a appearance: none
para quitar los estilos proporcionados por el navegador que no se reemplazaron por los otros estilos de botones.
:where(input[type="button"]),
:where(input[type="file"])::file-selector-button {
appearance: none;
}
Por último, se agrega un margen al inline-end
del botón para alejar el texto de span del botón y crear un 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 brillante
Para lograr este estilo de botón, reemplacé los elementos básicos directamente con colores azules. Si bien esto fue rápido y sencillo, quita los elementos adaptativos 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
Para lograr este estilo de botón, se debe modificar 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 lo bien que el botón controla 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 tú? 🙂
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