Skip to content
Español

Navigate

Type to search. Press Escape to close.

    Foundations

    Light and motion effects

    Small classes that give any block a sense of light: a glint that runs along the border once, a border that glows where the pointer comes near, a scan line crossing a large surface, sparks, a breathing halo, and elements that reveal as they scroll into view. Everything is CSS except two tiny components: Proximity, which feeds the pointer position into custom properties, and Reveal, which marks elements when they enter the viewport. Under reduced motion nothing moves and nothing is hidden.

    CSS: css/effects.css (in ivolt.css, after the surfaces). JS: @intervolutions/ivolt/proximity, @intervolutions/ivolt/reveal or auto.js. Added in v0.4.

    Edge glint and proximity

    iv-edge-glint sends a light around the border on hover, on focus within, or when the element is revealed. iv-edge-near lights the border where the pointer approaches; it needs a Proximity root above it (by default the whole page) and a fine pointer.

    Edge glint and proximityfixtures/effects/edges.htmlOpen alone — Edge glint and proximity
    <section id="effects-edges" class="iv-stack iv-u-gap-6" style="padding: clamp(2rem, 6vw, 4rem); border-radius: var(--iv-radius-lg); background-color: var(--iv-color-surface)">
      <p class="iv-u-m-0">Made-up cards, for the fixture only. Hover the first card to send a glint around its border; move the pointer near the second one to light the edge closest to you. The proximity glow needs JavaScript and a fine pointer: with a touch screen, or with scripting off, the cards keep their plain border.</p>
      <div class="iv-grid iv-grid--auto iv-u-gap-6">
        <article class="iv-card iv-edge-glint">
          <div class="iv-card__body">
            <span class="iv-badge iv-badge--primary">Glint</span>
            <h3 class="iv-card__title">A spark runs the border</h3>
            <p class="iv-u-m-0">Sample content, not a real product. <code>iv-edge-glint</code> sweeps a lit segment once around the border on hover, on focus inside, and when <code>Reveal</code> marks the card as seen.</p>
          </div>
          <div class="iv-card__footer">
            <a class="iv-button iv-button--ghost iv-button--sm" href="#effects-edges">Back to the top of the fixture</a>
          </div>
        </article>
        <article class="iv-card iv-edge-near">
          <div class="iv-card__body">
            <span class="iv-badge iv-badge--info">Proximity</span>
            <h3 class="iv-card__title">The edge follows the pointer</h3>
            <p class="iv-u-m-0">Sample content, not a real product. <code>iv-edge-near</code> plus the <code>Proximity</code> component light the border where the pointer is, and fade it out over the last 160&nbsp;pixels.</p>
          </div>
          <div class="iv-card__footer">
            <a class="iv-button iv-button--ghost iv-button--sm" href="#effects-edges">Back to the top of the fixture</a>
          </div>
        </article>
        <article class="iv-card iv-edge-glint iv-edge-near">
          <div class="iv-card__body">
            <span class="iv-badge iv-badge--success">Both</span>
            <h3 class="iv-card__title">Glint and proximity together</h3>
            <p class="iv-u-m-0">Sample content, not a real product. The two effects use different pseudo-elements, so one card can carry both: the glint answers the hover, the halo answers the distance.</p>
          </div>
          <div class="iv-card__footer">
            <a class="iv-button iv-button--ghost iv-button--sm" href="#effects-edges">Back to the top of the fixture</a>
          </div>
        </article>
      </div>
    </section>

    Scan, sparks and pulse

    iv-scan and iv-scan--v move a thin line across a large block in a slow loop; iv-spark twinkles; iv-pulse-glow breathes. Use them sparingly: one scan per hero, a handful of sparks.

    Scan, sparks and pulsefixtures/effects/scan.htmlOpen alone — Scan, sparks and pulse
    <section id="effects-scan" class="iv-stack iv-u-gap-6">
      <div class="iv-scan" style="min-block-size: 14rem; display: grid; align-content: end; gap: var(--iv-space-2); padding: clamp(1.5rem, 4vw, 2.5rem); border-radius: var(--iv-radius-lg); background-image: linear-gradient(140deg, var(--iv-palette-neutral-950), var(--iv-palette-green-900) 55%, var(--iv-palette-neutral-900)); color: var(--iv-palette-neutral-0); --iv-color-surface: var(--iv-palette-neutral-800)">
        <p class="iv-u-m-0" style="font-family: var(--iv-font-mono); font-size: var(--iv-text-xs); letter-spacing: 0.2em; text-transform: uppercase; color: var(--iv-color-accent)">Horizontal scan</p>
        <h2 class="iv-u-m-0">A line of light crosses the block</h2>
        <p class="iv-u-m-0" style="max-inline-size: 52ch; color: color-mix(in srgb, var(--iv-palette-neutral-0) 84%, transparent)">Made-up copy, for the fixture only. <code>iv-scan</code> repeats every six seconds and stops under <code>prefers-reduced-motion</code>; it belongs on large blocks such as a hero, never on a paragraph.</p>
      </div>
      <div class="iv-scan iv-scan--v" style="min-block-size: 14rem; display: grid; align-content: end; gap: var(--iv-space-2); padding: clamp(1.5rem, 4vw, 2.5rem); border-radius: var(--iv-radius-lg); background-image: linear-gradient(200deg, var(--iv-palette-neutral-950), var(--iv-palette-blue-600) 120%); color: var(--iv-palette-neutral-0); --iv-color-surface: var(--iv-palette-neutral-800)">
        <p class="iv-u-m-0" style="font-family: var(--iv-font-mono); font-size: var(--iv-text-xs); letter-spacing: 0.2em; text-transform: uppercase; color: var(--iv-color-accent)">Vertical scan</p>
        <h2 class="iv-u-m-0">The same sweep, top to bottom</h2>
        <p class="iv-u-m-0" style="max-inline-size: 52ch; color: color-mix(in srgb, var(--iv-palette-neutral-0) 84%, transparent)">Made-up copy, for the fixture only. <code>iv-scan--v</code> only swaps the direction and the keyframes; the duration stays in <code>--iv-scan-duration</code>.</p>
      </div>
      <div class="iv-cluster iv-u-gap-6" style="align-items: center; padding: var(--iv-space-6); border-radius: var(--iv-radius-lg); background-color: var(--iv-color-surface)">
        <p class="iv-u-m-0"><strong>Sparks</strong>, staggered with <code>--iv-i</code>:</p>
        <span class="iv-cluster iv-u-gap-4" style="align-items: center">
          <span class="iv-spark" style="--iv-i: 0" aria-hidden="true"></span>
          <span class="iv-spark" style="--iv-i: 1" aria-hidden="true"></span>
          <span class="iv-spark" style="--iv-i: 2" aria-hidden="true"></span>
        </span>
        <p class="iv-u-m-0"><strong>Breathing halo</strong> for a status indicator:</p>
        <span class="iv-badge iv-badge--success iv-pulse-glow">Nominal (sample)</span>
      </div>
    </section>
    .iv-root {
      --iv-color-surface: var(--iv-palette-neutral-800);
      --iv-i: 0;
    }

    Reveals

    Add data-iv-component="reveal" to a container and iv-reveal (with --iv-i for stagger) to its children: they fade and rise when they enter the viewport. Without JavaScript or under reduced motion they are simply visible.

    Reveals on scrollfixtures/effects/reveal.htmlOpen alone — Reveals on scroll
    <section id="effects-reveal" data-iv-component="reveal" class="iv-stack iv-u-gap-12">
      <p class="iv-u-m-0">Made-up copy, for the fixture only. Scroll down: each block fades up as it enters the viewport, staggered by its inline <code>--iv-i</code>. Without JavaScript every block is visible from the start, and so it is under <code>prefers-reduced-motion</code>.</p>
      <article class="iv-card iv-reveal iv-edge-glint" style="--iv-i: 0; min-block-size: 9rem">
        <div class="iv-card__body">
          <span class="iv-badge iv-badge--primary">01</span>
          <h3 class="iv-card__title">The first block is already in view</h3>
          <p class="iv-u-m-0">Entering also fires <code>iv-edge-glint</code>, so the border is drawn once as the card arrives.</p>
        </div>
      </article>
      <article class="iv-card iv-reveal" style="--iv-i: 1; min-block-size: 9rem">
        <div class="iv-card__body">
          <span class="iv-badge">02</span>
          <h3 class="iv-card__title">Eighty milliseconds later</h3>
          <p class="iv-u-m-0">The delay is <code>--iv-i</code> multiplied by the <code>stagger</code> option, written as <code>--iv-reveal-delay</code>.</p>
        </div>
      </article>
      <article class="iv-card iv-reveal" style="--iv-i: 2; min-block-size: 9rem">
        <div class="iv-card__body">
          <span class="iv-badge">03</span>
          <h3 class="iv-card__title">One observer for the whole container</h3>
          <p class="iv-u-m-0">The component keeps a single <code>IntersectionObserver</code>; no scroll listener, no layout read.</p>
        </div>
      </article>
      <article class="iv-card iv-reveal" style="--iv-i: 3; min-block-size: 9rem">
        <div class="iv-card__body">
          <span class="iv-badge">04</span>
          <h3 class="iv-card__title">Seen once, left alone</h3>
          <p class="iv-u-m-0">With <code>repeat</code> off the element stops being observed as soon as it has entered.</p>
        </div>
      </article>
      <article class="iv-card iv-reveal" style="--iv-i: 4; min-block-size: 9rem">
        <div class="iv-card__body">
          <span class="iv-badge">05</span>
          <h3 class="iv-card__title">Threshold at fifteen per cent</h3>
          <p class="iv-u-m-0">A block counts as arrived when 15&nbsp;% of it is visible; the <code>threshold</code> option moves that line.</p>
        </div>
      </article>
      <article class="iv-card iv-reveal" style="--iv-i: 5; min-block-size: 9rem">
        <div class="iv-card__body">
          <span class="iv-badge">06</span>
          <h3 class="iv-card__title">Destroy puts the markup back</h3>
          <p class="iv-u-m-0">The attribute and the delay are removed, and so is the style attribute when nothing else was using it.</p>
        </div>
      </article>
    </section>
    .iv-root {
      --iv-i: 0;
    }

    Options

    ComponentOptionDefaultEffect
    Proximityradius160Distance in pixels at which a border starts to light up.
    Revealthreshold0.15Visible fraction that triggers the reveal.
    RevealrepeatfalseReveal again each time the element re-enters.
    Revealstagger80Milliseconds between siblings, multiplied by --iv-i.

    Common mistakes

    • Glint or proximity on tiny elements: the effect reads as noise. Keep it for cards, panels and heroes.
    • Relying on a reveal to show content: it is decoration; the content is in the page from the start.
    • Scan lines over paragraphs: keep them on media and large surfaces.