Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions .agents/skills/transitions-dev/01-card-resize.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Card resize

## When to use

Tweening a container's width or height when its layout state changes (compact ↔ expanded card, collapsing panel, list row toggling extra detail). Pure CSS β€” no JS required beyond the class toggle that drives the size change.

## HTML usage

```html
<div class="t-resize">…</div>
```

Put `.t-resize` on any element and change its width/height
(directly, or via a state class such as `.is-small`). The
transition will tween the two sizes.

## Tunable variables

| Variable | Default | Notes |
| --- | --- | --- |
| `--resize-dur` | `300ms` | sourced from `--p4-dur` |
| `--resize-ease` | `cubic-bezier(0.22, 1, 0.36, 1)` | sourced from `--p4-ease` |

The `:root` defaults below match the live tuning on [transitions.dev](https://transitions.dev). Drop them into your global stylesheet once β€” every transition in this skill reads from semantic names like these, so multiple transitions can share a single `:root` block.

```css
:root {
--resize-dur: 300ms;
--resize-ease: cubic-bezier(0.22, 1, 0.36, 1);
}
```

## CSS

```css
.t-resize {
transition:
width var(--resize-dur) var(--resize-ease),
height var(--resize-dur) var(--resize-ease);
will-change: width, height;
}

@media (prefers-reduced-motion: reduce) {
.t-resize { transition: none !important; }
}
```

The `@media (prefers-reduced-motion: reduce)` guard at the bottom of the snippet is required β€” keep it. It zeroes the transition for users who have asked for less motion at the OS level.

## JavaScript orchestration

None β€” pure CSS. Toggle the documented HTML attributes or class names from whatever already drives state in your app.

119 changes: 119 additions & 0 deletions .agents/skills/transitions-dev/02-number-pop-in.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# Number pop-in

## When to use

Counters, prices, balances, or any number that updates and should re-enter from a direction with blur. Each character animates independently and the last two digits stagger so decimals feel alive without looking chaotic.

## HTML usage

```html
<span class="t-digit-group is-animating">
<span class="t-digit">1</span>
<span class="t-digit">2</span>
<span class="t-digit" data-stagger="1">.</span>
<span class="t-digit" data-stagger="2">3</span>
</span>
```

Replay:
- Remove `.is-animating`, re-render digits (or swap text),
force a reflow, then re-add `.is-animating`.
- Use data-stagger="1", "2", … to delay individual
digits by `n * var(--digit-stagger)`.

Direction:
--digit-dir-x / --digit-dir-y are unit-less multipliers
(e.g. 1, -1, 0) applied to --digit-distance.

## Tunable variables

| Variable | Default | Notes |
| --- | --- | --- |
| `--digit-dur` | `500ms` | sourced from `--p9-dur` |
| `--digit-distance` | `8px` | sourced from `--p9-distance` |
| `--digit-stagger` | `70ms` | sourced from `--p9-stagger` |
| `--digit-blur` | `2px` | sourced from `--p9-blur` |
| `--digit-ease` | `cubic-bezier(0.34, 1.45, 0.64, 1)` | sourced from `--p9-ease` |
| `--digit-dir-x` | `0` | sourced from `--p9-dir-x` |
| `--digit-dir-y` | `1` | sourced from `--p9-dir-y` |

The `:root` defaults below match the live tuning on [transitions.dev](https://transitions.dev). Drop them into your global stylesheet once β€” every transition in this skill reads from semantic names like these, so multiple transitions can share a single `:root` block.

```css
:root {
--digit-dur: 500ms;
--digit-distance: 8px;
--digit-stagger: 70ms;
--digit-blur: 2px;
--digit-ease: cubic-bezier(0.34, 1.45, 0.64, 1);
--digit-dir-x: 0;
--digit-dir-y: 1;
}
```

## CSS

```css
@keyframes t-digit-pop-in {
0% {
transform: translate(
calc(var(--digit-distance) * var(--digit-dir-x)),
calc(var(--digit-distance) * var(--digit-dir-y))
);
opacity: 0;
filter: blur(var(--digit-blur));
}
100% { transform: translate(0, 0); opacity: 1; filter: blur(0); }
}

.t-digit-group {
display: inline-flex;
align-items: baseline;
}
.t-digit {
display: inline-block;
will-change: transform, opacity, filter;
}
.t-digit-group.is-animating .t-digit {
animation: t-digit-pop-in var(--digit-dur) var(--digit-ease) both;
}
.t-digit-group.is-animating .t-digit[data-stagger="1"] {
animation-delay: var(--digit-stagger);
}
.t-digit-group.is-animating .t-digit[data-stagger="2"] {
animation-delay: calc(var(--digit-stagger) * 2);
}

@media (prefers-reduced-motion: reduce) {
.t-digit-group .t-digit { animation: none !important; }
}
```

The `@media (prefers-reduced-motion: reduce)` guard at the bottom of the snippet is required β€” keep it. It zeroes the transition for users who have asked for less motion at the OS level.

## JavaScript orchestration

```js
// Replay the digit pop-in: remove .is-animating, swap the digit spans,
// force a reflow, then re-add .is-animating. Mark the last two digits
// with data-stagger="1" / "2" so they ride in 1Γ— / 2Γ— --digit-stagger
// behind the leading digits.
const group = document.querySelector(".t-digit-group");

function setDigits(str) {
group.classList.remove("is-animating");
group.replaceChildren();
const chars = str.split("");
chars.forEach((ch, i) => {
const span = document.createElement("span");
span.className = "t-digit";
span.textContent = ch;
if (i === chars.length - 2) span.dataset.stagger = "1";
else if (i === chars.length - 1) span.dataset.stagger = "2";
group.appendChild(span);
});
void group.offsetHeight; // force reflow
group.classList.add("is-animating");
}
```

110 changes: 110 additions & 0 deletions .agents/skills/transitions-dev/03-notification-badge.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# Notification badge

## When to use

A small badge appearing on top of a trigger (bell, inbox, button). Slides in diagonally and pops the dot independently of the trigger so the trigger itself never moves.

## HTML usage

```html
<!-- Place .t-badge inside your trigger (bell icon, button, etc.). -->
<!-- The trigger must be position: relative so .t-badge can anchor to it. -->
<button class="your-trigger" style="position: relative">
<!-- your icon / trigger contents -->
<span class="t-badge" data-open="false">
<span class="t-badge-dot">1</span>
</span>
</button>
```

State: toggle data-open="true" / "false" on .t-badge.
Only the badge slides + pops β€” the trigger itself stays put.

## Tunable variables

| Variable | Default | Notes |
| --- | --- | --- |
| `--badge-slide-dur` | `260ms` | sourced from `--p1-pos-open-dur` |
| `--badge-pop-dur` | `500ms` | sourced from `--p1-scale-open-dur` |
| `--badge-pop-close-dur` | `180ms` | sourced from `--p1-scale-close-dur` |
| `--badge-fade-dur` | `400ms` | sourced from `--p1-opacity-open-dur` |
| `--badge-fade-close-dur` | `180ms` | sourced from `--p1-opacity-close-dur` |
| `--badge-blur` | `2px` | sourced from `--p1-blur` |
| `--badge-offset-x` | `-8.2px` | sourced from `--p1-distance-x` |
| `--badge-offset-y` | `12.4px` | sourced from `--p1-distance-y` |
| `--badge-slide-ease` | `cubic-bezier(0.22, 1, 0.36, 1)` | sourced from `--p1-ease-pos-open` |
| `--badge-pop-ease` | `cubic-bezier(0.34, 1.36, 0.64, 1)` | sourced from `--p1-ease-scale-open` |
| `--badge-close-ease` | `cubic-bezier(0.4, 0, 0.2, 1)` | sourced from `--p1-ease-close` |

The `:root` defaults below match the live tuning on [transitions.dev](https://transitions.dev). Drop them into your global stylesheet once β€” every transition in this skill reads from semantic names like these, so multiple transitions can share a single `:root` block.

```css
:root {
--badge-slide-dur: 260ms;
--badge-pop-dur: 500ms;
--badge-pop-close-dur: 180ms;
--badge-fade-dur: 400ms;
--badge-fade-close-dur: 180ms;
--badge-blur: 2px;
--badge-offset-x: -8.2px;
--badge-offset-y: 12.4px;
--badge-slide-ease: cubic-bezier(0.22, 1, 0.36, 1);
--badge-pop-ease: cubic-bezier(0.34, 1.36, 0.64, 1);
--badge-close-ease: cubic-bezier(0.4, 0, 0.2, 1);
}
```

## CSS

```css
@keyframes t-badge-slide-in {
from { transform: translate(var(--badge-offset-x), var(--badge-offset-y)); }
to { transform: translate(0, 0); }
}

/* .t-badge is the absolutely-positioned wrapper for the dot.
Adjust top/right (or left/bottom) to anchor it on your trigger. */
.t-badge {
position: absolute;
top: -6px;
right: -8px;
pointer-events: none;
will-change: transform;
}
.t-badge[data-open="true"] {
animation: t-badge-slide-in var(--badge-slide-dur) var(--badge-slide-ease);
}

.t-badge-dot {
display: block;
transform-origin: center;
transform: scale(1);
opacity: 1;
filter: blur(0);
transition:
transform var(--badge-pop-dur) var(--badge-pop-ease),
opacity var(--badge-fade-dur) var(--badge-pop-ease),
filter var(--badge-pop-dur) var(--badge-pop-ease);
will-change: transform, opacity, filter;
}
.t-badge[data-open="false"] .t-badge-dot {
transform: scale(0);
opacity: 0;
filter: blur(var(--badge-blur));
transition:
transform var(--badge-pop-close-dur) var(--badge-close-ease),
opacity var(--badge-fade-close-dur) var(--badge-close-ease),
filter var(--badge-pop-close-dur) var(--badge-close-ease);
}

@media (prefers-reduced-motion: reduce) {
.t-badge, .t-badge-dot { animation: none !important; transition: none !important; }
}
```

The `@media (prefers-reduced-motion: reduce)` guard at the bottom of the snippet is required β€” keep it. It zeroes the transition for users who have asked for less motion at the OS level.

## JavaScript orchestration

None β€” pure CSS. Toggle the documented HTML attributes or class names from whatever already drives state in your app.

97 changes: 97 additions & 0 deletions .agents/skills/transitions-dev/04-text-states-swap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# Text states swap

## When to use

Swapping the text of a status indicator in place β€” "Processing…" β†’ "Done", "Save" β†’ "Saved". The old text exits up with blur, the new text enters from below.

## HTML usage

```html
<span class="t-text-swap">Processing…</span>
```

Driven by JS (three-phase sequence):
1. Add `.is-exit` -> old text slides up + blurs + fades.
2. After --text-swap-dur: change textContent, then add
`.is-enter-start` (jumps to below, no transition).
3. Force reflow, remove `.is-enter-start` so the new text
animates back to 0 with the default transition.

## Tunable variables

| Variable | Default | Notes |
| --- | --- | --- |
| `--text-swap-dur` | `150ms` | sourced from `--p6-dur` |
| `--text-swap-translate-y` | `4px` | sourced from `--p6-translate-y` |
| `--text-swap-blur` | `2px` | sourced from `--p6-blur` |
| `--text-swap-ease` | `ease-in-out` | sourced from `--p6-ease` |

The `:root` defaults below match the live tuning on [transitions.dev](https://transitions.dev). Drop them into your global stylesheet once β€” every transition in this skill reads from semantic names like these, so multiple transitions can share a single `:root` block.

```css
:root {
--text-swap-dur: 150ms;
--text-swap-translate-y: 4px;
--text-swap-blur: 2px;
--text-swap-ease: ease-in-out;
}
```

## CSS

```css
.t-text-swap {
display: inline-block;
transform: translateY(0);
filter: blur(0);
opacity: 1;
transition:
transform var(--text-swap-dur) var(--text-swap-ease),
filter var(--text-swap-dur) var(--text-swap-ease),
opacity var(--text-swap-dur) var(--text-swap-ease);
will-change: transform, filter, opacity;
}
.t-text-swap.is-exit {
transform: translateY(calc(var(--text-swap-translate-y) * -1));
filter: blur(var(--text-swap-blur));
opacity: 0;
}
.t-text-swap.is-enter-start {
transform: translateY(var(--text-swap-translate-y));
filter: blur(var(--text-swap-blur));
opacity: 0;
transition: none;
}

@media (prefers-reduced-motion: reduce) {
.t-text-swap { transition: none !important; }
}
```

The `@media (prefers-reduced-motion: reduce)` guard at the bottom of the snippet is required β€” keep it. It zeroes the transition for users who have asked for less motion at the OS level.

## JavaScript orchestration

```js
// Three-phase text swap:
// 1. Add .is-exit β€” old text exits up with blur.
// 2. After --text-swap-dur, swap textContent and add .is-enter-start
// (jumps to "below, no transition"), force a reflow.
// 3. Remove .is-enter-start β€” new text animates back to rest.
const el = document.querySelector(".t-text-swap");
const dur = parseFloat(
getComputedStyle(document.documentElement).getPropertyValue("--text-swap-dur")
) || 200;

function swapText(next) {
el.classList.add("is-exit");
setTimeout(() => {
el.textContent = next;
el.classList.remove("is-exit");
el.classList.add("is-enter-start");
void el.offsetHeight; // force reflow so the next change transitions
el.classList.remove("is-enter-start");
}, dur);
}
```

Loading
Loading