Uma visão geral básica sobre como criar componentes de FABs adaptáveis a cores, responsivos e acessíveis.
Neste post, quero compartilhar minhas ideias sobre como criar componentes de FAB responsivos, adaptáveis a cores e acessíveis. Teste a demonstração e confira o código-fonte.
Se você preferir o vídeo, aqui está uma versão do YouTube desta postagem:
Visão geral
Os FABs são mais comuns em dispositivos móveis do que em computadores, mas são predominantes em ambos os cenários. Eles mantêm as ações principais em vista, tornando-as convenientes e onnipresentes. Esse estilo de experiência do usuário ficou famoso pela interface do Material Design. As sugestões de uso e posicionamento podem ser encontradas aqui (link em inglês).
Elementos e estilos
O HTML desses controles envolve um elemento de contêiner e um conjunto de um ou mais botões. O contêiner posiciona os FABs dentro da viewport e gerencia um intervalo entre os botões. Os botões podem ser mini ou padrão, oferecendo uma boa variação entre ações principais e secundárias.
Contêiner do FAB
Esse elemento pode ser um <div>
normal, mas vamos ajudar nossos usuários com deficiência visual
e marcar com alguns atributos úteis para explicar a finalidade e o conteúdo desse
contêiner.
Marcação de FABs
Comece com uma classe .fabs
para que o CSS se conecte para estilizar e, em seguida, adicione
role="group"
e aria-label
para que ele não seja apenas um contêiner genérico, mas
nomeado e com uma finalidade.
<div class="fabs" role="group" aria-label="Floating action buttons">
<!-- buttons will go here -->
</div>
Estilo dos FABs
Para que os botões flutuantes sejam convenientes, eles ficam sempre na janela de visualização.
Esse é um ótimo caso de uso para a posição
fixed
. Nesta
posição de viewport, escolhi usar
inset-block
e
inset-inline
para que a
posição complemente o modo de documento do usuário, como da direita para a esquerda ou
da esquerda para a direita. As propriedades personalizadas também são usadas para evitar repetições e garantir
distâncias iguais das bordas inferior e lateral da viewport:
.fabs {
--_viewport-margin: 2.5vmin;
position: fixed;
z-index: var(--layer-1);
inset-block: auto var(--_viewport-margin);
inset-inline: auto var(--_viewport-margin);
}
Em seguida, dou a exibição do contêiner
flex
e mudo a
direção do layout para
column-reverse
.
Isso empilha os filhos uns sobre os outros (coluna) e também inverte a
ordem visual deles. Isso tem o efeito de tornar o primeiro elemento focalizável o
elemento de baixo, em vez do de cima, que seria o foco normalmente
no documento HTML. A inversão da ordem visual une a experiência para usuários
com visão e com teclado, já que o estilo da ação principal é maior que
os botões pequenos, indicando aos usuários com visão que se trata de uma ação principal, e
os usuários com teclado vão focar nela como o primeiro item na fonte.
.fabs {
…
display: flex;
flex-direction: column-reverse;
place-items: center;
gap: var(--_viewport-margin);
}
O alinhamento central é processado com
place-items
, e
gap
adiciona espaço entre os
botões de ação flutuante colocados no contêiner.
Botões de ação flutuante
É hora de estilizar alguns botões para que pareçam flutuar sobre tudo.
Botão flutuante padrão
O primeiro botão a receber estilo é o padrão. Ele vai servir como base para todos os botões de ação flutuante. Mais tarde, vamos criar uma variante que alcança uma aparência alternativa, modificando o mínimo possível desses estilos básicos.
Marcação de FAB
O elemento <button>
é a escolha certa. Vamos começar com isso como base
porque ele oferece uma ótima experiência do usuário com mouse, toque e teclado. O aspecto mais
importante dessa marcação é ocultar o ícone dos usuários de leitores de tela com
aria-hidden="true"
e adicionar o texto de rótulo necessário à marcação <button>
em si. Ao adicionar rótulos nesses casos, também gosto de adicionar um title
para que os usuários
do mouse possam receber informações sobre o que o ícone espera comunicar.
<button data-icon="plus" class="fab" title="Add new action" aria-label="Add new action">
<svg aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">...</svg>
</button>
Estilo FAB
Primeiro, vamos transformar o botão em um botão redondo com preenchimento e uma sombra forte, já que esses são os primeiros recursos que definem o botão:
.fab {
--_size: 2rem;
padding: calc(var(--_size) / 2);
border-radius: var(--radius-round);
aspect-ratio: 1;
box-shadow: var(--shadow-4);
}
Em seguida, vamos adicionar cor. Vamos usar uma estratégia que já usamos em desafios de GUI. Crie um conjunto de propriedades personalizadas com nomes claros que mantenham estaticamente as cores claras e escuras e, em seguida, uma propriedade personalizada adaptável que será definida como as variáveis claras ou escuras, dependendo da preferência do sistema do usuário para cores:
.fab {
…
/* light button and button hover */
--_light-bg: var(--pink-6);
--_light-bg-hover: var(--pink-7);
/* dark button and button hover */
--_dark-bg: var(--pink-4);
--_dark-bg-hover: var(--pink-3);
/* adaptive variables set to light by default */
--_bg: var(--_light-bg);
/* static icon colors set to the adaptive foreground variable */
--_light-fg: white;
--_dark-fg: black;
--_fg: var(--_light-fg);
/* use the adaptive properties on some styles */
background: var(--_bg);
color: var(--_fg);
&:is(:active, :hover, :focus-visible) {
--_bg: var(--_light-bg-hover);
@media (prefers-color-scheme: dark) {
--_bg: var(--_dark-bg-hover);
}
}
/* if users prefers dark, set adaptive props to dark */
@media (prefers-color-scheme: dark) {
--_bg: var(--_dark-bg);
--_fg: var(--_dark-fg);
}
}
Em seguida, adicione alguns estilos para ajudar os ícones SVG a caberem no espaço.
.fab {
…
& > svg {
inline-size: var(--_size);
block-size: var(--_size);
stroke-width: 3px;
}
}
Por fim, remova o destaque de toque do botão, já que adicionamos nosso próprio feedback visual para interação:
.fab {
-webkit-tap-highlight-color: transparent;
}
Mini FAB
O objetivo desta seção é criar uma variante para o botão de ação flutuante. Ao tornar alguns dos botões flutuantes maiores do que a ação padrão, podemos promover a ação que o usuário realiza com mais frequência.
Marcação do Mini FAB
O HTML é o mesmo de um FAB, mas adicionamos uma classe ".mini" para dar ao CSS um gancho para a variante.
<button data-icon="heart" class="fab mini" title="Like action" aria-label="Like action">
<svg aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">...</svg>
</button>
Estilo Mini FAB
Graças ao uso de propriedades personalizadas, a única mudança necessária é um ajuste na
variável --_size
.
.fab.mini {
--_size: 1.25rem;
}
Acessibilidade
A parte mais importante a ser lembrada para a acessibilidade com os botões flutuantes é a colocação no fluxo do teclado da página. Essa demonstração tem apenas os FABs, não há nada para competir em termos de ordem e fluxo do teclado, o que significa que ela não tem uma oportunidade de demonstrar um fluxo de teclado significativo. Em um cenário em que há elementos concorrentes para o foco, sugiro pensar profundamente sobre em que parte desse fluxo o usuário deve entrar no fluxo do botão FAB.
Depois que o usuário focar no contêiner do FAB, já adicionamos
role="group"
e aria-label="floating action buttons"
, que informam os usuários
de leitores de tela sobre o conteúdo do que eles focaram. Eu coloquei
o FAB padrão primeiro, para que os usuários encontrem a ação principal primeiro. Em seguida,
utilizo flex-direction: column-reverse;
para ordenar visualmente o botão principal
na parte de baixo, perto dos dedos dos usuários para facilitar o acesso. Isso é uma vitória
porque o botão padrão é visualmente destacado e também é o primeiro para os usuários de teclado,
oferecendo a eles experiências muito semelhantes.
Por fim, não se esqueça de ocultar seus ícones dos usuários de leitores de tela e de
fornecer um rótulo para o botão para que ele não seja um mistério. Isso já foi
feito no HTML com aria-hidden="true"
no <svg>
e
aria-label="Some action"
nos <button>
s.
Animação
Vários tipos de animação podem ser adicionados para melhorar a experiência do usuário. Assim como em
outros desafios de GUI, vamos configurar algumas propriedades personalizadas para manter a
intenção de uma experiência de movimento reduzida e uma experiência de movimento total. Por padrão,
os estilos vão presumir que o usuário quer um movimento reduzido e, em seguida, usar a
consulta de mídia prefers-reduced-motion
para trocar o valor da transição para movimento total.
Uma estratégia de movimento reduzido com propriedades personalizadas
Três propriedades personalizadas são criadas no CSS a seguir: --_motion-reduced
,
--_motion-ok
e --_transition
. As duas primeiras contêm transições adequadas
de acordo com a preferência do usuário, e a última variável --_transition
será definida
como --_motion-reduced
ou --_motion-ok
, respectivamente.
.fab {
/* box-shadow and background-color can safely be transitioned for reduced motion users */
--_motion-reduced:
box-shadow .2s var(--ease-3),
background-color .3s var(--ease-3);
/* add transform and outline-offset for users ok with motion */
--_motion-ok:
var(--_motion-reduced),
transform .2s var(--ease-3),
outline-offset 145ms var(--ease-2);
/* default the transition styles to reduced motion */
--_transition: var(--_motion-reduced);
/* set the transition to our adaptive transition custom property*/
transition: var(--_transition);
/* if motion is ok, update the adaptive prop to the respective transition prop */
@media (prefers-reduced-motion: no-preference) {
--_transition: var(--_motion-ok);
}
}
Com o acima em vigor, as mudanças em box-shadow
, background-color
,
transform
e outline-offset
podem ser transferidas, dando ao usuário um bom feedback
de interface informando que a interação foi recebida.
Em seguida, adicione um pouco mais de estilo ao estado :active
ajustando
translateY
um pouco. Isso dá ao botão um efeito de pressionamento legal:
.fab {
…
&:active {
@media (prefers-reduced-motion: no-preference) {
transform: translateY(2%);
}
}
}
Por fim, faça a transição de todas as alterações nos ícones SVG nos botões:
.fab {
…
&[data-icon="plus"]:hover > svg {
transform: rotateZ(.25turn);
}
& > svg {
@media (prefers-reduced-motion: no-preference) {
will-change: transform;
transition: transform .5s var(--ease-squish-3);
}
}
}
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, envie links para mim e vou adicionar à seção de remixes da comunidade abaixo.
Remixes da comunidade
Ainda não há nada aqui.
Recursos
- Código-fonte no GitHub