GrIcon

  • core
  • data
  • API · 4 props

Renders an icon as a standalone interface element.

import { GrIcon } from '@feugene/granularity/components/GrIcon'

Overview

When to take it

  • the icon needs a uniform size — --gr-icon-size instead of a width and a height in every place;
  • the icon takes its colour from the palette — tone instead of a fill of your own;
  • the icon is meaningful — label gives it a name; without one it is hidden from a screen reader, and that is right by default;
  • the icon rotates — spin for a waiting state with no animation of your own.

When to take something else

NeedTake
An icon inside a buttonGrButton with square
The avatar of a personGrAvatar
A key on the keyboardGrKbd
A waiting indicatorGrLoading

The component does not draw the icon itself — it arrives through a slot from any set (unplugin-icons, an SVG of your own, a sprite). The package does not ship an icon set of its own and will not: duplicating a delivery is harmful.

Variants every tone and view, rendered by the real component

tone

  • primary
  • neutral
  • success
  • warning
  • danger
  • info
  • slate
  • azure

Sizes

  • xs
  • sm
  • md
  • lg

Playground controls come from the API, not from hand

Stage

Loading…

0 changed· state is in the URL

Examples 4 each with a live demo and its code

  • Decorative next to a word, named when alone

    Next to its word an icon repeats it, so by default it is hidden from the screen reader — no aria-hidden to remember. Alone in a cell it is the message, and label gives it role="img" and a name. The glyph comes from anywhere: the icon set or an SVG of your own.

    • PasswordChanged 3 months ago
    • Two-factor authenticationAuthenticator app
    • Active sessions3 devices
    Member2FAEmail
    Ada Lin
    Grace Wu
    Linus Ray
  • A tone for the meaning, the parent’s colour by default

    tone paints the glyph with a -text role, so a warning triangle stays readable on the page background. Without it the icon is current and follows its parent — the arrow turns into a link together with the link.

    • Deploy of api-gateway finished2 min
    • Disk usage on db-2 passed 85 %14 min
    • Payment webhook returned 502 three times31 min
    • Maintenance window starts at 02:00 UTC1 h
    • Grace Wu opened #41822 h
    • Project «Q2 launch» archivedyesterday
    View the full activity log
  • Sizes tied to the text

    Each step of size matches a step of the text it stands next to, and the scale lives in --gr-icon-size-* tokens. A number is the escape hatch for one-off art, and a toolbar sets the size once through GrConfigProvider.

    • Workspace settings lg
    • Billing and invoices md
    • Next invoice on 1 November sm
    • Prices exclude VAT xs
    No messages:size="48"
  • A spinner that names its state

    spin turns the glyph for work in progress, and under prefers-reduced-motion the base clamp stops it upright rather than at a random angle. Each status is the icon alone, so each carries a label.

    1. Install dependencies42 s
    2. Type check and lint1 min 08 s
    3. Unit tests4 812 of 5 947
    4. Build packageswaiting
    5. Publish to npmwaiting

API generated from the package · cannot drift

Props

PropTypedefaultDescription
toneGrIconTone | undefined"current"A colour from the palette. `current` — inherit the text colour of the parent.
sizenumber | "xs" | "sm" | "md" | "lg" | undefinedundefinedThe size by the scale of the package or an arbitrary one in pixels.
labelstring | undefinedundefinedThe name of a meaningful icon. If it is set, the icon is declared `role="img"` and stops being hidden. Unset — the icon is decorative.
spinboolean | undefinedfalseRotation — for spinners.

Slots

SlotTypeDescription
defaultanyMarkup of your own for the icon instead of a name from a set.

Types

Every exported type of the subpath, grouped by the file it lives in. Names, generics and bodies come from the package’s declaration files and cannot drift.

Import
import type { GrIconSize, GrIconTone, GrIconConfigurableProps } from '@feugene/granularity/components/GrIcon'

Filedist/types/components/GrIcon/config.d.ts

grIconConfigconst
const grIconConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrIcon">;

Filedist/types/components/GrIcon/safelist.d.ts

grIconSafelistconst
const grIconSafelist: string[];

Filedist/types/components/GrIcon/grIconStyles.d.ts

GrIconSizetype
type GrIconSize = GrComponentSize;
GrIconTonetype
type GrIconTone = GrTone | 'current';

Filedist/types/components/GrIcon/defaults.d.ts

GrIconConfigurablePropsinterface
interface GrIconConfigurableProps {
    size: GrComponentSize;
}

Filedist/types/components/GrIcon/GrIcon.vue.d.ts

GrIconPropsinterface
interface GrIconProps {
    size?: GrIconSize | number;
    label?: string;
    tone?: GrIconTone;
    spin?: boolean;
}

Usage from the library prose

Decorative and meaningful

By default an icon is decorative: the component sets aria-hidden="true" itself. In the library an icon almost always accompanies text, and the attribute used to be written by hand at every call site — forget it once and a screen reader reads the <title> from the SVG.

label makes an icon meaningful: role="img" and aria-label appear, and the hiding is removed.

vue
<!-- the text "Saved" is next to it — the icon is decorative -->
<GrIcon>
<IconCheck />
</GrIcon>

<!-- the icon carries the meaning itself -->
<GrIcon label="Verified">
<IconCheck />
</GrIcon>

The rare case of “the icon is meaningful, but the name is given by a neighbouring element” is expressed with an ordinary attribute: a fallthrough is stronger than the component’s own binding.

The size

size is the scale of the package (xs | sm | md | lg, md by default) or an arbitrary number in pixels. The scale is read from GrConfigProvider, and a number is a local escape hatch past the config.

The values of the scale live as the --gr-icon-size-xs…lg tokens, so the size of the icons is configured by the theme rather than by rebuilding the package. The --gr-icon-size variable itself is a point of customisation in place: both the wrapper and the nested SVG compute themselves from it.

The tone and the rotation

tone colours the icon with a text token (current by default — the colour is inherited from the parent). A saturated tone as a text colour is forbidden in the package: on a light background it gives a contrast of about 2:1, so the -text roles are used, as in GrAlert.

spin switches the rotation on for spinners. The component needs no separate support for prefers-reduced-motion: the global clamp in base.css holds the animations, and the rotation freezes in its starting position rather than at a random angle.

The style is deliberately global

The .gr-icon rule is not scoped: the SVG arrives through the consumer’s slot, and a scoped style does not reach it — a :deep would have to be written at every call site.