oriUI

Icon

A styled, accessible SVG icon. Decorative by default — renders with aria-hidden="true" and no role. Pass a label to expose it to assistive technology as an image: the icon switches to role="img" + aria-label automatically.

The examples are organised by layer: the class reference is the standalone @oriui/css layer, and the Framework API is the @oriui/vue component. Every example is live — flip its code between HTML (the standalone classes, also your htmx / Astro / Svelte / plain-HTML usage), Vue; HTML is the default.

Classes

An icon is a block class plus single-class token utilities — one class repoints one token; no separate base class is needed. The Vue props in Framework API map 1:1 to these. There is no variant or radius — an icon takes a size, an optional color, and optional layout modifiers.

ClassTypeDescription
ori-iconBlockRequired base class. Default size (text) and spacing are baked in — a bare block is valid.
ori-icon_* (size sugar)Sizetext (inherits) · xs · sm · md · lg · xl · xxl — text is the default
ori-color_*Colorprimary · secondary · success · warn · danger · info · surface · background — omit to inherit
ori-size-action-space_*Layoutadds margin equal to the action-space scale for the active size (the spaced opt-in); pair with a size modifier
ori-icon_inlineLayoutswitches to inline-flex with a small em-based margin for flow text
aria-hidden · role=imgStatedecorative by default (aria-hidden=true); a label switches to role=img + aria-label

Sizes

xsxxl. The default text means the icon inherits the current font size — use text to keep an icon visually matched to surrounding copy.

The text default inherits the surrounding font size — the icon below sits inside a sentence and tracks the paragraph's font-size with no explicit size prop:

html
<!-- ori-icon_xs / _md / _xxl drive the icon box; aria-hidden because decorative -->
<i class="ori-icon ori-icon_xs" aria-hidden="true">
    <svg viewBox="0 0 24 24"><path d="M22,13H13V22H11V13H2V11H11V2H13V11H22V13Z" /></svg>
</i>
<i class="ori-icon ori-icon_md" aria-hidden="true">
    <svg viewBox="0 0 24 24"><path d="M22,13H13V22H11V13H2V11H11V2H13V11H22V13Z" /></svg>
</i>
<i class="ori-icon ori-icon_xxl" aria-hidden="true">
    <svg viewBox="0 0 24 24"><path d="M22,13H13V22H11V13H2V11H11V2H13V11H22V13Z" /></svg>
</i>
<!-- no size modifier: inherits font size (text default) -->
<i class="ori-icon ori-icon_inline" aria-hidden="true">
    <svg viewBox="0 0 24 24"><path d="M22,13H13V22H11V13H2V11H11V2H13V11H22V13Z" /></svg>
</i>
vue
<OriIcon icon="M22,13H13V22H11V13H2V11H11V2H13V11H22V13Z" size="xs" />
<OriIcon icon="M22,13H13V22H11V13H2V11H11V2H13V11H22V13Z" size="md" />
<OriIcon icon="M22,13H13V22H11V13H2V11H11V2H13V11H22V13Z" size="xxl" />
<!-- default size="text": icon box matches the current font size -->
<OriIcon icon="M22,13H13V22H11V13H2V11H11V2H13V11H22V13Z" inline />

Colors

Every semantic role. surface / background are also available. Omit color to inherit currentcolor from the parent.

html
<!-- ori-color_<role> repoints the color token in one class -->
<i class="ori-icon ori-icon_lg ori-color_primary" aria-hidden="true">
    <svg viewBox="0 0 24 24"><path d="M22,13H13V22H11V13H2V11H11V2H13V11H22V13Z" /></svg>
</i>
<i class="ori-icon ori-icon_lg ori-color_danger" aria-hidden="true">
    <svg viewBox="0 0 24 24"><path d="M22,13H13V22H11V13H2V11H11V2H13V11H22V13Z" /></svg>
</i>
vue
<OriIcon icon="…" color="primary" size="lg" />
<OriIcon icon="…" color="success" size="lg" />
<OriIcon icon="…" color="danger" size="lg" />
<OriIcon icon="…" color="warn" size="lg" />

Inline

inline switches the icon to inline-flex with a small em-based margin so it flows naturally inside a sentence or a label without a wrapper element.

html
<p>
    Read the
    <i class="ori-icon ori-icon_inline ori-icon_md" aria-hidden="true">
        <svg viewBox="0 0 24 24"><path d="M13,9H11V7H13…" /></svg>
    </i>
    note carefully.
</p>
vue
<p>
    Read the
    <OriIcon icon="M13,9H11V7H13M13,17H11V11H13M12,2A10,10…" inline size="md" />
    note carefully.
</p>

Spaced

spaced adds a margin around the icon equal to the action-space scale for the active size — useful when the icon floats in open space rather than inside a button or label.

html
<i class="ori-icon ori-icon_lg ori-size-action-space_lg ori-color_primary" aria-hidden="true">
    <svg viewBox="0 0 24 24"><path d="M22,13H13V22H11V13H2V11H11V2H13V11H22V13Z" /></svg>
</i>
vue
<OriIcon icon="…" spaced size="lg" color="primary" />

Accessible (labelled)

Pass label to make the icon meaningful. The component sets role="img" and aria-label automatically and removes aria-hidden.

html
<!-- note: no aria-hidden; role="img" + aria-label carry the name -->
<i class="ori-icon ori-icon_lg" role="img" aria-label="Warning">
    <svg viewBox="0 0 24 24"><path d="M12,2A10,10…" /></svg>
</i>
vue
<!-- standalone icon with meaning: add a label -->
<OriIcon icon="…" size="lg" label="Warning" />

Custom SVG slot

Omit icon and place your own SVG (or any content) in the default slot. The slot content is sized to 100% × 100% and filled with currentcolor.

html
<i class="ori-icon ori-icon_lg ori-color_primary" aria-hidden="true">
    <svg viewBox="0 0 24 24">
        <circle cx="12" cy="12" r="10" />
        <path d="M12 8v4l3 3" stroke="white" stroke-width="2" fill="none" />
    </svg>
</i>
vue
<!-- custom multi-path SVG -->
<OriIcon size="lg" color="primary">
    <svg viewBox="0 0 24 24">
        <circle cx="12" cy="12" r="10" />
        <path d="M12 8v4l3 3" stroke="white" stroke-width="2" fill="none" />
    </svg>
</OriIcon>

Common patterns

An icon placed beside a status badge — size matches the action row, color follows the semantic role.

html
<div style="display: flex; align-items: center; gap: 0.5rem">
    <i class="ori-icon ori-icon_md ori-color_success" aria-hidden="true">
        <svg viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5…" /></svg>
    </i>
    <span>Deployed</span>
</div>
vue
<!-- status row: icon + label, icon is decorative (the text carries the meaning) -->
<div style="display: flex; align-items: center; gap: 0.5rem">
    <OriIcon icon="M21,7L9,19L3.5,13.5…" color="success" size="md" />
    <span>Deployed</span>
</div>
<div style="display: flex; align-items: center; gap: 0.5rem">
    <OriIcon icon="M13,13H11V7H13…" color="warn" size="md" />
    <span>Pending review</span>
</div>
<div style="display: flex; align-items: center; gap: 0.5rem">
    <OriIcon icon="M19,6.41L17.59,5…" color="danger" size="md" />
    <span>Build failed</span>
</div>

Accessibility

The accessibility contract holds across every layer — the standalone classes and the Vue component render the same attributes.

  • Decorative by default: renders <i aria-hidden="true"> with no role. Use this mode when the icon sits beside visible text that already communicates its meaning (status rows, labelled buttons, list items with text).
  • Meaningful icon: pass label — the component adds role="img" and aria-label and removes aria-hidden. Use this mode for standalone icons that carry meaning not expressed in nearby text (e.g. a lone star meaning "favourited").
  • When used inside an OriButton (or any control with its own accessible name), the icon is always decorative — let the button's own label or aria-label carry the name.
  • :focus-visible is not applicable — OriIcon is a presentational element, not interactive.
AttributeWhen set
aria-hidden="true"Default; no label prop.
role="img"label prop is provided.
aria-labelEqual to the label prop when provided.

Framework API

The props, events, and slots of the Vue component. The standalone CSS layer has no component API — its surface is the classes above. (Svelte bindings are planned.)

Props

PropTypeDefaultDescription
colorThemeColorSemantic color role. Omit to inherit currentcolor from the parent.
iconstringSVG <path d> string. Renders inside a 24 × 24 viewBox. Omit to use the default slot instead.
inlinebooleanfalseSwitches to inline-flex with an em-based margin for use inside text flow.
labelstringAccessible name. When set, adds role="img" and aria-label; removes aria-hidden.
sizeActionSize'text'Icon box size (xs · sm · md · lg · xl · xxl). text inherits the current font size.
spacedbooleanfalseAdds action-space margin scaled to the active size. Has no effect when size is not set.

Events & attributes

OriIcon declares no custom events and does not set inheritAttrs: false, so native attributes (class, style, data-*, event listeners, …) fall through directly to the rendered <i> element. A fall-through aria-label alone does not name the icon: when the label prop is unset the component always sets aria-hidden="true", which removes the element from the accessibility tree and causes assistive technology to ignore any aria-label on it. The label prop is the only correct way to name an icon — it sets role="img", aria-label, and removes aria-hidden together.

Slots

SlotDescription
defaultCustom icon content. When provided, the icon prop is ignored. Slot content is sized 100% × 100% via .ori-icon > *.