Tailwind v4MIT licensed

Tailwind Dual Layer Stroke Button with Offset Trailing Path

A crisp leader and a blurred follower 90ms behind it, which the eye reads as speed rather than as two lines. The offset is two utilities — duration-[620ms] on the leader, duration-[800ms] delay-[90ms] on the trail — and the comet pair animates the offset negative, so a 4.5% fraction of pathLength="1" travels the path instead of a line being drawn.

Live Demo
Plain CSSTailwind v4
Try it

Nucleo: 44,000+ SVG icons in five families · All Icons Bundle $149

Nucleo · Affiliate link

Get the Nucleo bundle

The code

<section class="svgb-12 relative block w-full min-h-svh overflow-hidden bg-svgb12-bg text-svgb12-fg font-svgb12-sans">
  <input class="peer absolute opacity-0 pointer-events-none" type="checkbox" id="svgb-12-theme">
  <label class="absolute z-[5] top-4 right-4 grid place-items-center size-11 rounded border border-current/22 bg-current/8 cursor-pointer transition-[background-color,transform] duration-150 hover:bg-current/15 active:scale-95 peer-focus-visible:outline-2 peer-focus-visible:outline-current peer-focus-visible:outline-offset-[3px] peer-checked:[&_svg]:rotate-180" for="svgb-12-theme">
    <svg class="size-[18px] transition-transform duration-[400ms] ease-[cubic-bezier(0.2,0.7,0.3,1)]" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" aria-hidden="true"><circle cx="12" cy="12" r="8.5"/><path d="M12 3.5a8.5 8.5 0 0 0 0 17Z" fill="currentColor" stroke="none"/></svg>
    <span class="absolute size-px overflow-hidden [clip-path:inset(50%)] whitespace-nowrap">Switch colour scheme</span>
  </label>
  <div class="relative min-h-svh grid place-items-center p-[clamp(22px,5vw,70px)]">
    <div class="flex flex-col items-start gap-[clamp(16px,3vw,26px)] w-full max-w-105">
      <div class="grid grid-cols-2 gap-x-5 gap-y-2.5 w-full">
        <div class="flex flex-col gap-0.5 pb-2 border-b border-svgb12-rest"><span class="font-svgb12-mono text-[9.5px] tracking-[0.22em] uppercase text-svgb12-muted">Lap</span><span class="text-[26px] font-bold italic tracking-[-0.01em] tabular-nums">14</span></div>
        <div class="flex flex-col gap-0.5 pb-2 border-b border-svgb12-rest"><span class="font-svgb12-mono text-[9.5px] tracking-[0.22em] uppercase text-svgb12-muted">Delta</span><span class="text-[26px] font-bold italic tracking-[-0.01em] tabular-nums text-svgb12-good">-0.412</span></div>
        <div class="flex flex-col gap-0.5 pb-2 border-b border-svgb12-rest"><span class="font-svgb12-mono text-[9.5px] tracking-[0.22em] uppercase text-svgb12-muted">Tyre</span><span class="text-[26px] font-bold italic tracking-[-0.01em] tabular-nums">C3 &middot; 62%</span></div>
        <div class="flex flex-col gap-0.5 pb-2 border-b border-svgb12-rest"><span class="font-svgb12-mono text-[9.5px] tracking-[0.22em] uppercase text-svgb12-muted">ERS</span><span class="text-[26px] font-bold italic tracking-[-0.01em] tabular-nums">2.1 MJ</span></div>
      </div>

      <button class="group relative w-[min(340px,92vw)] h-[78px] grid place-items-center border-0 p-0 bg-transparent isolate text-xl font-semibold italic tracking-[0.1em] uppercase cursor-pointer focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_color-mix(in_oklab,var(--color-svgb12-stroke)_34%,transparent)]" type="button">
        <svg class="absolute inset-0 w-full h-full overflow-visible" viewBox="0 0 340 78" aria-hidden="true">
          <path class="fill-none [stroke-linejoin:miter] stroke-svgb12-rest [stroke-width:1.4]" d="M26 3H337L314 75H3Z" pathLength="1" vector-effect="non-scaling-stroke"/>
          <path class="fill-none [stroke-linejoin:miter] stroke-svgb12-stroke text-svgb12-stroke [filter:drop-shadow(0_0_10px_currentColor)] [mix-blend-mode:var(--svgb-12-blend)] motion-reduce:!filter-none motion-reduce:opacity-0 [stroke-width:7] opacity-36 [stroke-dasharray:1] [stroke-dashoffset:1] transition-[stroke-dashoffset] duration-[800ms] delay-[90ms] ease-[cubic-bezier(0.22,1,0.36,1)] group-hover:[stroke-dashoffset:0] group-focus-visible:[stroke-dashoffset:0] motion-reduce:!transition-none" d="M26 3H337L314 75H3Z" pathLength="1" vector-effect="non-scaling-stroke"/>
          <path class="fill-none [stroke-linejoin:miter] stroke-svgb12-stroke [stroke-width:2] [stroke-dasharray:1] [stroke-dashoffset:1] transition-[stroke-dashoffset] duration-[620ms] ease-[cubic-bezier(0.22,1,0.36,1)] group-hover:[stroke-dashoffset:0] group-focus-visible:[stroke-dashoffset:0] motion-reduce:transition-none motion-reduce:[stroke-dasharray:none] motion-reduce:[stroke-dashoffset:0] motion-reduce:opacity-0 motion-reduce:group-hover:opacity-100 motion-reduce:group-focus-visible:opacity-100" d="M26 3H337L314 75H3Z" pathLength="1" vector-effect="non-scaling-stroke"/>
        </svg>
        <span class="relative z-[2] transition-[color,letter-spacing] duration-[400ms] motion-reduce:transition-none group-hover:text-svgb12-stroke group-hover:tracking-[0.16em] group-focus-visible:text-svgb12-stroke group-focus-visible:tracking-[0.16em]">Push lap</span>
      </button>

      <button class="group relative w-[min(340px,92vw)] h-[78px] grid place-items-center border-0 p-0 bg-transparent isolate text-xl font-semibold italic tracking-[0.1em] uppercase cursor-pointer focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_color-mix(in_oklab,var(--color-svgb12-stroke)_34%,transparent)]" type="button">
        <svg class="absolute inset-0 w-full h-full overflow-visible" viewBox="0 0 340 78" aria-hidden="true">
          <path class="fill-none [stroke-linejoin:miter] stroke-svgb12-rest [stroke-width:1.4]" d="M26 3H337L314 75H3Z" pathLength="1" vector-effect="non-scaling-stroke"/>
          <path class="fill-none [stroke-linejoin:miter] stroke-svgb12-stroke text-svgb12-stroke [filter:drop-shadow(0_0_10px_currentColor)] [mix-blend-mode:var(--svgb-12-blend)] motion-reduce:!filter-none motion-reduce:opacity-0 [stroke-width:6] opacity-32 [stroke-dasharray:0.16_0.84] [stroke-dashoffset:0.16] transition-[stroke-dashoffset] duration-[900ms] ease-linear group-hover:[stroke-dashoffset:-0.84] group-focus-visible:[stroke-dashoffset:-0.84] motion-reduce:!transition-none" d="M26 3H337L314 75H3Z" pathLength="1" vector-effect="non-scaling-stroke"/>
          <path class="fill-none [stroke-linejoin:miter] stroke-svgb12-fg [stroke-width:2.4] [stroke-dasharray:0.045_0.955] [stroke-dashoffset:0.045] transition-[stroke-dashoffset] duration-[900ms] ease-linear group-hover:[stroke-dashoffset:-0.955] group-focus-visible:[stroke-dashoffset:-0.955] motion-reduce:!transition-none motion-reduce:opacity-0" d="M26 3H337L314 75H3Z" pathLength="1" vector-effect="non-scaling-stroke"/>
        </svg>
        <span class="relative z-[2] transition-[color,letter-spacing] duration-[400ms] motion-reduce:transition-none group-hover:text-svgb12-stroke group-hover:tracking-[0.16em] group-focus-visible:text-svgb12-stroke group-focus-visible:tracking-[0.16em]">Deploy overtake</span>
      </button>

      <p class="m-0 font-svgb12-mono text-[10.5px] tracking-[0.04em] text-svgb12-muted">leader 620ms &middot; trail 800ms + 90ms &middot; paint order: trail first</p>
    </div>
  </div>
</section>
@import "tailwindcss";

@theme {
  --color-svgb12-bg: #08090a;
  --color-svgb12-bg: oklch(0.145 0.002 250);
  --color-svgb12-fg: #edeff1;
  --color-svgb12-fg: oklch(0.95 0.003 250);
  --color-svgb12-muted: #83878d;
  --color-svgb12-muted: oklch(0.645 0.008 250);
  --color-svgb12-rest: rgba(237, 239, 241, 0.14);
  --color-svgb12-rest: oklch(0.95 0.003 250 / 0.14);
  --color-svgb12-stroke: #00e5ff;
  --color-svgb12-stroke: oklch(0.85 0.15 205);
  --color-svgb12-good: #7cff8f;
  --color-svgb12-good: oklch(0.89 0.18 145);
  --font-svgb12-sans: 'Saira Condensed', system-ui, sans-serif;
  --font-svgb12-mono: 'JetBrains Mono', ui-monospace, monospace;
}
/* screen over a dark rack, multiply over a light one: the blend mode itself is
   scheme-dependent, which no utility can express as one class */

.svgb-12 {
  --svgb-12-blend: screen;
}

.svgb-12::before {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: var(--svgb-12-weave), radial-gradient(70% 60% at 50% 40%, color-mix(in oklab, var(--color-svgb12-stroke) 8%, transparent), transparent 70%);
}

.svgb-12 {
  --svgb-12-weave: repeating-linear-gradient(45deg, oklch(1 0 0 / 0.022) 0 2px, transparent 2px 5px), repeating-linear-gradient(-45deg, oklch(1 0 0 / 0.022) 0 2px, transparent 2px 5px);
}

.svgb-12:has(#svgb-12-theme:checked) {
  --color-svgb12-bg: #f1f2f4;
  --color-svgb12-bg: oklch(0.955 0.003 250);
  --color-svgb12-fg: #101214;
  --color-svgb12-fg: oklch(0.185 0.004 250);
  --color-svgb12-muted: #555a60;
  --color-svgb12-muted: oklch(0.49 0.008 250);
  --color-svgb12-rest: rgba(16, 18, 20, 0.16);
  --color-svgb12-rest: oklch(0.185 0.004 250 / 0.16);
  --color-svgb12-stroke: #00647f;
  --color-svgb12-stroke: oklch(0.45 0.11 215);
  --color-svgb12-good: #0d6b2c;
  --color-svgb12-good: oklch(0.46 0.14 148);
  --svgb-12-blend: multiply;
  --svgb-12-weave: repeating-linear-gradient(45deg, oklch(0 0 0 / 0.03) 0 2px, transparent 2px 5px), repeating-linear-gradient(-45deg, oklch(0 0 0 / 0.03) 0 2px, transparent 2px 5px);
}

Setup — do this once per project

Skip this if your project already runs Tailwind v4.

1Load Tailwind
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>

Compiles in the browser — perfect for a CodePen or a quick test, not for production.

2Add the stylesheetRequiredthe @theme block is what makes the custom utilities exist
@import "tailwindcss";

@theme {
  --color-svgb12-bg: #08090a;
  --color-svgb12-bg: oklch(0.145 0.002 250);
  --color-svgb12-fg: #edeff1;
  --color-svgb12-fg: oklch(0.95 0.003 250);
  --color-svgb12-muted: #83878d;
  --color-svgb12-muted: oklch(0.645 0.008 250);
  --color-svgb12-rest: rgba(237, 239, 241, 0.14);
  --color-svgb12-rest: oklch(0.95 0.003 250 / 0.14);
  --color-svgb12-stroke: #00e5ff;
  --color-svgb12-stroke: oklch(0.85 0.15 205);
  --color-svgb12-good: #7cff8f;
  --color-svgb12-good: oklch(0.89 0.18 145);
  --font-svgb12-sans: 'Saira Condensed', system-ui, sans-serif;
  --font-svgb12-mono: 'JetBrains Mono', ui-monospace, monospace;
}
/* screen over a dark rack, multiply over a light one: the blend mode itself is
   scheme-dependent, which no utility can express as one class */

.svgb-12 {
  --svgb-12-blend: screen;
}

.svgb-12::before {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: var(--svgb-12-weave), radial-gradient(70% 60% at 50% 40%, color-mix(in oklab, var(--color-svgb12-stroke) 8%, transparent), transparent 70%);
}

.svgb-12 {
  --svgb-12-weave: repeating-linear-gradient(45deg, oklch(1 0 0 / 0.022) 0 2px, transparent 2px 5px), repeating-linear-gradient(-45deg, oklch(1 0 0 / 0.022) 0 2px, transparent 2px 5px);
}

.svgb-12:has(#svgb-12-theme:checked) {
  --color-svgb12-bg: #f1f2f4;
  --color-svgb12-bg: oklch(0.955 0.003 250);
  --color-svgb12-fg: #101214;
  --color-svgb12-fg: oklch(0.185 0.004 250);
  --color-svgb12-muted: #555a60;
  --color-svgb12-muted: oklch(0.49 0.008 250);
  --color-svgb12-rest: rgba(16, 18, 20, 0.16);
  --color-svgb12-rest: oklch(0.185 0.004 250 / 0.16);
  --color-svgb12-stroke: #00647f;
  --color-svgb12-stroke: oklch(0.45 0.11 215);
  --color-svgb12-good: #0d6b2c;
  --color-svgb12-good: oklch(0.46 0.14 148);
  --svgb-12-blend: multiply;
  --svgb-12-weave: repeating-linear-gradient(45deg, oklch(0 0 0 / 0.03) 0 2px, transparent 2px 5px), repeating-linear-gradient(-45deg, oklch(0 0 0 / 0.03) 0 2px, transparent 2px 5px);
}
3Fonts used by this demooptional — skip if you have your own type stack
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Saira+Condensed:wght@400;600;700&family=JetBrains+Mono:wght@400;500&display=swap">

What changed in the port

Which parts became utilities, which stayed CSS, and why.

  • The webfonts load as <link> tags in setup, not as an @import inside the stylesheet. Tailwind emits user @import rules after its own @layer blocks, and a late @import is invalid CSS — the browser drops it and the demo silently falls back to system fonts.
  • Paint order is document order in SVG — z-index does nothing here — so the soft trail is written before the crisp leader in the markup, and no utility can rescue a wrong order.
  • The offset between the two layers is two utilities: the leader gets duration-[620ms], the trail duration-[800ms] delay-[90ms]. That 90ms is the whole sense of a wake.
  • The comet pair animates the offset negative — group-hover:[stroke-dashoffset:-0.84] — so a fractional segment travels the path instead of a line being drawn.
  • Both segments are fractions of pathLength=&quot;1&quot;: [stroke-dasharray:0.045_0.955] is a 4.5% spark on any perimeter.
  • [mix-blend-mode:var(--svgb-12-blend)] reads a variable because the correct blend flips with the scheme — screen on the dark rack, multiply on the light one.
  • The glow uses text-svgb12-stroke plus drop-shadow(...currentColor), which is how you tint an SVG filter from a token without repeating the colour.
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS demo from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Tailwind Dual Layer Stroke Button with Offset Trailing Path
Source: https://codefronts.com/components/tailwind-svg-stroke-buttons/tailwind-dual-layer-stroke-button-with-offset-trailing-path/

A crisp leader and a blurred follower 90ms behind it, which the eye reads as speed rather than as two lines. The offset is two utilities &mdash; duration-[620ms] on the leader, duration-[800ms] delay-[90ms] on the trail &mdash; and the comet pair animates the offset negative, so a 4.5% fraction of pathLength="1" travels the path instead of a line being drawn.
## HTML
```html
<section class="svgb-12 relative block w-full min-h-svh overflow-hidden bg-svgb12-bg text-svgb12-fg font-svgb12-sans">
  <input class="peer absolute opacity-0 pointer-events-none" type="checkbox" id="svgb-12-theme">
  <label class="absolute z-[5] top-4 right-4 grid place-items-center size-11 rounded border border-current/22 bg-current/8 cursor-pointer transition-[background-color,transform] duration-150 hover:bg-current/15 active:scale-95 peer-focus-visible:outline-2 peer-focus-visible:outline-current peer-focus-visible:outline-offset-[3px] peer-checked:[&_svg]:rotate-180" for="svgb-12-theme">
    <svg class="size-[18px] transition-transform duration-[400ms] ease-[cubic-bezier(0.2,0.7,0.3,1)]" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" aria-hidden="true"><circle cx="12" cy="12" r="8.5"/><path d="M12 3.5a8.5 8.5 0 0 0 0 17Z" fill="currentColor" stroke="none"/></svg>
    <span class="absolute size-px overflow-hidden [clip-path:inset(50%)] whitespace-nowrap">Switch colour scheme</span>
  </label>
  <div class="relative min-h-svh grid place-items-center p-[clamp(22px,5vw,70px)]">
    <div class="flex flex-col items-start gap-[clamp(16px,3vw,26px)] w-full max-w-105">
      <div class="grid grid-cols-2 gap-x-5 gap-y-2.5 w-full">
        <div class="flex flex-col gap-0.5 pb-2 border-b border-svgb12-rest"><span class="font-svgb12-mono text-[9.5px] tracking-[0.22em] uppercase text-svgb12-muted">Lap</span><span class="text-[26px] font-bold italic tracking-[-0.01em] tabular-nums">14</span></div>
        <div class="flex flex-col gap-0.5 pb-2 border-b border-svgb12-rest"><span class="font-svgb12-mono text-[9.5px] tracking-[0.22em] uppercase text-svgb12-muted">Delta</span><span class="text-[26px] font-bold italic tracking-[-0.01em] tabular-nums text-svgb12-good">-0.412</span></div>
        <div class="flex flex-col gap-0.5 pb-2 border-b border-svgb12-rest"><span class="font-svgb12-mono text-[9.5px] tracking-[0.22em] uppercase text-svgb12-muted">Tyre</span><span class="text-[26px] font-bold italic tracking-[-0.01em] tabular-nums">C3 &middot; 62%</span></div>
        <div class="flex flex-col gap-0.5 pb-2 border-b border-svgb12-rest"><span class="font-svgb12-mono text-[9.5px] tracking-[0.22em] uppercase text-svgb12-muted">ERS</span><span class="text-[26px] font-bold italic tracking-[-0.01em] tabular-nums">2.1 MJ</span></div>
      </div>

      <button class="group relative w-[min(340px,92vw)] h-[78px] grid place-items-center border-0 p-0 bg-transparent isolate text-xl font-semibold italic tracking-[0.1em] uppercase cursor-pointer focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_color-mix(in_oklab,var(--color-svgb12-stroke)_34%,transparent)]" type="button">
        <svg class="absolute inset-0 w-full h-full overflow-visible" viewBox="0 0 340 78" aria-hidden="true">
          <path class="fill-none [stroke-linejoin:miter] stroke-svgb12-rest [stroke-width:1.4]" d="M26 3H337L314 75H3Z" pathLength="1" vector-effect="non-scaling-stroke"/>
          <path class="fill-none [stroke-linejoin:miter] stroke-svgb12-stroke text-svgb12-stroke [filter:drop-shadow(0_0_10px_currentColor)] [mix-blend-mode:var(--svgb-12-blend)] motion-reduce:!filter-none motion-reduce:opacity-0 [stroke-width:7] opacity-36 [stroke-dasharray:1] [stroke-dashoffset:1] transition-[stroke-dashoffset] duration-[800ms] delay-[90ms] ease-[cubic-bezier(0.22,1,0.36,1)] group-hover:[stroke-dashoffset:0] group-focus-visible:[stroke-dashoffset:0] motion-reduce:!transition-none" d="M26 3H337L314 75H3Z" pathLength="1" vector-effect="non-scaling-stroke"/>
          <path class="fill-none [stroke-linejoin:miter] stroke-svgb12-stroke [stroke-width:2] [stroke-dasharray:1] [stroke-dashoffset:1] transition-[stroke-dashoffset] duration-[620ms] ease-[cubic-bezier(0.22,1,0.36,1)] group-hover:[stroke-dashoffset:0] group-focus-visible:[stroke-dashoffset:0] motion-reduce:transition-none motion-reduce:[stroke-dasharray:none] motion-reduce:[stroke-dashoffset:0] motion-reduce:opacity-0 motion-reduce:group-hover:opacity-100 motion-reduce:group-focus-visible:opacity-100" d="M26 3H337L314 75H3Z" pathLength="1" vector-effect="non-scaling-stroke"/>
        </svg>
        <span class="relative z-[2] transition-[color,letter-spacing] duration-[400ms] motion-reduce:transition-none group-hover:text-svgb12-stroke group-hover:tracking-[0.16em] group-focus-visible:text-svgb12-stroke group-focus-visible:tracking-[0.16em]">Push lap</span>
      </button>

      <button class="group relative w-[min(340px,92vw)] h-[78px] grid place-items-center border-0 p-0 bg-transparent isolate text-xl font-semibold italic tracking-[0.1em] uppercase cursor-pointer focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_color-mix(in_oklab,var(--color-svgb12-stroke)_34%,transparent)]" type="button">
        <svg class="absolute inset-0 w-full h-full overflow-visible" viewBox="0 0 340 78" aria-hidden="true">
          <path class="fill-none [stroke-linejoin:miter] stroke-svgb12-rest [stroke-width:1.4]" d="M26 3H337L314 75H3Z" pathLength="1" vector-effect="non-scaling-stroke"/>
          <path class="fill-none [stroke-linejoin:miter] stroke-svgb12-stroke text-svgb12-stroke [filter:drop-shadow(0_0_10px_currentColor)] [mix-blend-mode:var(--svgb-12-blend)] motion-reduce:!filter-none motion-reduce:opacity-0 [stroke-width:6] opacity-32 [stroke-dasharray:0.16_0.84] [stroke-dashoffset:0.16] transition-[stroke-dashoffset] duration-[900ms] ease-linear group-hover:[stroke-dashoffset:-0.84] group-focus-visible:[stroke-dashoffset:-0.84] motion-reduce:!transition-none" d="M26 3H337L314 75H3Z" pathLength="1" vector-effect="non-scaling-stroke"/>
          <path class="fill-none [stroke-linejoin:miter] stroke-svgb12-fg [stroke-width:2.4] [stroke-dasharray:0.045_0.955] [stroke-dashoffset:0.045] transition-[stroke-dashoffset] duration-[900ms] ease-linear group-hover:[stroke-dashoffset:-0.955] group-focus-visible:[stroke-dashoffset:-0.955] motion-reduce:!transition-none motion-reduce:opacity-0" d="M26 3H337L314 75H3Z" pathLength="1" vector-effect="non-scaling-stroke"/>
        </svg>
        <span class="relative z-[2] transition-[color,letter-spacing] duration-[400ms] motion-reduce:transition-none group-hover:text-svgb12-stroke group-hover:tracking-[0.16em] group-focus-visible:text-svgb12-stroke group-focus-visible:tracking-[0.16em]">Deploy overtake</span>
      </button>

      <p class="m-0 font-svgb12-mono text-[10.5px] tracking-[0.04em] text-svgb12-muted">leader 620ms &middot; trail 800ms + 90ms &middot; paint order: trail first</p>
    </div>
  </div>
</section>
```
## CSS
```css
@import "tailwindcss";

@theme {
  --color-svgb12-bg: #08090a;
  --color-svgb12-bg: oklch(0.145 0.002 250);
  --color-svgb12-fg: #edeff1;
  --color-svgb12-fg: oklch(0.95 0.003 250);
  --color-svgb12-muted: #83878d;
  --color-svgb12-muted: oklch(0.645 0.008 250);
  --color-svgb12-rest: rgba(237, 239, 241, 0.14);
  --color-svgb12-rest: oklch(0.95 0.003 250 / 0.14);
  --color-svgb12-stroke: #00e5ff;
  --color-svgb12-stroke: oklch(0.85 0.15 205);
  --color-svgb12-good: #7cff8f;
  --color-svgb12-good: oklch(0.89 0.18 145);
  --font-svgb12-sans: 'Saira Condensed', system-ui, sans-serif;
  --font-svgb12-mono: 'JetBrains Mono', ui-monospace, monospace;
}
/* screen over a dark rack, multiply over a light one: the blend mode itself is
   scheme-dependent, which no utility can express as one class */

.svgb-12 {
  --svgb-12-blend: screen;
}

.svgb-12::before {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: var(--svgb-12-weave), radial-gradient(70% 60% at 50% 40%, color-mix(in oklab, var(--color-svgb12-stroke) 8%, transparent), transparent 70%);
}

.svgb-12 {
  --svgb-12-weave: repeating-linear-gradient(45deg, oklch(1 0 0 / 0.022) 0 2px, transparent 2px 5px), repeating-linear-gradient(-45deg, oklch(1 0 0 / 0.022) 0 2px, transparent 2px 5px);
}

.svgb-12:has(#svgb-12-theme:checked) {
  --color-svgb12-bg: #f1f2f4;
  --color-svgb12-bg: oklch(0.955 0.003 250);
  --color-svgb12-fg: #101214;
  --color-svgb12-fg: oklch(0.185 0.004 250);
  --color-svgb12-muted: #555a60;
  --color-svgb12-muted: oklch(0.49 0.008 250);
  --color-svgb12-rest: rgba(16, 18, 20, 0.16);
  --color-svgb12-rest: oklch(0.185 0.004 250 / 0.16);
  --color-svgb12-stroke: #00647f;
  --color-svgb12-stroke: oklch(0.45 0.11 215);
  --color-svgb12-good: #0d6b2c;
  --color-svgb12-good: oklch(0.46 0.14 148);
  --svgb-12-blend: multiply;
  --svgb-12-weave: repeating-linear-gradient(45deg, oklch(0 0 0 / 0.03) 0 2px, transparent 2px 5px), repeating-linear-gradient(-45deg, oklch(0 0 0 / 0.03) 0 2px, transparent 2px 5px);
}
```

How this works

Two layers, one gap that opens and closes. The leader finishes first; the trail is both slower and later, so the distance between them grows through the middle of the draw and closes at the end:

<!-- written FIRST: the soft trail -->
class="[stroke-width:9] [filter:blur(6px)]
       duration-[800ms] delay-[90ms]"

<!-- written SECOND: the crisp leader -->
class="[stroke-width:2] duration-[620ms]"

Order is structural here. SVG paints in document order and ignores z-index entirely, so the trail must appear earlier in the markup. This is the one thing in the port that utilities cannot express — it is a decision about where an element lives, not what class it has.

Negative offsets travel; positive offsets draw. The comet spark is a short dash with a long gap. Animating its offset to a negative value slides that dash forward along the path forever, rather than revealing a line from a start point. Same two properties, completely different read.

class="[stroke-dasharray:0.045_0.955]
       group-hover:[stroke-dashoffset:-0.84]"

Fractions, because the path is normalised. With pathLength="1", 0.045 is 4.5% of the perimeter whatever the blade's actual size. The skew and the geometry can change without touching the spark.

The blend has to flip. A glow that screens beautifully on carbon black turns to mud on a light panel, where multiply is correct. Blend mode is therefore a variable rather than a utility, read inline with [mix-blend-mode:var(--svgb-12-blend)] — one of the few cases where the right value genuinely depends on the scheme.

Make it yours

  • Tune the lag with delay-[90ms]. Below about 50ms the two layers read as one thick line; past 150ms they read as two separate strokes.
  • Widen the wake by raising the trail's stroke width and blur together — blur alone just makes it faint.
  • Lengthen the spark with the first number in [stroke-dasharray:0.045_0.955]; keep the pair summing to 1.
  • Reverse the comet by flipping the sign on its target offset.
  • Remove the blur layer entirely for a flat motorsport look; the skewed blade and the negative-offset spark carry it.

Gotchas — read before shipping

  • z-index does nothing in SVG. If the trail paints over the leader, the fix is moving the element, not adding a class.
  • A blurred stroke needs room: without overflow-visible on the SVG the wake is clipped at the viewBox edge.
  • The trail must be both slower and delayed. Delay alone makes it arrive late rather than lag behind.
  • Fractional dash values require the path to carry pathLength=&quot;1&quot;; without it, 0.045 is 4.5 hundredths of a user unit and effectively invisible.
  • mix-blend-mode blends against whatever is behind it in the stacking context, so a blend inside a transformed or filtered ancestor can behave unexpectedly.
  • Blur filters are the most expensive thing in this collection to paint. One wake on one hero control is fine; a grid of them is not.

Browser support

ChromeSafariFirefoxEdge
111+16.4+128+111+

Blur filters, blend modes and dash animation are all long-standing. The floor is Tailwind v4's generated sheet. Note that mix-blend-mode on SVG children is correct everywhere current but historically inconsistent in older WebKit, which is why the value is a variable you can set to normal.

Techniques used in this demo

Utilitiesfirst-class utility or variant

Search CodeFronts

Loading…