# Hover Icons

> Stroke icons that act out their meaning on hover or keyboard focus: the bell rings, the bin lifts its lid, the plane flies off and back, the cog turns, refresh spins, the padlock opens. CSS only.

SVG & Drawing · svg, icon, hover, animated icons, icon button, bell, trash, send, settings, refresh, lock, css only · HTML and CSS only · no dependencies · updated 2026-11-04
Demo: https://kodza.dev/components/hover-icons/
Source on GitHub: https://github.com/kodza-dev/kodza/tree/main/components/hover-icons

## Files

The code below, served as files: byte for byte what the Kodza site copies (the CSS includes its theme guard).

- [`kodza-hover-icons.css`](https://kodza.dev/registry/hover-icons/kodza-hover-icons.css) · 12.4 KB
- [`kodza-hover-icons.html`](https://kodza.dev/registry/hover-icons/kodza-hover-icons.html) · 2.7 KB · the markup, to adapt and paste into your page

**Can you run shell commands? Download the CSS, don't retype it.** Retyping costs thousands of tokens and can drop or change lines of the CSS. Put the files where your stack expects them (Use it says where: change the `-o` paths):

```bash
curl -fsSL --create-dirs -o kodza/kodza-hover-icons.css https://kodza.dev/registry/hover-icons/kodza-hover-icons.css
```

Then load the file from your page as Use it shows, instead of pasting the content. The HTML is the part you adapt (text, links, modifiers): paste it from the Code section. Copy the CSS from the Code section only if you can't run commands, exactly as it is. (Windows PowerShell: `curl.exe`.)

## API

- Root: `.kodza-hover-icons`
- Variables (set them on the root element itself: it declares its own defaults):
  - `--kz-hover-icons-height: 40px`
  - `--kz-hover-icons-icon: 18px`
  - `--kz-hover-icons-stroke: 1.75px` — in icon units, 24 across
  - `--kz-hover-icons-ink: #0a0a0a`
  - `--kz-hover-icons-on-ink: #ffffff` — text and icon of the solid pill
  - `--kz-hover-icons-bg: #ffffff`
  - `--kz-hover-icons-line: rgba(10, 10, 10, 0.12)`
  - `--kz-hover-icons-line-hover: rgba(10, 10, 10, 0.28)`
  - `--kz-hover-icons-solid-hover: #2a2a2a`
  - `--kz-hover-icons-duration: 0.5s` — cog, lid and padlock; the bell, the plane and the arrows take longer
  - `--kz-hover-icons-ease: cubic-bezier(0.34, 1.45, 0.64, 1)` — a small overshoot, so the part clicks into place
- Modifiers: `.kodza-hover-icons--icon`, `.kodza-hover-icons--solid`

## Dark mode

The values Kodza uses on a dark background (`#0a0a0a`). For a page that follows the system theme, add them after the component's CSS:

```css
@media (prefers-color-scheme: dark) {
  .kodza-hover-icons {
    --kz-hover-icons-ink: #f2f2f0;
    --kz-hover-icons-on-ink: #0a0a0a;
    --kz-hover-icons-bg: #161616;
    --kz-hover-icons-line: rgba(255, 255, 255, 0.12);
    --kz-hover-icons-line-hover: rgba(255, 255, 255, 0.26);
    --kz-hover-icons-solid-hover: #ffffff;
  }
}
```

A site with its own theme switch puts the same rules under its dark selector instead (`[data-theme="dark"] .kodza-hover-icons { … }`); a single dark section, under that section's class.

## Use it

### Plain HTML page

- CSS: `kodza/kodza-hover-icons.css` next to the page (the Files commands as they are), linked in `<head>`: `<link rel="stylesheet" href="kodza/kodza-hover-icons.css">`. Without a shell, paste it in a `<style>` in `<head>` instead. It only touches `kodza-` classes.
- HTML: where the component goes. Paste it as many times as you need: instances never interfere.
- No JS.
- Restyle with the `--kz-…` variables, set on the component's root element itself (the root declares its own defaults, so a value on an ancestor is ignored), and with the modifiers / attributes listed under API.
- Font: it takes the page's own. A site with its own type sets it once for every Kodza component, on `:root`, never by editing the CSS (unset, nothing changes): `--kz-font-label`, the face of the small labels (meta, captions, counters, hints, footer links, button labels); `--kz-font-label-size`, their size, one for all of them (`11px`…); `--kz-font-label-weight`, their weight; `--kz-font-label-tracking`, their letter-spacing; `--kz-font-label-case`, their case (`uppercase`…; code and units keep theirs). The Kodza look is Geist: `<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Geist:wght@400;500;600;700&family=Geist+Mono:wght@400;500&display=swap">`.
- Corners: a site with its own shapes sets them once for every Kodza component, on `:root`, never by editing the CSS (unset, nothing changes): `--kz-radius-pill` sets the pills (`0` squares them).
- Motion: a site sets its pace once for every Kodza component, on `:root`, never by editing the CSS or the JS: `--kz-motion-scale` multiplies every duration (`0.5` twice as fast, `2` twice as slow, `0` no motion at all, loops stopped, like reduced motion). Unset, nothing changes.

### Claude artifact (claude.ai)

- Paste the CSS in a `<style>`, in the artifact itself: it loads stylesheets only from Google Fonts and scripts only from a few CDNs, so no `<link>` or `<script src>` to Kodza.
- Writing the artifact file with a shell? Insert the downloaded files (Files) with a command instead of retyping them: a line `KODZA-CSS` alone inside the `<style>`, then `sed -e '/^KODZA-CSS$/{r kodza/kodza-hover-icons.css' -e 'd' -e '}' page.html > artifact.html`.
- Font: load Geist from Google Fonts (allowed): `<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Geist:wght@400;500;600;700&family=Geist+Mono:wght@400;500&display=swap">`. A site with its own type sets it once for every Kodza component, on `:root`, never by editing the CSS (unset, nothing changes): `--kz-font-label`, the face of the small labels (meta, captions, counters, hints, footer links, button labels); `--kz-font-label-size`, their size, one for all of them (`11px`…); `--kz-font-label-weight`, their weight; `--kz-font-label-tracking`, their letter-spacing; `--kz-font-label-case`, their case (`uppercase`…; code and units keep theirs).
- Corners: a site with its own shapes sets them once for every Kodza component, on `:root`, never by editing the CSS (unset, nothing changes): `--kz-radius-pill` sets the pills (`0` squares them).
- Motion: a site sets its pace once for every Kodza component, on `:root`, never by editing the CSS or the JS: `--kz-motion-scale` multiplies every duration (`0.5` twice as fast, `2` twice as slow, `0` no motion at all, loops stopped, like reduced motion). Unset, nothing changes.
- Dark mode: the viewer sets `data-theme="dark"` or `"light"` on `<html>`, or nothing when it follows the system. Write the Dark mode rules twice: inside `@media (prefers-color-scheme: dark)` with `:root:not([data-theme="light"])` in front of each selector, and with `:root[data-theme="dark"]` in front of each selector.

### WordPress

- HTML: a **Custom HTML** block (or a pattern / template part). WordPress keeps this markup as it is only for users allowed to post unfiltered HTML (Administrators; Super Admins on multisite): for anyone else it strips tags such as `<svg>`, `<input>` or `<dialog>`, so add it from the theme (a pattern or template part) instead.
- This one is inline (it flows like a word): pasted alone, many themes leave it at the edge of the page (block themes, and classic ones like Twenty Twenty-One or Blocksy), so put the Custom HTML block inside a **Group** block to line it up with the text.
- CSS: the downloaded file goes in the theme, under `wp-content/themes/<your-theme>/kodza/` (the child theme if there is one), enqueued from its functions.php (or a code-snippets plugin):
  ```php
  add_action('wp_enqueue_scripts', function () {
    $dir = get_stylesheet_directory() . '/kodza/';
    $uri = get_stylesheet_directory_uri() . '/kodza/';
    wp_enqueue_style('kodza-hover-icons', $uri . 'kodza-hover-icons.css', [], filemtime($dir . 'kodza-hover-icons.css'));
  });
  ```
  Scoped to `kodza-` classes, the CSS never touches the theme.
- No access to the theme files? The CSS can also go, as it is, in **Appearance → Customize → Additional CSS** (classic theme) or **Styles → Additional CSS** in the Site Editor (block theme).
- No JS.
- Colours: it takes the colour of the text around it (or keeps its own monochrome): nothing to point at the theme.
- WordPress turns straight quotes and `--` into typographic ones in the text of a Custom HTML block (not inside `<code>` or `<pre>`).

#### With Elementor

- HTML: an **HTML** widget (Elementor shows it to administrators only). It keeps the code exactly as pasted, straight quotes included.
- CSS: as above. Elementor's Site Settings → Theme Style styles every button, field, link and image of the page, components included; the theme guard at the end of the CSS keeps the component's buttons, fields and link colours as they are.
- No child theme: the CSS can go in the HTML widget instead, in a `<style>` before the HTML.

### Shopify

- Files: `assets/kodza-hover-icons.css` (the Files commands with `-o assets/…`).
- In the section: `{{ 'kodza-hover-icons.css' | asset_url | stylesheet_tag }}`, then the HTML.

### React / Next.js

- Files, side by side (e.g. in `components/kodza/`): `HoverIcons.tsx` (below), the CSS block under Code saved as `hover-icons.css`, both as they are. The HTML block is already inside the component: skip it.
- Use it: `import { HoverIcons } from '@/components/kodza/HoverIcons';` then `<HoverIcons />`. Props: `variant` ("icon", "solid", or several in an array), `vars` (the `--kz-…` variables listed under API, e.g. `vars={{ '--kz-hover-icons-height': … }}`), `className`, `style` and the other props of its `<button>` (`onClick`, `aria-…`).
- Next.js App Router: it is a Client Component already (`'use client'`). Pages Router: Next.js takes global CSS only in `pages/_app`, so move the `import './hover-icons.css'` line there.

```tsx
'use client';
// Kodza — Hover Icons, for React. From https://kodza.dev/components/hover-icons/
// Kodza License: free to use, client work included. Not to resell or redistribute, ported or not: https://kodza.dev/docs/#print-license
// Next to this file: hover-icons.css, the component's CSS from Kodza, as they are.
// Yours to edit: the markup is plain JSX below.
import { useRef, type ComponentPropsWithoutRef, type ComponentRef, type CSSProperties } from 'react';
import './hover-icons.css';

type Variant = "icon" | "solid";

export type HoverIconsProps = Omit<ComponentPropsWithoutRef<'button'>, "children" | "variant" | "vars"> & {
  /** Modifier class(es): .kodza-hover-icons--icon, .kodza-hover-icons--solid */
  variant?: Variant | Variant[];
  /** Settings, as CSS variables on the root */
  vars?: {
    /** default: 40px */
    "--kz-hover-icons-height"?: string | number;
    /** default: 18px */
    "--kz-hover-icons-icon"?: string | number;
    /** in icon units, 24 across — default: 1.75px */
    "--kz-hover-icons-stroke"?: string | number;
    /** default: #0a0a0a */
    "--kz-hover-icons-ink"?: string | number;
    /** text and icon of the solid pill — default: #ffffff */
    "--kz-hover-icons-on-ink"?: string | number;
    /** default: #ffffff */
    "--kz-hover-icons-bg"?: string | number;
    /** default: rgba(10, 10, 10, 0.12) */
    "--kz-hover-icons-line"?: string | number;
    /** default: rgba(10, 10, 10, 0.28) */
    "--kz-hover-icons-line-hover"?: string | number;
    /** default: #2a2a2a */
    "--kz-hover-icons-solid-hover"?: string | number;
    /** cog, lid and padlock; the bell, the plane and the arrows take longer — default: 0.5s */
    "--kz-hover-icons-duration"?: string | number;
    /** a small overshoot, so the part clicks into place — default: cubic-bezier(0.34, 1.45, 0.64, 1) */
    "--kz-hover-icons-ease"?: string | number;
  };
  className?: string;
};

export function HoverIcons({ variant, vars, className, style, ...rest }: HoverIconsProps) {
  const ref = useRef<ComponentRef<'button'>>(null);
  const classes = ["kodza-hover-icons", "kodza-hover-icons--icon", ...([] as string[]).concat(variant ?? []).map((v) => `kodza-hover-icons--${v}`), className].filter(Boolean).join(' ');

  return (
    <button type="button" aria-label="Refresh" {...rest} className={classes} style={{ ...vars, ...style } as CSSProperties} ref={ref}>
      <svg viewBox="0 0 24 24" aria-hidden="true" focusable="false">
        <g className="kodza-hover-icons__turn">
          <path d="M5.19 9.52A7.25 7.25 0 0 1 18.57 8.94M15.63 7.69 18.57 8.94 19.51 5.88" />
          <path d="M18.81 14.48A7.25 7.25 0 0 1 5.43 15.06M8.37 16.31 5.43 15.06 4.49 18.12" />
        </g>
      </svg>
    </button>
  );
}
```

## Code

```html
<button class="kodza-hover-icons kodza-hover-icons--icon" type="button" aria-label="Refresh">
  <svg viewBox="0 0 24 24" aria-hidden="true" focusable="false">
    <g class="kodza-hover-icons__turn">
      <path d="M5.19 9.52A7.25 7.25 0 0 1 18.57 8.94M15.63 7.69 18.57 8.94 19.51 5.88"/>
      <path d="M18.81 14.48A7.25 7.25 0 0 1 5.43 15.06M8.37 16.31 5.43 15.06 4.49 18.12"/>
    </g>
  </svg>
</button>

<button class="kodza-hover-icons kodza-hover-icons--icon" type="button" aria-label="Settings">
  <svg viewBox="0 0 24 24" aria-hidden="true" focusable="false">
    <g class="kodza-hover-icons__gear">
      <path d="M18.6 9.98 20.83 10.28V13.72L18.6 14.02A6.9 6.9 0 0 1 18.09 15.24L19.46 17.03 17.03 19.46 15.24 18.09A6.9 6.9 0 0 1 14.02 18.6L13.72 20.83H10.28L9.98 18.6A6.9 6.9 0 0 1 8.76 18.09L6.97 19.46 4.54 17.03 5.91 15.24A6.9 6.9 0 0 1 5.4 14.02L3.17 13.72V10.28L5.4 9.98A6.9 6.9 0 0 1 5.91 8.76L4.54 6.97 6.97 4.54 8.76 5.91A6.9 6.9 0 0 1 9.98 5.4L10.28 3.17H13.72L14.02 5.4A6.9 6.9 0 0 1 15.24 5.91L17.03 4.54 19.46 6.97 18.09 8.76A6.9 6.9 0 0 1 18.6 9.98Z"/>
      <circle cx="12" cy="12" r="2.75"/>
    </g>
  </svg>
</button>

<button class="kodza-hover-icons kodza-hover-icons--icon" type="button" aria-label="Notifications">
  <svg viewBox="0 0 24 24" aria-hidden="true" focusable="false">
    <path class="kodza-hover-icons__bell" d="M12 3.25V5.25M5.5 17H18.5C17.4 15.8 16.75 14.4 16.75 12.5V10A4.75 4.75 0 0 0 7.25 10V12.5C7.25 14.4 6.6 15.8 5.5 17Z"/>
    <path class="kodza-hover-icons__clapper" d="M10.25 19.5A1.9 1.9 0 0 0 13.75 19.5"/>
  </svg>
</button>

<button class="kodza-hover-icons" type="button">
  <svg viewBox="0 0 24 24" aria-hidden="true" focusable="false">
    <g class="kodza-hover-icons__shackle">
      <path class="kodza-hover-icons__swing" d="M8.25 10.5V7.75A3.75 3.75 0 0 1 15.75 7.75V10.5"/>
    </g>
    <rect x="5" y="10.5" width="14" height="10" rx="2.5"/>
    <path d="M12 14.5V16.5"/>
  </svg>
  <span class="kodza-hover-icons__label">Unlock</span>
</button>

<button class="kodza-hover-icons" type="button">
  <svg viewBox="0 0 24 24" aria-hidden="true" focusable="false">
    <path class="kodza-hover-icons__lid" d="M4.5 7H19.5M9.25 7V5.25C9.25 4.7 9.7 4.25 10.25 4.25H13.75C14.3 4.25 14.75 4.7 14.75 5.25V7"/>
    <path d="M6.5 7 7.3 18.6C7.37 19.65 8.25 20.5 9.3 20.5H14.7C15.75 20.5 16.63 19.65 16.7 18.6L17.5 7M10.25 11V16.5M13.75 11V16.5"/>
  </svg>
  <span class="kodza-hover-icons__label">Delete</span>
</button>

<button class="kodza-hover-icons kodza-hover-icons--solid" type="button">
  <svg viewBox="0 0 24 24" aria-hidden="true" focusable="false">
    <path class="kodza-hover-icons__plane" d="M20.5 3.5 3.5 10.25 10.5 13.5 13.75 20.5ZM20.5 3.5 10.5 13.5"/>
  </svg>
  <span class="kodza-hover-icons__label">Send</span>
</button>
```

```css
/* Kodza License: free to use, client work included. Not to resell or redistribute, ported or not: https://kodza.dev/docs/#print-license */
/* Kodza — Hover Icons
   Icon buttons that act out what they do before you click: the bell rings, the bin lifts its lid,
   the plane leaves and comes back, the cog turns one tooth, the refresh arrows go round, the
   padlock opens. CSS only, on hover (pointer devices) and on keyboard focus. Each icon is plain
   inline SVG drawn with currentColor, so it takes the button's colour, and moves by transforms
   only. Three looks: a white pill with a label, the same pill with the icon alone (--icon, give it
   an aria-label), and the solid ink pill (--solid). Everything is tuned with the
   --kz-hover-icons-* variables. */
.kodza-hover-icons {
  --kz-hover-icons-height: 40px;
  --kz-hover-icons-icon: 18px;
  --kz-hover-icons-stroke: 1.75px; /* in icon units, 24 across */
  --kz-hover-icons-ink: #0a0a0a;
  --kz-hover-icons-on-ink: #ffffff; /* text and icon of the solid pill */
  --kz-hover-icons-bg: #ffffff;
  --kz-hover-icons-line: rgba(10, 10, 10, 0.12);
  --kz-hover-icons-line-hover: rgba(10, 10, 10, 0.28);
  --kz-hover-icons-solid-hover: #2a2a2a;
  --kz-hover-icons-duration: 0.5s; /* cog, lid and padlock; the bell, the plane and the arrows take longer */
  --kz-hover-icons-ease: cubic-bezier(0.34, 1.45, 0.64, 1); /* a small overshoot, so the part clicks into place */

  display: inline-flex;
  flex: none;
  align-items: center;
  justify-content: center;
  gap: 8px;
  box-sizing: border-box;
  height: var(--kz-hover-icons-height);
  margin: 0;
  padding: 0 16px 0 13px;
  border: 1px solid var(--kz-hover-icons-line);
  border-radius: var(--kz-radius-pill, 999px);
  background: var(--kz-hover-icons-bg);
  color: var(--kz-hover-icons-ink);
  font: inherit;
  font-family: var(--kz-font-label, inherit);
  font-size: var(--kz-font-label-size, 15px);
  font-weight: var(--kz-font-label-weight, 500);
  letter-spacing: var(--kz-font-label-tracking, -0.01em);
  text-transform: var(--kz-font-label-case, revert);
  line-height: 1;
  white-space: nowrap;
  text-decoration: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition:
    border-color calc(0.2s * var(--kz-motion-scale, 1)) ease,
    background-color calc(0.2s * var(--kz-motion-scale, 1)) ease,
    scale calc(0.15s * var(--kz-motion-scale, 1)) cubic-bezier(0.22, 1, 0.36, 1);
}

/* The icon alone: a round button, its name in aria-label */
.kodza-hover-icons--icon {
  width: var(--kz-hover-icons-height);
  padding: 0;
}

.kodza-hover-icons--solid {
  border-color: var(--kz-hover-icons-ink);
  background: var(--kz-hover-icons-ink);
  color: var(--kz-hover-icons-on-ink);
}

.kodza-hover-icons:focus-visible {
  outline: 2px solid var(--kz-hover-icons-ink);
  outline-offset: 3px;
}

.kodza-hover-icons:active {
  scale: 0.96;
}

@media (hover: hover) {
  .kodza-hover-icons:hover {
    border-color: var(--kz-hover-icons-line-hover);
  }

  .kodza-hover-icons--solid:hover {
    border-color: var(--kz-hover-icons-solid-hover);
    background: var(--kz-hover-icons-solid-hover);
  }
}

.kodza-hover-icons svg {
  display: block;
  flex: none;
  width: var(--kz-hover-icons-icon);
  height: var(--kz-hover-icons-icon);
  overflow: hidden; /* the plane leaves through the edge and comes back through the other */
  fill: none;
  stroke: currentColor;
  stroke-width: var(--kz-hover-icons-stroke);
  stroke-linecap: round;
  stroke-linejoin: round;
  pointer-events: none;
}

/* Every moving part turns around a point of its own icon, in icon units */
.kodza-hover-icons svg * {
  transform-box: view-box;
}

/* At rest: the way back. Each part returns by a transition, from wherever it was */
.kodza-hover-icons__turn {
  transform-origin: 12px 12px;
  transition: transform calc(0.6s * var(--kz-motion-scale, 1)) cubic-bezier(0.65, 0, 0.35, 1);
}

.kodza-hover-icons__gear {
  transform-origin: 12px 12px;
  transition: transform calc(var(--kz-hover-icons-duration) * var(--kz-motion-scale, 1)) var(--kz-hover-icons-ease);
}

.kodza-hover-icons__bell,
.kodza-hover-icons__clapper {
  transform-origin: 12px 3.25px;
}

.kodza-hover-icons__lid {
  transform-origin: 4.5px 7px;
  transition: transform calc(var(--kz-hover-icons-duration) * var(--kz-motion-scale, 1)) var(--kz-hover-icons-ease);
}

/* The shackle comes up, then swings open on its left leg; it closes first, then goes down */
.kodza-hover-icons__shackle {
  transition: transform calc(0.25s * var(--kz-motion-scale, 1)) cubic-bezier(0.22, 1, 0.36, 1) calc(0.12s * var(--kz-motion-scale, 1));
}

.kodza-hover-icons__swing {
  transform-origin: 8.25px 10.5px;
  transition: transform calc(0.2s * var(--kz-motion-scale, 1)) cubic-bezier(0.22, 1, 0.36, 1);
}

/* The act: on hover with a pointer, and on keyboard focus */
@media (hover: hover) {
  .kodza-hover-icons:hover .kodza-hover-icons__turn {
    transform: rotate(180deg); /* the two arrows are the same at half a turn: it reads as a full turn */
  }

  .kodza-hover-icons:hover .kodza-hover-icons__gear {
    transform: rotate(45deg); /* one tooth of eight */
  }

  .kodza-hover-icons:hover .kodza-hover-icons__bell {
    animation: kodza-hover-icons-ring calc(0.9s * var(--kz-motion-scale, 1)) ease-out;
  }

  .kodza-hover-icons:hover .kodza-hover-icons__clapper {
    animation: kodza-hover-icons-ring calc(0.9s * var(--kz-motion-scale, 1)) ease-out calc(0.06s * var(--kz-motion-scale, 1)) backwards;
  }

  .kodza-hover-icons:hover .kodza-hover-icons__lid {
    transform: translateY(-1px) rotate(-12deg);
  }

  .kodza-hover-icons:hover .kodza-hover-icons__shackle {
    transform: translateY(-2px);
    transition-delay: 0s;
  }

  .kodza-hover-icons:hover .kodza-hover-icons__swing {
    transform: rotate(-18deg);
    transition: transform calc(var(--kz-hover-icons-duration) * var(--kz-motion-scale, 1)) var(--kz-hover-icons-ease) calc(0.12s * var(--kz-motion-scale, 1));
  }

  .kodza-hover-icons:hover .kodza-hover-icons__plane {
    animation: kodza-hover-icons-fly calc(0.8s * var(--kz-motion-scale, 1)) both;
  }
}

.kodza-hover-icons:focus-visible .kodza-hover-icons__turn {
  transform: rotate(180deg);
}

.kodza-hover-icons:focus-visible .kodza-hover-icons__gear {
  transform: rotate(45deg);
}

.kodza-hover-icons:focus-visible .kodza-hover-icons__bell {
  animation: kodza-hover-icons-ring calc(0.9s * var(--kz-motion-scale, 1)) ease-out;
}

.kodza-hover-icons:focus-visible .kodza-hover-icons__clapper {
  animation: kodza-hover-icons-ring calc(0.9s * var(--kz-motion-scale, 1)) ease-out calc(0.06s * var(--kz-motion-scale, 1)) backwards;
}

.kodza-hover-icons:focus-visible .kodza-hover-icons__lid {
  transform: translateY(-1px) rotate(-12deg);
}

.kodza-hover-icons:focus-visible .kodza-hover-icons__shackle {
  transform: translateY(-2px);
  transition-delay: 0s;
}

.kodza-hover-icons:focus-visible .kodza-hover-icons__swing {
  transform: rotate(-18deg);
  transition: transform calc(var(--kz-hover-icons-duration) * var(--kz-motion-scale, 1)) var(--kz-hover-icons-ease) calc(0.12s * var(--kz-motion-scale, 1));
}

.kodza-hover-icons:focus-visible .kodza-hover-icons__plane {
  animation: kodza-hover-icons-fly calc(0.8s * var(--kz-motion-scale, 1)) both;
}

/* A swing that dies down, like a real bell */
@keyframes kodza-hover-icons-ring {
  0% { transform: rotate(0deg); }
  14% { transform: rotate(18deg); }
  30% { transform: rotate(-14deg); }
  46% { transform: rotate(9deg); }
  62% { transform: rotate(-5deg); }
  78% { transform: rotate(2deg); }
  100% { transform: rotate(0deg); }
}

/* Out through the top right, speeding up; back in from the bottom left, slowing down */
@keyframes kodza-hover-icons-fly {
  0% {
    transform: translate(0, 0);
    animation-timing-function: cubic-bezier(0.5, 0, 0.75, 0);
  }
  40% {
    transform: translate(20px, -20px);
    animation-timing-function: steps(1, end);
  }
  40.01% {
    transform: translate(-20px, 20px);
    animation-timing-function: cubic-bezier(0.22, 1, 0.36, 1);
  }
  100% {
    transform: translate(0, 0);
  }
}

@media (prefers-reduced-motion: reduce) {
  .kodza-hover-icons,
  .kodza-hover-icons svg * {
    transition-duration: 0.01ms !important;
    transition-delay: 0s !important;
  }

  /* The icons stay still: what they act out is a bonus, the label says it */
  .kodza-hover-icons svg * {
    transform: none !important;
    animation: none !important;
  }
}

/* kodza-guard ed4c08481ced — generated by Kodza, do not edit.
   Theme guard: keeps a site's own CSS (WordPress themes, Bootstrap, CSS resets…) off this
   component. :not(#_) is always true and weighs as much as an id, so these lines win over
   theme rules made of classes and tags. Customise the component through its --kz-* variables. */

/* What the browser gives, where the component sets it only in some states: at the lowest weight, so its own rules below put it back */

:where(.kodza-hover-icons):not(#_) {
  outline: revert;
  outline-offset: revert;
}

/* What the browser gives, on everything the component leaves to it */

.kodza-hover-icons:not(#_) {
  list-style: revert;
  position: revert;
  box-shadow: revert;
  appearance: revert;
  max-height: revert;
  text-underline-offset: revert;
  text-align: revert;
  text-indent: revert;
  text-shadow: revert;
  vertical-align: revert;
  min-height: revert;
}

.kodza-hover-icons__turn:not(#_),
.kodza-hover-icons__gear:not(#_),
.kodza-hover-icons__bell:not(#_),
.kodza-hover-icons__clapper:not(#_),
.kodza-hover-icons__shackle:not(#_),
.kodza-hover-icons__swing:not(#_),
.kodza-hover-icons__label:not(#_),
.kodza-hover-icons__lid:not(#_),
.kodza-hover-icons__plane:not(#_) {
  box-sizing: revert;
}

.kodza-hover-icons svg:where(:not([class*="kodza-"])):not(#_) {
  border: revert;
  margin: revert;
  padding: revert;
  border-radius: revert;
  outline: revert;
  text-decoration: revert;
  transition: revert;
  list-style: revert;
  box-sizing: revert;
  float: revert;
  position: revert;
  background-color: revert;
  background-image: revert;
  box-shadow: revert;
  outline-offset: revert;
  appearance: revert;
  max-width: revert;
  max-height: revert;
  color: revert;
  font-family: revert;
  font-size: revert;
  font-weight: revert;
  font-style: revert;
  line-height: revert;
  letter-spacing: revert;
  text-transform: revert;
  text-underline-offset: revert;
  text-align: revert;
  text-indent: revert;
  text-shadow: revert;
  vertical-align: revert;
}

/* The component's own rules, one step stronger: real buttons and fields, what it sets on tags themes style (links, lists, headings, images…), and the root width (a theme may narrow it, never widen it) */

.kodza-hover-icons:not(#_) {
  display: inline-flex;
  flex: none;
  align-items: center;
  justify-content: center;
  gap: 8px;
  box-sizing: border-box;
  height: var(--kz-hover-icons-height);
  margin: 0;
  padding: 0 16px 0 13px;
  border: 1px solid var(--kz-hover-icons-line);
  border-radius: var(--kz-radius-pill, 999px);
  background: var(--kz-hover-icons-bg);
  color: var(--kz-hover-icons-ink);
  font: inherit;
  font-family: var(--kz-font-label, inherit);
  font-size: var(--kz-font-label-size, 15px);
  font-weight: var(--kz-font-label-weight, 500);
  letter-spacing: var(--kz-font-label-tracking, -0.01em);
  text-transform: var(--kz-font-label-case, revert);
  line-height: 1;
  white-space: nowrap;
  text-decoration: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: border-color calc(0.2s * var(--kz-motion-scale, 1)) ease,
    background-color calc(0.2s * var(--kz-motion-scale, 1)) ease,
    scale calc(0.15s * var(--kz-motion-scale, 1)) cubic-bezier(0.22, 1, 0.36, 1);
}

.kodza-hover-icons--icon:not(#_) {
  width: var(--kz-hover-icons-height);
  padding: 0;
}

.kodza-hover-icons--solid:not(#_) {
  border-color: var(--kz-hover-icons-ink);
  background: var(--kz-hover-icons-ink);
  color: var(--kz-hover-icons-on-ink);
}

.kodza-hover-icons:focus-visible:not(#_) {
  outline: 2px solid var(--kz-hover-icons-ink);
  outline-offset: 3px;
}

.kodza-hover-icons:active:not(#_) {
  scale: 0.96;
}

@media (hover: hover) {
  .kodza-hover-icons:hover:not(#_) {
    border-color: var(--kz-hover-icons-line-hover);
  }
}

@media (hover: hover) {
  .kodza-hover-icons--solid:hover:not(#_) {
    border-color: var(--kz-hover-icons-solid-hover);
    background: var(--kz-hover-icons-solid-hover);
  }
}

.kodza-hover-icons svg:not(#_) {
  display: block;
}

@media (prefers-reduced-motion: reduce) {
  .kodza-hover-icons:not(#_) {
    transition-duration: 0.01ms !important;
    transition-delay: 0s !important;
  }
}
```

Free to use, from Kodza (https://kodza.dev).
