Irozon Design System — design foundations, tokens and components by Irozon AB.
Irozon Design System · v1.0
Components / Loaders

A quiet circular system with one precise point of energy.

Loader v1 is Irozon's second canonical component — packages/design-system/src/components/loader.css. A ring and one orbiting point, monochrome via currentColor so the surface it sits on decides its color. Not a generic browser spinner — an Irozon Orbit.

Base class
.iz-loader — a <span>, ring + orbit point.
Variant
data-variant="brand" — optional, for deliberate brand moments only.
Size
data-size="small | large" — default requires no attribute.
Color
Always currentColor — no iz-loader--green, --white, etc.
<span class="iz-loader" role="status" aria-label="Loading"></span>
Default / Brand

One default. One deliberate exception.

Default is monochrome and belongs anywhere in the product. Brand is Green-on-the- point, reserved for app-load, a branded processing screen, or a splash moment — never inside an ordinary button.

Default — currentColor
data-variant="brand" — Green point, Ink ring
Sizes

Three sizes, matched to Button's scale.

16px · ~1.2s/rev
32px · ~1.4s/rev · no attribute needed
48px · ~1.6s/rev
Context

currentColor reads the room.

The same markup, three surfaces — the loader never needs a color prop.

Light surface → Ink
Dark surface → light
Green surface → Ink
Button loading

Button doesn't have its own spinner. It has this.

Button's loading state nests an .iz-loader at data-size="small", aria-hidden since the button's own aria-busy already carries the meaning. It inherits currentColor, so it's always the button's own foreground — the Brand variant never appears inside a button.

Primary → Ink loader
Secondary → Ink loader
Destructive → its own error-color loader
Secondary, dark surface → light loader
Accessibility

Named when standalone. Silent when nested.

Standalone loader
Needs role="status" and an aria-label (or visible adjacent text) — it's the only thing on screen announcing that something is happening.
Nested loader (e.g. inside Button)
Marked aria-hidden="true" — the parent control's aria-busy="true" already carries the semantics; a second announcement would be redundant.
Reduced motion
Rotation is removed entirely under prefers-reduced-motion: reduce — the ring and offset point stay as a static, legible busy indicator. Animation was never the thing carrying the meaning; role="status"/aria-busy always was.
React

Loader from @irozon/design-system-react.

Sets role="status" and an accessible label for you; pass aria-hidden when the loader sits beside its own visible loading text so the state isn't announced twice.

import { Loader } from "@irozon/design-system-react";

<p style={{ display: "flex", alignItems: "center", gap: 12 }}>
  <Loader size="small" aria-hidden />
  Loading invoices…
</p>