Opent in een nieuw tabblad

CSS-lab · Knoppen

Pijl-knop met schuivende cirkel

Een pill-knop met een ronde pijl rechts die bij hover uitgroeit tot de hele knop vult, terwijl het label van kleur wisselt.

  • Gemiddeld
  • Pure CSS
  • MIT-licentie
  • ::before
  • width transition
  • isolation
  • cubic-bezier
  • inline SVG
HTML
<div class="pk-wrap">
  <a href="#" class="pk-btn">
    <span class="pk-label">Plan een kennismaking</span>
    <span class="pk-arrow" aria-hidden="true">
      <svg viewBox="0 0 24 24" focusable="false"><path d="M5 12h14M13 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/></svg>
    </span>
  </a>
  <a href="#" class="pk-btn pk-btn--violet">
    <span class="pk-label">Bekijk cases</span>
    <span class="pk-arrow" aria-hidden="true">
      <svg viewBox="0 0 24 24" focusable="false"><path d="M5 12h14M13 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/></svg>
    </span>
  </a>
</div>
CSS
/* Pijl-knop met schuivende cirkel - prefix .pk- */
.pk-wrap {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 20px;
}

.pk-btn {
  --accent: #C6FF3D;
  --ink: #0C0C0D;
  --paper: #F9F8F6;
  --dot: 46px;
  --pad: 6px;
  --speed: 0.55s;
  --ease: cubic-bezier(0.7, 0, 0.2, 1);
  position: relative;
  isolation: isolate;
  display: inline-flex;
  align-items: center;
  min-height: calc(var(--dot) + var(--pad) * 2);
  padding: 0 calc(var(--dot) + var(--pad) * 2 + 14px) 0 28px;
  font: 600 1.02rem/1 system-ui, sans-serif;
  color: var(--paper);
  text-decoration: none;
  border: 1.5px solid rgba(249, 248, 246, 0.3);
  border-radius: 999px;
  overflow: hidden;
  transition:
    color var(--speed) var(--ease),
    border-color var(--speed) var(--ease);
}

.pk-btn--violet {
  --accent: #9B5CFF;
}

/* De cirkel: groeit vanaf rechts naar de volle breedte */
.pk-btn::before {
  content: "";
  position: absolute;
  z-index: -1;
  top: var(--pad);
  right: var(--pad);
  bottom: var(--pad);
  width: var(--dot);
  border-radius: 999px;
  background: var(--accent);
  transition: width var(--speed) var(--ease);
}

/* Pijl ligt altijd boven op de cirkel */
.pk-arrow {
  position: absolute;
  top: 50%;
  right: var(--pad);
  display: grid;
  place-items: center;
  width: var(--dot);
  height: var(--dot);
  color: var(--ink);
  transform: translateY(-50%);
}

.pk-arrow svg {
  width: 20px;
  height: 20px;
  transition: transform var(--speed) var(--ease);
}

/* Hover en focus: vullen, kleur wisselen, pijl draaien */
.pk-btn:hover,
.pk-btn:focus-visible {
  color: var(--ink);
  border-color: var(--accent);
}

.pk-btn:hover::before,
.pk-btn:focus-visible::before {
  width: calc(100% - var(--pad) * 2);
}

.pk-btn:hover .pk-arrow svg,
.pk-btn:focus-visible .pk-arrow svg {
  transform: rotate(-45deg);
}

.pk-btn:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 4px;
}

@media (prefers-reduced-motion: reduce) {
  .pk-btn,
  .pk-btn::before,
  .pk-arrow svg {
    transition: none;
  }
}

Wat het is en waar je het gebruikt

Deze pill-knop begint ingetogen: een dunne rand, lichte tekst en rechts een gekleurde cirkel met een pijl. Beweeg je eroverheen, dan groeit die cirkel naar links uit tot hij de hele knop vult. Het label wisselt tegelijk van licht naar donker en de pijl draait een kwartslag omhoog. De knop voelt daardoor alsof hij naar je toe komt.

Het patroon past goed bij secundaire en primaire acties in donkere interfaces: een kennismaking plannen, een case bekijken of door naar een volgende stap. In de demo staan twee varianten naast elkaar, in acid lime en in sunset violet.

Zo werkt het

  • De gekleurde vulling is het pseudo-element .pk-btn::before. Het staat met position: absolute rechts in de knop en heeft standaard de breedte van de cirkel (var(--dot)).
  • Bij :hover en :focus-visible wordt de breedte calc(100% - var(--pad) * 2). Omdat het element aan de rechterkant vastzit, groeit het naar links. Dankzij border-radius: 999px blijft het een pil tijdens de hele beweging.
  • isolation: isolate op de knop zorgt dat de z-index: -1 van de vulling binnen de knop blijft en niet achter de pagina verdwijnt. Het label ligt er dus altijd boven.
  • De pijl is een inline SVG met stroke="currentColor" in een eigen laag. Die draait met transform: rotate(-45deg).
  • Eén gedeelde easing in --ease laat vulling, kleur en pijl als één beweging aanvoelen.

Aanpassen

De variabelen staan op .pk-btn. --accent is de kleur van cirkel en vulling, --ink de donkere tekstkleur na hover en --paper de lichte tekstkleur in rust. --dot bepaalt de grootte van de cirkel en daarmee de hoogte van de knop. --pad is de ruimte tussen cirkel en rand. Met --speed en --ease stel je het karakter van de beweging in. De modifier .pk-btn--violet laat zien hoe je met één regel een variant maakt.

Toegankelijkheid en browsersupport

Het zijn echte links met zichtbare tekst. De SVG en de cirkel zijn decoratief en hebben aria-hidden="true". Toetsenbordgebruikers krijgen hetzelfde effect via :focus-visible, plus een outline in de accentkleur. Donkere tekst op acid lime of violet en lichte tekst op carbon hebben allemaal voldoende contrast. Met prefers-reduced-motion verandert de staat direct, zonder groei- of draaibeweging. Het animeren van width is minder zuinig dan transform, maar voor één knop is dat geen probleem. Het werkt in alle actuele browsers.

Verder lezen

Meer ideeën voor hover-states vind je in ons artikel over CSS-knoppen en hover-effecten. Wil je zulke interacties op je eigen site? Bekijk onze aanpak voor interactief webdesign.