HabIcon
The glyph renderer. Path data in, four sizes, decorative.
<script setup>
import { mdiLockOutline } from "@mdi/js";
</script>
<template>
<HabIcon :icon="mdiLockOutline" />
</template>Most icons in this system never reach for it: they are a prop on the component that owns them — HabButton's icon, HabListRow's, a column's, a nav item's — and that is still the right way to spell one. Reach for HabIcon for the glyph that belongs to no component: beside a heading, in a table cell you are rendering yourself, as a static mark on a row.
It takes path data, not a name
icon is an SVG path string, so import the glyph from @mdi/js:
import { mdiCheck } from "@mdi/js";Every MDI name maps to its export by one rule — kebab-case to mdi plus PascalCase — so cog-outline transcribes to mdiCogOutline with no lookup. A typo is a typecheck failure rather than a hole in the UI. An icon whose identity arrives at runtime cannot be served this way; render your own markup instead.
The size is a control step, not a type step
sm, md, lg, xl, from --hab-icon-*. Never size an icon off the type ladder — doing that once created a fifth icon size in a system that documents four. An icon beside a control takes the control's step: sm controls take sm.
It is always decorative
aria-hidden is unconditional and there is no way to turn it off. Every icon here sits beside a label, or inside a control that carries its own accessible name. An icon that needs its own name means the thing around it is missing one — give the button an aria-label rather than reaching for a labelled glyph.
That also means an icon must never be the only thing carrying a meaning. A status mark needs its status in text as well.
API
Props
| Prop | Type | Default | Description |
|---|---|---|---|
icon | string | undefined | SVG path data, e.g. mdiCheck from @mdi/js. |
size | "md" | "sm" | "lg" | "xl" | "md" | One of the four icon sizes. Never sized off the type ladder. |