SPB Git

spb/svgarden Public

SVGarden — searchable bank of 74 self-contained SVG+CSS animation snippets (svgarden.dev)

HTML 79.2% Astro 10.3% JavaScript 6% CSS 3.7% Shell 0.8%
3.0 KB · 81 lines html
Raw Blame History
1<!--2  ============================================================3  SVGarden — https://www.svgarden.dev4  Author : Simon-Pierre Boucher5  Contact: contact@spboucher.ai6  File   : snippets/hover/card-lift-border.html7  Desc   : Card whose SVG border draws itself around it on hover8  ============================================================9-->10<!--svgarden11title: Card lift & border12slug: card-lift-border13category: hover14tags: [hover, card, border, draw]15difficulty: intermediate16techniques: [pathLength, stroke-dashoffset, transition-on-hover, preserveAspectRatio-none]17how_it_works: >18  An SVG rect stretched over the card (preserveAspectRatio="none", with a19  viewBox matched to the card's aspect ratio so the stroke stays visually even)20  starts fully hidden because its dash gap (102) is longer than the normalized21  path (pathLength 100) — the extra 2 units keep the round line caps off-path22  at rest. Hovering transitions stroke-dashoffset to zero, so the border traces23  itself around the perimeter — a transition, not a keyframe animation, which24  means it elegantly reverses when the pointer leaves instead of snapping.25customizable:26  - { var: "--sg-color",    label: "Border",  type: color, default: "#7F77DD" }27  - { var: "--sg-size",     label: "Width",   type: range, min: 140, max: 360, default: 220, unit: px }28  - { var: "--sg-duration", label: "Speed",   type: range, min: 0.2, max: 2, step: 0.1, default: 0.7, unit: s }29created: 2026-08-1030-->31<div class="sg-card-lift-border" tabindex="0" style="--sg-color:#7F77DD; --sg-size:220px; --sg-duration:0.7s;">32  <svg viewBox="0 0 200 100" preserveAspectRatio="none" aria-hidden="true" focusable="false">33    <rect x="1" y="1" width="198" height="98" rx="8" pathLength="100" />34  </svg>35  <strong>Hover me</strong>36  <span>The border draws itself around the card, then un-draws on leave.</span>37</div>38<style>39  .sg-card-lift-border {40    position: relative;41    width: var(--sg-size);42    padding: 1.1rem 1.2rem;43    border-radius: 10px;44    background: rgba(127, 119, 221, 0.08);45    font-family: inherit;46    display: flex;47    flex-direction: column;48    gap: 0.4rem;49    cursor: pointer;50    transition: transform var(--sg-duration) ease;51  }52  .sg-card-lift-border strong { font-size: 1rem; }53  .sg-card-lift-border span { font-size: 0.85rem; opacity: 0.75; }54  .sg-card-lift-border svg {55    position: absolute;56    inset: 0;57    width: 100%;58    height: 100%;59    pointer-events: none;60  }61  .sg-card-lift-border rect {62    /* no non-scaling-stroke: Chromium computes dash patterns in screen space63       with it, which breaks the pathLength=100 normalization */64    fill: none;65    stroke: var(--sg-color);66    stroke-width: 2;67    stroke-linecap: round;68    stroke-dasharray: 100 102;69    stroke-dashoffset: 101;70    transition: stroke-dashoffset var(--sg-duration) ease-in-out;71  }72  .sg-card-lift-border:hover,73  .sg-card-lift-border:focus-visible {74    transform: translateY(-4px);75  }76  .sg-card-lift-border:hover rect,77  .sg-card-lift-border:focus-visible rect {78    stroke-dashoffset: 0;79  }80</style>81