# DevOps Animations 1.0.0

1510 CSS animations from the DevOps app's Motion library, packaged for the web: plain CSS, SCSS, Sass, Less, Stylus,
Tailwind (v4 and v3), CSS Modules, an ES module + UMD script for the Web Animations API, TypeScript types, and custom elements.
No dependencies. Open [`index.html`](index.html) for the live gallery.

Every animation has a stable kebab-case slug from its name (`Slide In Up · Far · Spring` → `slide-in-up-far-spring`).
The class and the keyframes are both `da-<slug>`. Browse every slug, its category, tags, timing and file paths in [`index.json`](index.json).

URLs below use `https://cdn.htmltool.io/animations/1.0.0/`; pin the version for immutable caching, or use `latest/` (short cache) to follow releases.

## Plain CSS

```html
<!-- one animation -->
<link rel="stylesheet" href="https://cdn.htmltool.io/animations/1.0.0/css/animations/fade-in-up.min.css">
<!-- one category, or everything -->
<link rel="stylesheet" href="https://cdn.htmltool.io/animations/1.0.0/css/categories/entrances.min.css">
<link rel="stylesheet" href="https://cdn.htmltool.io/animations/1.0.0/css/devops-animations.min.css">

<h1 class="da-fade-in-up">Hello</h1>
```

Override timing per element (or on any ancestor) with custom properties:

```css
.hero { --da-duration: 1.2s; --da-delay: 200ms; --da-easing: ease-out; --da-iterations: 2; --da-direction: alternate; --da-fill-mode: both; }
```

- **Hover animations** (category Hover, and anything tagged `hover`) run on `:hover`, or while the element has the `da-active` class.
- **Reduced motion**: under `prefers-reduced-motion: reduce` every `da-*` animation plays a single 1 ms iteration, so elements land in
  their final state without moving.
- **Custom subjects** (loaders, 3D cards, staggered dots, text effects…) come with the markup and part styles they need: copy the `html`
  from `index.json`. Their part classes are namespaced `da-<slug>__<part>`, e.g. for `spinner-ring`:

  ```html
  <div class="da-spinner-ring__ring da-spinner-ring"></div>
  ```

| Category | Slug | Count | Bundle |
| --- | --- | ---: | --- |
| Entrances | `entrances` | 444 | `css/categories/entrances.css` |
| Exits | `exits` | 280 | `css/categories/exits.css` |
| Attention | `attention` | 195 | `css/categories/attention.css` |
| Loaders | `loaders` | 118 | `css/categories/loaders.css` |
| Text | `text` | 95 | `css/categories/text.css` |
| Hover | `hover` | 103 | `css/categories/hover.css` |
| Backgrounds | `backgrounds` | 63 | `css/categories/backgrounds.css` |
| 3D | `3d` | 44 | `css/categories/3d.css` |
| Paths | `paths` | 72 | `css/categories/paths.css` |
| Colors | `colors` | 58 | `css/categories/colors.css` |
| Sequences | `sequences` | 38 | `css/categories/sequences.css` |

## SCSS

Each partial has the keyframes, a mixin and (by default) the class. Mixin arguments become the fallbacks of the `--da-*` properties.

```scss
// only keyframes + mixins, no .da-* classes (optional; must come first)
@use "devops-animations/scss/config" with ($emit-classes: false);

@use "devops-animations/scss/animations/fade-in-up" as fade;
.card { @include fade.da-fade-in-up($duration: 1s, $delay: 100ms); }

// everything, or one category
@use "devops-animations/scss" as da;
@use "devops-animations/scss/categories/loaders";
```

Download the `scss/` folder (or add the CDN copy to your load paths); Sass can't `@use` over HTTP.

## Sass (indented)

```sass
@use "devops-animations/sass/animations/fade-in-up" as fade
.card
  +fade.da-fade-in-up($duration: 1s)
```

## Less

```less
@import "devops-animations/less/animations/fade-in-up.less";
.card { .da-fade-in-up-mixin(@duration: 1s; @delay: 100ms); }
@da-emit-classes: false; // optional, after the imports: keep only keyframes + mixins
```

## Stylus

```styl
da-emit-classes = false   // optional, before the import
vendors = official        // optional: skip Stylus's -moz-/-webkit-/-o- keyframes copies
@import 'devops-animations/styl/animations/fade-in-up'

.card
  da-fade-in-up(duration: 1s, delay: 100ms)
```

`@import 'devops-animations/styl'` (index.styl) pulls in all 1510; `styl/categories/<category>` one category.

## Tailwind CSS

v4: import the theme file; utilities are `animate-da-<slug>` and only used keyframes are emitted.

```css
@import "tailwindcss";
@import "https://cdn.htmltool.io/animations/1.0.0/tailwind/devops-animations.css";   /* or tailwind/categories/<category>.css, tailwind/animations/<slug>.css */
```

```html
<div class="animate-da-fade-in-up motion-reduce:animate-none">…</div>
```

v3: add the preset (download `tailwind/tailwind.preset.js`):

```js
// tailwind.config.js
module.exports = { presets: [require('./tailwind.preset.js')] };
```

Tailwind entries carry the keyframes and timing; `transform-origin` and custom-subject part styles live in the plain CSS.

## CSS Modules

```js
import styles from './css-modules/animations/fade-in-up.module.css';
el.className = styles.animation;
// bundle: import all from './css-modules/devops-animations.module.css'; all['da-fade-in-up']
```

## JavaScript (Web Animations API)

```js
import { animate, animations, names } from 'https://cdn.htmltool.io/animations/1.0.0/js/index.mjs';
const animation = animate(document.querySelector('h1'), 'fade-in-up', { duration: 900, delay: '100ms' });
await animation.finished;

// or just one animation (small):
import { animate as fadeInUp } from 'https://cdn.htmltool.io/animations/1.0.0/js/animations/fade-in-up.mjs';
fadeInUp(element, { iterations: 2 });
```

Options: `duration`, `delay` (ms or CSS time), `iterations` (number or `'infinite'`), `easing`, `direction`, `fill`,
`ignoreReducedMotion`. Under reduced motion `animate()` jumps straight to the end (or leaves infinite animations still).
Types: `js/index.d.ts` (`AnimationName` is the union of all slugs).

Script tag (UMD, defines `window.DevOpsAnimations`):

```html
<script src="https://cdn.htmltool.io/animations/1.0.0/js/devops-animations.min.js"></script>
<script>DevOpsAnimations.animate(document.body, 'fade-in-up');</script>
```

## Custom elements

```html
<script type="module" src="https://cdn.htmltool.io/animations/1.0.0/elements/index.mjs"></script>
<!-- or a classic script: <script src="https://cdn.htmltool.io/animations/1.0.0/elements/devops-animations-elements.min.js"></script> -->

<da-animate name="fade-in-up" trigger="visible" duration="600ms">
  <img src="hero.png" alt="">
</da-animate>

<da-sequence name="fade-in-up" stagger="80ms" trigger="visible">
  <div>One</div><div>Two</div><div>Three</div>
</da-sequence>

<da-loader type="spinner-ring" label="Loading results"></da-loader>
<da-text-effect effect="typewriter">Ship it.</da-text-effect>
```

| Attribute / property | Elements | Meaning |
| --- | --- | --- |
| `name` | all | Animation slug (`da-` prefix optional). |
| `type` | `da-loader` | Loader slug, or the start of one (`ring`, `dots-3`). |
| `effect` | `da-text-effect` | Text effect slug, or the start of one (`typewriter`, `letter-wave`). |
| `trigger` | all | `load` (default), `visible`, `hover`, `click`, `manual`. |
| `threshold`, `repeat` | all | IntersectionObserver ratio for `visible`; `repeat` replays every time it enters. |
| `duration`, `delay`, `iterations`, `easing`, `direction`, `fill` | all | Timing overrides. |
| `stagger` | `da-sequence` | Delay between children (default `80ms`). |
| `ignore-reduced-motion` | all | Animate even under `prefers-reduced-motion`. |

Attributes are reflected as properties (`el.trigger = 'click'`); changing one rebuilds the animation. Methods: `play()` (Promise),
`pause()`, `cancel()`, `reverse()`, `finish()`; `el.animations` holds the `Animation` objects. Events (bubbling, composed):
`da-start`, `da-end`, `da-cancel`, `da-error`. Shadow parts: `da-animate::part(content)`, `da-loader::part(stage)` /
`::part(loader)`, `da-text-effect::part(text)`, `da-sequence::part(items)`.

`<da-animate>` animates itself, so it works next to the plain CSS classes on the content inside it. Elements fetch only the
animation they need (`js/animations/<slug>.mjs`); if `js/index.mjs` or the UMD script is loaded, call `register(animations)` (ESM) or
nothing (UMD) to skip those requests.

## Files

```
1.0.0/
  index.json, index.html, README.md
  css/            devops-animations(.min).css, categories/<category>(.min).css, animations/<slug>(.min).css
  scss/ sass/     _index, _config, categories/_<category>, animations/_<slug>
  less/ styl/     index, config, categories/<category>, animations/<slug>
  tailwind/       devops-animations.css (v4 @theme), tailwind.preset.js (v3), categories/, animations/
  css-modules/    devops-animations.module.css, categories/, animations/<slug>.module.css
  js/             index.mjs, core.mjs, index.d.ts, devops-animations(.min).js (UMD), animations/<slug>.mjs
  elements/       index.mjs, base.mjs, da-*.mjs, index.d.ts, devops-animations-elements(.min).js
  demo/           gallery styles and script for index.html
```

A separate PostCSS build isn't shipped: the plain CSS uses only standard syntax, so it is already valid PostCSS input.
