Skip to content

Using icons

Use animated icons to show actions and states in your interface.

Every icon is drawn on the same grid and moves on the same clock as the rest of your interface. Here's one taken apart: the grid it's drawn on, its resting state, the path it travels and where it lands. Pick another to see how it moves.

The light and dark icon pulled apart into four layers: the 24-unit grid it is drawn on, its light state, the path between its states drawn as frames of its own transition (path morph · staggered rays, 0 milliseconds), and its dark state. Assembled, it is a key that switches between the two states.

Use icons to identify actions and states in buttons, menus, tabs and status lines. Their animation gives feedback on hover and keyboard focus. For inline text or dense tables, use trigger="none" to keep them still.

Installation

The package ships the whole set from one entry.

npm i @youj/icons
import { BellIcon, MenuIcon } from '@youj/icons'

Or copy icons in one at a time with the Youj CLI. Each is its own item, icon-<name>, and brings the icon engine with it.

npx @youj/cli add icon-bell icon-menu

Usage

Every icon is a component. It's decorative by default (aria-hidden); give it an aria-label when it carries meaning on its own.

import { BellIcon } from '@youj/icons'

<button type="button">
  <BellIcon size={18} />
  Notifications
</button>

Icons play on hover by default, and they listen to their closest interactive parent, so hovering or keyboard-focusing the button above rings the bell. Mark any other container with data-yj-trigger to make it the trigger.

Examples

Triggers

  • hover plays the choreography while the trigger is hovered or keyboard-focused, and settles back when it's released. The trigger is the closest ancestor with data-yj-trigger, otherwise the closest button, link, summary, label or element with a button, link, menu item, tab, switch or option role, otherwise the icon itself. Only keyboard focus counts, so a button that keeps focus after a click still settles when the pointer leaves.
  • mount plays once, as the icon appears.
  • loop repeats while the icon is on screen and the tab is visible, and pauses otherwise. Most icons cycle: they play for two deliberate steps and rest for one. Loaders (LoaderRingIcon, LoaderDotsIcon and HourglassIcon) run continuously instead, and they loop by default.
  • none holds the resting pose.

A controlled active wins over every trigger. true holds the icon in its active pose, false at rest.

Reference

Propssize, strokeWidth, aria-label… (8)

Props

IconProps props
PropType
sizenumber | string

Rendered width and height.

default 24

24
strokeWidthnumber

Stroke width in viewBox units.

default 1.5

1.5
classNamestring–
styleCSSProperties–
aria-labelstring

Gives the icon role="img". Without it the icon is decorative (aria-hidden).

–
triggerIconTrigger
'hover''mount''loop''none'

What plays the choreography. Hover also listens to a hovered/focused parent button or data-yj-trigger group.

–
activeboolean

Controlled state for state icons (menu → close, play → pause…). Overrides the trigger.

–
refRef<SVGSVGElement>–
MotionChoreography steps, feedback, draw on… · reduced motion

Motion

Motion tokens
MomentTokens
Choreography stepsmost parts move in short tweens
duration.fastease.standard

140ms · cubic-bezier(0.2, 0, 0, 1)

140mscubic-bezier(0.2, 0, 0, 1)
Feedbackflips, snaps and presses
spring.snappy

0.2s, bounce 0

0.2s, bounce 0
Draw ondrawIn, a stroke writing itself
duration.slowease.emphasized

340ms · cubic-bezier(0.6, 0, 0.1, 1)

340mscubic-bezier(0.6, 0, 0.1, 1)
RetractdrawOut
duration.fastease.exit

140ms · cubic-bezier(0.4, 0, 1, 1)

140mscubic-bezier(0.4, 0, 1, 1)
Arrival and successa check landing, a bookmark filling
spring.settle

0.45s, bounce 0.3

0.45s, bounce 0.3
Loop cycleplays for 2 steps, rests for 1
duration.deliberate

560ms

560ms

Reduced motion

Icons hold their resting pose and never play. State icons switch instantly, and loops stop.

Reduced motion

An icon holds its resting pose and never plays, and loops stop. A controlled active still sets the pose, so a state icon switches straight to its new state: a muted microphone still shows its slash. The meaning survives; only the motion goes.

AccessibilityKeyboard, ARIA, focus

Accessibility

  • Icons are decorative by default: aria-hidden, and never focusable. Inside a labeled button, leave them that way, so the button has one name.
  • aria-label turns an icon into role="img" with that name. Use it only for an icon that stands alone and means something, like a status mark with no text beside it.
  • Hover choreography also plays on visible keyboard focus, so keyboard users get the same feedback as pointer users.
  • A state icon shows its state but doesn't announce it. Put the state on the control, with aria-pressed or a name that changes, as useIconState does.