Documentation

optical-center bakes perceptual centering into the elements that hold your icons. One declaration, two surfaces, five patterns — always on a wrapper, never on the icon itself.

Two layers

optical-center meets your project at two levels. Most people only ever touch the first.

  • Automatic — the icons you import. Add the Vite plugin and the icon libraries you already use (Iconify, your own SVG files, unplugin-icons, a hand-rolled icon map) come out optically centered with no markup at all. The build finds the icon SVG by shape and bakes the shift into the asset. This is the path most projects want — Any icon library covers it case by case.
  • The directive — when you want explicit control. For icons the build can't reach (a component library's render output, a runtime-fetched glyph) or when you'd rather centre the slot than the asset, you write optical-center: auto on the container. That declaration, and the five patterns that apply it, are the rest of this page.

The two compose freely — automatic correction for your imported icons, the directive wherever you reach for it.

The rule

The directive lives on the container that wraps the icon, never on the icon itself. The icon comes in unchanged — your imported <Play />, your designer's <svg>, your utility's mask: url(…svg). The container picks up optical-center: auto and the build step does the rest.

Conceptually the directive replaces flex centering: justify-content: center and align-items: center tell the browser where the bounding box's middle is; optical-center: auto tells it where the eye actually wants the icon to land. Same surface, same property tier, more accurate result.

Two surfaces, one declaration

optical-center: auto can arrive in two places — they mean the exact same thing.

In CSS, on a rule that targets the container

styles.css css
.badge {
  optical-center: auto;
}

As an attribute on the container, in HTML or JSX

App.tsx tsx
<div optical-center="auto">
  <Play />
</div>

Both forms compile to the same emitted CSS: display: flex on the wrapper, margin: auto on the icon child, plus a translate that carries the per-icon perceptual shift. Centering happens via the well-known flex auto-margin trick — no justify-content, no align-items, no position: absolute on the child.

The five patterns

Pick whichever surface fits how your project already declares icons. All five compose; you can mix CSS-class wrappers in one component and Tailwind utilities in another without conflict.

Status