Documentation
Getting started with tlp-icons
26,278 icons in 35 categories, every one animated. One small custom element, <tlp-icon>, works in plain HTML and every framework. No dependencies.
Installation
npm
npm install tlp-iconsCDN, no build step
Loads the element and fetches only the categories your page uses.
<script type="module" src="https://unpkg.com/tlp-icons/dist/tlp-icon.js"></script>
<tlp-icon name="bell"></tlp-icon>Offline, one file
A classic script with every icon inside (about 2.3 MB, 390 KB gzip), in the tlp-icons-data package. Works from file://, with no network and no module loader.
npm install tlp-icons-data<script src="node_modules/tlp-icons-data/tlp-icons.all.js"></script>
<tlp-icon name="bell"></tlp-icon>Basic usage
Write the element with an icon name. Find names in the icon gallery. Hover these:
<tlp-icon name="bell"></tlp-icon>
<tlp-icon name="settings" size="32" stroke="1.5"></tlp-icon>Attributes
| Attribute | Values | Default | What it does |
|---|---|---|---|
name | icon name or alias | Which icon to show. | |
size | number (px) or CSS length | 24 | Width and height (same as --tlp-size). |
stroke | number | 2 | Stroke width in the 24×24 grid (same as --tlp-stroke). |
trigger | hover hold loop click none | hover | When the animation plays. hold: plays to its peak on hover, stays, returns when the mouse leaves. |
duotone | boolean | off | Soft tinted fill on the icon's main body. |
flip | rtl x | off | Mirror in right-to-left pages, or always. |
animated | false | animated | Static icon that never animates. |
Triggers
<tlp-icon name="heart"></tlp-icon> <!-- hover (default) -->
<tlp-icon name="trash" trigger="hold"></tlp-icon> <!-- hover: animate and stay, back on leave -->
<tlp-icon name="bell" animated="false"></tlp-icon> <!-- static, never animates -->
<tlp-icon name="loader-circle" trigger="loop"></tlp-icon> <!-- again and again -->
<tlp-icon name="party-popper" trigger="click"></tlp-icon> <!-- on click -->
<tlp-icon name="anchor" trigger="none"></tlp-icon> <!-- only el.play() -->Frameworks
Import the element once at startup, then use <tlp-icon> anywhere. This website is built with Next.js and uses the package exactly like this.
React / Next.js
'use client';
import 'tlp-icons/element';
export function SaveButton() {
return (
<button>
<tlp-icon name="save" /> Save
</button>
);
}The import is safe during server rendering. TypeScript types for the tag ship with the package (since 0.2.0): add one import and name, trigger and flip are type-checked.
import 'tlp-icons/jsx'; // types <tlp-icon> in JSX (React 18 and 19)React 18 wrapper
React 18 turns boolean props into strings. Use the tiny optional wrapper instead of the bare tag:
import { TlpIcon } from 'tlp-icons/react';
<TlpIcon name="heart" size={32} duotone className="like" />Angular
// main.ts
import 'tlp-icons/element';
// any component
@Component({
selector: 'app-toolbar',
schemas: [CUSTOM_ELEMENTS_SCHEMA],
template: `<button><tlp-icon name="bell"></tlp-icon> Alerts</button>`,
})
export class ToolbarComponent {}Vue / Nuxt
// main.ts
import 'tlp-icons/element';
// vite.config.ts
vue({ template: { compilerOptions: { isCustomElement: tag => tag === 'tlp-icon' } } })<template>
<tlp-icon :name="done ? 'circle-check' : 'circle'" />
</template>Styling
Icons use currentColor, so they take the text color. Theme them with CSS variables:
tlp-icon { --tlp-size: 20px; --tlp-stroke: 1.75; }
.danger { color: #dc2626; } /* icon color = text color */
.card { --tlp-duo: .22; } /* duotone strength */The hover rule
Hovering an icon animates it. Hovering anything else walks up to the nearest element that holds exactly one icon (a button, a menu item) and animates that one. If it reaches an element with several icons first (a card, a menu, a table row), nothing animates. An animation always plays to the end.
JavaScript API
const el = document.querySelector('tlp-icon');
el.play(); // play once
el.setAttribute('name', 'bell'); // swap the icon
import { icons, categories, getSvg, getIcon } from 'tlp-icons';
getSvg('bell'); // static SVG markup
getSvg('bell', { size: 32, stroke: 1.5, duotone: true });
getSvg('bell', { animated: true }); // motion baked in
icons.bell; // { svg, m: [[part, anim, dur, delay, origin?], ...] }
import { config, register } from 'tlp-icons/element';
config.speed = 0.8; // default 0.6 (about 0.9 s per icon)Importing icons pulls in the whole set. In pages, prefer <tlp-icon>: it downloads one category at a time.
Animation library
Every choreography is built from these 100 animations. Entrances bring a part into place; gestures move it and return exactly to rest. All keyframes are prefixed tlp- so they never clash with Tailwind or your CSS. Hover a tile:
Accessibility
- Icons are decorative by default (
aria-hidden). When an icon carries meaning, addrole="img"andaria-label. - With
prefers-reduced-motionenabled, icons never animate. - Animations are short (0.9 s on average, 1.6 s at most) and always end exactly at rest.
Right-to-left
Motion follows the drawing: an arrow pointing right moves right. On RTL pages, mirror directional icons with flip="rtl"; their motion flips with them.
<html dir="rtl">
<button><tlp-icon name="arrow-right" flip="rtl"></tlp-icon> التالي</button>Custom choreography
Improve one icon with overrides/<name>.json, or add your own drawing to originals/. Tag moving pieces with p1…p4, the main body with d, then list the steps:
{
"name": "court-hearing",
"category": "legal",
"svg": "<path class=\"d p1\" d=\"...\"/><g class=\"p2\">...</g>",
"motion": [
{ "part": "p1", "anim": "fillIn", "dur": 900, "delay": 0 },
{ "part": "p2", "anim": "strike", "dur": 1200, "delay": 150, "origin": "5px 19px" }
]
}Build from source
git clone https://github.com/Tamer-Mansour/tlp-icons
cd tlp-icons
npm run build # offline: upstream/ + config/ + overrides/ + originals/ -> dist/
npm test # offline test suite
npm run sync # online: latest Lucide + Tabler, then rebuild
cd web && npm install && npm run dev # this website