# Icon

> An icon from the app's own set, as large as the text around it. A CSS component with one class and no JavaScript.

The class goes on an `<svg>`. LO-TEK ships no icons, so the svg comes from the set the app already uses. The class makes the icon as large as the surrounding text and centres it on the line, and `lt.icon()` hides it from screen readers when it is only decoration.

## How to use it

**Plain HTML**

```html
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@lo-tek/interfaces@0.40.0/src/tokens/tokens.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@lo-tek/interfaces@0.40.0/src/components/css/button/button.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@lo-tek/interfaces@0.40.0/src/components/css/icon/icon.css">
<!-- Or everything in one file: https://cdn.jsdelivr.net/npm/@lo-tek/interfaces@0.40.0/dist/lotek.css -->
```

```html
<button class="lt-button" type="button">
<svg class="lt-icon" aria-hidden="true" viewBox="0 0 24 24" fill="none"
     stroke="currentColor" stroke-width="2">
  <path d="M12 5v14M5 12h14" />
</svg>
New ticket
</button>


<button class="lt-button" data-variant="ghost" type="button" aria-label="Delete ticket #0331">
<svg class="lt-icon" aria-hidden="true" viewBox="0 0 24 24" fill="none"
     stroke="currentColor" stroke-width="2">
  <path d="M4 7h16M9 7V4h6v3M6 7l1 13h10l1-13" />
</svg>
</button>
```

**TypeScript**

```ts
import "@lo-tek/interfaces/tokens.css"
import "@lo-tek/interfaces/button.css"
import "@lo-tek/interfaces/icon.css"
```

**If you create the markup,** spread the attributes in:

```tsx
import { lt } from "@lo-tek/interfaces/react"


<button {...lt.button({ variant: "ghost" })} type="button" aria-label="Delete ticket #0331">
  <svg {...lt.icon()} viewBox="0 0 24 24">…</svg>
</button>
```

**If the element is already there,** set them on it:

```ts
import { lt } from "@lo-tek/interfaces"


const icon = document.querySelector<SVGSVGElement>("#warning-icon")


lt.setAttributes(icon, lt.icon({ label: "Warning" }))
```

Without `label`, `lt.icon()` gives `aria-hidden="true"`. With `label` it gives `role="img"` and `aria-label` instead. See [Type-safe use](/type-safe-use.md).

**kotlinx.html**

```kotlin
// Once for the whole app, in the page template: every stylesheet, from the WebJar.
head { link(rel = "stylesheet", href = LotekWebJar.CSS) }
```

On a Kotlin or Java server, the same with kotlinx.html and `lo-tek-kotlinx-html`. The builder functions are in `Lt`, with an enum for each list of values:

```kotlin
import kotlinx.html.*
import no.lotek.*
import no.lotek.html.lt


fun FlowContent.tickets() {
    button {
        lt(Lt.button())
        type = ButtonType.button
        svg {
            lt(Lt.icon())
            attributes["viewBox"] = "0 0 24 24"
            attributes["fill"] = "none"
            attributes["stroke"] = "currentColor"
            attributes["stroke-width"] = "2"
            unsafe { +"""<path d="M12 5v14M5 12h14" />""" }
        }
        +"New ticket"
    }
    button {
        lt(Lt.button(variant = ButtonVariant.GHOST))
        type = ButtonType.button
        attributes["aria-label"] = "Delete ticket #0331"
        svg {
            lt(Lt.icon())
            attributes["viewBox"] = "0 0 24 24"
            attributes["fill"] = "none"
            attributes["stroke"] = "currentColor"
            attributes["stroke-width"] = "2"
            unsafe { +"""<path d="M4 7h16M9 7V4h6v3M6 7l1 13h10l1-13" />""" }
        }
    }
}
```

The first button has text, and the icon is decoration next to it. The second has only the icon, and gets its name from `aria-label` on the button.

## Size and colour

The icon is `1em` wide and high, so it follows the font size where it stands. If you want it larger or smaller than the text, set `font-size` on the icon.

The colour comes from the svg. Write `currentColor` in `fill` or `stroke`, as icon sets do, and the icon gets the colour of the surrounding text. It also follows the text in forced colours mode. The stylesheet does not set `fill` itself: an icon drawn with strokes has `fill="none"`, and a rule in the stylesheet would fill it.

## An icon that means something

If the icon stands alone and says something the reader needs, such as a warning sign with no text next to it, it gets a name:

```html
<svg class="lt-icon" role="img" aria-label="Warning" viewBox="0 0 24 24"
     fill="none" stroke="currentColor" stroke-width="2">
  <path d="M12 3l9 16H3zM12 10v4M12 17v.01" />
</svg>
```

If text next to it says the same thing, the icon is decoration and should have `aria-hidden="true"`. Otherwise the same thing is read out twice.

## Classes you can style

| Class | What it is |
| - | - |
| `.lt-icon` | The icon, on an `<svg>`. |

See [Customising](/customising.md) for how the layer works.

## Accessibility

What a screen reader is given when the recipe above is used, step by step. It is checked through AT-SPI, the interface Orca reads, in Chromium and Firefox, on every change, by `check-accessibility-tree.ts`, and every change the check lists as a mutation must make it fail.

1. Focus `button:nth-of-type(1)`: focus on the `button` "New ticket".
2. Focus `button:nth-of-type(2)`: focus on the `button` "Delete ticket #0331".

- A button or link with only an icon in it needs a name: `aria-label` on the button, or a text in an element with the class `lt-sr-only`. `lotek check` and the editors report `without-name` when it is missing. A `<title>` in the svg names it too, when the svg has no `aria-hidden`.
- Write what the button does, not what the icon depicts. “Delete ticket #0331” is better than “Rubbish bin”.
- An icon alone is hard for many people to understand, including those who can see it. If you have room, text next to it is better than only a name for the screen reader.
