Uma visão geral fundamental de como criar componentes <button> responsivos, acessíveis e adaptáveis a cores.
Nesta postagem, quero compartilhar minhas ideias sobre como criar um elemento <button> adaptável a cores, responsivo e acessível.
Teste a demonstração e veja a fonte
Se preferir vídeo, confira uma versão deste post no YouTube:
Visão geral
O elemento
<button>
foi criado para a 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
nenhuma personalização. Use color-scheme para ativar os botões claro e escuro fornecidos pelo navegador.
Há também diferentes tipos de
botões,
cada um mostrado na incorporação do Codepen anterior. Um <button> sem um tipo vai se adaptar a estar dentro de um <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 vai receber estilos para ajudar a diferenciar visualmente a intenção deles. Os botões de redefinição terão cores de aviso porque são destrutivos, e os botões de envio receberão um texto de destaque azul para que pareçam um pouco mais promovidos do que os botões normais.
Os botões também têm pseudoclasses
para o CSS usar na estilização. Essas classes fornecem hooks de CSS para personalizar a aparência 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 ajudar na estilização de tecnologia assistiva.
button:hover {}
button:active {}
button:focus {}
button:focus-visible {}
Marcação
Além dos tipos de botão fornecidos pela especificação HTML, adicionei um
botão com um ícone e um botão 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. Assim, posso garantir que os estilos sejam atualizados adequadamente para o botão padrão, que se comporta como um botão de envio. Também mudo a estratégia de ícones, de SVG in-line para um SVG mascarado, para garantir que ambos 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 é bem grande neste momento. Entre tipos de botões, 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 de botão são naturalmente acessíveis, mas há algumas melhorias comuns.
Passar o cursor e focar ao mesmo tempo
Gosto de agrupar :hover e :focus com o pseudoseletor funcional :is(). Isso ajuda a garantir que minhas interfaces sempre considerem estilos de teclado
e tecnologia assistiva.
button:is(:hover, :focus) {
…
}
Círculo de foco interativo
Gosto de animar o anel de foco para usuários de teclado e tecnologia adaptativa. Para isso, animo o contorno para longe do botão em 5 px, mas apenas quando ele não está ativo. Isso cria um efeito que faz o anel de foco encolher de volta ao tamanho do botão quando pressionado.
:where(button, input):where(:not(:active)):focus-visible {
outline-offset: 5px;
}
Como garantir um contraste de cor adequado
Há pelo menos quatro combinações de cores diferentes em claro e escuro 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 vez:
Ocultar ícones de pessoas que não podem ver
Ao criar um botão de ícone, o ícone precisa dar suporte visual ao texto do botão. Isso também significa que o ícone não é útil 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 de botões decorativos:
<button>
<svg … aria-hidden="true">...</svg>
Icon Button
</button>
Estilos
Na próxima seção, vou estabelecer 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 adaptativa de propriedade personalizada
A estratégia de propriedade personalizada usada neste desafio de GUI é muito semelhante à usada para criar 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 manter o valor atual do tema e é atribuída a uma propriedade CSS. Mais tarde, a única propriedade personalizada pode ser atualizada para um valor diferente, atualizando 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 eu gosto é que os temas claro e escuro são declarativos e claros. O redirecionamento e a abstração são descarregados na propriedade personalizada --_bg, que agora é a única propriedade "reativa". --_bg-light e --_bg-dark são estáticos. Também fica claro que o tema claro é o padrão e o escuro só é aplicado condicionalmente.
Preparação para consistência de design
O seletor compartilhado
O seletor a seguir é usado para segmentar todos os tipos de botões e é um pouco complexo no início. :where() é
usado para que a personalização do botão não exija 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 serão incluídas nesse seletor. É hora de revisar todas as propriedades personalizadas. Há algumas propriedades personalizadas usadas nesse botão. Vou descrever cada grupo à medida que avançamos e, no final da seção, vou compartilhar os contextos de movimento reduzido e escuro.
Cor de destaque do botão
Botões de envio e ícones são ótimos lugares para 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 branco ou preto, mas versões escurecidas ou clareadas de --_accent usando hsl() e mantendo o matiz 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 os botões do tema claro, que são definidos como brancos para que a superfície deles pareça estar perto do usuário ou na frente de outras superfícies:
--_bg-light: hsl(0 0% 100%);
--_bg-dark: hsl(210 9% 31%);
--_bg: var(--_bg-light);
Bloco de plano de fundo do botão
Essa cor de plano de fundo faz com que uma superfície apareça atrás de outras, o que é útil para o plano de fundo da entrada de 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 crítico quando botões grandes podem simplesmente aumentar o font-size e as escalas de botões proporcionalmente:
--_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 de arquivo corresponda aos outros botões. As cores da borda 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 sobre o botão
Essas propriedades estabelecem uma propriedade de tamanho para transição na interação, e a cor de destaque segue o sistema de cores adaptável. Vamos abordar como eles
interagem mais adiante nesta postagem, mas, em última análise, são usados para um efeito de 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 camada de apresentação refinada.
--_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 à unidade de comprimento relativo ch, que ajuda a dimensionar o ícone proporcionalmente ao texto do botão. A cor do ícone usa o --_accent-color para uma cor adaptativa e dentro do tema.
--_icon-size: 2ch;
--_icon-color: var(--_accent);
Sombra do botão
Para que as sombras se adaptem corretamente à luz e à escuridão, elas precisam mudar a cor e a opacidade. As sombras do tema claro ficam melhores quando são sutis e têm uma tonalidade em direção à cor da superfície em que estão sobrepostas. As sombras do tema escuro precisam ser mais escuras e saturadas para que possam se sobrepor a 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, posso 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 caixa-sombra 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 ter uma boa leitura, os consumidores desses botões personalizados podem usar as propriedades simples com a confiança de que elas vão se adaptar adequadamente às preferências do usuário.
Adaptações de movimento reduzido
Se o movimento não for um problema para o 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
Os botões e entradas precisam ter as fontes definidas como inherit para corresponderem ao
restante das fontes da página. Caso contrário, o navegador vai definir o estilo. Isso também se aplica a letter-spacing. Definir line-height como 1.5 define o tamanho da caixa de carta 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);
}

Estilizar botões
Ajuste do seletor
O seletor input[type="file"] não é a parte do botão da entrada, mas 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, eu estilizo o cursor para o estilo pointer, o que ajuda o botão a indicar
para usuários de mouse que ele é interativo. Em seguida, adiciono touch-action: manipulation para
que os cliques não precisem esperar e observar um possível clique duplo, fazendo com que os
botões pareçam 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 das 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 técnicas excelentes aplicadas. O
text-shadow é
adaptável a claro e escuro, criando uma aparência sutil agradável do texto do botão
assentado bem em cima do plano de fundo. Para o
box-shadow,
três sombras são atribuídas. A primeira, --_shadow-2, é uma sombra de caixa normal.
A segunda sombra é um truque para o olho que faz o botão parecer um pouco chanfrado. A última sombra é para o destaque ao passar o cursor, inicialmente
com tamanho 0, mas ela vai receber um tamanho depois e será transicionada para que pareça
crescer 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 se ajusta ao conteúdo. Em seguida, centralizo o texto e alinho os filhos vertical e horizontalmente ao centro. Isso ajuda 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 elementos irmãos
se toquem e propriedades
lógicas para padding. Assim, o espaçamento
dos botões funciona para 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 de toque e mouse
A próxima seção é destinada principalmente a usuários de dispositivos móveis com tela sensível ao toque. A primeira propriedade, user-select, é para todos os usuários e impede que o texto do botão seja destacado. Isso é mais
perceptível em dispositivos sensíveis ao toque quando um botão é tocado e pressionado, e o sistema
operacional destaca o texto do botão.
Em geral, descobri que essa não é a experiência do usuário com botões em apps
integrados. Por isso, desativo essa opção definindo user-select como "none". Tocar nas cores de destaque
(-webkit-tap-highlight-color)
e nos menus de contexto do sistema operacional
(-webkit-touch-callout)
são outros recursos de botão muito centrados na Web que não estão alinhados com as expectativas gerais
dos usuários de botões. Por isso, 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 adaptativa --_transition é 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 aparência de foco agradável que parece crescer de dentro do botão:
:where(
button,
input[type="button"],
input[type="submit"],
input[type="reset"]
):where(:not(:active):hover) {
--_highlight-size: .5rem;
}
Ao focar, aumente o deslocamento do contorno de foco do botão, a ele uma aparência de foco agradável que parece crescer de dentro do botão:
:where(button, input):where(:not(:active)):focus-visible {
outline-offset: 5px;
}
Ícones
Para processar ícones, o seletor tem um seletor :where() adicional para filhos SVG diretos ou elementos 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, assim como um
drop-shadow
para corresponder ao text-shadow. flex-shrink está definido como 0 para que o ícone nunca seja
comprimido. Por fim, seleciono ícones com linhas e atribuo esses estilos aqui com
terminações e junções de linha fill: none e 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;
}

Personalizar botões de envio
Eu queria que os botões de envio tivessem uma aparência um pouco mais destacada, e consegui isso definindo a cor do texto dos botões como 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 alerta integrados para alertar os usuários sobre o comportamento potencialmente destrutivo deles. Também escolhi estilizar o botão do tema claro com mais detalhes em vermelho do que o tema escuro. A personalização é feita mudando a cor clara ou escura adequada, 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 legal se a cor do contorno de foco correspondesse ao destaque do
vermelho. A cor do texto muda de um vermelho escuro para um vermelho claro. Faço com que a cor do contorno corresponda a isso com a palavra-chave currentColor:
:where([type="reset"]):focus-visible {
outline-color: currentColor;
}

Personalizar botões desativados
É muito comum que botões desativados tenham um contraste de cores ruim durante a tentativa de reduzir o botão desativado para que ele pareça menos ativo. Teste cada conjunto de cores e verifique se eles foram aprovados, ajustando o valor de luminosidade HSL até que a pontuação fosse aprovada 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 intervalo e um botão. O CSS consegue
estilizar um pouco o contêiner de entrada e o botão aninhado, mas não
o span. O contêiner recebe max-inline-size para não crescer mais do que o necessário, enquanto inline-size: 100% se encolhe e se ajusta a contêineres menores. A cor de fundo é definida como uma cor adaptável mais escura que outras superfícies, para que ela apareça atrás do botão do seletor de arquivos.
:where(input[type="file"]) {
inline-size: 100%;
max-inline-size: max-content;
background-color: var(--_input-well);
}
O botão de seleção de arquivos e os botões de tipo de entrada recebem especificamente
appearance: none para remover 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 afastar o texto do intervalo
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
Dei aos botões de ação principais um fundo mais escuro para um texto de maior contraste, conferindo-lhes uma aparência ligeiramente mais destacada.
@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 praticidade, 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 conseguir esse estilo de botão, sobrescrevi as propriedades básicas diretamente com cores azuis. Embora isso tenha sido rápido e fácil, ele remove as propriedades adaptáveis e tem a mesma aparência nos temas claro e escuro.
.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, sendo dimensionados proporcionalmente com o novo tamanho.
.btn-large {
--_size: 1.5rem;
}

Botão de ícone
Esse efeito de ícone não tem nada a ver com nossos estilos de botão, mas mostra como conseguir isso com apenas algumas propriedades de CSS e como o botão lida bem com ícones que não são SVG inline.
[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, como você faria? 🙂
Vamos diversificar nossas abordagens e aprender todas as maneiras de criar na Web.
Crie uma demonstração, me envie um tweet com o link, e eu vou adicionar à seção de remixes da comunidade abaixo.
Remixes da comunidade
Ainda não há nada aqui.
Recursos
- Código-fonte no GitHub