# Animations

Cards that fade in on scroll, numbers that count up, skeletons for loading content, AdminKit's motion tokens, and how all of it respects reduced motion.

AdminKit 4 has a small set of animations, in the free version and in AdminKit PRO: cards fade up as they scroll into view, stat numbers count up, charts animate in, and content that JavaScript draws shows a skeleton until it is ready. With `prefers-reduced-motion: reduce`, nothing moves.

## Reveal on scroll

Every top-level card inside `.ak-reveal-cards` fades up when it scrolls into view; the layouts put that class on `<main>`, so the cards of every page do it without extra markup. Add `.ak-reveal` to any other element to animate it the same way. Elements that come into view together are staggered by 60 ms each, up to eight steps.

```html
<main class="content ak-reveal-cards">
  <div class="card">...</div>
  <div class="card">...</div>
  <p class="ak-reveal">Fades up as well.</p>
</main>
```

The hidden starting state only applies under `html.js-reveal`, which an inline script in `<head>` sets. Copy that script from `src/partials/site-meta.hbs` into your own `<head>`, next to the [color mode script](/docs/getting-started/dark-mode/). Without it, nothing is hidden. If the page scripts have not run after three seconds, the script shows the content anyway.

## Count-up numbers

`data-count-up` counts a number up from zero the first time it scrolls into view. The element's text is the final value and sets the format: a prefix or suffix (`$`, `%`, `+`), `,` thousands separators and decimals stay as written.

```html
<div class="row">
  <div class="col-6">
    <div class="card">
      <div class="card-body">
        <h2 class="card-title">Earnings</h2>
        <div class="h1 mt-1 mb-0" data-count-up>$21,300</div>
      </div>
    </div>
  </div>
  <div class="col-6">
    <div class="card">
      <div class="card-body">
        <h2 class="card-title">Conversion</h2>
        <div class="h1 mt-1 mb-0" data-count-up>3.65%</div>
      </div>
    </div>
  </div>
</div>
```

Put it on an element that holds only the number and sits on a line of its own, like the `.h1` of the stat cards, so its changing width moves nothing else. The text in the HTML is always the final value, which is what screen readers read.

## Skeletons

For content that JavaScript draws, such as charts, maps or rows from an API, show a skeleton until it is ready. Put `aria-busy="true"` on the card body and the skeleton markup inside a `position-relative` element that has the content's final height, so nothing moves when the content arrives:

```html
<div class="card">
  <div class="card-body" aria-busy="true">
    <div class="position-relative" style="min-height: 120px">
      <div class="ak-skeleton placeholder-glow" aria-hidden="true">
        <span class="placeholder"></span>
      </div>
      <div id="report">
        <p class="mb-0">The report has loaded.</p>
      </div>
    </div>
  </div>
  <div class="card-footer">
    <button type="button" class="btn btn-primary" id="report-ready">
      Mark as ready
    </button>
  </div>
</div>
<script>
  document.getElementById("report-ready").addEventListener("click", function () {
    adminkit.ready(document.getElementById("report"));
  });
</script>
```

In the template's Handlebars pages the skeleton is the `{{> skeleton }}` partial. `adminkit.chartjs()` and `adminkit.vectorMap()` remove it when they have drawn (in AdminKit PRO also `adminkit.apexcharts()` and the DataTables `init`). For anything else, call `adminkit.ready(element)`: it removes the skeletons in the closest `aria-busy="true"` element, sets `aria-busy="false"` and fires `adminkit:ready` on `element`.

## Motion tokens

Durations and easings are Sass variables in `src/scss/1-variables/_app.scss`, exposed as custom properties:

| Sass variable | Custom property | Value |
| --- | --- | --- |
| `$ak-transition-duration-fast` | `--ak-duration-fast` | `0.15s` |
| `$ak-transition-duration-base` | `--ak-duration-base` | `0.25s` |
| `$ak-transition-duration-slow` | `--ak-duration-slow` | `0.35s` |
| `$ak-transition-easing-standard` | `--ak-ease-standard` | `cubic-bezier(0.4, 0, 0.2, 1)` |
| `$ak-transition-easing-emphasized` | `--ak-ease-emphasized` | `cubic-bezier(0.2, 0, 0, 1)` |

Bootstrap's fade and collapse transitions, the dropdowns and the sidebar use them (in AdminKit PRO also the modal, offcanvas and toast transitions). Use the custom properties in your own CSS, and write Sass transitions with Bootstrap's `@include transition(...)` mixin, which turns them off under reduced motion.

## Reduced motion

When the operating system asks for reduced motion (`prefers-reduced-motion: reduce`):

- revealed cards only fade in, without moving up
- `data-count-up` numbers show their final value at once
- charts draw without their entrance animation
- transitions written with the `transition()` mixin are turned off

Keep it that way in your own additions: an animation may fade under reduced motion, but must not move anything.

Clickable cards (`a.card`, or a card with a `.stretched-link`) lift on hover and keyboard focus; that needs no markup either.

---

Canonical HTML page: https://adminkit.io/docs/getting-started/animations/

Site overview for agents: https://adminkit.io/llms.txt (expanded: https://adminkit.io/llms-full.txt) · Documentation: https://adminkit.io/docs/ · Questions: support@adminkit.io
