Skip to content

Repository files navigation

secondStack — motion lab

A gallery page for the motion of the secondStack mark: seven states of one asterisk, each of them plain CSS on an SVG.

Live: https://koalakate.github.io/secondstack-motion-lab/

The format is borrowed from orbs.jakubantalik.com; the visual language is its own — a dark lab sheet, hairline grid, monospaced labels, colour drawn only from the mark's palette.

Running it

Double-click index.html — everything works from a plain file:// URL, no build, no dependencies. If you'd rather serve it: npx serve .

What's in here

File Purpose
index.html Page markup: masthead, hero, grid, detail panel
styles.css Styling, plus the mark's base rules and colourways
anims.js All the motion: arm geometry + seven sets of keyframes
app.js Builds the SVG, the gallery, the controls, the code panel
logo.svg The static mark (favicon, export)
figma-master-spectrum*.svg Source exports the geometry was checked against

The mark

The geometry is taken from the source file — SecondStack.ai → "logo potion / Master · spectrum" (node 2525-9) — not eyeballed from a screenshot:

  • 8 arms every 45°, offset by 15° — so 15°, 60°, 105°, 150°, 195°, 240°, 285°, 330°
  • in the original (120 × 120.822): arm thickness 13.15, reach from centre ≈ 47.6 → rescaled here to a 200 × 200 square: thickness 21.9, reach 79, keeping the 1 : 3.6 proportion
  • 150° and 330° are a single solid bar running straight through the centre
  • arm roles in the spectrum layout: 15° blue #2FB5F4, 60° lime #B6F500, 105° coral #FF6F5E, 150° + 330° the bar in ink, 195° / 240° / 285° outlined
  • the outlined arms are painted first — in the source they sit under the bar

Mark frames in the source file are #F2F3F5 (light) and #14171B (dark); the page themes match.

One rule for the page

The mark on the first screen is the master: it does not rotate and does not loop. Its arms assemble once on load and stop in exactly the position from the source file. The masthead mark is static too and only comes alive under the cursor. Everything that moves lives in the grid below, where motion is what you came for.

By the same logic pulse, snap, glitch sit at the exact master mark when at rest and only depart from it inside the cycle. The deliberate exceptions are drift and wobble, where the rotation is the idea, and bloom / trace, which build the mark from nothing.

Features

  • Seven states: drift, pulse, bloom, snap, wobble, glitch, trace
  • Three colourways: mono, prism (the exact layout from the source file), outline
  • Speed 0.5× / 1× / 2× globally, plus a duration slider per animation
  • Light and dark themes — the mark is checked in both
  • Detail panel on click: the mark large, checked at 72/40/24/16 px, checked in context at 1 em next to text and inside a button, and the ready-to-paste CSS + markup with a copy button
  • move between states, esc closes
  • Honours prefers-reduced-motion, with a way to override it

Deep links

The page state lives in the URL, which makes it easy to send someone a specific state:

index.html#pulse                       opens that animation
index.html?theme=light                 light theme
index.html?colorway=prism#trace        colourway + animation

Using it inline

The mark is sized in em and painted with currentColor, so a single element covers every context — it inherits the surrounding type size and colour:

<span role="status" aria-label="Loading">
  <svg class="mark mark--inline" data-anim="pulse" viewBox="0 0 200 200"></svg>
  Thinking…
</span>
.mark--inline { width: 1em; height: 1em; vertical-align: -.15em; }

Adding your own animation

One object in the ANIMS array in anims.js — the card, the code panel and the copy snippet all appear on their own:

{
  id: 'my-anim',
  name: 'my-anim',
  note: 'One line on where this belongs.',
  dur: 2.4,
  css: `
[data-anim="my-anim"] .blade {
  animation: ss-my var(--dur) ease-in-out infinite;
  animation-delay: calc(var(--i) * var(--dur) / var(--n) * -1);
}
@keyframes ss-my { 0%,100% { transform: scaleY(1) } 50% { transform: scaleY(1.1) } }`,
}

Variables available inside an animation:

Variable Meaning
--dur duration, already divided by the global speed
--i arm index around the circle, 0…7
--n number of arms (8)
--s ±1, alternating by parity — for mirrored offsets
--p perimeter of one arm — for stroke-dashoffset (see trace)
--ac arm colour in the current colourway

Individual arms are selected by role: [data-role="ghost"] for the three outlined ones, [data-role="ink"] for the solid bar, blue / lime / coral for the coloured ones.

Taking it into a project

The detail panel copies a self-contained block: the SVG markup, two base rules, and the keyframes of the chosen animation. No libraries, and only transform and opacity are animated.

The two rules the mark falls apart without — they put the transform origin at its centre:

.mark .rot   { transform-box: view-box; transform-origin: 100px 100px; }
.mark .blade { transform-box: view-box; transform-origin: 0 0; }

Arm geometry (angles, lengths, thickness, roles) lives in ARMS in anims.js — if the mark is revised in the source file, there is one place to change.

About

Motion lab for the secondStack mark: seven pure-CSS states of one asterisk

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages