A single icon from the Loom Site set, drawn inline so it takes the text colour and needs no request.

Atom · beta · v1.0.0 · owner: Site system lead · contract 19 of 20 points (open: figma)

Do and don't

Do: After a call to action

Rendered by icon.ts

Open example: Icon · Do: After a call to action Use the full-page example to follow page links.

The words name the action; the arrow only shows direction.

Do: Labelled when it stands alone

Rendered by icon.ts

Open example: Icon · Do: Labelled when it stands alone Use the full-page example to follow page links.

A lone icon must be named for assistive technology.

Don't: A lone arrow as the only content of a link

Rendered by icon.ts

Open example: Icon · Don't: A lone arrow as the only content of a link Use the full-page example to follow page links.

Without a label nobody using a screen reader knows where the link goes.

Don't: An unknown name

Rendered by icon.ts

Refused by the element. icon: "sparkle" is not in the Loom Site icon set

Only icons in the governed set may be drawn; the build rejects any other name.

Rules

RuleWhyCheck
Draw only icons from the Loom Site set.One governed set keeps stroke, grid and meaning the same on every page.Automated: scripts/site/system/check.ts (icon names in examples and pages)
An icon without a label is aria-hidden.Next to words it repeats them; hidden, it never adds noise to a screen reader.Automated: tests/unit/site-system.test.ts
Icons stroke with currentColor.They then follow the text role and the dark mode with no extra token.Automated: tests/unit/site-system.test.ts

API

PropTypeRequiredDefaultWhat it does
namearrow-rightYesWhich icon from the set.
labelstringNoAccessible name when the icon stands alone; omitted icons are hidden from assistive technology.
classNamestringNo"arrow"The class the parent animates (the arrow nudges on hover).

Contract details

The full contract: identity, anatomy, variants, states, tokens, layout, motion, accessibility, content, tests

Identity

AspectValue
Idicon
LayerAtoms
Categorymedia
OwnerSite system lead
Statusbeta
Version1.0.0
Sourceapps/showcase/site/system/atoms/icon.ts (private repository; the contract and the render function are this one module)
VersionDateChange
1.0.02026-10-26The arrow that leads every call to action, drawn once for the whole site.

Use when

  • An action or link needs a direction cue, such as the arrow after a call to action. Why: The arrow tells the reader the link moves them forward, without adding words.
  • A small symbol repeats across the site and must look the same everywhere. Why: One drawing in one place cannot drift between pages.

Do not use when

  • The icon would be the only label of a control. Why: An icon alone is ambiguous; every control needs a text name.
  • As decoration in running text. Why: Decorative symbols slow reading and add nothing a word cannot say.

Anatomy

PartWhat it is
rootAn inline svg with class arrow (or the given class), aria-hidden unless labelled.
pathThe stroked path on the 16 or 24 px grid.

Made of: tokens:colour.

Used in: arrow-link, button, header, jobs-strip, rows-list.

Variants

VariantWhenWhy
decorativeNext to words that already name the action.Screen readers would otherwise read the symbol twice.
labelledThe icon carries meaning the words do not.Assistive technology then announces the label.

States

StateRendered or not applicable
defaultdefault
hoverNot applicable: An icon is not interactive, so it has no hover state.
focus-visibleNot applicable: An icon does not take focus; links and buttons inside it carry their own focus ring.
activeNot applicable: An icon cannot be pressed.
disabledNot applicable: An icon has nothing to disable.
loadingNot applicable: An icon is rendered at build time and is complete when the page loads.
emptyNot applicable: An icon is not rendered without content; the builder skips it.
errorNot applicable: An icon shows no data that can fail.
currentNot applicable: An icon has no current item.
visitedNot applicable: An icon is not a link.
expandedNot applicable: An icon does not open or close.

Tokens

PartToken
root.strokecurrentColor (inherits the text role of its parent)
root.size1em, set by the parent's type role

Layout

AspectValue
min16 px
max24 px
breakpointsRequired review at 390, 768, 1024 and 1440 px; execution is not implied by this declaration; the icon scales with the text it sits in.
wrappingNever wraps; flex: none in buttons and links.
textExpansionNot affected: icons carry no text.

Spacing and rhythm

AspectValue
internalNone: the drawing's own 2 px padding inside its box.
externalThe parent's gap (--space-4 in buttons).
rhythmNot a section element.

Motion

AspectValue
NoneThe icon does not move by itself; the arrow's nudge on hover belongs to the button and arrow link.

Runtime and reuse

  1. Render-only element; mount any registered child enhancements separately. Preserve required Site CSS, fonts, responsive and locale rules from nativeElement.

Accessibility passport

AspectValue
roleimg when labelled, otherwise presentation (aria-hidden)
nameThe label prop, or none when decorative
keyboardNot focusable (focusable=false).
focusNever in the focus order.
screenReaderDecorative icons are silent; labelled icons read their label once.
contrastInherits the text colour, so it meets the same 4.5:1 as its text.
targetNot a target; the parent link or button is.

Content

SlotLengthVoice
label3 wordsPlain verb or noun, as the action says it

Good: “Book a conversation →”

Bad: “→ alone as a button”

Japanese: The label prop is translated with the page; decorative icons need no Japanese.

Icons and illustrations

  • icon:arrow-right

Decisions and exceptions

Usage

511 uses across the built pages (measured by npm run site:system).

Shared styles

SVG stroke inherits currentColor and size is 1em; the parent action owns spacing and hover motion. No independent palette alias is needed.

Style owners: arrow-link, button

  • apps/showcase/site/system/styles/core/arrow-link.css
  • apps/showcase/site/system/styles/core/button.css

Remaining review: Standalone labelled/decorative and parent-action state review remains required.

Tests

AspectValue
unittests/unit/site-system.test.ts: renders every do and don't example through the registry and checks the classes it owns
visualNot yet: there are no screenshot baselines; the Library page is checked for sideways scroll at 1440 px by tests/site/system.spec.ts
a11ytests/site/system.spec.ts: axe (0 serious or critical) on the element's Library page in light and dark
parityscripts/site/system/check.ts: Library examples are rendered by the same render function the site imports

Figma

not-yet: The Loom Site Figma library is the last stage of the plan; this element is not mapped yet.

Open in Storybook

Start with a conversation about your system.

We read your system, show you what we found, and agree with your team what to fix first.