Skip to main content

Core · Show content

Icons

Lets readers browse a set of icons by name and copy the SVG of each one.

Example

The icons LUZ draws, each with a button that copies its SVG.

  • Menu
  • Close
  • Search
  • Check
  • Plus
  • Minus
  • Chevron down
  • Chevron up
  • Chevron end
  • Arrow end
  • Cart
  • User
  • Mail
  • Calendar
  • Clock
  • Map pin
  • Download
  • External link
  • Info
  • Warning
  • Error
  • Heart
  • Filter
  • Robot
  • Book
  • Globe
  • Layers
  • Package
  • Grid
  • Feather
  • Terminal
  • List
  • Code
  • Code slash
  • Form
  • Brackets
  • Message
  • Sliders
  • Check circle
  • Help
  • Sun
  • Moon
  • Contrast
  • Auto
  • Page
  • Edit
  • App
  • Play
  • Pause
  • Volume
  • Volume off
  • Git branch
  • Feed

The logo is not an icon of the set: it is filled, in four shades of the text colour, on its own 32 × 32 grid. Copy it as it is; do not draw it with a stroke.

Each icon has a button that copies its SVG. These are LUZ's own icons, drawn for it: copy the SVG or the sprite.

They are not a package, so nothing is loaded for them.

Known issues

None known.

Code

Copy the markup

HTML
<ul class="luz-icon-grid">
  <li class="luz-icon-item">
    <span class="luz-icon-display"><svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M12 5v14M5 12h14"/></svg></span>
    <span class="luz-icon-name" id="icon-plus">Plus</span>
    <button class="luz-copy-btn" type="button" id="icon-plus-copy" aria-labelledby="icon-plus-copy icon-plus" data-copy="&lt;svg …&gt;…&lt;/svg&gt;">Copy SVG</button>
  </li>
</ul>

The same icons as a sprite: paste it once in the page, then draw each one with <use>. The sprite works only in the same document (href="#id"); a <use> that points at a file on another origin, a CDN included, is blocked by browsers.

HTML
<svg width="0" height="0" aria-hidden="true" focusable="false">
  <symbol id="luz-icon-menu" viewBox="0 0 24 24"><path d="M4 6h16M4 12h16M4 18h16"/></symbol>
  <symbol id="luz-icon-close" viewBox="0 0 24 24"><path d="M6 6l12 12M18 6L6 18"/></symbol>
  <!-- …the rest of the set, one symbol each -->
</svg>

<svg width="24" height="24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><use href="#luz-icon-cart"/></svg>

An icon beside a label, and an icon alone. A button names itself from its text, so give an icon-only button a name in a .u-sr-only span, not an aria-label: the span is translated with the page.

HTML
<!-- With a label: the icon is 16 px inside a button -->
<button class="luz-btn luz-btn--primary" type="submit">
  <svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M3 4h2.5l2.2 10.5h9.6L19.5 7H6.2"/><circle cx="9" cy="19" r="1.4"/><circle cx="17" cy="19" r="1.4"/></svg>
  Add to cart
</button>

<!-- Icon only: the name is real text, hidden from the eye -->
<button class="luz-btn luz-btn--ghost" type="button">
  <svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M4 6h16M4 12h16M4 18h16"/></svg>
  <span class="u-sr-only">Menu</span>
</button>

Two icons point to the end of the line: chevron-end and arrow-end, marked with data-luz-flip. In right-to-left text, LUZ mirrors anything with that attribute using the rule below, in core's utilities layer, so you write nothing.

Add the attribute to an icon of your own that points to the end of the line.

CSS
:where([data-luz-flip]):dir(rtl) {
  transform: scaleX(-1);
}

Variants and options

There are no modifiers. The grid fits as many columns of at least 100 px as the width allows.

ClassWhat it does
.luz-icon-grid
The list: a grid with no bullets
.luz-icon-item
One bordered cell: icon, name, copy button
.luz-icon-display
A 48 × 48 px box that centres the icon, in the secondary text colour
.luz-icon-name
The name under the icon

How it works

A <ul> of icons, styled by components/icon-grid.css. The copy buttons are copy buttons, wired by clipboard.js.

  • Each button carries the icon's SVG, escaped, in data-copy. On this page a script fills it from the icon beside the button, so the page does not carry every icon twice.
  • The icons are LUZ's own, drawn for it on a 24 × 24 grid with a 1.5 px round stroke in currentColor. They are not a package and no file is loaded: copy the SVG, or the sprite, into your page.
  • An icon is 24 × 24 px as written, and 16 px inside a .luz-btn. For an icon that must follow the text size, set width="1.5em" height="1.5em".

Without JavaScript

The copy buttons are hidden. The grid still shows every icon and its name.

Keyboard and ARIA

Tab reaches each copy button; Enter or Space copies the SVG.

  • Each button is named by its own text and the icon's name, with aria-labelledby: "Copy SVG Plus".
  • The icons are aria-hidden="true" and focusable="false": the name is the text under them, or the .u-sr-only text of the button that holds them.
  • Info and warning always come with words. A lone glyph says nothing to someone who cannot see its shape or its colour: put the words beside it, or give a stand-alone icon role="img" and a name.
  • The result of each copy is announced in a polite live region.

Contrast and focus are in the Design tab.

Design

When to use

Use an icon grid to let readers browse a set of icons and copy the one they need.

When not to use

Do not use it to lay out icons that do something: it is a catalogue, not a toolbar.

  • For a row of actions, use buttons with text in an action group.
  • Never use an icon as the only label of an action: give it its name as text, visible or in a .u-sr-only span.
  • For a logo or an illustration, use an <img> or an SVG at its own size.
  • For hundreds of icons, bring your own set.

Do and don't

Do Name each icon by what it shows, in one word.
Don't File names as labels: “ic_add_24” and “ic_ok_24” make the reader decode them, and “ok” is a use, not what the icon shows.

Writing the content

Name each icon by what it shows, in one word if you can: "Plus", "Check", "Arrow".

  • Keep names unique in one grid.
  • Draw every icon on the same grid and stroke, with currentColor, so it follows the text colour in every mode.
  • Pair Info and Warning with words, in the same place. They are shapes, not status colours: an alert keeps its own.

Contrast and focus

Icons and names are secondary text on the page, so an icon drawn in currentColor keeps more than 3:1. Elsewhere an icon takes the colour of its text, and so its contrast: a lone icon that carries meaning needs 3:1.

PartColour on backgroundLightDarkHigh contrastNeeds
Icon and name
--luz-text-secondary on --luz-surface-page
6.2:1
7.5:1
19.3:1
4.5:1
Copy button focus ring
--luz-focus-ring on --luz-surface-page
5.6:1
7.7:1
21.0:1
3:1

Measured from the token values of each mode, rounded down. Needs is the WCAG 2.2 AA minimum: 4.5:1 for text, 3:1 for focus rings, borders and icons.

  • Focus: only the copy buttons take it, with the copy button's ring.
  • Edges: the item border (--luz-divider) is decorative: the name labels each icon.

Tokens it uses

The 6 tokens that components/icon-grid.css reads, by family. Each family links to its page, with the values in every mode. In Figma they are variables with the same names: see Tokens in Figma.

Spacing and sizing
--luz-space-md, --luz-space-xs
Typography
--luz-text-xs
Radius
--luz-radius-md

Keyboard and ARIA are in the Code tab.