agentsclimarketplace

Gmira motion

Skill OthmanAdi/gmira/skills/gmira-motion

21 Claude Code skills for building web interfaces that do not look AI-generated. Forces a written visual direction before any element is placed, wires 7 shadcn registries (514 components), sets a GPU performance floor for WebGL and canvas work, and gates every build with Playwright at 5 viewports. Next.js, React, Tailwind v4.

Install
npx -y skills add OthmanAdi/gmira --skill gmira-motion

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • 12 days oldThe repository was created 12 days ago. New is not bad, but a brand new repository carrying a familiar-sounding name is the shape a typosquat arrives in, and there has been no time for anyone else to find a problem with it.
  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Use when adding, reviewing, or cutting motion on a web surface: scroll reveals, hero entrances, hover feedback, page and view transitions, pointer-following effects, and canvas or shader timing. Also use when every section fades up identically, when the page renders blank because a reveal script did not run, when an animation feels right on a 60Hz laptop and twitchy on a 120Hz phone, when reduced motion only slows things down instead of stopping them, or when someone asks to "add some animation" with no stated reason. Owns the one authored moment, the duration and easing table, the material palette past transform and opacity, and frame-rate independent easing.

SKILL.md

14.3 KB, ~3.4k tokens by cl100k_base, as published. Nobody here has run it

Motion

One authored moment. Not scattered effects, and not one identical entrance on every section.

Load ../gmira/references/DOCTRINE.md first. This skill implements its Part 3.5. Anything running on a canvas or a GPU also loads gmira-canvas.

The premise

Motion that does not carry meaning is decoration, and decoration on a page is weight, latency, and one more thing to break. The verification bar is a single sentence you must be able to finish for every animation on the surface: removing this would lose meaning or authored character. If the honest ending is "it would look a bit flatter", cut it and spend the budget on the one moment that does carry meaning.

The failure this skill exists to stop is not a missing animation. It is twelve of them, each 0.6s, each identical, each triggered by scroll.

Step 1: write the motion thesis before touching a keyframe

Four parts, four lines, in the surface brief. Any animation that does not trace to one of these lines does not get built.

PartQuestion
Focal momentWhich single sequence deserves authorship on this surface, if any
ContinuityWhich state, layout, or navigation changes need explaining rather than replacing
FeedbackWhich controls and outcomes need acknowledgment, and how small that can be
BudgetWhich effects may be expensive, how often they run, and what stops them

The focal moment comes from this product and this surface concept. A generic fade-and-rise, a hover lift, a parallax layer, or a scroll reveal is not a thesis. If the focal moment could be described without naming the product, go back to gmira-direction.

Budget per mode, from the doctrine:

ModeMotion budget
Persuade, ExperienceMotion may carry the voice. One rehearsed focal sequence beats repeated section reveals.
ReadLow. Nothing animated inside the measure. Reading is the task.
OperateFeedback, state, and continuity only. 150 to 250ms on most transitions. No page-load choreography.

Step 2: durations and easing

Timing expresses distance and consequence. Pick the range from what the thing is doing, not from a house default applied everywhere.

DurationWhat the range is forExamples
100 to 150msimmediate feedback, the control acknowledging the pointerbutton fill, checkbox tick, icon color, link underline
150 to 300msroutine state changetab switch, accordion, dropdown, toast entering, filter applying
300 to 500mslayout, overlay, or view transitionmodal, drawer, route change, shared-element move
500 to 800msthe one authored focal entrancethe hero sequence, once per surface

Three rules that hang off that table:

  1. Exit faster than entrance. 60 to 70% of the entrance duration. An element leaving does not need to be admired.
  2. Above 800ms reads as latency, not intent. The visitor stops reading it as design and starts reading it as a slow site.
  3. Cap total stagger at 300ms. Twelve items at 100ms each is 1.2 seconds, and the last card arrives after the visitor has already scrolled past it. Twelve items across 300ms is 25ms each. Stagger is for a list that appears as a list, never for reinterpreting every scrolled section as one.
:root {
  --ease-out-expo:  cubic-bezier(0.16, 1, 0.3, 1);   /* confident arrival, the default */
  --ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);   /* quieter, the Operate default */
  --ease-in-out:    cubic-bezier(0.65, 0, 0.35, 1);  /* only when a thing leaves and returns */
}

No bounce, no elastic, by reflex. Real objects decelerate smoothly. A spring is legitimate only when the committed world named one: a toy, a game, a physical mechanism in the brief.

Step 3: the resting state is the readable state

The default state in the stylesheet is what a visitor sees when the reveal script fails, arrives late, is blocked by an extension, or throws on an unrelated error. Never author opacity: 0 as the resting state. A page whose content is hidden until JavaScript un-hides it is a page that renders blank whenever JavaScript does not run.

INCORRECT   .reveal { opacity: 0; transform: translateY(24px) }
            .reveal.in { opacity: 1; transform: none; transition: all .6s }
            One failed chunk load and the entire page is a blank scroll.

CORRECT     .reveal { opacity: 1; transform: none;
                      transition: opacity 500ms var(--ease-out-expo),
                                  transform 500ms var(--ease-out-expo) }
            .js .reveal:not(.in) { opacity: 0; transform: translateY(16px) }
            The `js` class is written by the same script that does the revealing, so the
            hidden state only exists once the thing that can un-hide it is proven alive.

Verify on the built page. Anything over 20 characters of text sitting invisible at rest is the failed-reveal signature:

[...document.querySelectorAll('body *')].filter(e => {
  const s = getComputedStyle(e);
  return e.textContent.trim().length > 20 &&
         (parseFloat(s.opacity) === 0 || s.visibility === 'hidden');
}).length   // must be 0 with scripts running, and 0 with scripts disabled

Step 4: the section-entrance reflex

Every section entering identically is tell number 5 in the doctrine's list. The page ends up with no emphasis, only a queue: the visitor learns after two sections that nothing is coming and stops watching.

INCORRECT   <motion.section initial={{opacity:0, y:40}} whileInView={{opacity:1, y:0}}
                            transition={{duration:0.6, delay:i*0.1}} />
            applied to all seven sections, so the entrance means "a section exists".

CORRECT     One section gets the authored moment: the hero mask wipes open along the
            product's own axis, 700ms, once. The other six enter by not entering. Their
            emphasis comes from density, scale, and spacing, which cost nothing and
            survive a failed script.

If several sections genuinely need to arrive rather than exist, differentiate what arrives: one reveals by mask, one by measure (a rule drawing across), one by a counter settling. Same grammar, different sentence. Identical is the tell, not motion.

Source-side check:

rg -c "whileInView|data-reveal|animate-fade-up" src/
rg -no "duration: *[0-9.]+" src/ | sed 's/.*duration: *//' | sort | uniq -c | sort -rn | head

Four or more sections sharing one duration, delay, and offset triple is a finding.

Step 5: never transition: all

transition: all animates properties you did not choose, including ones that force layout, and it turns every future style change on that element into an accidental animation. It is also the reason a hover that was meant to change a background color also animates a border, a shadow, and a transform at the same wrong duration.

INCORRECT   transition: all 300ms ease;
CORRECT     transition: background-color 150ms var(--ease-out-quart),
                        box-shadow      150ms var(--ease-out-quart);
[...document.styleSheets].flatMap(s => { try { return [...s.cssRules] } catch { return [] } })
  .filter(r => r.style && /\ball\b/.test(r.style.transitionProperty || ''))
  .map(r => r.selectorText)

Tailwind: transition-all is the same bug with a shorter name. Use transition-colors, transition-transform, transition-opacity, or a named transition-[opacity,transform].

Step 6: the material palette

Transform and opacity are a reliable foundation, not the whole palette. Each material communicates something specific, and the choice is a meaning choice before it is a performance choice.

MaterialCommunicatesCost
transform translate, scale, rotateposition, arrival, relationshipcompositor, cheapest
opacitypresence, attentioncompositor, cheapest
filter: blur()one plane receding, focus pullingpaint, bound the region
backdrop-filtera surface in front of live contentpaint, expensive over large areas
clip-pathreveal as composition, something uncoveredpaint, animatable between same-type shapes only
mask-image position or sizea wipe that follows the world's own geometrypaint
box-shadowelevation change under the pointerpaint, prefer animating a pseudo-element's opacity
color, gradient positionmaterial and energy, temperature shiftpaint, register with @property to interpolate
displacement, refractionmaterial under pressure, glass, depthGPU tier
flow field, advectioncurrent, direction, time passingGPU tier
feedback bufferpersistence, trail, memoryGPU tier
SDF morphone thing becoming anotherGPU tier
particle advectionquantity, swarm, scaleGPU tier

Pick the one whose meaning matches the brief. If two are equally apt, take the cheaper one. Do not stack techniques for spectacle: one strong material idea carried through the focal sequence and quiet supporting states is enough. The GPU tier belongs to gmira-canvas and carries the whole Part 4 floor with it.

What not to animate: width, height, top, left, margin, padding. Use a transform, FLIP, or grid-template-rows: 0fr to 1fr for a height reveal. will-change goes on during a known animation and comes off after; left on permanently it holds a compositor layer per element for the life of the page.

Step 7: frame-rate independent easing

A fixed lerp factor is a per-frame constant pretending to be a per-second one. The same code is a different animation on every display, and nobody catches it because the authoring machine is 60Hz.

INCORRECT   x += (target - x) * 0.1;    // "10% per frame"

            Remaining distance after one second:
              30Hz   0.9^30  = 0.042
              60Hz   0.9^60  = 0.0018      <- what you authored against
              144Hz  0.9^144 = 0.0000003
            On a 120Hz phone the follow is roughly twice as fast as authored, which
            reads as twitch, and on a throttled tab it lags.

CORRECT     const delta = Math.min((now - last) / 1000, 1 / 30);   // clamp, see below
            last = now;
            const k = 1 - Math.exp(-delta / tau);                  // tau in seconds
            x += (target - x) * k;

tau is the time constant: after tau seconds the value has covered roughly 63% of the remaining distance, on every display. Authoring values: 0.08 to 0.12 for a cursor follow that feels attached to the pointer, 0.25 to 0.4 for a lazy parallax or a settling counter.

Two more forms of the same identity:

const ease  = 1 - Math.exp(-delta * rate);            // rate form, higher is snappier
const decay = Math.pow(perFrameConstant, delta * 60); // converts an authored per-frame value

Three details that go with it:

  1. Clamp delta to 1/30. A backgrounded tab returns with a multi-second delta, and an unclamped integration jumps or explodes on the first frame back.
  2. Reset last on resume. Starting a stopped loop without resetting the clock integrates the entire idle period.
  3. Snap on settle. When the remaining distance is under an epsilon, assign the target exactly and stop the loop. The exponential tail never reaches zero, so without a snap the rAF runs forever for sub-pixel motion nobody can see.

Step 8: reduced motion is a total kill switch

Not a slowdown, not a shorter duration. The CSS floor:

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

That block does nothing for JavaScript timelines, rAF loops, canvas work, or a smooth-scroll library. Query the media feature, listen for changes, and take one named strategy per effect:

StrategyFits
Snap the easing constant to 1pointer followers, lenses, magnifiers: it still tracks, it just teleports
Kill the timeline, render the target statetimed sequences, glitch, looping ambience
Refuse new input, keep the rendersplash and impulse effects
Pass a reduced uniform the shader branches oncanvas work with an authored still frame
const mq = window.matchMedia('(prefers-reduced-motion: reduce)');
let reduced = mq.matches;
mq.addEventListener('change', () => { reduced = mq.matches; start(); });
const k = reduced ? 1 : 1 - Math.exp(-delta / tau);

Lenis and any smooth-scroll layer must be destroyed under reduced motion, not slowed. Verify in the browser with devtools Rendering, "Emulate CSS media feature prefers-reduced-motion":

document.getAnimations().filter(a => a.playState === 'running' &&
  (a.effect?.getTiming().duration || 0) > 1)      // must be empty

Canvas effects freeze at a still frame you chose by looking at it. t = 0 is usually the least composed frame the effect ever has.

Checks before this skill is done

  • The motion thesis exists in writing, and every animation on the surface traces to one of its four lines
  • Exactly one authored focal moment, and no two sections enter identically
  • Every duration falls in a range from the table, and every exit is faster than its entrance
  • Zero transition: all and zero transition-all in the source
  • Content is visible at rest with JavaScript disabled: the invisible-text query returns 0
  • No rAF loop uses a fixed lerp factor; every one clamps delta and snaps on settle
  • Reduced motion emulated in devtools: no running animations, rAF loops stopped, smooth scroll destroyed
  • For every remaining animation you can finish the sentence "removing this would lose meaning or authored character"

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.