Icon
A small drawing that stands for an action or a thing, from Lucide. Give it a label when it says something on its own; without one it is decoration and hidden from assistive technology.
The preview needs JavaScript. The code is in the Code tab.
<template>
<div>
<Icon name="check" />
<Icon name="search" size="sm" />
<Icon name="settings" size="lg" label="Settings" />
<Button><Icon slot="icon" name="plus" />New</Button>
</div>
</template>
Installation
npx @pixelscriptio/tessera add Iconpnpm exec tessera add Iconyarn tessera add Iconbunx @pixelscriptio/tessera add Icon- Needs the npm package
lucide1.62.0;--installinstalls it. - Works with Tessera 0.9.0 or newer.
Install the dependencies
npm install lucide@1.62.0Copy and paste the code into your project
Put each file in
system/components.system/components/Icon.tess <contract> { "name": "Icon", "role": "img", "description": "A small drawing that stands for an action or a thing, from Lucide. Give it a label when it says something on its own; without one it is decoration and hidden from assistive technology.", "since": "0.8.0", "parts": ["root"], "variants": { "size": ["md", "sm", "lg"] // first value is the default }, "preview": { "props": { "name": "settings", "label": "Settings" } } } </contract> <script> import { ICONS } from './icons.ts' // `name` picks one of the icons Tessera UI knows; `icon` is any Lucide icon, imported from `lucide`. const { name, icon, label } = props({ name: '', icon: null, label: '' }) const shapes = derived(() => icon.value ?? ICONS[name.value] ?? []) // Each shape gets an id for `each` to key by: its place in the icon. const of = (tag) => shapes.value.flatMap((shape, id) => (shape[0] === tag ? [{ ...shape[1], id }] : [])) const paths = derived(() => of('path')) const circles = derived(() => of('circle')) const rects = derived(() => of('rect')) const lines = derived(() => of('line')) const ellipses = derived(() => of('ellipse')) const polylines = derived(() => of('polyline')) const polygons = derived(() => of('polygon')) </script> <template> <svg class="root" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" role="img" :aria-label="label || null" :aria-hidden="label ? null : 'true'" > <path each="p in paths" key="p.id" :d="p.d" /> <circle each="c in circles" key="c.id" :cx="c.cx" :cy="c.cy" :r="c.r" :fill="c.fill ?? null" /> <rect each="r in rects" key="r.id" :x="r.x" :y="r.y" :width="r.width" :height="r.height" :rx="r.rx ?? null" :ry="r.ry ?? null" /> <line each="l in lines" key="l.id" :x1="l.x1" :y1="l.y1" :x2="l.x2" :y2="l.y2" /> <ellipse each="e in ellipses" key="e.id" :cx="e.cx" :cy="e.cy" :rx="e.rx" :ry="e.ry" /> <polyline each="p in polylines" key="p.id" :points="p.points" /> <polygon each="p in polygons" key="p.id" :points="p.points" /> </svg> </template> <style> .root { flex: none; width: $space.5; height: $space.5; &[data-size='sm'] { width: $space.4; height: $space.4; } &[data-size='lg'] { width: $space.6; height: $space.6; } } </style>system/components/icons.ts // The icons the `Icon` component knows by name: a small, common set from Lucide (https://lucide.dev, ISC license). // Each is imported by name, so a project that builds with Tessera UI ships these and no others. An icon outside the // set is used by importing it from `lucide` and giving it to `Icon` as `icon`. import { ArrowDown, ArrowLeft, ArrowRight, ArrowUp, Bell, Bookmark, Calendar, Check, ChevronDown, ChevronLeft, ChevronRight, ChevronUp, ChevronsUpDown, CircleAlert, CircleCheck, CircleHelp, CircleX, Clock, Copy, Download, Ellipsis, EllipsisVertical, ExternalLink, Eye, EyeOff, File, Filter, Folder, Globe, GripVertical, Heart, House, Image, Info, Link, LoaderCircle, Lock, LogIn, LogOut, Mail, Menu, Minus, Moon, PanelLeft, Paperclip, Pencil, Plus, RefreshCw, Save, Search, Send, Settings, Share, SlidersHorizontal, Sparkles, Star, Sun, Trash2, TriangleAlert, Undo2, Redo2, Upload, User, Users, X, Zap, } from 'lucide' /** One shape of an icon: its element name and attributes, as Lucide describes it. */ export type IconNode = Array<[string, Record<string, string | number>]> export const ICONS: Record<string, IconNode> = { 'arrow-down': ArrowDown, 'arrow-left': ArrowLeft, 'arrow-right': ArrowRight, 'arrow-up': ArrowUp, bell: Bell, bookmark: Bookmark, calendar: Calendar, check: Check, 'chevron-down': ChevronDown, 'chevron-left': ChevronLeft, 'chevron-right': ChevronRight, 'chevron-up': ChevronUp, 'chevrons-up-down': ChevronsUpDown, 'circle-alert': CircleAlert, 'circle-check': CircleCheck, 'circle-help': CircleHelp, 'circle-x': CircleX, clock: Clock, copy: Copy, download: Download, ellipsis: Ellipsis, 'ellipsis-vertical': EllipsisVertical, 'external-link': ExternalLink, eye: Eye, 'eye-off': EyeOff, file: File, filter: Filter, folder: Folder, globe: Globe, 'grip-vertical': GripVertical, heart: Heart, house: House, image: Image, info: Info, link: Link, 'loader-circle': LoaderCircle, lock: Lock, 'log-in': LogIn, 'log-out': LogOut, mail: Mail, menu: Menu, minus: Minus, moon: Moon, 'panel-left': PanelLeft, paperclip: Paperclip, pencil: Pencil, plus: Plus, redo: Redo2, 'refresh-cw': RefreshCw, save: Save, search: Search, send: Send, settings: Settings, share: Share, 'sliders-horizontal': SlidersHorizontal, sparkles: Sparkles, star: Star, sun: Sun, 'trash-2': Trash2, 'triangle-alert': TriangleAlert, undo: Undo2, upload: Upload, user: User, users: Users, x: X, zap: Zap, }Check your design tokens
Your design system needs these.
tessera addreports the ones that are missing; by hand, runnpx @pixelscriptio/tessera check.space.4space.5space.6
Usage
Using Icon
A small drawing from Lucide for an action or a thing.
namepicks one of the icons Tessera UI knows:check,x,plus,search,chevron-down,trash-2,settingsand about sixty more. Names are Lucide's, in kebab case.- For any other Lucide icon, import it from
lucidein your script and pass it asicon. Only the icons you import are in your build. - An icon next to text is decoration and says nothing. An icon that stands alone, such as a button with no words, needs a
label, which becomes its accessible name. On a Button it is the button that needs the name. sizeissm,mdorlg. The icon takes the color of the text around it.- Put one in a component's
iconslot:<Icon slot="icon" name="plus" />.
The preview needs JavaScript. The code is in the Code tab.
<template>
<div>
<Icon name="check" />
<Icon name="search" size="sm" />
<Icon name="settings" size="lg" label="Settings" />
<Button><Icon slot="icon" name="plus" />New</Button>
</div>
</template>
API reference
Variants
| Variant | Values |
|---|---|
size | md default sm lg |
Anatomy
| Role | img |
| Parts | root |
| Since | 0.8.0 |
Design tokens it uses
Your design system needs these. tessera add checks that it has them, and can add the ones it lacks.
space.4 space.5 space.6
Checksum
8208c784222be71b19e6c6299f4e3de39aab207dd6d353c38e09de309bf316f5