Uma visão geral básica de como criar componentes <button>
adaptáveis, responsivos e acessíveis a cores.
Nesta postagem, quero compartilhar minha opinião sobre como criar um elemento <button>
adaptável, responsivo e acessível.
Experimente a demonstração e veja a
fonte
Se preferir vídeo, aqui está uma versão do YouTube desta postagem:
Visão geral
O elemento
<button>
foi criado para interação do usuário. O evento click
é acionado por teclado,
mouse, toque, voz e muito mais, com regras inteligentes sobre o
tempo. Ele também vem com alguns estilos padrão em cada navegador, para que você possa usá-los diretamente, sem personalização. Use color-scheme
para ativar
os botões claros e escuros fornecidos pelo navegador.
Há também diferentes tipos de
botões,
cada um mostrado na incorporação anterior do Codepen. Um <button>
sem um tipo se adaptará a uma <form>
, mudando para o tipo de envio.
<!-- 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">
No desafio da GUI deste mês, cada botão terá estilos para ajudar a diferenciar visualmente a intent. Os botões de redefinição terão cores de aviso, por serem destrutivos, e os botões de envio vão ter um texto em destaque azul para que apareçam um pouco mais promovidos do que os botões comuns.
![Prévia do conjunto final de todos os tipos de botões, mostrado em um formulário e não em um formulário, com boas adições para botões de ícones e botões personalizados.](https://web.dev/static/articles/building/a-button-component/image/preview-the-final-set-a-f0edd66ff9463.png?authuser=5&hl=pt)
Os botões também têm pseudoclasses que o CSS pode usar para definir o estilo. Essas classes fornecem ganchos CSS para personalizar a
funcionalidade do botão: :hover
para quando um mouse está sobre o botão,
:active
para quando um mouse
ou teclado está pressionando e
:focus
ou
:focus-visible
para auxiliar no estilo de tecnologia adaptativa.
button:hover {}
button:active {}
button:focus {}
button:focus-visible {}
![Prévia do conjunto final de todos os tipos de botão no tema escuro.](https://web.dev/static/articles/building/a-button-component/image/preview-the-final-set-a-65448775506f6.png?authuser=5&hl=pt)
Marcação
Além dos tipos de botão fornecidos pela especificação HTML, adicionei um
botão com um ícone e outro com uma classe 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">
Em seguida, para teste, cada botão é colocado dentro de um formulário. Dessa forma, posso garantir que os estilos sejam atualizados corretamente para o botão padrão, que se comporta como um botão de envio. Também mudo a estratégia de ícones, do SVG inline para um SVG mascarado, para garantir que os dois funcionem igualmente bem.
<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>
A matriz de combinações é bastante complexa neste momento. Entre tipos de botão, pseudoclasses e estar dentro ou fora de um formulário, há mais de 20 combinações de botões. Ainda bem que o CSS pode nos ajudar a articular cada um deles com clareza.
Acessibilidade
Os elementos do botão são naturalmente acessíveis, mas há algumas melhorias comuns.
Passar o cursor e focar juntos
Gosto de agrupar :hover
e :focus
com o pseudoseletor funcional :is()
. Isso ajuda a garantir que minhas interfaces sempre considerem
os estilos de teclado e tecnologia adaptativa.
button:is(:hover, :focus) {
…
}
Anel de foco interativo
Gosto de animar o anel de foco para usuários de teclado e tecnologia assistiva. Posso fazer isso animando o contorno do botão em 5px, mas somente quando o botão não está ativo. Isso cria um efeito que faz o anel de foco reduzir ao tamanho do botão quando pressionado.
:where(button, input):where(:not(:active)):focus-visible {
outline-offset: 5px;
}
Garantir a transmissão do contraste de cores
Há pelo menos quatro combinações diferentes de cores claras e escuras que precisam considerar o contraste de cores: botão, botão de envio, botão de redefinição e botão desativado. O VisBug é usado aqui para inspecionar e mostrar todas as pontuações de uma só vez:
Ocultando ícones de pessoas que não conseguem ver
Ao criar um botão de ícone, o ícone precisa oferecer suporte visual ao texto do botão. Isso também significa que o ícone não tem valor para alguém com perda de visão. Felizmente, o navegador oferece uma maneira de ocultar itens da tecnologia de leitor de tela para que pessoas com perda de visão não sejam incomodadas com imagens decorativas de botões:
<button>
<svg … aria-hidden="true">...</svg>
Icon Button
</button>
![Chrome DevTools mostrando a árvore de acessibilidade do botão. A árvore ignora a imagem do botão porque tem aria-hidden definido como true.](https://web.dev/static/articles/building/a-button-component/image/chrome-devtools-showing-4f9517c567e4c.png?authuser=5&hl=pt)
Estilos
Na próxima seção, primeiro estabelecemos um sistema de propriedades personalizadas para gerenciar os estilos adaptáveis do botão. Com essas propriedades personalizadas, posso começar a selecionar elementos e personalizar a aparência deles.
Uma estratégia de propriedade personalizada adaptável
A estratégia de propriedade personalizada usada neste desafio da GUI é muito semelhante à usada na criação de um esquema de cores. Para um sistema adaptável de cores claras e escuras, uma propriedade personalizada para cada tema é definida e nomeada de acordo. Em seguida, uma única propriedade personalizada é usada para armazenar o valor atual do tema e é atribuída a uma propriedade CSS. Depois, a única propriedade personalizada pode ser atualizada para um valor diferente e, em seguida, atualizar o estilo do botão.
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);
}
}
O que gosto é que os temas claro e escuro são declarativos e claros. A indireção e a abstração são descarregadas na propriedade personalizada --_bg
, que agora é a única propriedade "reativa". --_bg-light
e --_bg-dark
são estáticas. Também fica claro que o tema claro é o tema padrão e
o escuro só é aplicado condicionalmente.
Como se preparar para a consistência do design
Seletor compartilhado
O seletor a seguir é usado para segmentar todos os vários tipos de botões e é
um pouco complicado no início. O :where()
é
usado. Portanto, a personalização do botão não requer especificidade. Os botões geralmente são
adaptados para cenários alternativos, e o seletor :where()
garante que a tarefa
seja fácil. Dentro de :where()
, cada tipo de botão é selecionado, incluindo o
::file-selector-button
, que não pode ser
usado
dentro de :is()
ou :where()
.
:where(
button,
input[type="button"],
input[type="submit"],
input[type="reset"],
input[type="file"]
),
:where(input[type="file"])::file-selector-button {
…
}
Todas as propriedades personalizadas terão o escopo definido dentro desse seletor. É hora de revisar todas as propriedades personalizadas. Há algumas propriedades personalizadas usadas nesse botão. Vou descrever cada grupo conforme avançamos, depois compartilhar os contextos de movimento escuros e reduzidos no final da seção.
Cor de destaque do botão
Botões e ícones de envio são um ótimo lugar para dar um toque de cor:
--_accent-light: hsl(210 100% 40%);
--_accent-dark: hsl(210 50% 70%);
--_accent: var(--_accent-light);
Cor do texto do botão
As cores do texto do botão não são brancas ou pretas. Elas são versões escurecidas
de --_accent
usando
hsl()
e
mantendo a tonalidade 210
:
--_text-light: hsl(210 10% 30%);
--_text-dark: hsl(210 5% 95%);
--_text: var(--_text-light);
Cor do plano de fundo do botão
Os planos de fundo dos botões seguem o mesmo padrão hsl()
, exceto pelos botões do tema
claro, que são definidos como brancos para que a superfície deles apareça perto do
usuário ou na frente de outras plataformas:
--_bg-light: hsl(0 0% 100%);
--_bg-dark: hsl(210 9% 31%);
--_bg: var(--_bg-light);
Bom plano de fundo do botão
Essa cor de plano de fundo serve para fazer com que uma superfície apareça atrás de outras, útil para o plano de fundo da entrada do arquivo:
--_input-well-light: hsl(210 16% 87%);
--_input-well-dark: hsl(204 10% 10%);
--_input-well: var(--_input-well-light);
Padding do botão
O espaçamento ao redor do texto no botão é feito usando a unidade
ch
, um comprimento relativo ao tamanho da fonte. Isso se torna fundamental quando botões
grandes podem simplesmente aumentar a font-size
e as escalas
de forma proporcional:
--_padding-inline: 1.75ch;
--_padding-block: .75ch;
Borda do botão
O raio da borda do botão é armazenado em uma propriedade personalizada para que a entrada do arquivo possa corresponder aos outros botões. As cores das bordas seguem o sistema de cores adaptáveis estabelecido:
--_border-radius: .5ch;
--_border-light: hsl(210 14% 89%);
--_border-dark: var(--_bg-dark);
--_border: var(--_border-light);
Efeito de destaque ao passar o cursor do mouse
Essas propriedades estabelecem uma propriedade de tamanho para a transição na interação, e
a cor de destaque segue o sistema de cores adaptáveis. Abordaremos como eles
interagem mais adiante nesta postagem, mas, em última instância, eles são usados para um efeito
box-shadow
:
--_highlight-size: 0;
--_highlight-light: hsl(210 10% 71% / 25%);
--_highlight-dark: hsl(210 10% 5% / 25%);
--_highlight: var(--_highlight-light);
Sombra do texto do botão
Cada botão tem um estilo sutil de sombra de texto. Isso ajuda o texto a ficar em cima do botão, melhorando a legibilidade e adicionando uma boa camada de polimento da apresentação.
--_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);
Ícone de botão
Os ícones têm o tamanho de dois caracteres graças ao tamanho relativo da unidade ch
novamente, o que ajuda o ícone a ser dimensionado proporcionalmente ao texto do botão. A
cor do ícone se baseia na --_accent-color
para uma cor adaptável e dentro do
tema.
--_icon-size: 2ch;
--_icon-color: var(--_accent);
Sombra do botão
Para que as sombras se adaptem corretamente à luz e ao escuro, elas precisam mudar a cor e a opacidade. Sombras do tema claro são melhores quando são sutis e coloridas em relação à cor da superfície sobreposta. As sombras do tema escuro precisam ser mais escuras e mais saturadas para que possam sobrepor cores de superfície mais escuras.
--_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);
Com cores e intensidades adaptáveis, é possível montar duas 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%));
Além disso, para dar aos botões uma aparência levemente 3D, uma sombra de caixa 1px
cria a ilusão:
--_shadow-depth-light: 0 1px var(--_border-light);
--_shadow-depth-dark: 0 1px var(--_bg-dark);
--_shadow-depth: var(--_shadow-depth-light);
Transições de botões
Seguindo o padrão de cores adaptáveis, crio duas propriedades estáticas para manter as opções do sistema de design:
--_transition-motion-reduce: ;
--_transition-motion-ok:
box-shadow 145ms ease,
outline-offset 145ms ease
;
--_transition: var(--_transition-motion-reduce);
Todas as propriedades juntas no seletor
: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); }
Adaptações do tema escuro
O valor do padrão de propriedades estáticas -light
e -dark
fica claro quando
as propriedades do tema escuro são definidas:
@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);
}
}
Além de ser uma boa leitura, os consumidores desses botões personalizados podem usar os acessórios básicos com a certeza de que eles se adaptarão adequadamente às preferências do usuário.
Redução das adaptações de movimento
Se o movimento for aceitável para esse usuário visitante, atribua --_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);
}
}
Alguns estilos compartilhados
As fontes dos botões e entradas precisam ser definidas como inherit
para que correspondam ao
restante das fontes da página. Caso contrário, elas serão estilizadas pelo navegador. Isso também
se aplica a letter-spacing
. Definir line-height
como 1.5
define o tamanho da caixa de texto
para dar ao texto algum espaço acima e abaixo:
: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);
}
Como definir o estilo de botões
Ajuste do seletor
O seletor input[type="file"]
não é a parte do botão da entrada, o
pseudoelemento ::file-selector-button
é. Por isso, removi input[type="file"]
da lista:
:where(
button,
input[type="button"],
input[type="submit"],
input[type="reset"],
input[type="file"]
),
:where(input[type="file"])::file-selector-button {
}
Ajustes de cursor e toque
Primeiro, defina o estilo do cursor como pointer
, que ajuda o botão a indicar
aos usuários do mouse que ele é interativo. Em seguida, adiciono touch-action: manipulation
para
fazer com que os cliques não precisem esperar e notar um possível clique duplo, fazendo
os botões parecerem mais 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;
}
Cores e bordas
Em seguida, personalizo o tamanho da fonte, o plano de fundo, o texto e as cores da borda usando algumas propriedades personalizadas adaptáveis estabelecidas 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
Os botões têm algumas ótimas técnicas aplicadas. O
text-shadow
é
adaptável à iluminação e ao escuro, criando uma aparência sutil e agradável do texto
do botão sobre o plano de fundo. Para a
box-shadow
,
três sombras são atribuídas. A primeira, --_shadow-2
, é uma sombra de caixa normal.
A segunda sombra é um truque para os olhos que faz o botão parecer
um pouco chanfrado. A última sombra é para o destaque ao passar o cursor, inicialmente
em um tamanho de 0, mas recebe um tamanho depois e é transferido para que pareça crescer
a partir do botão.
: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);
}
Layout
Dei ao botão um layout flexbox,
especificamente um layout inline-flex
que vai se adequar ao conteúdo. Em seguida, centralizo
o texto e alinho os filhos vertical e horizontalmente ao
centro. Isso ajudará os ícones e outros
elementos de botão a se alinharem corretamente.
: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;
}
Espaçamento
Para o espaçamento dos botões, usei
gap
para evitar que irmãos
toquem e propriedades
lógicas para preenchimento, para que o espaçamento
dos botões funcione em todos os layouts 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 por toque e mouse
A próxima seção é voltada principalmente para usuários de toque em dispositivos móveis. A primeira
propriedade,
user-select
,
é destinada a todos os usuários e impede que o texto do botão seja destacado. Isso é principalmente
perceptível em dispositivos touchscreen quando um botão é tocado e pressionado e o sistema
operacional destaca o texto dele.
Geralmente, descobri que essa não é a experiência do usuário com botões em apps
integrados, então desativo user-select
como nenhum. As cores de destaque de toque
(-webkit-tap-highlight-color
)
e os menus de contexto do sistema operacional
(-webkit-touch-callout
)
são outros recursos de botão muito focados na Web que não estão alinhados com as expectativas
gerais do usuário, então eu também os removo.
: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;
}
Transições
A variável --_transition
adaptável é atribuída à propriedade transition:
:where(
button,
input[type="button"],
input[type="submit"],
input[type="reset"]
),
:where(input[type="file"])::file-selector-button {
…
transition: var(--_transition);
}
Ao passar o cursor, enquanto o usuário não estiver pressionando ativamente, ajuste o tamanho do destaque da sombra para dar uma boa aparência de foco, que pareça crescer dentro do botão:
:where(
button,
input[type="button"],
input[type="submit"],
input[type="reset"]
):where(:not(:active):hover) {
--_highlight-size: .5rem;
}
No foco, aumente o deslocamento do contorno do foco do botão, oferecendo uma melhor aparência de foco que pareça crescer dentro do botão:
:where(button, input):where(:not(:active)):focus-visible {
outline-offset: 5px;
}
Ícones
Para o processamento de ícones, o seletor tem um seletor :where()
adicionado para filhos ou elementos SVG
diretos com o atributo personalizado data-icon
. O tamanho do ícone é definido
com a propriedade personalizada usando propriedades lógicas inline e de bloco. A cor do traço
é definida e uma
drop-shadow
para corresponder ao text-shadow
. flex-shrink
está definido como 0
para que o ícone nunca seja
comprimido. Por fim, seleciono ícones alinhados e atribuo esses estilos aqui com
limites de linha fill: none
e round
e junções de linha:
: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;
}
Como personalizar os botões de envio
Eu queria que os botões de envio tivessem uma aparência ligeiramente promovida e consegui isso tornando a cor do texto dos botões a cor de destaque:
:where(
[type="submit"],
form button:not([type],[disabled])
) {
--_text: var(--_accent);
}
Personalizar botões de redefinição
Eu queria que os botões de redefinição tivessem alguns sinais de aviso integrados para alertar os usuários sobre o comportamento potencialmente destrutivo. Também escolhi estilizar o botão de tema claro com mais tons de vermelho do que o tema escuro. A personalização é feita mudando a cor clara ou escura subjacente apropriada, e o botão atualiza o 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%);
}
Também achei que seria bom a cor do contorno do foco combinar com
o destaque do vermelho. A cor do texto muda de vermelho escuro para vermelho-claro. Faço a correspondência com a cor do contorno
com a palavra-chave
currentColor
:
:where([type="reset"]):focus-visible {
outline-color: currentColor;
}
Personalizar botões desativados
É muito comum que os botões desativados tenham um contraste de cor ruim durante a tentativa de diminuir o botão desativado para que ele pareça menos ativo. Testei cada conjunto de cores e verifiquei se eles eram aprovados, alertando o valor de luminosidade do HSL até que a pontuação fosse transmitida no DevTools ou no 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);
}
Como personalizar botões de entrada de arquivo
O botão de entrada de arquivo é um contêiner para um período e um botão. O CSS pode estilizar um pouco o contêiner de entrada e o botão aninhado, mas não o período. O contêiner recebe max-inline-size
para que não fique maior do que
o necessário, enquanto inline-size: 100%
permite reduzir e ajustar
contêineres menores do que é. A cor de fundo é definida como uma cor adaptável
mais escura do que outras superfícies, por isso fica atrás do botão seletor de arquivos.
:where(input[type="file"]) {
inline-size: 100%;
max-inline-size: max-content;
background-color: var(--_input-well);
}
O botão seletor de arquivos e os botões de tipo de entrada recebem especificamente
appearance: none
para remover todos os estilos fornecidos pelo navegador que não foram
substituídos pelos outros estilos de botão.
:where(input[type="button"]),
:where(input[type="file"])::file-selector-button {
appearance: none;
}
Por fim, a margem é adicionada ao inline-end
do botão para empurrar o texto do período
para longe do botão, criando algum espaço.
:where(input[type="file"])::file-selector-button {
margin-inline-end: var(--_padding-inline);
}
Exceções especiais do tema escuro
Apliquei aos botões de ação principais um plano de fundo mais escuro para que o texto tenha um contraste mais alto, proporcionando uma aparência um pouco mais promovida.
@media (prefers-color-scheme: dark) {
:where(
[type="submit"],
[type="reset"],
[disabled],
form button:not([type="button"])
) {
--_bg: var(--_input-well);
}
}
Criar variantes
Por diversão e por ser prático, escolhi mostrar como criar algumas variantes. Uma variante é muito vibrante, semelhante à aparência dos botões principais. Outra variante é grande. A última variante tem um ícone preenchido com gradiente.
Botão vibrante
Para alcançar esse estilo de botão, substituí os acessórios de base diretamente por cores azuis. Embora tenha sido rápido e fácil, ele remove os acessórios adaptáveis e tem a mesma aparência em temas claros e escuros.
.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ão grande
Esse estilo de botão é alcançado modificando a propriedade personalizada --_size
.
O padding e outros elementos de espaço são relativos a esse tamanho, escalonando
proporcionalmente ao novo tamanho.
.btn-large {
--_size: 1.5rem;
}
Botão de ícone
Esse efeito de ícone não tem nada a ver com os estilos de botão, mas mostra como conseguir isso com apenas algumas propriedades CSS e como o botão lida com ícones que não estão inline no SVG.
[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));
}
Conclusão
Agora que você sabe como eu fiz isso, o que você faria ‽ 🙂
Vamos diversificar nossas abordagens e aprender todas as maneiras de criar na Web.
Crie uma demonstração, envie um tweet para mim e os adicionarei à seção de remixes da comunidade abaixo.
Remixes da comunidade
Ainda não há nada aqui.
Recursos
- Código-fonte no GitHub