The framework's structural icons are drawn with a single masked primitive, .pa-icon, so every glyph inherits the current text colour, scales with font size, and is
themeable from one place. At this layer an icon is a raw <span class="pa-icon pa-icon--NAME"> you drop inside any of our components — this is how
the library's own chrome renders affordances. There is also an <Icon> component that resolves names through
swappable provider sets (Font Awesome, Lucide, …) — see Svelte › Icon Component. This page lists the
masked icons that ship as variables, shows them inside components, and explains the hover-to-fill
affordance.
The masked icon primitive
Each masked glyph is a <span class="pa-icon pa-icon--name">. The glyph is an SVG mask; the box is painted in currentColor and shows through it. That means an
icon automatically matches its surrounding text colour and scales with font-size (default 1em, override with --pa-icon-size).
Same class, sized with --pa-icon-size and coloured with color / currentColor.
<!-- default 1em, inherits text colour -->
<span class="pa-icon pa-icon--search" aria-hidden="true"></span>
<!-- sized + coloured -->
<span class="pa-icon pa-icon--search"
style="--pa-icon-size: 2.4rem; color: var(--pc-primary-color);"></span>Icon variables
These structural affordances ship as icon variables. Add the modifier class to a .pa-icon span; each modifier points --pa-icon-src at the matching --pa-icon-name token.
Close / clear / remove
--x--clear--removeDisclosure & direction
The four chevron directions are one glyph rotated with a transform, so overriding the chevron re-skins all four at once. Same for the ellipsis (horizontal / vertical).
--chevron--chevron-up--chevron-right--chevron-down--chevron-left--caret-up--caret-down--expand--collapseActions
--add--edit--delete--save--copy--downloadUtility
--search--refresh--filter--check--ellipsis--ellipsis-vertical--link--external-link--settings--bell--user--favoritesSeverity
One shared family used by toasts, alerts, callouts, notifications and popconfirm, so every severity surface shows the same mark. Shown here in their semantic colours.
--info--success--warning--dangerUsing icons with components
Because an icon is just a span, it drops into any component that accepts children or an icon snippet. A few common placements:
In buttons
Icon-only buttons take the glyph as their child; icon+label buttons put it in the icon snippet so it lands in the centred .pa-btn__icon slot.
<!-- icon-only: glyph is the child -->
<Button variant="danger" isIconOnly titleText="Delete">
<span class="pa-icon pa-icon--delete" aria-hidden="true"></span>
</Button>
<!-- icon + label: glyph in the icon snippet -->
<Button variant="secondary">
{#snippet icon()}<span class="pa-icon pa-icon--download" aria-hidden="true"></span>{/snippet}
Export
</Button> In alerts & callouts
Pass a severity glyph to the icon snippet — it inherits the alert/callout colour via currentColor.
Heads up
The same.pa-icon--warning glyph, tinted by the callout.<Alert variant="info">
{#snippet icon()}<span class="pa-icon pa-icon--info" aria-hidden="true"></span>{/snippet}
Masked severity marks share one family across every surface.
</Alert> In tabs & badges
How the tokens re-skin
Most icons trace through a shared, cross-ecosystem contract, so a single override re-skins Pure Admin, the pure-css shell and the KeenMate web components together:
.pa-icon--search → var(--pa-icon-src)
→ var(--pa-icon-search)
→ var(--base-icon-search, /* inline Lucide fallback */) --base-icon-*— the shared contract (published in@keenmate/base-css-variablesand mirrored by@keenmate/pure-css). Override once to re-skin everything.--pa-icon-*— Pure Admin's family; falls back to an inline Lucide glyph so icons always render even without the contract loaded.- pa-only glyphs — a few (e.g.
--pa-icon-favorites) are Pure Admin-only and are not part of the--base-*contract, so they're defined directly with no--base-icon-*route.
To retarget a single glyph, set its --pa-icon-name (or the shared --base-icon-name) at :root or on any subtree.
Hover-to-fill (enabled buttons & tabs)
An icon can swap to a filled variant when its enabled button or tab is hovered. It's opt-in on two axes:
- Per glyph — an icon opts in by declaring a filled source on
--pa-icon-src-hover. Icons without one are left untouched. - Per context — the swap only fires inside an enabled
.pa-btnor.pa-tabs__itemon:hover. Disabled controls never react.
The default Lucide set is outline-only
Lucide ships no filled glyphs, so hover-to-fill is dormant out of the box — the mechanism is present but there's nothing to fill to. To activate it, point--pa-icon-src-hover at a filled glyph from a set that has one — Tabler (a
Lucide look-alike), Fluent, or Material. The live demo below uses a Tabler regular/filled bell pair.Live demo
Hover each control. The first uses a Tabler outline→filled bell and morphs; the second is the Lucide bell
and stays outline (nothing to fill to); the third is the same Tabler bell but disabled, so it
never reacts despite being fillable (proving the disabled gate). No transition — mask-image can't tween, so the fill is an instant swap.
How to enable it
Define a modifier that declares both a resting (outline) and a hover (filled) source. The framework's global rule swaps them on hover:
/* your glyph — resting outline + filled-on-hover source */
.pa-icon--bell {
--pa-icon-src: var(--pa-icon-bell); /* Lucide outline */
--pa-icon-src-hover: url("…tabler filled bell…"); /* filled variant */
}
/* framework rule (ships in core) — no-op unless a glyph declared a hover src. */
.pa-btn:not(:disabled):hover .pa-icon,
.pa-tabs__item:not(:disabled):hover .pa-icon {
mask-image: var(--pa-icon-src-hover, var(--pa-icon-src));
} The fallback to --pa-icon-src is what makes the rule safe to ship globally: any icon that
didn't declare a filled source simply keeps its resting glyph.