Skip to content

Icons

An icon in Tela is a tela.Icon (the text of an SVG document) shown with h.Icon. Tela draws it as vectors at the device scale, in the text color of its element, so one icon serves every size, color and display scale. This page covers using icons, the Lucide set, making your own set from SVG files, and what icons cost in a binary.

import (
"github.com/kuronosu/tela/icons/lucide"
. "github.com/kuronosu/tela/h"
)
Button(Class("inline-flex items-center gap-2 rounded-md bg-primary px-3 py-2 text-sm text-primary-foreground"),
Icon(lucide.Check, Class("size-4")),
Text("Save"))
Icon(lucide.Search, Class("size-5 text-muted-foreground"), AriaLabel("Search"))
  • An icon is 1em by 1em, so it follows the font size; size-*, w-* and h-* change it. It is a block (Class("inline") puts it on a line of text).
  • currentColor is the text color: text-* classes, theme tokens and transitions recolor it without parsing the SVG again. fill-*, stroke-* and stroke-[1.5], on the icon or on an ancestor such as the button, override the document’s own paints.
  • Icons are decorative (hidden from accessibility). AriaLabel or AriaLabelledBy makes one an image with that name.
  • Your own icon is any SVG: var Logo = tela.Icon{SVG: "<svg ...>"}, or tela.MustSVG(data) for an embedded file. tela.ParseSVG returns the parse error instead of panicking. The text is parsed once and cached by the text.

Lucide lives in its own module in this repository, so programs that do not use it do not download it:

Terminal window
go get github.com/kuronosu/tela/icons/lucide

It has one exported variable per icon, named after Lucide’s file name (arrow-up-right is lucide.ArrowUpRight; Lucide’s deprecated aliases such as lucide.AlertCircle are there too) and no table of icons by name. The module’s version is Lucide’s: the tag icons/lucide/v1.61.0 has the icons of Lucide 1.61.0 (lucide.Version). The set needs a Tela release with tela.Icon.

Inside a clone of the repository the module is tested from its own directory (cd icons/lucide && CGO_ENABLED=0 go test ./...): the root module does not import it and go test ./... at the root skips it. The module’s README says how to regenerate it for a new Lucide release.

tela icons gen turns a directory of SVG files into one Go file:

Terminal window
tela icons gen -pkg icons -o ./icons ./svg

(Install the command once with CGO_ENABLED=0 go install github.com/kuronosu/tela/cmd/tela@latest; see CLI. Without it, go run github.com/kuronosu/tela/cmd/tela icons gen ... does the same.)

// ArrowUp is the icon "arrow-up".
var ArrowUp = tela.Icon{SVG: `<svg xmlns="http://www.w3.org/2000/svg" ...>...</svg>`}
Flag Meaning
-pkg name Package name of the generated file (required)
-o dir Output directory (default .); the file is icons_gen.go
-r Read the subdirectories too (solid/arrow-up.svg is SolidArrowUp)
-prefix text, -suffix text Added around every name (-prefix Icon: IconArrowUp); a prefix starts with a capital
-skip-invalid Leave out files that cannot be parsed, with a warning (without it, they stop the command)
-minify=false Keep the SVG files as they are (only trimmed)
-source text, -license name, -license-file file Where the icons come from and their license, written in the file header and the package documentation (the license file’s text is copied in)

How it works:

  • Validation. Every file is parsed with the parser Tela draws with; the error is reported with the file’s path. Two files that make the same name are an error, and no file is written when anything fails.
  • Minifying. The XML declaration, comments, DOCTYPE, metadata, title and desc, editor namespaces (Inkscape, Illustrator), version, class and unreferenced id attributes (when the file has no <style>), empty defs and white space between elements go. The generator checks that the minified document draws the same display lists as the original, in several boxes, colors and with the SVG paint classes, and fails otherwise (-minify=false is the escape hatch). A file that declares entities is kept as written.
  • Names. File names are split at every character that is not a letter or a digit and each piece is capitalized: arrow-up is ArrowUp, volume_2 is Volume2. A name that does not start with a capital gets Icon in front (3d-rotate is Icon3dRotate). Digits that were apart stay apart with an underscore (arrow-down-0-1 is ArrowDown0_1, arrow-down-01 is ArrowDown01).
  • Output. One gofmt’d file sorted by name, so the output is the same on every run, with a // Code generated by tela icons gen; DO NOT EDIT. header, a package documentation and a doc comment per icon.

A //go:generate line keeps the set reproducible (the Lucide module does this). It uses go run, which needs nothing installed and uses the version of Tela your go.mod requires:

//go:generate go run github.com/kuronosu/tela/cmd/tela icons gen -pkg icons -o . ./svg

There is one variable of static data per icon and no map by name on purpose. The linker drops the variables a program does not use, so an unused icon costs nothing; a map would reference all of them (about 0.9 MB for 1,600 icons). A small Tela program (a Div and a Text, stripped, Linux amd64) is 19,873,952 bytes with no Lucide icon and with one, and 19,902,624 bytes (+28 KiB) with 50 of them. If you need to find an icon by a name known at run time, build a map of the icons you use.