Skip to content

AnimatedButton

component

A button, or a link when it has an address, that moves under the pointer and the focus: a light sweeping across it, a lift or its arrow pushed forward. Pure CSS.

Preview

Light

Code
<twig:AnimatedButton effect="arrow">Get started</twig:AnimatedButton>

Dark

Code
<twig:AnimatedButton effect="arrow">Get started</twig:AnimatedButton>

Try it

Change the props; the example is rendered again by the server.

<twig:AnimatedButton effect="arrow">Get started</twig:AnimatedButton>

Variants

effect

How it moves.

effect="shine"
effect="lift"
effect="arrow"

type

The type of the button.

type="button"
type="submit"
type="reset"

loading

Whether what it started is still running.

loading="false"
loading="true"

Installation

php bin/console ux:install animated-button --kit=github.com/parler-haut-interagir-librement/ux-kit

Props

PropTypeDefaultDescription
effect 'shine'|'lift'|'arrow' 'shine' How it moves.
href string|null null Where the link leads; a button when null.
type 'button'|'submit'|'reset' 'button' The type of the button.
loading bool false Whether what it started is still running.

Accessibility

The animation adds nothing to what the button says, and it stands still under prefers-reduced-motion. While loading, the button has aria-busy="true"; the arrow is hidden from screen readers and turns around right to left.

Files to copy

templates/components/AnimatedButton.html.twig
{#-
 # A button, or a link when it has an address, that moves under the pointer and the focus, and stands still under
 # reduced motion. While `loading`, it says so and waits under a spinner.
 #}
{%- props
    ## 'shine'|'lift'|'arrow' How it moves.
    effect = 'shine',
    ## string|null Where the link leads; a button when null.
    href = null,
    ## 'button'|'submit'|'reset' The type of the button.
    type = 'button',
    ## bool Whether what it started is still running.
    loading = false
-%}
{%- set _effect = {shine: 'hasShine', lift: 'hasLift', arrow: 'hasArrow'}[effect] ?? 'hasShine' -%}
{%- set _class = 'isAnimatedButton ' ~ _effect ~ (loading ? ' isLoading' : '') -%}
{%- set _tag = href is null ? 'button' : 'a' -%}
<{{ _tag }}{% if href is null %} type="{{ type in ['submit', 'reset'] ? type : 'button' }}"{% else %} href="{{ href }}"{% endif %}{{ attributes.defaults({class: _class ~ (href is null ? '' : ' isButton')}|merge(loading ? {'aria-busy': 'true'} : {})) }}><span class="isAnimatedButtonLabel">
{##- What the button says. -#}
{%- block content %}{% endblock -%}
</span>{% if effect is same as('arrow') %}<span class="isAnimatedButtonArrow" aria-hidden="true">&rarr;</span>{% endif %}</{{ _tag }}>