Features
Transitions
Transitions animate elements as they enter and leave the page. Add nitro:transition to an element inside an @if or a @foreach, and Nitro animates it when it appears and when it goes.
On this page
To animate an element, add nitro:transition to an element that an @if or a
@foreach renders. The element animates as it enters. When it leaves, Nitro keeps it in the
page until its animation ends. Without a name, the transition is a built-in fade and scale that needs no
CSS.
- Ada
- Grace
- Alan
Try adding and removing names, then shuffle the list. The rows that stay slide to their new places, thanks
to .move. When you switch tabs, the old tab leaves before the new one enters, thanks to
.out-in. When you open the panel, it uses the built-in fade and scale, growing from its top
edge.
The component
<?php
namespace App\Nitro\Components;
use Nitro\Component;
class TransitionDemo extends Component
{
public array $people = ['Ada', 'Grace', 'Alan'];
public array $waiting = ['Katherine', 'Margaret', 'Edsger', 'Barbara', 'Donald'];
public bool $open = false;
public string $tab = 'list';
public function add(): void
{
if ($this->waiting !== []) {
$this->people[] = array_shift($this->waiting);
}
}
public function remove(string $name): void
{
$this->people = array_values(array_filter($this->people, fn ($person) => $person !== $name));
$this->waiting[] = $name;
}
public function shuffle(): void
{
shuffle($this->people);
}
}
<div class="w-full max-w-md space-y-4 text-sm">
<div class="flex gap-1">
@foreach (['list' => 'A list', 'panel' => 'A panel'] as $value => $label)
<button type="button" nitro:click="$set('tab', $value)" @class(['rounded-lg px-3 py-1.5 font-medium', 'bg-slate-900 text-white dark:bg-white dark:text-slate-900' => $tab === $value, 'ring-1 ring-slate-300 dark:ring-white/15' => $tab !== $value])>{{ $label }}</button>
@endforeach
</div>
@if ($tab === 'list')
<section nitro:transition.out-in class="space-y-2">
<div class="flex gap-2">
<button type="button" nitro:click="add" class="rounded-lg px-3 py-1.5 ring-1 ring-slate-300 dark:ring-white/15">Add</button>
<button type="button" nitro:click="shuffle" class="rounded-lg px-3 py-1.5 ring-1 ring-slate-300 dark:ring-white/15">Shuffle</button>
</div>
<ul class="space-y-1">
@foreach ($people as $person)
<li nitro:key="{{ $person }}" nitro:transition.move.duration.250ms class="flex items-center justify-between rounded-lg bg-white px-3 py-2 ring-1 ring-slate-200 dark:bg-slate-900 dark:ring-white/10">
{{ $person }}
<button type="button" nitro:click="remove($person)" class="text-slate-400 hover:text-red-500" aria-label="Remove {{ $person }}">×</button>
</li>
@endforeach
</ul>
</section>
@else
<section nitro:transition.out-in class="space-y-2">
<button type="button" nitro:click="$toggle('open')" class="rounded-lg px-3 py-1.5 ring-1 ring-slate-300 dark:ring-white/15">{{ $open ? 'Hide' : 'Show' }} the panel</button>
@if ($open)
<div nitro:transition.duration.200ms.origin.top class="rounded-xl bg-blue-50 p-4 text-blue-900 ring-1 ring-blue-200 dark:bg-blue-500/10 dark:text-blue-200 dark:ring-blue-500/20">
The built-in fade and scale: no CSS of your own.
</div>
@endif
</section>
@endif
</div>
Compiling…
Using the built-in transition
The built-in transition fades and scales the element. Modifiers let you change how it animates:
@if ($open)
<div class="panel" nitro:transition.duration.200ms.origin.top>...</div>
@endif
| Modifier | Effect |
|---|---|
.opacity
,
.scale
|
Fade only, or scale only (both by default) |
.duration.300ms
|
The duration, 150ms by default |
.origin.top
|
The transform origin:
top
,
bottom
,
left
,
right
or
center
|
.in
,
.out
|
Animate only when entering, or only when leaving |
.appear
|
Animate on the component's first render in the browser too |
.out-in
,
.in-out
|
For an element that replaces another (
@if
and
@else
, on both): the old one leaves before the new one enters, or after it
|
.move
|
On the rows of a
@foreach
: the rows that stay slide to their new places when the list changes
|
Writing your own transition
When you give a transition a name, your CSS classes decide what moves and for how long. This works the same way as Vue's transitions:
<p nitro:transition="fade">...</p>
.fade-enter-active, .fade-leave-active { transition: opacity .3s ease; }
.fade-enter-from, .fade-leave-to { opacity: 0; }
.fade-move { transition: transform .3s; }
Animating lists and tabs
Give each row of a list a nitro:key, so Nitro can follow it as the list changes. When two
elements replace each other, put .out-in on both of them.
@foreach ($people as $person)
<li nitro:key="{{ $person }}" nitro:transition.move.duration.250ms>{{ $person }}</li>
@endforeach
@if ($tab === 'list')
<section nitro:transition.out-in>...</section>
@else
<section nitro:transition.out-in>...</section>
@endif
When nothing animates
Nitro doesn't animate the server's HTML, elements in a hidden tab, or anything for visitors who have asked their system for reduced motion.