@genarou/blazir-icons 1.2.20 → 1.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/LICENSE +121 -0
  2. package/README.md +1206 -0
  3. package/dist/CustomIcon.svelte +30 -0
  4. package/dist/CustomIcon.svelte.d.ts +14 -0
  5. package/dist/Icon.svelte +282 -102
  6. package/dist/Icon.svelte.d.ts +12 -5
  7. package/dist/IconBadge.svelte +75 -0
  8. package/dist/IconBadge.svelte.d.ts +16 -0
  9. package/dist/IconBase.svelte +89 -57
  10. package/dist/effects.js +1 -3
  11. package/dist/icons/Camera.svelte +19 -0
  12. package/dist/icons/Camera.svelte.d.ts +4 -0
  13. package/dist/icons/Cards.svelte +19 -0
  14. package/dist/icons/Cards.svelte.d.ts +4 -0
  15. package/dist/icons/CloudAlert.svelte +19 -0
  16. package/dist/icons/CloudAlert.svelte.d.ts +4 -0
  17. package/dist/icons/CloudCheck.svelte +19 -0
  18. package/dist/icons/CloudCheck.svelte.d.ts +4 -0
  19. package/dist/icons/CloudDownload.svelte +19 -0
  20. package/dist/icons/CloudDownload.svelte.d.ts +4 -0
  21. package/dist/icons/CreditCard.svelte +19 -0
  22. package/dist/icons/CreditCard.svelte.d.ts +4 -0
  23. package/dist/icons/Desktop.svelte +19 -0
  24. package/dist/icons/Desktop.svelte.d.ts +4 -0
  25. package/dist/icons/DoughnutChart.svelte +19 -0
  26. package/dist/icons/DoughnutChart.svelte.d.ts +4 -0
  27. package/dist/icons/Earth.svelte +19 -0
  28. package/dist/icons/Earth.svelte.d.ts +4 -0
  29. package/dist/icons/Globe.svelte +19 -0
  30. package/dist/icons/Globe.svelte.d.ts +4 -0
  31. package/dist/icons/LightHub.svelte +19 -0
  32. package/dist/icons/LightHub.svelte.d.ts +4 -0
  33. package/dist/icons/Link.svelte +19 -0
  34. package/dist/icons/Link.svelte.d.ts +4 -0
  35. package/dist/icons/Power.svelte +19 -0
  36. package/dist/icons/Power.svelte.d.ts +4 -0
  37. package/dist/icons/Receipt.svelte +19 -0
  38. package/dist/icons/Receipt.svelte.d.ts +4 -0
  39. package/dist/icons/Sync.svelte +19 -0
  40. package/dist/icons/Sync.svelte.d.ts +4 -0
  41. package/dist/icons/Upload.svelte +12 -57
  42. package/dist/icons/Wifi.svelte +19 -0
  43. package/dist/icons/Wifi.svelte.d.ts +4 -0
  44. package/dist/icons/lazy-registry.d.ts +21 -0
  45. package/dist/icons/lazy-registry.js +251 -0
  46. package/dist/icons/registry.d.ts +145 -134
  47. package/dist/icons/registry.js +177 -137
  48. package/dist/icons-api.d.ts +65 -267
  49. package/dist/icons-api.js +82 -465
  50. package/dist/index.d.ts +5 -5
  51. package/dist/index.js +14 -11
  52. package/dist/plugin/index.d.ts +46 -0
  53. package/dist/plugin/index.js +327 -0
  54. package/dist/smart-cache.d.ts +35 -0
  55. package/dist/smart-cache.js +192 -0
  56. package/dist/types.d.ts +19 -2
  57. package/dist/utils/sanitize.d.ts +25 -0
  58. package/dist/utils/sanitize.js +109 -0
  59. package/package.json +23 -13
  60. package/dist/icons/Aws.svelte +0 -19
  61. package/dist/icons/Aws.svelte.d.ts +0 -4
  62. package/dist/icons/Facebook.svelte +0 -18
  63. package/dist/icons/Facebook.svelte.d.ts +0 -4
  64. package/dist/icons/Golang.svelte +0 -17
  65. package/dist/icons/Golang.svelte.d.ts +0 -4
  66. package/dist/icons/Google.svelte +0 -18
  67. package/dist/icons/Google.svelte.d.ts +0 -4
  68. package/dist/icons/Paypal.svelte +0 -21
  69. package/dist/icons/Paypal.svelte.d.ts +0 -4
package/README.md ADDED
@@ -0,0 +1,1206 @@
1
+ # blazir-icons
2
+
3
+ A high-performance Svelte 5 icon library with smart caching, lazy loading, presets, variants, and a declarative effects system.
4
+
5
+ - **163 icons** — original SVG artwork
6
+ - **Svelte 5 runes** — `$props`, `$derived`, `$effect`
7
+ - **Smart LRU cache** — referential stability, zero re-renders on stable props
8
+ - **Lazy loading** — per-icon code splitting via `import()`
9
+ - **Vite plugin** — automatic tree shaking, only used icons in the bundle
10
+ - **Full TypeScript** — autocomplete on every prop, icon name, preset, and variant
11
+ - **Accessible** — `aria-label`, `title`, `decorative` support out of the box
12
+
13
+ ---
14
+
15
+ ## Table of Contents
16
+
17
+ - [Installation](#installation)
18
+ - [Quick Start](#quick-start)
19
+ - [How It Works](#how-it-works)
20
+ - [Icon Names](#icon-names)
21
+ - [Props Reference](#props-reference)
22
+ - [Presets](#presets)
23
+ - [Variants](#variants)
24
+ - [Lazy Loading](#lazy-loading)
25
+ - [Effects System](#effects-system)
26
+ - [Actions System](#actions-system)
27
+ - [IconBadge](#iconbadge)
28
+ - [CustomIcon](#customicon)
29
+ - [Vite Plugin (Tree Shaking)](#vite-plugin-tree-shaking)
30
+ - [Direct Icon Imports](#direct-icon-imports)
31
+ - [TypeScript](#typescript)
32
+ - [CSS Custom Properties](#css-custom-properties)
33
+ - [Accessibility Guide](#accessibility-guide)
34
+ - [Real-World Examples](#real-world-examples)
35
+
36
+ ---
37
+
38
+ ## Installation
39
+
40
+ ```bash
41
+ npm install @genarou/blazir-icons
42
+ # or
43
+ pnpm add @genarou/blazir-icons
44
+ ```
45
+
46
+ **Peer dependency:** Svelte 5
47
+
48
+ ---
49
+
50
+ ## Quick Start
51
+
52
+ ```svelte
53
+ <script>
54
+ import { Icon } from '@genarou/blazir-icons'
55
+ </script>
56
+
57
+ <Icon name="check" />
58
+ <Icon name="home" size={24} color="var(--primary)" />
59
+ <Icon name="settings" preset="muted" />
60
+ <Icon name="trash" variant="danger" />
61
+ ```
62
+
63
+ > The CSS animation stylesheet is injected automatically on import. No manual CSS setup required.
64
+
65
+ ---
66
+
67
+ ## How It Works
68
+
69
+ ### Architecture overview
70
+
71
+ ```
72
+ <Icon> ← Public wrapper
73
+
74
+ ├─ preset + variant → getPresetVariantMerge() (frozen, stable reference)
75
+ ├─ user props → arePropsStable() check
76
+ │ ├─ all primitives → LRU cache (128 entries) — same config = same reference
77
+ │ └─ objects/actions → direct merge (no cache, but baseProps stays stable)
78
+
79
+ ├─ lazy=false (default) → iconRegistry[name] (static import, zero latency)
80
+ └─ lazy=true → getLoadedIcon() || loadIcon() (dynamic import + skeleton)
81
+
82
+ <IconBase> ← SVG renderer
83
+ ├─ receives merged IconProps
84
+ ├─ applies mode (solid / outline / duotone)
85
+ ├─ computes hover color, transitions, animations
86
+ ├─ binds effects action to the <g> layer
87
+ └─ renders <svg> with full a11y attributes
88
+ ```
89
+
90
+ ### Smart Cache (LRU)
91
+
92
+ The cache operates in two layers:
93
+
94
+ 1. **Preset/Variant merge** — `getPresetVariantMerge(preset, variant)` returns the **exact same frozen object reference** for any given combination. Because `$derived` tracks object identity, this prevents unnecessary re-renders when only the reference changed but the content is identical.
95
+
96
+ 2. **Full props merge (128-entry LRU)** — when all user props are primitives (strings, numbers, booleans), a deterministic structural key is built and the result is stored in an LRU cache. The next render with the same config returns the cached reference — Svelte's scheduler sees no change and skips the component update entirely.
97
+
98
+ Props containing objects (`effects`, `actions`, `attrs`, `parentHoverContext`) bypass the global LRU and are merged directly. The preset/variant base remains stable regardless.
99
+
100
+ This design makes blazir-icons suitable for high-frequency render contexts like data grids, virtualized lists, and real-time dashboards.
101
+
102
+ ### Lazy Loading internals
103
+
104
+ When `lazy` is omitted (default), the icon component is resolved synchronously from the static registry — no async code, no skeleton, same behavior as a plain `import`.
105
+
106
+ When `lazy={true}`:
107
+ 1. Svelte checks `getLoadedIcon(name)` — a synchronous in-memory cache of already-loaded modules.
108
+ 2. If found, renders immediately (zero visible latency on subsequent navigations).
109
+ 3. If not found, sets `isLoading = true` (shows a skeleton placeholder), calls `loadIcon(name)` (dynamic `import()`), and updates the component when the module resolves.
110
+ 4. If the `name` prop changes before the load completes, the stale result is discarded — no race conditions.
111
+
112
+ ### Actions diff bridge
113
+
114
+ The `actions` prop accepts an array of `[actionFn, params]` tuples. On update, the bridge diffs the new array against the previous one:
115
+
116
+ - If all action **functions** are the same references → only `update(newParams)` is called. Internal state is preserved (e.g. tooltip position, IntersectionObserver).
117
+ - If any function reference changed → `destroy()` + `recreate()` for that action.
118
+
119
+ This is critical for stateful actions like tooltips or lazy-load observers that should not be torn down on every prop change.
120
+
121
+ ---
122
+
123
+ ## Icon Names
124
+
125
+ Every icon has a direct string name and a typed alias via `bzIcons`.
126
+
127
+ ```svelte
128
+ <script>
129
+ import { Icon, bzIcons } from '@genarou/blazir-icons'
130
+ </script>
131
+
132
+ <!-- Direct string (plugin can tree-shake this) -->
133
+ <Icon name="check" />
134
+
135
+ <!-- Typed alias — full autocomplete, same result -->
136
+ <Icon name={bzIcons.Check} />
137
+ ```
138
+
139
+ ### Available `bzIcons` aliases
140
+
141
+ All 163 icons organized by category. Use the alias for TypeScript autocomplete or the name as a plain string.
142
+
143
+ #### Navigation & Layout
144
+
145
+ | Alias | `name` |
146
+ |---|---|
147
+ | `bzIcons.Home` | `home` |
148
+ | `bzIcons.Dashboard` | `dashboard` |
149
+ | `bzIcons.DashboardOutline` | `dashboardOutline` |
150
+ | `bzIcons.Settings` | `settings` |
151
+ | `bzIcons.Search` | `search` |
152
+ | `bzIcons.Filter` | `filter` |
153
+ | `bzIcons.Menu` | `menu` |
154
+ | `bzIcons.Close` | `close` |
155
+ | `bzIcons.Plus` | `plus` |
156
+ | `bzIcons.Copy` | `copy` |
157
+ | `bzIcons.Rocket` | `rocket` |
158
+ | `bzIcons.Sidebar` | `sidebar` |
159
+ | `bzIcons.More` | `more` |
160
+
161
+ #### Users & Auth
162
+
163
+ | Alias | `name` |
164
+ |---|---|
165
+ | `bzIcons.User` | `user` |
166
+ | `bzIcons.UserTie` | `userTie` |
167
+ | `bzIcons.Team` | `team` |
168
+ | `bzIcons.Group` | `group` |
169
+ | `bzIcons.Supervisor` | `supervisor` |
170
+ | `bzIcons.Enterprise` | `enterprise` |
171
+ | `bzIcons.Lock` | `lock` |
172
+ | `bzIcons.LockOpen` | `lockOpen` |
173
+ | `bzIcons.Key` | `key` |
174
+ | `bzIcons.Logout` | `logout` |
175
+ | `bzIcons.Eye` | `eye` |
176
+ | `bzIcons.EyeOff` | `eyeOff` |
177
+ | `bzIcons.Shield` | `shield` |
178
+ | `bzIcons.Fingerprint` | `fingerprint` |
179
+ | `bzIcons.Contact` | `contact` |
180
+ | `bzIcons.Security` | `security` |
181
+
182
+ #### Communication
183
+
184
+ | Alias | `name` |
185
+ |---|---|
186
+ | `bzIcons.Email` | `email` |
187
+ | `bzIcons.EmailAnimated` | `emailAnimated` |
188
+ | `bzIcons.Phone` | `phone` |
189
+ | `bzIcons.Bell` | `bell` |
190
+ | `bzIcons.Send` | `send` |
191
+ | `bzIcons.Share` | `share` |
192
+ | `bzIcons.World` | `world` |
193
+ | `bzIcons.Chat` | `chat` |
194
+ | `bzIcons.Link` | `link` |
195
+
196
+ #### Business & Finance
197
+
198
+ | Alias | `name` |
199
+ |---|---|
200
+ | `bzIcons.Building` | `building` |
201
+ | `bzIcons.Handshake` | `handshake` |
202
+ | `bzIcons.Money` | `money` |
203
+ | `bzIcons.Wallet` | `wallet` |
204
+ | `bzIcons.Profit` | `profit` |
205
+ | `bzIcons.Safe` | `safe` |
206
+ | `bzIcons.Cost` | `cost` |
207
+ | `bzIcons.Exchange` | `exchange` |
208
+ | `bzIcons.Swap` | `swap` |
209
+ | `bzIcons.Bank` | `bank` |
210
+ | `bzIcons.Pay` | `pay` |
211
+ | `bzIcons.CreditCard` | `creditCard` |
212
+ | `bzIcons.Receipt` | `receipt` |
213
+ | `bzIcons.Buy` | `buy` |
214
+
215
+ #### Charts & Data
216
+
217
+ | Alias | `name` |
218
+ |---|---|
219
+ | `bzIcons.Chart` | `chart` |
220
+ | `bzIcons.ChartDocument` | `chartDocument` |
221
+ | `bzIcons.ChartSquare` | `chartSquare` |
222
+ | `bzIcons.ChartDoughnut` | `chartDoughnut` |
223
+ | `bzIcons.Table` | `table` |
224
+ | `bzIcons.List` | `list` |
225
+ | `bzIcons.ListDots` | `listDots` |
226
+ | `bzIcons.ListGroup` | `listGroup` |
227
+ | `bzIcons.TrendingDown` | `trendingDown` |
228
+ | `bzIcons.Database` | `database` |
229
+ | `bzIcons.Server` | `server` |
230
+
231
+ #### Files & Documents
232
+
233
+ | Alias | `name` |
234
+ |---|---|
235
+ | `bzIcons.File` | `file` |
236
+ | `bzIcons.Pdf` | `pdf` |
237
+ | `bzIcons.Excel` | `excel` |
238
+ | `bzIcons.ExcelAnimated` | `excelAnimated` |
239
+ | `bzIcons.Word` | `word` |
240
+ | `bzIcons.PowerPoint` | `powerPoint` |
241
+ | `bzIcons.Csv` | `csv` |
242
+ | `bzIcons.Xml` | `xml` |
243
+ | `bzIcons.Zip` | `zip` |
244
+ | `bzIcons.Png` | `png` |
245
+ | `bzIcons.Notes` | `notes` |
246
+ | `bzIcons.Book` | `book` |
247
+ | `bzIcons.Image` | `image` |
248
+ | `bzIcons.ImageAnimated` | `imageAnimated` |
249
+ | `bzIcons.Folder` | `folder` |
250
+ | `bzIcons.FolderShared` | `folderShared` |
251
+ | `bzIcons.Attachment` | `attachment` |
252
+ | `bzIcons.Save` | `save` |
253
+ | `bzIcons.Form` | `form` |
254
+ | `bzIcons.Print` | `print` |
255
+
256
+ #### Calendar & Time
257
+
258
+ | Alias | `name` |
259
+ |---|---|
260
+ | `bzIcons.Calendar` | `calendar` |
261
+ | `bzIcons.CalendarPlus` | `calendarPlus` |
262
+ | `bzIcons.CalendarEdit` | `calendarEdit` |
263
+ | `bzIcons.Timer` | `timer` |
264
+
265
+ #### Arrows & Navigation
266
+
267
+ | Alias | `name` |
268
+ |---|---|
269
+ | `bzIcons.ArrowRight` | `arrowRight` |
270
+ | `bzIcons.ArrowUp` | `arrowUp` |
271
+ | `bzIcons.ArrowUpDown` | `arrowUpDown` |
272
+ | `bzIcons.ArrowLeftAnimated` | `arrowLeftAnimated` |
273
+ | `bzIcons.ChevronDown` | `chevronDown` |
274
+ | `bzIcons.ChevronUpDown` | `chevronUpDown` |
275
+
276
+ #### File Actions
277
+
278
+ | Alias | `name` |
279
+ |---|---|
280
+ | `bzIcons.Download` | `download` |
281
+ | `bzIcons.DownloadAnimated` | `downloadAnimated` |
282
+ | `bzIcons.Upload` | `upload` |
283
+ | `bzIcons.UploadAnimated` | `uploadAnimated` |
284
+ | `bzIcons.UploadProgress` | `uploadProgress` |
285
+ | `bzIcons.FileUploadAnimated` | `fileUploadAnimated` |
286
+ | `bzIcons.Refresh` | `refresh` |
287
+
288
+ #### States & Feedback
289
+
290
+ | Alias | `name` |
291
+ |---|---|
292
+ | `bzIcons.Check` | `check` |
293
+ | `bzIcons.CheckOutline` | `checkOutline` |
294
+ | `bzIcons.CheckList` | `checkList` |
295
+ | `bzIcons.CircleCheck` | `circleCheck` |
296
+ | `bzIcons.Error` | `error` |
297
+ | `bzIcons.ErrorOutline` | `errorOutline` |
298
+ | `bzIcons.CircleExclamation` | `circleExclamation` |
299
+ | `bzIcons.CircleExclamationOutline` | `circleExclamationOutline` |
300
+ | `bzIcons.Warning` | `warning` |
301
+ | `bzIcons.CircleInfo` | `circleInfo` |
302
+ | `bzIcons.CircleInfoOutline` | `circleInfoOutline` |
303
+ | `bzIcons.CircleQuestion` | `circleQuestion` |
304
+ | `bzIcons.CircleQuestionOutline` | `circleQuestionOutline` |
305
+ | `bzIcons.Star` | `star` |
306
+ | `bzIcons.Favorites` | `favorites` |
307
+ | `bzIcons.Heart` | `heart` |
308
+ | `bzIcons.Reset` | `reset` |
309
+ | `bzIcons.Height` | `height` |
310
+ | `bzIcons.Alternate` | `alternate` |
311
+ | `bzIcons.Moon` | `moon` |
312
+ | `bzIcons.Sun` | `sun` |
313
+ | `bzIcons.Colors` | `colors` |
314
+
315
+ #### Loading & Progress
316
+
317
+ | Alias | `name` |
318
+ |---|---|
319
+ | `bzIcons.SpinnerDots` | `spinnerDots` |
320
+ | `bzIcons.Spinner` | `spinner` |
321
+
322
+ #### Commerce & Sales
323
+
324
+ | Alias | `name` |
325
+ |---|---|
326
+ | `bzIcons.Product` | `product` |
327
+ | `bzIcons.Bag` | `bag` |
328
+ | `bzIcons.POS` | `pos` |
329
+ | `bzIcons.Category` | `category` |
330
+ | `bzIcons.CategoryAdd` | `categoryAdd` |
331
+ | `bzIcons.CategorySearch` | `categorySearch` |
332
+ | `bzIcons.BoxAdd` | `boxAdd` |
333
+ | `bzIcons.Measure` | `measure` |
334
+ | `bzIcons.Box` | `box` |
335
+ | `bzIcons.Scan` | `scan` |
336
+ | `bzIcons.Cart` | `cart` |
337
+ | `bzIcons.Cards` | `cards` |
338
+
339
+ #### Logistics & Transport
340
+
341
+ | Alias | `name` |
342
+ |---|---|
343
+ | `bzIcons.Truck` | `truck` |
344
+ | `bzIcons.TruckReturn` | `truckReturn` |
345
+ | `bzIcons.Warehouse` | `warehouse` |
346
+ | `bzIcons.Location` | `location` |
347
+ | `bzIcons.LocationAnimated` | `locationAnimated` |
348
+
349
+ #### Tools & Utilities
350
+
351
+ | Alias | `name` |
352
+ |---|---|
353
+ | `bzIcons.AI` | `ai` |
354
+ | `bzIcons.Edit` | `edit` |
355
+ | `bzIcons.EditOutline` | `editOutline` |
356
+ | `bzIcons.Trash` | `trash` |
357
+ | `bzIcons.TrashOutline` | `trashOutline` |
358
+ | `bzIcons.Tags` | `tags` |
359
+ | `bzIcons.Tools` | `tools` |
360
+ | `bzIcons.ObjectGroup` | `objectGroup` |
361
+ | `bzIcons.Project` | `project` |
362
+
363
+ #### Media & Devices
364
+
365
+ | Alias | `name` |
366
+ |---|---|
367
+ | `bzIcons.Camera` | `camera` |
368
+ | `bzIcons.Desktop` | `desktop` |
369
+ | `bzIcons.Wifi` | `wifi` |
370
+ | `bzIcons.Power` | `power` |
371
+
372
+ #### Cloud
373
+
374
+ | Alias | `name` |
375
+ |---|---|
376
+ | `bzIcons.CloudAlert` | `cloudAlert` |
377
+ | `bzIcons.CloudCheck` | `cloudCheck` |
378
+ | `bzIcons.CloudDownload` | `cloudDownload` |
379
+
380
+ #### Other
381
+
382
+ | Alias | `name` |
383
+ |---|---|
384
+ | `bzIcons.Blaze` | `blaze` |
385
+ | `bzIcons.Earth` | `earth` |
386
+ | `bzIcons.Globe` | `globe` |
387
+ | `bzIcons.LightHub` | `lightHub` |
388
+ | `bzIcons.Sync` | `sync` |
389
+
390
+ ---
391
+
392
+ ## Props Reference
393
+
394
+ All props are optional. Every prop can be passed to `<Icon>` or `<IconBase>`.
395
+
396
+ ### Visual
397
+
398
+ | Prop | Type | Default | Description |
399
+ |---|---|---|---|
400
+ | `name` | `IconName` | — | Icon identifier |
401
+ | `size` | `number \| string` | `24` | Width and height. Accepts `px`, `rem`, `em`, token (`sm`, `md`, `lg`, etc.) or a plain number |
402
+ | `color` | `string` | `currentColor` | Fill or stroke color |
403
+ | `stroke` | `string` | — | Explicit stroke color (overrides `color` for stroke) |
404
+ | `strokeWidth` | `number \| string` | — | SVG stroke width |
405
+ | `strokeLinecap` | `"round" \| "butt" \| "square"` | — | SVG stroke-linecap attribute |
406
+ | `strokeLinejoin` | `"round" \| "miter" \| "bevel"` | — | SVG stroke-linejoin attribute |
407
+ | `fill` | `string` | — | Explicit fill color |
408
+ | `mode` | `"solid" \| "outline" \| "duotone"` | `"solid"` | Rendering mode |
409
+ | `viewBox` | `string` | `"0 0 24 24"` | SVG viewBox attribute |
410
+ | `preserveAspectRatio` | `string` | — | SVG preserveAspectRatio attribute |
411
+ | `class` | `string` | — | CSS class on the wrapper element |
412
+ | `className` | `string` | — | Alias for `class` |
413
+ | `style` | `string` | — | Inline style on the wrapper element |
414
+ | `attrs` | `Record<string, any>` | — | Arbitrary extra attributes forwarded to the `<svg>` element (excluding `width`/`height`) |
415
+
416
+ ### Size Tokens
417
+
418
+ | Token | Value |
419
+ |---|---|
420
+ | `xs` | 12px |
421
+ | `sm` | 16px |
422
+ | `md` | 20px |
423
+ | `lg` | 24px |
424
+ | `xl` | 32px |
425
+ | `2xl` | 40px |
426
+ | `3xl` | 48px |
427
+
428
+ ```svelte
429
+ <Icon name="check" size="sm" />
430
+ <Icon name="check" size={32} />
431
+ <Icon name="check" size="1.5rem" />
432
+ ```
433
+
434
+ ### Transform
435
+
436
+ | Prop | Type | Default | Description |
437
+ |---|---|---|---|
438
+ | `rotate` | `number \| string` | — | Rotation in degrees or CSS string |
439
+ | `flipH` | `boolean` | `false` | Flip horizontally |
440
+ | `flipV` | `boolean` | `false` | Flip vertically |
441
+ | `nonScalingStroke` | `boolean` | `false` | Keep stroke width fixed regardless of transform (`vector-effect="non-scaling-stroke"`) |
442
+
443
+ ```svelte
444
+ <Icon name="arrow" rotate={45} />
445
+ <Icon name="arrow" flipH />
446
+ <Icon name="arrow" rotate={90} flipV />
447
+ ```
448
+
449
+ ### Animations
450
+
451
+ | Prop | Type | Default | Description |
452
+ |---|---|---|---|
453
+ | `spin` | `boolean \| number \| string` | `false` | Continuous rotation. `true` = 1s linear, number = duration in ms |
454
+ | `pulse` | `boolean` | `false` | Opacity + scale pulse |
455
+ | `bounce` | `boolean` | `false` | Vertical bounce |
456
+ | `wiggle` | `boolean` | `false` | Left-right rotation wiggle |
457
+ | `heartbeat` | `boolean` | `false` | Double-pulse heartbeat effect |
458
+ | `elastic` | `boolean` | `false` | Elastic scale pop |
459
+ | `animationDuration` | `number \| string` | — | Override animation duration (`400`, `"400ms"`, `"0.4s"`) |
460
+ | `animationDelay` | `number \| string` | — | Animation start delay |
461
+ | `animationEasing` | `string` | — | CSS easing function |
462
+
463
+ ```svelte
464
+ <Icon name="loadingDots" spin />
465
+ <Icon name="loadingDots" spin={800} />
466
+ <Icon name="bell" bounce animationDuration={600} />
467
+ <Icon name="heart" heartbeat animationDelay="200ms" />
468
+ <Icon name="star" pulse animationEasing="ease-in-out" />
469
+
470
+ <!-- Combine multiple animations -->
471
+ <Icon name="loadingRegular" spin pulse />
472
+ ```
473
+
474
+ ### Entry Transitions
475
+
476
+ | Prop | Type | Description |
477
+ |---|---|---|
478
+ | `scaleIn` | `boolean` | Animate in with a scale effect |
479
+ | `fadeIn` | `boolean` | Animate in with a fade effect |
480
+ | `slideIn` | `boolean` | Animate in with a slide effect |
481
+ | `slideDirection` | `"up" \| "down" \| "left" \| "right"` | Direction for `slideIn` |
482
+ | `morphTo` | `"play" \| "pause" \| "menu" \| "close" \| "arrow" \| "check"` | Morph the icon shape to a target state |
483
+ | `chevronState` | `"open" \| "closed" \| "up" \| "down" \| "left" \| "right"` | Rotate a chevron icon to indicate state |
484
+ | `chevronOpen` | `boolean` | Shorthand: `true` = `chevronState="open"` |
485
+
486
+ ```svelte
487
+ <!-- Animated menu toggle -->
488
+ <Icon name="hamburguer" morphTo={isOpen ? 'close' : 'menu'} />
489
+
490
+ <!-- Animated accordion chevron -->
491
+ <Icon name="chevronDown" chevronOpen={isExpanded} />
492
+
493
+ <!-- Entry animations -->
494
+ <Icon name="check" scaleIn />
495
+ <Icon name="warning" fadeIn />
496
+ <Icon name="bell" slideIn slideDirection="down" />
497
+ ```
498
+
499
+ ### Interaction
500
+
501
+ | Prop | Type | Default | Description |
502
+ |---|---|---|---|
503
+ | `hoverColor` | `string` | — | Color applied on hover |
504
+ | `activeColor` | `string` | — | Color applied on press/active. Priority: `activeColor` > `hoverColor` > `color` |
505
+ | `disabled` | `boolean` | `false` | Reduces opacity to 0.4 and disables all pointer events and hover effects |
506
+ | `loading` | `boolean` | `false` | Replaces the icon with a spinning circle until `false`. Reuses internal spin keyframe — zero extra weight |
507
+ | `tooltip` | `string` | — | Native browser tooltip. Shorthand for `effects={{ tooltip: "..." }}` |
508
+ | `hoverSpeed` | `"instant" \| "fast" \| "normal" \| "slow" \| "sluggish"` | `"normal"` | Semantic preset for hover transition speed |
509
+ | `transitionMs` | `number \| string` | `250` | Exact hover transition duration. Overrides `hoverSpeed` |
510
+ | `transitionEasing` | `string` | `"ease"` | CSS easing for hover transition |
511
+ | `parentHoverContext` | `{ hovered: boolean }` | — | Trigger hover state from a parent element |
512
+
513
+ #### `hoverSpeed` presets
514
+
515
+ | Value | Duration |
516
+ |---|---|
517
+ | `"instant"` | 0ms |
518
+ | `"fast"` | 120ms |
519
+ | `"normal"` | 250ms ← default |
520
+ | `"slow"` | 400ms |
521
+ | `"sluggish"` | 600ms |
522
+
523
+ ```svelte
524
+ <!-- Hover color with speed preset -->
525
+ <Icon name="edit" hoverColor="var(--primary)" hoverSpeed="fast" />
526
+
527
+ <!-- Active/press color -->
528
+ <Icon name="heart" hoverColor="#f97316" activeColor="#dc2626" />
529
+
530
+ <!-- Disabled state -->
531
+ <Icon name="send" disabled />
532
+ <Icon name="save" disabled={!isFormValid} />
533
+
534
+ <!-- Loading state — auto spinner, no wrapper needed -->
535
+ <Icon name="save" loading={isSaving} />
536
+
537
+ <!-- Tooltip -->
538
+ <Icon name="info" tooltip="More information" />
539
+
540
+ <!-- Custom easing -->
541
+ <Icon name="bell" hoverColor="var(--primary)" transitionMs={350} transitionEasing="ease-in-out" />
542
+
543
+ <!-- Trigger icon hover when parent row is hovered -->
544
+ <script>
545
+ let hovered = $state(false)
546
+ const ctx = $derived({ hovered })
547
+ </script>
548
+ <tr onmouseenter={() => hovered = true} onmouseleave={() => hovered = false}>
549
+ <td><Icon name="edit" parentHoverContext={ctx} hoverColor="var(--primary)" /></td>
550
+ </tr>
551
+ ```
552
+
553
+ ### Compact Mode
554
+
555
+ Reduces visual weight of solid icons at small sizes by adjusting opacity.
556
+
557
+ | Prop | Type | Default | Description |
558
+ |---|---|---|---|
559
+ | `compact` | `"off" \| "auto" \| "outline" \| "duotone"` | `"off"` | Compact rendering mode |
560
+ | `compactSizeThreshold` | `number` | `20` | Size in px below which compact activates (when `"auto"`) |
561
+ | `compactFillOpacity` | `number` | `0.15` | Fill opacity in compact mode |
562
+ | `compactStrokeWidth` | `number \| string` | — | Stroke width in compact mode |
563
+
564
+ ```svelte
565
+ <!-- Auto: switches to outline style below 20px -->
566
+ <Icon name="user" compact="auto" size={16} />
567
+
568
+ <!-- Always compact duotone -->
569
+ <Icon name="folder" compact="duotone" compactFillOpacity={0.2} />
570
+ ```
571
+
572
+ ### Accessibility
573
+
574
+ | Prop | Type | Default | Description |
575
+ |---|---|---|---|
576
+ | `ariaLabel` | `string` | Icon name | `aria-label` on the SVG |
577
+ | `title` | `string` | — | SVG `<title>` element (tooltip on hover in browsers) |
578
+ | `titleId` | `string` | — | `id` for the `<title>` element (for `aria-labelledby`) |
579
+ | `decorative` | `boolean` | `false` | Sets `aria-hidden="true"` — use when icon is purely decorative |
580
+ | `testId` | `string` | — | `data-testid` attribute |
581
+
582
+ ```svelte
583
+ <!-- Decorative icon next to a labeled button -->
584
+ <button>
585
+ <Icon name="download" decorative />
586
+ Download
587
+ </button>
588
+
589
+ <!-- Standalone meaningful icon -->
590
+ <Icon name="check" ariaLabel="Task completed" />
591
+
592
+ <!-- With visible tooltip -->
593
+ <Icon name="info" title="More information" ariaLabel="More information" />
594
+ ```
595
+
596
+ ### Lazy Loading Prop
597
+
598
+ | Prop | Type | Default | Description |
599
+ |---|---|---|---|
600
+ | `lazy` | `boolean` | `false` | Load the icon as a separate dynamic import, enabling per-icon code splitting. Shows a skeleton placeholder while loading. |
601
+
602
+ ```svelte
603
+ <!-- Eager (default) — bundled with registry -->
604
+ <Icon name="check" />
605
+
606
+ <!-- Lazy — separate chunk, skeleton while loading -->
607
+ <Icon name="check" lazy />
608
+ ```
609
+
610
+ ---
611
+
612
+ ## Presets
613
+
614
+ Presets apply a predefined set of props in one shot. Define colors, sizes, and behaviors using your CSS design tokens.
615
+
616
+ ```svelte
617
+ <Icon name="check" preset="success" />
618
+ <Icon name="home" preset="nav" />
619
+ <Icon name="plus" preset="button" />
620
+ ```
621
+
622
+ | Preset | Properties |
623
+ |---|---|
624
+ | `primary` | `color: var(--primary)`, `hoverColor: var(--primary-hover)` |
625
+ | `secondary` | `color: var(--secondary)`, `hoverColor: var(--secondary-foreground)` |
626
+ | `accent` | `color: var(--accent-color)`, `hoverColor: var(--accent-hover)` |
627
+ | `success` | `color: var(--success-color)`, `hoverColor: var(--success-hover)` |
628
+ | `warning` | `color: var(--warning-color)`, `hoverColor: var(--warning-hover)` |
629
+ | `danger` | `color: var(--danger-color)`, `hoverColor: var(--danger-hover)` |
630
+ | `muted` | `color: var(--icon-fg, var(--ui-muted-fg))`, hover → foreground |
631
+ | `nav` | `size: 24`, nav colors, subtle scale on hover |
632
+ | `sidebar` | `size: 18`, sidebar colors, scale on hover |
633
+ | `button` | `size: 20`, `currentColor`, cursor pointer, scale on hover |
634
+ | `badge` | `size: 16`, compact auto |
635
+ | `hero` | `size: 48`, primary color, bounce on hover |
636
+ | `loading` | `spin: true`, `size: 24`, primary color |
637
+
638
+ Props passed directly always override preset values.
639
+
640
+ ```svelte
641
+ <!-- preset sets size=24, but size={32} wins -->
642
+ <Icon name="check" preset="nav" size={32} />
643
+
644
+ <!-- preset + variant combination -->
645
+ <Icon name="trash" preset="button" variant="danger" />
646
+ ```
647
+
648
+ ---
649
+
650
+ ## Variants
651
+
652
+ Variants apply a color scheme without touching size or behavior. Combine with presets for full control.
653
+
654
+ ```svelte
655
+ <Icon name="check" preset="button" variant="success" />
656
+ ```
657
+
658
+ | Variant | Color token |
659
+ |---|---|
660
+ | `primary` | `var(--primary)` |
661
+ | `secondary` | `var(--secondary)` |
662
+ | `accent` | `var(--accent-color)` |
663
+ | `success` | `var(--success-color)` |
664
+ | `warning` | `var(--warning-color)` |
665
+ | `danger` | `var(--danger-color)` |
666
+ | `muted` | `var(--icon-fg, var(--ui-muted-fg))` |
667
+ | `ghost` | `var(--ui-muted-fg)` |
668
+
669
+ ---
670
+
671
+ ## Lazy Loading
672
+
673
+ By default icons load eagerly (zero latency, no skeletons). Add `lazy` to defer loading and enable per-icon code splitting.
674
+
675
+ ```svelte
676
+ <!-- Eager (default) — same bundle chunk as always -->
677
+ <Icon name="check" />
678
+
679
+ <!-- Lazy — loaded on first render, skeleton shown while loading -->
680
+ <Icon name="check" lazy />
681
+ ```
682
+
683
+ ### Manual preload
684
+
685
+ Preload a set of icons before they are needed (e.g. on route anticipation):
686
+
687
+ ```ts
688
+ import { preloadIcons } from '@genarou/blazir-icons'
689
+
690
+ // Runs in background via requestIdleCallback
691
+ preloadIcons(['check', 'home', 'settings', 'user'])
692
+ ```
693
+
694
+ ### Programmatic load
695
+
696
+ ```ts
697
+ import { loadIcon, getLoadedIcon } from '@genarou/blazir-icons'
698
+
699
+ // Async load + cache
700
+ const Check = await loadIcon('check')
701
+
702
+ // Synchronous lookup (returns null if not yet loaded)
703
+ const Cached = getLoadedIcon('check')
704
+ ```
705
+
706
+ ---
707
+
708
+ ## Effects System
709
+
710
+ Declarative interactive effects applied via the `effects` prop. All effects respect `prefers-reduced-motion`.
711
+
712
+ ```svelte
713
+ <Icon
714
+ name="settings"
715
+ effects={{ spinOnHover: true, cursor: 'pointer' }}
716
+ />
717
+
718
+ <Icon
719
+ name="trash"
720
+ effects={{ wiggleOnHover: true, elasticOnClick: true }}
721
+ />
722
+
723
+ <Icon
724
+ name="heart"
725
+ effects={{ heartbeatOnHover: true, hoverScale: 1.15 }}
726
+ />
727
+ ```
728
+
729
+ ### `IconEffectOptions`
730
+
731
+ | Option | Type | Description |
732
+ |---|---|---|
733
+ | `spinOnHover` | `boolean` | Spin on mouse enter |
734
+ | `bounceOnHover` | `boolean` | Bounce on mouse enter |
735
+ | `wiggleOnHover` | `boolean` | Wiggle on mouse enter |
736
+ | `slideOnHover` | `"up" \| "down" \| "left" \| "right"` | Slide translate on hover |
737
+ | `morphOnHover` | `"play" \| "pause" \| "menu" \| "close" \| "arrow" \| "check"` | Morph shape on hover |
738
+ | `elasticOnClick` | `boolean` | Elastic scale pop on click |
739
+ | `heartbeatOnActive` | `boolean` | Heartbeat on mousedown |
740
+ | `heartbeatOnHover` | `boolean` | Heartbeat on hover |
741
+ | `pulse` | `boolean` | Continuous pulse |
742
+ | `hoverScale` | `number` | Scale factor on hover (`1.1` = 110%) |
743
+ | `pressScale` | `number` | Scale factor on press |
744
+ | `rotateOnHover` | `number` | Degrees to rotate on hover |
745
+ | `transitionMs` | `number` | Transition duration in ms |
746
+ | `easing` | `string` | CSS easing function |
747
+ | `cursor` | `string` | CSS cursor on hover |
748
+ | `tooltip` | `string` | Native title tooltip |
749
+ | `focusRing` | `boolean` | Show visible focus ring |
750
+ | `kbFocusAttr` | `string` | Data attribute set on keyboard focus |
751
+
752
+ ### Using `iconEffects` as a Svelte action
753
+
754
+ For advanced cases where you need to apply effects to a custom element:
755
+
756
+ ```svelte
757
+ <script>
758
+ import { iconEffects } from '@genarou/blazir-icons'
759
+ </script>
760
+
761
+ <button use:iconEffects={{ spinOnHover: true, hoverScale: 1.1 }}>
762
+ Click me
763
+ </button>
764
+ ```
765
+
766
+ ---
767
+
768
+ ## Actions System
769
+
770
+ Pass Svelte actions to the icon wrapper via the `actions` prop. Actions are diffed on update — state is preserved when params change.
771
+
772
+ ```svelte
773
+ <script>
774
+ import { Icon } from '@genarou/blazir-icons'
775
+ import { tooltip } from '$lib/actions/tooltip'
776
+ import { lazyLoad } from '$lib/actions/lazy'
777
+
778
+ let opts = $state({ text: 'Save file' })
779
+ </script>
780
+
781
+ <Icon
782
+ name="save"
783
+ actions={[[tooltip, opts], [lazyLoad]]}
784
+ />
785
+ ```
786
+
787
+ Each entry in `actions` is a tuple `[actionFn, params?]`. When `params` change, `update()` is called. When the action function itself changes, the action is destroyed and recreated.
788
+
789
+ ---
790
+
791
+ ## IconBadge
792
+
793
+ `IconBadge` wraps any icon with an absolute-positioned notification badge. Zero weight unless imported.
794
+
795
+ ```svelte
796
+ <script>
797
+ import { IconBadge } from '@genarou/blazir-icons'
798
+ </script>
799
+
800
+ <!-- Dot (boolean) -->
801
+ <IconBadge name="bell" badge />
802
+
803
+ <!-- Count -->
804
+ <IconBadge name="bell" badge={5} />
805
+
806
+ <!-- Text -->
807
+ <IconBadge name="chat" badge="!" />
808
+
809
+ <!-- Custom color and size -->
810
+ <IconBadge name="bell" badge={12} badgeColor="#f59e0b" badgeSize={18} />
811
+
812
+ <!-- Position (default: top-right) -->
813
+ <IconBadge name="bell" badge={3} badgePosition="top-left" />
814
+ ```
815
+
816
+ ### `IconBadge` props
817
+
818
+ Accepts all `Icon` props plus:
819
+
820
+ | Prop | Type | Default | Description |
821
+ |---|---|---|---|
822
+ | `badge` | `boolean \| number \| string` | — | `true` = dot · number/string = label |
823
+ | `badgeColor` | `string` | `#ef4444` | Badge background color |
824
+ | `badgeSize` | `number` | `8` (dot) / `16` (label) | Badge diameter in px |
825
+ | `badgePosition` | `"top-right" \| "top-left" \| "bottom-right" \| "bottom-left"` | `"top-right"` | Corner placement |
826
+
827
+ > `badge={false}` or omitting `badge` renders the icon directly without any wrapper element — no DOM overhead.
828
+
829
+ ---
830
+
831
+ ## CustomIcon
832
+
833
+ Render any custom SVG string using the same `IconBase` engine — all effects, transitions, hover colors, animations and a11y props work out of the box.
834
+
835
+ ```svelte
836
+ <script>
837
+ import { CustomIcon } from '@genarou/blazir-icons'
838
+
839
+ const logoSvg = `<path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/>`
840
+ </script>
841
+
842
+ <!-- Same API as Icon — all props supported -->
843
+ <CustomIcon svgContent={logoSvg} size={32} color="var(--primary)" />
844
+
845
+ <!-- With hover and effects -->
846
+ <CustomIcon
847
+ svgContent={logoSvg}
848
+ hoverColor="#6366f1"
849
+ hoverSpeed="fast"
850
+ effects={{ hoverScale: 1.1 }}
851
+ />
852
+
853
+ <!-- With animations -->
854
+ <CustomIcon svgContent={logoSvg} spin pulse />
855
+
856
+ <!-- Custom viewBox for non-24x24 SVGs -->
857
+ <CustomIcon svgContent={logoSvg} viewBox="0 0 48 48" size={48} />
858
+ ```
859
+
860
+ ### `CustomIcon` props
861
+
862
+ | Prop | Type | Default | Description |
863
+ |---|---|---|---|
864
+ | `svgContent` | `string` | **required** | Raw SVG inner markup (paths, circles, etc. — not the `<svg>` tag itself) |
865
+ | `viewBox` | `string` | `"0 0 24 24"` | ViewBox for the SVG container |
866
+ | + all `IconProps` | | | Every prop from the standard `Icon` is supported |
867
+
868
+ > The content is rendered via `{@html}`. For static `svgContent` (most common case) the cost is a single `innerHTML` assignment at mount — identical to any other icon. All `IconBase` effects run on the containing `<g>` layer.
869
+
870
+ ---
871
+
872
+ ## Vite Plugin (Tree Shaking)
873
+
874
+ Without the plugin, all 152 icons are included in your bundle. With the plugin, only the icons you statically reference are bundled.
875
+
876
+ ```ts
877
+ // vite.config.ts
878
+ import { blazirIconsPlugin } from '@genarou/blazir-icons/plugin'
879
+ import { svelte } from '@sveltejs/vite-plugin-svelte'
880
+
881
+ export default {
882
+ plugins: [
883
+ blazirIconsPlugin(),
884
+ svelte(),
885
+ ]
886
+ }
887
+ ```
888
+
889
+ The plugin scans your source files at build time, detects static `name="..."` values, and generates a minimal registry.
890
+
891
+ ```svelte
892
+ <!-- Detected statically → only Check.svelte in bundle -->
893
+ <Icon name="check" />
894
+
895
+ <!-- Also detected statically -->
896
+ <Icon name={"home"} />
897
+
898
+ <!-- Dynamic → cannot detect → falls back to full registry (safe) -->
899
+ <Icon name={bzIcons.User} />
900
+ <Icon name={someVariable} />
901
+ ```
902
+
903
+ ### Plugin Options
904
+
905
+ ```ts
906
+ blazirIconsPlugin({
907
+ // Root directory to scan. Default: Vite's config.root
908
+ root: './src',
909
+
910
+ // File extensions to include in scan
911
+ extensions: ['.svelte', '.ts', '.tsx', '.js', '.jsx'],
912
+ })
913
+ ```
914
+
915
+ ### Bundle size comparison
916
+
917
+ | Scenario | Bundle (gzip) |
918
+ |---|---|
919
+ | No plugin — all 163 icons | ~11 KB |
920
+ | With plugin — 10 static icons | ~1–2 KB |
921
+ | With plugin — dynamic names detected | ~11 KB (safe fallback) |
922
+
923
+ ---
924
+
925
+ ## Direct Icon Imports
926
+
927
+ Import a single icon component directly for maximum tree shaking without the plugin:
928
+
929
+ ```ts
930
+ import Check from '@genarou/blazir-icons/icons/Check'
931
+ import Home from '@genarou/blazir-icons/icons/Home'
932
+ import Settings from '@genarou/blazir-icons/icons/Settings'
933
+ ```
934
+
935
+ Use them as regular Svelte components. All `IconProps` are supported, but presets and variants are not applied automatically.
936
+
937
+ ```svelte
938
+ <Check size={20} color="var(--success-color)" />
939
+ <Home size={24} hoverColor="var(--primary)" />
940
+ ```
941
+
942
+ ---
943
+
944
+ ## TypeScript
945
+
946
+ Full type coverage on all props and icon names.
947
+
948
+ ```ts
949
+ import type { IconProps, IconName, IconMode } from '@genarou/blazir-icons'
950
+ import type { IconEffectOptions } from '@genarou/blazir-icons'
951
+ import type { BzIconKey } from '@genarou/blazir-icons'
952
+
953
+ // Typed icon name
954
+ const icon: IconName = 'check'
955
+
956
+ // Typed effect options
957
+ const effects: IconEffectOptions = {
958
+ spinOnHover: true,
959
+ hoverScale: 1.1,
960
+ }
961
+
962
+ // Typed bzIcons alias
963
+ const key: BzIconKey = 'Check'
964
+ ```
965
+
966
+ ### Extending the registry
967
+
968
+ ```ts
969
+ import { iconRegistry } from '@genarou/blazir-icons'
970
+
971
+ // Autocomplete on all registry keys
972
+ type MyIconName = keyof typeof iconRegistry
973
+ ```
974
+
975
+ ---
976
+
977
+ ## CSS Custom Properties
978
+
979
+ Customize animations globally via CSS variables.
980
+
981
+ | Variable | Default | Description |
982
+ |---|---|---|
983
+ | `--bz-icon-bounce-amt` | `18%` | Bounce height |
984
+ | `--bz-icon-pulse-scale` | `1.04` | Pulse scale factor |
985
+ | `--bz-icon-ring` | primary | Focus ring color |
986
+ | `--bz-icon-wiggle-angle` | `3deg` | Wiggle rotation amount |
987
+ | `--bz-icon-slide-distance` | `8px` | Slide effect distance |
988
+ | `--bz-icon-size` | set by component | Icon size (set automatically) |
989
+
990
+ ```css
991
+ /* globals.css */
992
+ :root {
993
+ --bz-icon-bounce-amt: 24%;
994
+ --bz-icon-wiggle-angle: 5deg;
995
+ }
996
+ ```
997
+
998
+ ---
999
+
1000
+ ## Accessibility Guide
1001
+
1002
+ ```svelte
1003
+ <!-- Decorative icon — hidden from screen readers -->
1004
+ <button>
1005
+ <Icon name="trash" decorative />
1006
+ Delete item
1007
+ </button>
1008
+
1009
+ <!-- Meaningful standalone icon -->
1010
+ <Icon name="warning" ariaLabel="Warning: unsaved changes" />
1011
+
1012
+ <!-- With visible tooltip -->
1013
+ <Icon name="info" title="More information" ariaLabel="More information" />
1014
+
1015
+ <!-- In a labeled group -->
1016
+ <div aria-label="Status">
1017
+ <Icon name="circleCheck" decorative />
1018
+ <span>All systems operational</span>
1019
+ </div>
1020
+ ```
1021
+
1022
+ ---
1023
+
1024
+ ## Real-World Examples
1025
+
1026
+ ### Sidebar navigation item
1027
+
1028
+ ```svelte
1029
+ <script>
1030
+ import { Icon } from '@genarou/blazir-icons'
1031
+ export let active = false
1032
+ export let label = ''
1033
+ export let icon = 'home'
1034
+ </script>
1035
+
1036
+ <a class="nav-item" class:active>
1037
+ <Icon
1038
+ name={icon}
1039
+ preset="sidebar"
1040
+ color={active ? 'var(--primary)' : undefined}
1041
+ effects={{ hoverScale: 1.08 }}
1042
+ decorative
1043
+ />
1044
+ <span>{label}</span>
1045
+ </a>
1046
+ ```
1047
+
1048
+ ### Loading button
1049
+
1050
+ ```svelte
1051
+ <script>
1052
+ import { Icon } from '@genarou/blazir-icons'
1053
+ let saving = $state(false)
1054
+
1055
+ async function submit() {
1056
+ saving = true
1057
+ await doWork()
1058
+ saving = false
1059
+ }
1060
+ </script>
1061
+
1062
+ <!-- The icon swaps to a spinner automatically while saving -->
1063
+ <button onclick={submit} disabled={saving}>
1064
+ <Icon name="save" loading={saving} decorative />
1065
+ {saving ? 'Saving...' : 'Save'}
1066
+ </button>
1067
+ ```
1068
+
1069
+ ### Animated accordion chevron
1070
+
1071
+ ```svelte
1072
+ <script>
1073
+ import { Icon } from '@genarou/blazir-icons'
1074
+ let open = $state(false)
1075
+ </script>
1076
+
1077
+ <button onclick={() => open = !open}>
1078
+ Details
1079
+ <Icon name="chevronRight" chevronOpen={open} preset="muted" decorative />
1080
+ </button>
1081
+ ```
1082
+
1083
+ ### Password visibility toggle
1084
+
1085
+ ```svelte
1086
+ <script>
1087
+ import { Icon } from '@genarou/blazir-icons'
1088
+ let visible = $state(false)
1089
+ </script>
1090
+
1091
+ <div class="input-wrapper">
1092
+ <input type={visible ? 'text' : 'password'} />
1093
+ <button
1094
+ onclick={() => visible = !visible}
1095
+ aria-label={visible ? 'Hide password' : 'Show password'}
1096
+ >
1097
+ <Icon
1098
+ name={visible ? 'eyeOff' : 'eye'}
1099
+ preset="muted"
1100
+ effects={{ hoverScale: 1.1, cursor: 'pointer' }}
1101
+ decorative
1102
+ />
1103
+ </button>
1104
+ </div>
1105
+ ```
1106
+
1107
+ ### Notification bell with badge
1108
+
1109
+ ```svelte
1110
+ <script>
1111
+ import { IconBadge } from '@genarou/blazir-icons'
1112
+ let count = $state(0)
1113
+ </script>
1114
+
1115
+ <button aria-label="Notifications ({count} unread)">
1116
+ <IconBadge
1117
+ name="bell"
1118
+ size={22}
1119
+ badge={count > 0 ? count : false}
1120
+ effects={{ wiggleOnHover: true, cursor: 'pointer' }}
1121
+ decorative
1122
+ />
1123
+ </button>
1124
+ ```
1125
+
1126
+ ### Table row with hover-revealed actions
1127
+
1128
+ ```svelte
1129
+ <script>
1130
+ import { Icon } from '@genarou/blazir-icons'
1131
+ let hovered = $state(false)
1132
+ const ctx = $derived({ hovered })
1133
+ </script>
1134
+
1135
+ <tr
1136
+ onmouseenter={() => hovered = true}
1137
+ onmouseleave={() => hovered = false}
1138
+ >
1139
+ <td>{row.name}</td>
1140
+ <td class="actions">
1141
+ <Icon name="edit" parentHoverContext={ctx} hoverColor="var(--primary)" preset="button" ariaLabel="Edit row" />
1142
+ <Icon name="trash" parentHoverContext={ctx} hoverColor="var(--danger-color)" preset="button" ariaLabel="Delete row" />
1143
+ </td>
1144
+ </tr>
1145
+ ```
1146
+
1147
+ ### Dynamic status icon
1148
+
1149
+ ```svelte
1150
+ <script>
1151
+ import { Icon } from '@genarou/blazir-icons'
1152
+ import type { IconName } from '@genarou/blazir-icons'
1153
+
1154
+ type Status = 'success' | 'warning' | 'error' | 'loading'
1155
+
1156
+ const iconMap: Record<Status, IconName> = {
1157
+ success: 'circleCheck',
1158
+ warning: 'warning',
1159
+ error: 'error',
1160
+ loading: 'loadingDots',
1161
+ }
1162
+
1163
+ export let status: Status = 'loading'
1164
+ </script>
1165
+
1166
+ <Icon
1167
+ name={iconMap[status]}
1168
+ variant={status === 'loading' ? 'muted' : status}
1169
+ spin={status === 'loading'}
1170
+ size={18}
1171
+ ariaLabel="Status: {status}"
1172
+ />
1173
+ ```
1174
+
1175
+ ### Preloading icons on route change (SvelteKit)
1176
+
1177
+ ```ts
1178
+ // src/routes/dashboard/+page.ts
1179
+ import { preloadIcons } from '@genarou/blazir-icons'
1180
+
1181
+ export const load = async () => {
1182
+ // Icons are prefetched in background while page data loads
1183
+ preloadIcons(['chart', 'table', 'user', 'settings', 'logout', 'bell'])
1184
+ return {}
1185
+ }
1186
+ ```
1187
+
1188
+ ### Custom element with effects action
1189
+
1190
+ ```svelte
1191
+ <script>
1192
+ import { iconEffects } from '@genarou/blazir-icons'
1193
+ import Check from '@genarou/blazir-icons/icons/Check'
1194
+ </script>
1195
+
1196
+ <div
1197
+ class="status-chip"
1198
+ use:iconEffects={{ pulse: true, hoverScale: 1.05, cursor: 'pointer' }}
1199
+ >
1200
+ <Check size={14} color="var(--success-color)" decorative />
1201
+ Active
1202
+ </div>
1203
+ ```
1204
+
1205
+ ---
1206
+