Compare commits

..

264 Commits

Author SHA1 Message Date
Eric Fennis
797cf7667d Adjust styling 2025-12-04 12:21:24 +01:00
Eric Fennis
979e07fbf9 Add examples 2025-11-28 17:22:23 +01:00
Eric Fennis
2f5f78c1c2 Save this 2025-11-28 14:39:50 +01:00
Eric Fennis
634322e96c Get new editor 2025-11-28 12:54:51 +01:00
Eric Fennis
53ebc4c076 Add snackplayer 2025-11-28 11:06:09 +01:00
Eric Fennis
7d023209f0 Merge branch 'next' of https://github.com/lucide-icons/lucide into update-site 2025-11-27 16:09:32 +01:00
Eric Fennis
d58a2e43c6 Merge branch 'next' of https://github.com/lucide-icons/lucide into next 2025-11-27 10:58:43 +01:00
Eric Fennis
5ecf78bb8a Merge branch 'main' of https://github.com/lucide-icons/lucide into next 2025-11-27 10:58:34 +01:00
Eric Fennis
f78061b488 ci(workflows): Enable trusted publishing in release (#3808)
* Add permissions for trusted publishing to workflows

* update pnpm

* Update workflow deps

* format code
2025-11-27 10:58:14 +01:00
Eric Fennis
aa8f74eb9e fix(icons): Remove brand icons (#3639)
* Remove brand icons

* Apply feedback

* adjust schema
2025-11-27 10:56:32 +01:00
Eric Fennis
7327637532 Merge branch 'main' of https://github.com/lucide-icons/lucide into next 2025-11-27 10:56:13 +01:00
Eric Fennis
076e0bbcd9 chore(dependencies): Update dependencies (#3809)
* update deps

* Remove ignoredBuiltDependencies

* Fix build errors

* try this

* update config

* add coma

* Try this

* Revert import change

* try this

* add wasm import

* Load wasm

* try this

* Revert nitro version change

* Revert nitro config change
2025-11-27 10:50:19 +01:00
Muhammad Aqib Bashir
33bb95edcd feat(icon): add book-search icon (#3573) (#3580)
* feat(icon): add book-search icon

* fix(icon): fix vertical alignment of `book-search` icon  .

- Fix the vertical alignment of the icon in the 24x24 grid of pixels 
- It had 3 columns on the left side and 1 on the right side, now made 2 on both sides

* fix: remove extra tag "book search"
2025-11-26 09:29:40 +01:00
Jakob Guddas
4c8650a7bf feat(icons): added form icon (#3558)
* Added icons/form.svg

* Added icons/form.json

* Update icons/form.json

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Update icons/form.json

* Updated icons/form.svg

* Updated icons/form.json

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-11-26 09:27:36 +01:00
Jamie Law
ebf400befd fix(icons): changed scale icon (#3800)
* Updated icons/scale.svg

* Updated icons/scale.json
2025-11-26 09:26:04 +01:00
Eric Fennis
0f6dc52a8a Add waves arrow up down (#3463) 2025-11-23 17:45:08 +01:00
Eric Fennis
08bd4b33a0 Merge branch 'main' of https://github.com/lucide-icons/lucide into next 2025-11-21 14:58:50 +01:00
Eric Fennis
83d5ccc3e8 chore(metadata): Adjust navigation category (#3461)
* update categories

* Remove navigation from arrows and chevron icons
2025-11-21 14:10:05 +01:00
Jeevan Mahesha
a3886eb829 feat(angular): update peer dependencies for Angular to support version 21.x (#3807) 2025-11-21 10:37:24 +01:00
Epifânio Francisco
86957f0205 fix(docs): correct package name and description for Flutter and Lustre package (#3701) (#3703)
* fix(docs): correct package name and description for Flutter package (#3701)

* Revert "fix(docs): correct package naming in Packages section (#3701)"

This reverts commit c9b738b4af1ffc114bf7e8e2b81754a54b118b97.

* fix: correct package name from lucide-lustre to lucide_lustre and fix typo (lustre → flutter)
2025-11-21 09:30:20 +01:00
Eric Fennis
b23d4f110b Adjust examples preact docs 2025-11-19 23:24:08 +01:00
Jakob Guddas
465b98df74 fix(icons): changed calendars icon (#3795)
* Added icons/calendars.svg

* Added icons/calendars.json

* Updated icons/calendars.svg

* Updated icons/calendars.svg

* Updated icons/calendars.json

* Updated icons/calendars.svg
2025-11-17 17:19:26 +01:00
Jakob Guddas
d3b2255c0a feat(icons): added calendars icon (#3788)
* Added icons/calendars.svg

* Added icons/calendars.json

* Updated icons/calendars.svg

* Updated icons/calendars.svg

* Updated icons/calendars.json
2025-11-17 10:06:12 +01:00
Jamie Law
a4beac39a3 fix(icons): update anchor icon (#2523)
* Update `anchor` icon

* Update anchor.svg

* Update anchor.json

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-11-17 10:04:50 +01:00
Jakob Guddas
53311befff fix(icons): changed folder-git-2 icon (#3790)
* Updated icons/folder-git-2.svg

* Updated icons/folder-git-2.json
2025-11-17 09:34:51 +01:00
Jakob Guddas
156b3379e4 fix(icons): changed ampersand icon (#3771)
* Updated icons/ampersand.svg

* Updated icons/ampersand.json
2025-11-17 09:34:10 +01:00
theiereman
8dd82dd592 feat(docs): added lucide-rails third-party package (#3769) 2025-11-17 09:32:52 +01:00
Eric Fennis
80d6f737e0 fix(icons): Rename fingerprint icon to fingerprint-pattern (#3767)
* Rename fingerprint icon

* Fix as well for lucide-react

* Fix lint error
2025-11-17 09:32:09 +01:00
Eric Fennis
7d86d083bd Merge branch 'main' of https://github.com/lucide-icons/lucide into update-site 2025-11-14 15:48:57 +01:00
Karsa
a94044cc3a feat(icons): added chess pieces (#1945)
* fix(icons): add more Lucide-ish rounding to sparkle icons

* chore(icons): sharpen points ever so slightly for better visual weight

* chore(icons): update sparkles & moon-star icon as per Discord

* feat(icons): added chess icons

* chore(icons): revert commits from #1937

* chore(icons): revert commits from #1937

* Update icons/chess-rook.svg

* Update icons/chess-rook.svg

* Update icons/chess-rook.json

* fix(icons/chess): finetune queen & adjust bases for better scalability

* fix(icons/chess): decrease rook base width

* fix(icons/chess): decrease knight base width

* fix(icons/chess): fix linting issue with chess-rook.json

---------

Co-authored-by: Karsa <karsa@sztaki.hu>
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
Co-authored-by: Jakob Guddas <github@jguddas.de>
2025-11-07 10:39:37 +01:00
ocavue
2c46ea9948 fix(lucide-preact): handle className prop (#3751) 2025-11-07 10:07:57 +01:00
Rokas Brazdžionis
190e8372af feat(docs): add keyboard shortcut for search (#3718) 2025-11-07 10:04:40 +01:00
Eric Fennis
5c523be780 update preact docs 2025-11-04 17:35:43 +01:00
Karsa
d3826ce952 fix(icons): changed ruler-dimension-line icon (#3433)
* [github] Added issue template forms

* [github] yaml => yml

* Syntax fixes

* Further syntax fixes

* Sort issue templates

* Update 02_bug_report.yml

* Updated icons/ruler-dimension-line.svg

* Updated icons/ruler-dimension-line.json

---------

Co-authored-by: Karsa <karsa@karsa.org>
2025-10-31 10:37:37 +01:00
Domingas P
ee66dd258d feat(icons): added mouse-pointer-2-off icon (#3570)
* Added icons/mouse-pointer-2-off.svg

* Added icons/mouse-pointer-2-off.json

* Update icons/mouse-pointer-2-off.svg

* Update icons/mouse-pointer-2-off.json

* Update icons/mouse-pointer-2-off.json

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Apply @jguddas alternative 2

* Apply suggestion from @jguddas

Co-authored-by: Jakob Guddas <github@jguddas.de>

---------

Co-authored-by: Jakob Guddas <github@jguddas.de>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-10-31 10:34:58 +01:00
Usama Khan
265770857f feat(icons): added solar-panel icon (#2780)
* Added icons/solar-panel.svg

* Added icons/solar-panel.json

* Updated icons/solar-panel.svg

* Updated icons/solar-panel.json

* Update solar-panel.svg

* Update solar-panel.json

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-10-31 10:32:49 +01:00
Karsa
1c20fc66d7 fix(icons/file): arcified folds (#3587)
* fix(icons/file): add rounding to file fold corners & apply this change to other icons with folds.

* fix(icons/file): update file-digit.svg with the correct version

* fix(icons/file): update file-key for better centre of gravity

* fix(icons/file): optimized & formatted icons

* fix(icons/file): increase file-type-corner T size to better match other addons

* feat(docs): increase size of braces & rename file-json to file-braces

* refactor(icons): further increase size of T in file-type-corner.svg

* refactor(icons): upgrade file-digit to match binary

* refactor(icons): upgrade file-check-corner to match other operational icons (plus/minus/x)

* Update file-scan.svg

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-10-31 10:31:07 +01:00
Jakob Guddas
c34c5f2aea feat(icons): added clock-check icon (#2402)
* Added icons/clock-check.svg

* Added icons/clock-check.json

* Updated icons/clock-check.svg

* Updated icons/clock-check.json

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-10-31 10:21:50 +01:00
liloudreams
6432dddfe0 feat(icons): added helicopter icon (#2760)
* Added icons/helicopter.svg

* Added icons/helicopter.json

* Update helicopter.svg

* Update helicopter.svg

Review gap and stroke violations

* Update icons/helicopter.json

Co-authored-by: Karsa <contact@karsa.org>

* Update icons/helicopter.json

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Update icons/helicopter.svg

Co-authored-by: Karsa <contact@karsa.org>

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
Co-authored-by: Karsa <contact@karsa.org>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-10-31 10:18:54 +01:00
Joris Gallot
028fc12657 feat(icons): added git-branch-minus icon (#3586)
* Added icons/git-branch-minus.svg

* Added icons/git-branch-minus.json

* chore: optimise with circles

Co-authored-by: Karsa <contact@karsa.org>

* Update git-branch-minus.json

* Update git-branch.json

* Update git-branch-plus.json

---------

Co-authored-by: Karsa <contact@karsa.org>
2025-10-31 10:15:31 +01:00
Victor Kaugesaar
2e794a296a feat(docs): add new package for Go (#3736) 2025-10-31 09:54:31 +01:00
Eric Fennis
498b8390d7 Added solid docs 2025-10-27 21:27:42 +01:00
Eric Fennis
528de5284b Add group icons 2025-10-26 17:21:55 +01:00
Eric Fennis
a19fb21c5d remove spiral animation component 2025-10-26 17:19:29 +01:00
Eric Fennis
5770f62d02 Add solid pages 2025-10-26 17:14:13 +01:00
/Aljoscha
be59e647c1 Replace pnpm install with pnpm add across documentation. (#3735) 2025-10-26 17:01:12 +01:00
felipeajzanetti
e560b8bf3a feat(icons): added gamepad-directional icon (#3693)
* Added icons/gamepad-directional.svg

* Added icons/gamepad-directional.json

* Update icons/gamepad-directional.json

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Icon update

* Update icons/gamepad-directional.json

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Jakob Guddas <github@jguddas.de>
2025-10-24 20:29:07 +02:00
Eric Fennis
74d6fbc941 Fix default prop (#3730) 2025-10-24 16:44:49 +02:00
Eric Fennis
94e3f56166 Finish svelte docs 2025-10-24 15:18:25 +02:00
Matthes Kaiser
a2a0821c66 docs: add practical introductions to package documentation (#3643) 2025-10-24 14:18:51 +02:00
Lance
b787576f4a feat(docs): add new package for Slint (#3698) 2025-10-24 13:54:24 +02:00
Hieu
375ad12aeb feat(icons): added birdhouse icon (#3378)
* Added icons/birdhouse.svg

* Added icons/birdhouse.json

* Update birdhouse.svg

Edit .svg with the suggested changes

* Update icons/birdhouse.json

Co-authored-by: Jakob Guddas <github@jguddas.de>

---------

Co-authored-by: Jakob Guddas <github@jguddas.de>
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-10-24 13:04:53 +02:00
Eric Fennis
a432aeee69 Adjust home hero animation 2025-10-24 09:39:26 +02:00
Eric Fennis
b0ed2827f8 Adjust animation home 2025-10-21 21:11:58 +02:00
Eric Fennis
bfbd5d7bda Write docs for Vue 2025-10-20 20:25:34 +02:00
Eric Fennis
1090cbc810 adjust easing home animation 2025-10-20 19:15:12 +02:00
Eric Fennis
fa6e015520 fix framework select 2025-10-20 19:13:38 +02:00
Eric Fennis
1756617885 Fix layout accessibility page 2025-10-17 15:23:11 +02:00
Eric Fennis
f5d0a2a220 Add docs for vue types 2025-10-17 14:58:00 +02:00
Khalid Alansary
be55088e75 fix(packages/lucide): replace elements inside <template> (#2635) (#3576)
* fix(packages/lucide): replace elements inside `<template>` (#2635)

* Added replaceInsideTemplates option

* Format code

* Simply code and add some documentation

* Fix vercel build

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-10-17 11:46:55 +02:00
dependabot[bot]
32e93c043f chore(deps): bump astro from 5.5.2 to 5.14.4 (#3683)
Bumps [astro](https://github.com/withastro/astro/tree/HEAD/packages/astro) from 5.5.2 to 5.14.4.
- [Release notes](https://github.com/withastro/astro/releases)
- [Changelog](https://github.com/withastro/astro/blob/main/packages/astro/CHANGELOG.md)
- [Commits](https://github.com/withastro/astro/commits/astro@5.14.4/packages/astro)

---
updated-dependencies:
- dependency-name: astro
  dependency-version: 5.14.4
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-16 13:59:49 +02:00
Karsa
920fa7888b fix(): update brand logo statement link in github action (#3630) 2025-10-16 13:59:35 +02:00
Karsa
95766b6682 feat(icons): added monitor-cloud icon (#3037)
* [github] Added issue template forms

* [github] yaml => yml

* Syntax fixes

* Further syntax fixes

* Sort issue templates

* Update 02_bug_report.yml

* Added icons/monitor-cloud.svg

* Added icons/monitor-cloud.json

---------

Co-authored-by: Karsa <karsa@karsa.org>
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-10-16 13:55:01 +02:00
Jakob Guddas
db15bb85c0 Updated icons/sword.svg (#3421) 2025-10-16 13:53:00 +02:00
Eric Fennis
b1675c4c33 chore(packages): Remove umd exports (#3641)
* Remove UMD export

* Revert package file change
2025-10-16 13:49:19 +02:00
Daniel Bayley
84c210acf5 feat(icons): add VS Code squircle base shape snippet (#3674)
* feat(icons): add VS Code `squircle` base shape snippet

* config: add VS Code snippets schema
2025-10-16 13:48:06 +02:00
Eric Fennis
5e27d7d11a Add scaledown animation 2025-10-15 23:01:05 +02:00
Eric Fennis
3a608bac58 Updates docs 2025-10-15 21:08:38 +02:00
Eric Fennis
bcebce5927 Adjust animation 2025-10-15 14:30:50 +02:00
Eric Fennis
84346a148e Some adjustments 2025-10-15 12:25:27 +02:00
Eric Fennis
fac9fa4efd Add animation? 2025-10-14 18:36:53 +02:00
Eric Fennis
cde1ebaa0b Fix dynamic sidebar 2025-10-14 15:29:55 +02:00
Eric Fennis
9f67c0943b Add svelte docs 2025-10-14 13:24:42 +02:00
Eric Fennis
999bca4575 Add basics section to vue pages 2025-10-12 00:01:29 +02:00
Niels
3c7815c5c0 Update index.md (#3673)
removed duplicate text
2025-10-10 15:47:55 +02:00
Eric Fennis
d7e7493bd6 Add typescript documentation 2025-10-10 13:01:50 +02:00
Eric Fennis
f34e889831 Add matrix hero 2025-10-10 09:38:55 +02:00
Jakob Guddas
7bb27142c8 fix(icons): changed receipt-text icon (#3553)
* Updated icons/receipt-text.svg

* Updated icons/receipt-text.json

* Updated icons/receipt-text.svg

* Updated icons/receipt-text.svg

* Updated icons/receipt-text.json
2025-10-06 21:03:54 +02:00
Jamie Law
2811d60184 feat(icons): Add motorbike icon (#3371)
* Add `motorbike` and `dirtbike` icons

* Renamed `dirtbike` to `dirt-bike`

* Deleted `dirt-bike` in order to move it to a separate PR

* Update motorbike.svg

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-10-06 20:58:56 +02:00
dependabot[bot]
730c24ec55 chore(deps): bump devalue from 5.1.1 to 5.3.2 (#3638)
Bumps [devalue](https://github.com/sveltejs/devalue) from 5.1.1 to 5.3.2.
- [Release notes](https://github.com/sveltejs/devalue/releases)
- [Changelog](https://github.com/sveltejs/devalue/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/devalue/compare/v5.1.1...v5.3.2)

---
updated-dependencies:
- dependency-name: devalue
  dependency-version: 5.3.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-06 20:56:31 +02:00
Karsa
56ebfeb596 Updated icons/building-2.svg (#3509) 2025-10-06 20:55:54 +02:00
Jakob Guddas
905d38186c fix(icons): changed combine icon (#3200)
* Updated icons/combine.svg

* Updated icons/combine.json

* Updated icons/combine.svg

* Updated icons/combine.svg

* Updated icons/replace.svg

* Updated icons/replace.svg

* Updated icons/combine.svg

* Updated icons/replace.svg

* Updated icons/replace-all.svg

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-10-06 20:55:14 +02:00
Eric Fennis
65403c9298 update accessebility 2025-09-24 12:18:48 +02:00
Eric Fennis
8eb64f4574 Adjust color 2025-09-20 22:54:48 +02:00
Eric Fennis
d1dd94a717 Update images 2025-09-19 15:04:51 +02:00
Eric Fennis
758fa4b75f Merge branch 'main' into next 2025-09-19 13:59:13 +02:00
dependabot[bot]
1cfb3ff70e chore(deps-dev): bump vite from 6.3.5 to 6.3.6 (#3611)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 6.3.5 to 6.3.6.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v6.3.6/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v6.3.6/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 6.3.6
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-09-19 10:08:00 +02:00
Eric Fennis
7bbb1e1fea feat(@lucide/vue): Rename Vue package name to @lucide/vue (#3337)
* Remove old vue 2 package

* Add @lucide/vue package

* Remove old vue 2 doc

* Update docs

* Adjust export template

* Adjust vue package!

* Fix tests

* Format code

* Update packages/vue/src/Icon.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Adjust vue package in docs

* Update deadlinks

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-11 14:38:34 +02:00
Jakob Guddas
31cfa79818 fix(icons): arcified square-m icon (#3549)
* Updated icons/square-m.svg

* Updated icons/square-m.json

* Updated icons/square-m.svg

* Updated icons/square-m.json
2025-09-11 13:01:58 +02:00
Jamie Law
d1903220db fix(icons): changed flame icon (#3600)
* Updated icons/flame.svg

* Updated icons/flame.json

* Update icons/flame.json

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-09-11 12:59:06 +02:00
Usama Khan
ec567e59fc feat(icons): added ev-charger icon (#2781)
* Added icons/ev-charger.svg

* Added icons/ev-charger.json

* Updated icons/ev-charger.json

* Updated icons/ev-charger.svg

* Update ev-charger.svg

* Update icons/ev-charger.json

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Updated icons/fuel.svg

* Updated icons/fuel.json

* Update ev-charger.json

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-09-11 11:52:13 +02:00
Eric Fennis
1d6b5d6858 Update snapshot 2025-09-11 09:47:08 +02:00
Pascal Duez
c368d01d92 docs: update lucide-static documentation about raw string imports (#3524) 2025-09-11 09:41:17 +02:00
Daniel Bayley
99427026a3 feat(icons): added house-heart icon (#3239)
* feat(icons): added `house-heart` icon

* Optimise `house-*` icons
2025-09-09 08:36:31 +02:00
vuong quang huy
81a1f6e1dc feat(packages): add new package for flutter (#3536)
* add new package for flutter

* update logo

* fix: correct URL for Total Downloads image in packageData.thirdParty.json
2025-09-05 12:26:54 +02:00
Eric Fennis
7fb4ffc7a7 update sponsors 2025-09-02 08:17:21 +02:00
dependabot[bot]
30529bbeb2 chore(deps): bump astro from 5.5.2 to 5.13.2 (#3564)
Bumps [astro](https://github.com/withastro/astro/tree/HEAD/packages/astro) from 5.5.2 to 5.13.2.
- [Release notes](https://github.com/withastro/astro/releases)
- [Changelog](https://github.com/withastro/astro/blob/main/packages/astro/CHANGELOG.md)
- [Commits](https://github.com/withastro/astro/commits/astro@5.13.2/packages/astro)

---
updated-dependencies:
- dependency-name: astro
  dependency-version: 5.13.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-29 10:20:03 +02:00
Jakob Guddas
279ac9ce13 fix(icons): changed calendar-cog icon (#3583)
* Updated icons/calendar-cog.svg

* Updated icons/calendar-cog.json
2025-08-29 10:19:37 +02:00
Jakob Guddas
2dede9886b Updated icons/id-card-lanyard.json (#3534) 2025-08-29 10:18:57 +02:00
Jakob Guddas
781fc399a2 fix(meta): added tag to ship metadata (#3559)
* Updated icons/ship.svg

* Updated icons/ship.json

* Updated icons/ship.json
2025-08-29 10:09:33 +02:00
Jakob Guddas
087b59d55e fix(icons): changed message-square-quote icon (#3550)
* Updated icons/message-square-quote.svg

* Updated icons/message-square-quote.json
2025-08-29 10:08:17 +02:00
Jakob Guddas
e1f96d8f54 fix(icons): changed panel-top-bottom-dashed icon (#3584)
* Updated icons/panel-top-bottom-dashed.svg

* Updated icons/panel-top-bottom-dashed.json

* Updated icons/panel-left-right-dashed.svg

* Updated icons/panel-left-right-dashed.json
2025-08-29 10:07:43 +02:00
Karsa
7cf38b5b6e fix(icons): changed list/text & derived icons (#3568)
* fix(icons): increase base height of list/text and derived icons for more consistent optical volume

* refactor(icons): upgrade list-chevrons-down-up
feat(icons): add list-chevrons-up-down

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-08-29 10:05:16 +02:00
Jamie Law
9c441bd4ff fix(icons): Optimise bug icons (#3574)
* Optimise `bug` icons

* Remove unnecessary connecting line
2025-08-29 09:53:32 +02:00
Jamie Law
507a86a890 chore(metadata): Added tags to messages-square (#3529)
* Discussion - GitHub's discussions section uses this type of icon
* Interview - e.g. a category of content, alongside "article", "video", "podcast"
* Debate - in a similar vein to "interview"
2025-08-29 09:51:56 +02:00
Karsa
8d4d9cd6d5 fix(icons): changed church icon (#2971)
* [github] Added issue template forms

* [github] yaml => yml

* Syntax fixes

* Further syntax fixes

* Sort issue templates

* Update 02_bug_report.yml

* Updated icons/church.svg

* Update church.svg

* Update church.svg

* Update icons/church.svg

* Update church.svg

* Update church.svg

---------

Co-authored-by: Karsa <karsa@karsa.org>
Co-authored-by: Jakob Guddas <github@jguddas.de>
2025-08-29 09:51:25 +02:00
Jakob Guddas
341be34b1f feat(preview-comment): put x-ray at top if there are more than 7 changed icons to prevent them from being cut of (#3589) 2025-08-29 09:48:45 +02:00
Julian Kelly
d52c369b99 feat(icons): added list-chevrons-down-up icon (#3492)
* Added icons/list-chevrons-down-up.svg

* Added icons/list-chevrons-down-up.json

---------

Co-authored-by: Jakob Guddas <github@jguddas.de>
2025-08-26 13:20:48 +02:00
Karsa
a7a1973d3f feat(docs): add MDN Web Docs & Nuxt to showcase (#3590) 2025-08-25 14:56:00 +02:00
Irvine Acosta
b6dfcef41b feat(icons): added 'panel-dashed' variants + update tags on existing icons (#3500)
* feat(icons): add panel-{top-bottom,left-right,all-sides}-dashed; update tags on existing panel icons

* removed the all-sides variant

* added bleed to keyword list
2025-08-22 10:27:22 +02:00
Jakob Guddas
1c5a9dafc1 feat(dpi-preview): add previous version for easier comparison (#3532)
* feat(dpi-preview): add previous version for easier comparison

* feat(preview comment): add link to lucide studio to x-ray images (#3552)
2025-08-22 10:26:16 +02:00
Karsa
546bb1f391 Updated icons/building.svg (#3510) 2025-08-22 10:23:40 +02:00
Jakob Guddas
64c1a55e01 Updated icons/cog.svg (#3548) 2025-08-22 10:22:52 +02:00
Jakob Guddas
0e3ac6beb9 fix(icons): changed wallpaper icon (#3566)
* Updated icons/wallpaper.svg

* Updated icons/wallpaper.json

* Update icons/wallpaper.json

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-08-22 10:22:14 +02:00
Jakob Guddas
c7c0e2bead fix(icons): optimized chrome icon & renamed to chromium (#3572)
* Updated icons/chrome.svg

* fix(icons): renamed chrome to chromium due to https://github.com/lucide-icons/lucide/issues/3571

* fix(icons/chromium): added missing EOL to json

---------

Co-authored-by: Karsa <contact@karsa.org>
2025-08-22 10:03:47 +02:00
Karsa
85dba80322 feat(packages/lucide): added support for providing a custom root element (#3543)
* feat(packages/lucide): added support for providing a custom root element

* feat(packages/lucide): ran prettier & renamed from root ⇒ context
2025-08-18 15:25:08 +02:00
Jakob Guddas
f8a1a76b83 feat(icons): added rose icon (#1972)
* Added icons/rose.svg

* Added icons/rose.json

* Updated icons/rose.svg

* Updated icons/rose.json

* Updated icons/rose.svg

* Updated icons/rose.json

* Updated icons/rose.svg

* Updated icons/rose.json

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-08-18 15:15:57 +02:00
Karsa
bd50af7afb fix(icons): changed camera icon (#3539)
* Updated icons/camera.svg

* Updated icons/camera.json

* Updated icons/camera-off.svg

* Apply suggestion from @github-actions[bot]

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-08-18 15:13:21 +02:00
Karsa
cfabc9f608 docs(site): added official statement on brand logos in Lucide (#3541)
* docs(): added official statement on brand logos in Lucide

* docs(): fix accidental typo from adding link

* docs(): migrating some stuff to a more accessible plain language format

* Update .github/ISSUE_TEMPLATE/01_icon_request.yml

* Update .github/ISSUE_TEMPLATE/01_icon_request.yml

Co-authored-by: Jakob Guddas <github@jguddas.de>

* Update README.md

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update .github/pull_request_template.md

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Jakob Guddas <github@jguddas.de>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-18 15:11:06 +02:00
Karsa
09f98127c6 fix(icons): changed umbrella icon (#3490)
* Updated icons/umbrella.svg

* Updated icons/umbrella-off.svg

* Updated icons/umbrella.svg

* Updated icons/umbrella-off.svg
2025-08-18 15:09:45 +02:00
Jakob Guddas
867c0581af fix(license): add full text of Feather license (#3530)
* fix(license): add full text of Feather license

* Update LICENSE

* Update LICENSE
2025-08-09 05:54:10 +02:00
Eric Fennis
987edb14f8 ci(release.yml): Disable lucide-vue release (Deprecated) 2025-08-08 13:41:34 +02:00
Karsa
9db4dc7e1c feat(icons): added brick-wall-shield icon (#3476)
* Added icons/brick-wall-shield.svg

* Added icons/brick-wall-shield.json
2025-08-08 10:28:41 +02:00
Jacob Padilla
2150ce75ce feat(icons): added kayak icon (#3054)
* kayak svg and json schema

* made kayak icon 1px smaller on all sides

* added jguddas as contributor

* fixed gap in kayak paddle

* indentation
2025-08-08 10:14:20 +02:00
Jakob Guddas
106e37c3ce fix: deprecate rail-symbol (#2862) 2025-08-08 10:12:40 +02:00
dependabot[bot]
528ffd6fa5 chore(deps): bump astro from 5.5.2 to 5.12.8 (#3523)
Bumps [astro](https://github.com/withastro/astro/tree/HEAD/packages/astro) from 5.5.2 to 5.12.8.
- [Release notes](https://github.com/withastro/astro/releases)
- [Changelog](https://github.com/withastro/astro/blob/main/packages/astro/CHANGELOG.md)
- [Commits](https://github.com/withastro/astro/commits/astro@5.12.8/packages/astro)

---
updated-dependencies:
- dependency-name: astro
  dependency-version: 5.12.8
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-08 09:50:44 +02:00
LieOnLion
3b92fae633 fix(icons): changed mic-off icon (#2823)
* Updated icons/mic-off.svg

* Updated icons/mic-off.json

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-08-08 09:38:22 +02:00
Karsa
fcf42bf754 Updated icons/store.svg (#3501) 2025-08-08 09:29:49 +02:00
Karsa
0b6149c02d fix(icons): changed apple icon (#3505)
* Updated icons/apple.svg

* Updated icons/apple.json

* Update apple.svg

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-08-08 09:26:35 +02:00
Karsa
bc9a8de419 feat(icons): added circle-star icon (#3507)
* Added icons/circle-star.svg

* Added icons/circle-star.json

* Added icons/square-star.svg

* Added icons/square-star.json

* Update square-star.json

* Update circle-star.json
2025-08-07 12:22:36 +02:00
Jakob Guddas
26ab33a832 fix: fixed array length error in diff endpoint (#3498) 2025-08-07 11:29:21 +02:00
Karsa
ac4c3581c4 fix(site): fix icon style customizer (#3511) 2025-08-07 11:26:07 +02:00
Karsa
1ddba6da04 fix(icons): changed school icon (#2968)
* [github] Added issue template forms

* [github] yaml => yml

* Syntax fixes

* Further syntax fixes

* Sort issue templates

* Update 02_bug_report.yml

* Updated icons/school.svg

* Update school.svg

---------

Co-authored-by: Karsa <karsa@karsa.org>
2025-08-05 17:19:48 +02:00
Karsa
8754c9b6fd fix(icons): changed spade icon (#3502)
* Updated icons/spade.svg

* Updated icons/spade.json
2025-08-05 14:31:40 +02:00
Jakob Guddas
e9febe4fab docs: add rule against war/violence related imagery (#3506)
* docs: add rule against war/violence related imagery

* Update 01_icon_request.yml
2025-08-05 10:29:23 +02:00
Jamie Law
570f7d7e76 chore(metadata): Add tags to x icon (#3341)
Added 'ex' and 'cross' as tags
2025-08-01 13:15:17 +02:00
Karsa
6d43b9feb3 feat(icons): added turntable icon (#3429)
* [github] Added issue template forms

* [github] yaml => yml

* Syntax fixes

* Further syntax fixes

* Sort issue templates

* Update 02_bug_report.yml

* Added icons/turntable.svg

* Added icons/turntable.json

---------

Co-authored-by: Karsa <karsa@karsa.org>
2025-08-01 12:30:47 +02:00
Jakob Guddas
16166f838b feat(lucide-static): include aliases in icons directory (#3368) 2025-08-01 12:21:31 +02:00
Jakob Guddas
425bcc3f3d fix(icons): arcified cake-slice icon (#3345)
* Updated icons/cake-slice.svg

* Updated icons/cake-slice.json
2025-08-01 12:19:13 +02:00
Jakob Guddas
982af91724 fix(icons): changed a-arrow-* icons (#3474)
* Updated icons/a-arrow-up.svg

* Updated icons/a-arrow-down.svg
2025-08-01 12:09:22 +02:00
Eric Fennis
a3ec4428f1 fix(lucide-vue-next): Support for kebabCase props (#3477)
* Fix kebabCase Vue props

* format code

* Fix tests

* Format code
2025-08-01 11:39:49 +02:00
Abdelrahman
673e76831d fix: add extensions to imports (#2950)
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-08-01 11:39:36 +02:00
Karsa
b4291576b7 fix(icons): changed hospital icon (#2969)
* [github] Added issue template forms

* [github] yaml => yml

* Syntax fixes

* Further syntax fixes

* Sort issue templates

* Update 02_bug_report.yml

* Updated icons/hospital.svg

* Update hospital.svg

---------

Co-authored-by: Karsa <karsa@karsa.org>
2025-07-31 13:54:05 +02:00
Karsa
95f5ecacad fix(icons): arcified message icons & fixed optical volume (#3473)
* fix(icons): arcify message icons & fix optical volume

* fix(icons): increase optical volume of messages-square
2025-07-31 12:57:36 +02:00
Karsa
ef5c1b9bdd feat(icons): added hand-fist, renamed grab to hand-grab (#1843) 2025-07-31 12:44:26 +02:00
Eric Fennis
b01715ebe8 chore(docs): Update vitepress deps (#3470)
* Update vitepress

* Format code

* Update deps

* Fix build

* Update lockfile
2025-07-31 11:45:59 +02:00
Karsa
05dd5fcfde fix(icons): changed tiny heart icons (#3471)
* Updated icons/file-heart.svg

* Updated icons/folder-heart.svg

* Updated icons/calendar-heart.svg

* Updated icons/scan-heart.svg

* Updated icons/book-heart.svg

* Updated icons/book-heart.json

* Updated icons/hand-heart.svg

* Updated icons/hand-heart.json

* Updated icons/message-circle-heart.svg

* Updated icons/message-circle-heart.json

* fix(icons): revert changes made to message-circle-heart in favour of https://github.com/lucide-icons/lucide/pull/3473
2025-07-31 10:40:17 +02:00
Jakob Guddas
e71198d9b3 chore: icon alias improvements (#2861)
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-07-30 16:52:50 +02:00
Jakob Guddas
57714e36ea feat(SvgPreview): add features from lucide studio (#3365)
* feat(SvgPreview): add features from lucide studio

* Update docs/.vitepress/lib/SvgPreview/index.tsx

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-07-30 16:20:24 +02:00
Epifânio Francisco
358c9c1e80 fix(docs): update prop name from absolute-stroke-width to absoluteStrokeWidth in vue-next guide (#3322)
- Changed kebab-case to camelCase for consistency with Vue prop naming conventions
- Updated props table in lucide-vue-next documentation
2025-07-30 15:56:48 +02:00
Alex
64ddff76c5 fix: properly select icons dir for tags & error if no metadata found (#3411)
Fixes #3359
Throwing an error in readAllMetadata now to ensure this error can't happen again in the future.
The error was caused by this script being moved to a different dir, but the relative url not being updated, causing no icons to be found.
2025-07-30 11:59:46 +02:00
Jakob Guddas
31a9cdcbcc ci: add "telegram" to close-issue-with-banned-phrases (#3475) 2025-07-30 09:04:52 +02:00
Karsa
69319ee2f0 feat(icons): arcified moon icons (#3380) 2025-07-29 20:50:39 +02:00
Karsa
7beb3ee666 fix(icons): changed gavel icon (#3465)
* Updated icons/gavel.svg

* Updated icons/gavel.json

* Update icons/gavel.json

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

---------

Co-authored-by: Jakob Guddas <github@jguddas.de>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-07-29 19:16:31 +02:00
Eric Fennis
396d6d42ce ci(suggest-tags): Switch to github.event.pull_request.head.sha 2025-07-29 16:19:30 +02:00
Eric Fennis
37a60eefb2 ci: Switch to $GITHUB_SHA 2025-07-29 16:11:08 +02:00
Jakob Guddas
c2c46b21ed fix(icons): changed mails, tags, files, file-stack, book-copy and folders icon (#2935)
* Updated icons/folders.svg

* Updated icons/folders.svg

* Updated icons/folders.svg

* Updated icons/folders.svg

* Updated icons/files.svg

* Updated icons/files.json

* Updated icons/mails.svg

* Updated icons/mails.json

* Updated icons/tags.svg

* Updated icons/tags.json

* Updated icons/files.svg

* Updated icons/file-stack.svg

* Updated icons/file-stack.json

* Updated icons/files.json

* Updated icons/images.svg

* Updated icons/square-stack.svg

* Updated icons/square-stack.json

* Updated icons/square-stack.svg

* Updated icons/files.svg

* Update icons/square-stack.json

* Update icons/square-stack.svg

* Updated icons/book-copy.svg

* Updated icons/tags.svg

* Updated icons/tags.svg

* Update icons/mails.svg

Co-authored-by: Karsa <contact@karsa.org>

* Update icons/folders.svg

Co-authored-by: Karsa <contact@karsa.org>

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
Co-authored-by: Karsa <contact@karsa.org>
2025-07-29 09:37:58 +02:00
Karsa
0a0c8aa7e7 fix(icons): changed settings icon (#3467)
* Updated icons/settings.svg

* Updated icons/settings.json

* Updated icons/settings.svg

* Update settings.svg
2025-07-29 09:35:23 +02:00
Usama Khan
dc14e6ea8e feat(icons): added closed-caption icon (#2910)
* Added icons/closed-caption.svg

* Added icons/closed-caption.json

* Update closed-caption.json

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-07-29 09:28:40 +02:00
Jamie Law
1f67ef5307 fix(icons): Rounded and optically-centred flag-triangle-* icons (#3438)
* Rounded and optically-centred `flag-triangle-*` icons

* Increase size of flag
2025-07-29 09:23:43 +02:00
Jakob Guddas
ff6f9a09ad Updated icons/folder-symlink.svg (#2558) 2025-07-29 09:22:54 +02:00
Jakob Guddas
585b65f28f fix(icons): added rounding to heart-* icons (#3061)
* Updated icons/heart-handshake.svg

* Updated icons/heart-handshake.json

* Updated icons/heart-handshake.svg

* Updated icons/heart-handshake.svg

* Updated icons/heart-handshake.svg

* Updated icons/heart-handshake.svg

* Updated icons/heart-handshake.svg

* Updated icons/heart-handshake.svg

* Updated icons/heart.svg

* Updated icons/heart.json

* Updated icons/heart-pulse.svg

* Updated icons/heart-pulse.json

* Updated icons/heart-crack.svg

* Updated icons/heart-crack.json

* Updated icons/heart-off.svg

* Updated icons/heart-off.json

* Updated icons/heart-crack.svg

* Updated icons/heart-handshake.svg

* Updated icons/heart-minus.svg

* Updated icons/heart-minus.json

* Update icons/heart-minus.json

* Update icons/heart-minus.json

* Update icons/heart-minus.json

* Apply suggestions from code review

* Updated icons/heart-minus.svg

* Updated icons/heart-minus.svg

* Updated icons/heart-off.svg

* Updated icons/heart-plus.svg

* Updated icons/heart-plus.json

* Updated icons/heart-plus.json

* Update icons/heart-handshake.svg

Co-authored-by: Karsa <contact@karsa.org>

* Update icons/heart-handshake.svg

* Update icons/heart-crack.svg

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
Co-authored-by: Karsa <contact@karsa.org>
2025-07-29 07:31:19 +02:00
Victor Soteras
405feea8f6 fix(icons): Changed a-large-small icon (#3396)
* fix(icons): Changed a-large-small icon

* fix(icons): Changed case-sensitive icon

* fix(icons): Changed case-lower icon

* fix(icons): Changed case-upper icon

* Apply suggestions from code review

* Update .gitignore

* Update icons/a-large-small.json

* Update icons/case-upper.svg

* Update icons/case-upper.svg

---------

Co-authored-by: Jakob Guddas <github@jguddas.de>
Co-authored-by: Karsa <contact@karsa.org>
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-07-28 17:08:39 +02:00
Karsa
5931a4a227 fix(icons): changed sailboat icon (#3460)
* [github] Added issue template forms

* [github] yaml => yml

* Syntax fixes

* Further syntax fixes

* Sort issue templates

* Update 02_bug_report.yml

* Updated icons/sailboat.svg

---------

Co-authored-by: Karsa <karsa@karsa.org>
2025-07-28 15:59:58 +02:00
Zlendy
d3bafc2848 fix(docs): Icon Component Example for Svelte 5 does not compile (#3416) 2025-07-28 15:58:31 +02:00
Karsa
a6f4a08bda fix(icons): changed wrench icon (#3459)
* [github] Added issue template forms

* [github] yaml => yml

* Syntax fixes

* Further syntax fixes

* Sort issue templates

* Update 02_bug_report.yml

* Updated icons/wrench.svg

* Updated icons/wrench.json

* Updated icons/pickaxe.svg

* Updated icons/gavel.svg

* Updated icons/hammer.svg

* Updated icons/hammer.json

* Updated icons/pickaxe.svg

---------

Co-authored-by: Karsa <karsa@karsa.org>
2025-07-28 15:57:03 +02:00
Jamie Law
5a93aff903 feat(icons): added handbag icon (#3298)
* Added icons/handbag.svg

* Added icons/handbag.json

* Update handbag.json

Added extra tags

* Fix trailing comma

* Update icons/handbag.svg

Co-authored-by: Karsa <contact@karsa.org>

* Update handbag.json

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
Co-authored-by: Karsa <contact@karsa.org>
2025-07-28 15:56:37 +02:00
Bean Duong
e6c50d543b feat(icons): added clipboard-clock icon (#3427)
* Added icons/clipboard-clock.svg

* Added icons/clipboard-clock.json

* Flip clock side

Co-authored-by: Karsa <contact@karsa.org>

* Add contributors

Co-authored-by: Karsa <contact@karsa.org>

* Extend use cases

Co-authored-by: Karsa <contact@karsa.org>

* Update icons/clipboard-clock.json

* Remove duplicated tag

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>

---------

Co-authored-by: Karsa <contact@karsa.org>
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-07-28 15:51:52 +02:00
Karsa
0d7c70cab6 fix(icons): changed user-star icon (#3464)
* Updated icons/user-star.svg

* Updated icons/user-star.json

* Update user-star.json
2025-07-28 15:51:29 +02:00
Martin Vítek
379d1b1002 feat(icons): added user-star icon (#3331)
* Added icons/user-star.svg

* Added icons/user-star.json

* enlarge star in user-star icon

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-07-28 15:07:27 +02:00
Mariano Franzese
7d6998941d feat(icons): added map-minus icon (#3259)
* Added icons/map-minus.svg

* Added icons/map-minus.json

* Update icons/map-minus.svg

Co-authored-by: Jakob Guddas <github@jguddas.de>

* Update icons/map-minus.json

Co-authored-by: Jakob Guddas <github@jguddas.de>

* Update icons/map-minus.json

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>

---------

Co-authored-by: Jakob Guddas <github@jguddas.de>
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-07-28 14:59:25 +02:00
Eric Fennis
667bb8034d fix(icons): Rename location-edit to map-pin-pencil (#3462)
* Rename `location-edit` to `map-pin-pencil`

* Updated "aliases" object
2025-07-28 14:51:16 +02:00
Karsa
fd9c489d7a fix(icons): changed castle icon (#2972)
* [github] Added issue template forms

* [github] yaml => yml

* Syntax fixes

* Further syntax fixes

* Sort issue templates

* Update 02_bug_report.yml

* Updated icons/castle.svg

---------

Co-authored-by: Karsa <karsa@karsa.org>
2025-07-28 13:57:04 +02:00
Karsa
acb0e37ac2 fix(icons): changed shovel icon (#3458)
* [github] Added issue template forms

* [github] yaml => yml

* Syntax fixes

* Further syntax fixes

* Sort issue templates

* Update 02_bug_report.yml

* Updated icons/shovel.svg

* Updated icons/shovel.json

---------

Co-authored-by: Karsa <karsa@karsa.org>
2025-07-28 13:46:09 +02:00
Karsa
6c25cbee67 feat(icons): added hat-glasses icon (#3431)
* [github] Added issue template forms

* [github] yaml => yml

* Syntax fixes

* Further syntax fixes

* Sort issue templates

* Update 02_bug_report.yml

* Added icons/hat-glasses.svg

* Added icons/hat-glasses.json

---------

Co-authored-by: Karsa <karsa@karsa.org>
2025-07-28 10:41:43 +02:00
Jamie Law
08121af31e fix(icons): arcify arrow-big- icons (#3356)
* fix(icons): arcify `arrow-big-` icons

* give correct attribution

* shorten stems of `arrow-big-*-dash` icons
2025-07-28 10:30:59 +02:00
Karsa
2d484469cd fix(icons): changed sparkles icon (#3426)
* [github] Added issue template forms

* [github] yaml => yml

* Syntax fixes

* Further syntax fixes

* Sort issue templates

* Update 02_bug_report.yml

* Updated icons/sparkle.svg

* Updated icons/sparkles.svg

* Updated icons/sparkles.json

---------

Co-authored-by: Karsa <karsa@karsa.org>
2025-07-28 10:24:37 +02:00
Karsa
f66d167ead fix(icons): arcified playback icons (#3425)
* fix(icons): arcified playback icons

* fix(icons): fix file-play name & file-video-2 => file-video-camera

* fix(icons): add `stop` tags to square

* fix(icons): fix lines on skip icons

---------

Co-authored-by: Jakob Guddas <github@jguddas.de>
2025-07-28 10:17:44 +02:00
Jamie Law
c63e5a456e fix(icons): changed sprout icon (#3430)
* Updated icons/sprout.svg

* Updated icons/sprout.json

* Add a bend to the sprout stalk

* Add rounding to the leaf tip
2025-07-28 10:11:49 +02:00
Karsa
aac12f991a fix(icons): changed croissant icon (#3428)
* [github] Added issue template forms

* [github] yaml => yml

* Syntax fixes

* Further syntax fixes

* Sort issue templates

* Update 02_bug_report.yml

* Updated icons/croissant.svg

---------

Co-authored-by: Karsa <karsa@karsa.org>
2025-07-28 10:11:12 +02:00
Jamie Law
3844a4b346 Refine refresh-ccw-dot to match other refresh- icons (#3437)
Also update attributions to include contributors to `refresh-ccw`, ignoring optimisations
2025-07-28 10:10:41 +02:00
Karsa
98b43826a4 fix(icons): changed gem icon (#3442)
* [github] Added issue template forms

* [github] yaml => yml

* Syntax fixes

* Further syntax fixes

* Sort issue templates

* Update 02_bug_report.yml

* Updated icons/gem.svg

* Updated icons/gem.json

* Update gem.svg

* Update gem.svg

* Update gem.json

---------

Co-authored-by: Karsa <karsa@karsa.org>
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-07-28 10:10:14 +02:00
Jakob Guddas
e8194cbabe fix(icons): changed lasso icon (#3454)
* Updated icons/lasso.svg

* Updated icons/lasso.json
2025-07-28 09:57:12 +02:00
Kyle Angelo Galendez
17bbbe1cbb feat(icons): added spotlight icon (#3450)
* Added icons/spotlight.svg

* Added icons/spotlight.json
2025-07-28 09:56:11 +02:00
Jakob Guddas
c698121170 Updated icons/monitor-dot.svg (#3452) 2025-07-28 09:01:20 +02:00
Luis David Lopera
7a380a8177 feat(icons): added wifi-sync icon (#3132) 2025-07-26 21:04:49 +02:00
Karsa
55623e1611 fix(icons): changed magnet icon (#3435)
* [github] Added issue template forms

* [github] yaml => yml

* Syntax fixes

* Further syntax fixes

* Sort issue templates

* Update 02_bug_report.yml

* Updated icons/magnet.svg

---------

Co-authored-by: Karsa <karsa@karsa.org>
2025-07-22 18:47:49 +02:00
Karsa
803d237fef fix(icons): fixed ban icon (#3434)
* [github] Added issue template forms

* [github] yaml => yml

* Syntax fixes

* Further syntax fixes

* Sort issue templates

* Update 02_bug_report.yml

* Updated icons/ban.svg

* Updated icons/ban.json

---------

Co-authored-by: Karsa <karsa@karsa.org>
2025-07-22 18:35:40 +02:00
Jakob Guddas
151bd33cd7 fix(icons): changed pen-line and pencil-line icon (#2663)
* Updated icons/pencil-line.svg

* Updated icons/pencil-line.json

* Updated icons/pen-line.svg

* Updated icons/pen-line.json

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-07-21 18:01:47 +02:00
Karsa
b3b8cfd59c fix(icons): changed trash icon (#3394)
* [github] Added issue template forms

* [github] yaml => yml

* Syntax fixes

* Further syntax fixes

* Sort issue templates

* Update 02_bug_report.yml

* Updated icons/trash-2.svg

* Updated icons/trash.svg

---------

Co-authored-by: Karsa <karsa@karsa.org>
2025-07-07 09:12:51 +02:00
Jakob Guddas
996fc68c7e fix(icons): changed podcast icon (#3385)
* Updated icons/podcast.svg

* Updated icons/podcast.json

* Update icons/podcast.svg

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-07-04 17:34:00 +02:00
Eric Fennis
69e99093b8 ci(suggest-tags): Fix types import 2025-07-04 17:32:32 +02:00
Eric Fennis
030c7b9cb9 Merge branch 'main' of https://github.com/lucide-icons/lucide 2025-07-04 17:27:11 +02:00
Eric Fennis
6f384680b4 ci(suggest-tags): Fix failing job 2025-07-04 17:27:07 +02:00
Jakob Guddas
e810b7273f fix(icons): changed cable icon (#3374)
* Updated icons/cable.svg

* Update cable.svg

* Update cable.svg

* Updated icons/cable.svg

* Updated icons/cable.json

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-07-04 17:12:42 +02:00
Jamie Law
599c517188 fix(icons): changed brain icon (#3381)
* Updated icons/brain.svg

* Updated icons/brain.json
2025-07-04 17:06:45 +02:00
Jamie Law
95830a0d88 Add Turkish lira icons (#2873)
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-06-30 21:59:19 +02:00
Eric Fennis
5f0c19afaa ci(suggest-tags): Allow to always run 2025-06-30 21:50:58 +02:00
Eric Fennis
37135af384 ci(suggest-tags): Switch to pull_request_target with suggest tags 2025-06-30 21:49:06 +02:00
Jakob Guddas
0fdc2413f6 Updated icons/circle-parking-off.svg (#3343) 2025-06-30 20:35:00 +02:00
Eric Fennis
d565b951d7 ci(suggest-tags): Fix tags suggestions 2025-06-30 20:25:12 +02:00
Itrulia
3e7d748827 docs(site): small changes for DynamicIcon (#3355)
* docs(lucide-react): small changes for DynamicIcon

Changed the sentence structure slightly for DynamicIcon to be easier to understand.

* Update lucide-react.md
2025-06-27 17:13:44 +02:00
Eric Fennis
0e54626bdb ci(icons): Add ChatGPT tags suggestions on icon PRs (#3372)
* Add gpt tags

* Add github actions flow

* Add link so people can use the chat

* Fix workflow

* Add openai dep

* Add actions core

* Try gh pr review in actions

* Try with octokit

* Remove changed files part

* Try with createReview function

* Try this

* fix broken json file

* Turn on review by gh action

* Try this

* Update icons/trash.json

* Update the runner

* Remove added tags

* Add more checks
2025-06-27 17:11:00 +02:00
Alex Naskidashvili
110d8cce27 feat(icons): added tool-case icon (#3333)
* Added icons/tool-case.svg

* Added icons/tool-case.json

* Update tool-case.svg

slightly alter the icon SVG code to match with the "archive" icon

* Update tool-case.svg

* Optimized version of tool-case.svg

* Update icons/tool-case.json

Co-authored-by: Jakob Guddas <github@jguddas.de>

* Update tool-case.svg

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
Co-authored-by: Jakob Guddas <github@jguddas.de>
2025-06-27 16:51:29 +02:00
Jamie Law
9db00bfe3c fix(icons): changed flag icon (#3370)
* Updated icons/flag.svg

* Updated icons/flag.json

* Update flag-off.svg

Applied same arcification to `flag-off`

* Update flag-off.json

Update attribute and add tags to `flag-off`
2025-06-27 12:11:16 +02:00
Zishan Kadri
e21d0e2dd6 fix(icons): changed sun-moon icon (#3328)
* Updated icons/sun-moon.svg

* Updated icons/sun-moon.json

* Updated icons/sun-moon.svg

* Updated icons/sun-moon.svg

* Update icons/sun-moon.json

Fix tag listing format to match original code style

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>

* Updated icons/sun-moon.svg

* Updated icons/sun-moon.svg

* Updated icons/sun-moon.json

* Updated icons/sun-moon.svg

* Updated icons/sun-moon.json

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
Co-authored-by: Jakob Guddas <github@jguddas.de>
2025-06-27 11:39:31 +02:00
Jakob Guddas
483a594622 fix(icons): arcified shrub icon (#2292)
* Updated icons/shrub.svg

* Updated icons/shrub.json

* Updated icons/shrub.svg

* Updated icons/shrub.svg

* Updated icons/shrub.svg

* Updated icons/shrub.svg

* Updated icons/shrub.svg

* Updated icons/shrub.svg

* Updated icons/shrub.svg

* Update icons/shrub.svg

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-06-27 11:39:04 +02:00
Jamie Law
2136843515 fix(icons): Changed megaphone icon (#2448)
* Lucidify megaphone.svg

Rounded the corners and increased the gap inside the handle

* Update megaphone.json

* Add bullhorn.json

* Add bullhorn.svg

* Lucidify megaphone-off.svg

* Update megaphone-off.json

* Add bullhorn-off.svg

* Add bullhorn-off.json

* Delete icons/megaphone-off.svg

* Delete icons/megaphone-off.json

* Delete icons/megaphone.svg

* Delete icons/megaphone.json

* Rename `bullhorn` to `megaphone`

* Update icons/megaphone-off.svg

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
Co-authored-by: Jakob Guddas <github@jguddas.de>
2025-06-27 11:36:48 +02:00
Kyle Angelo Galendez
ad842796dc feat(icons): added vector-square icon (#2531)
* Rename box-select.svg to square-dashed.svg

* Update and rename box-select.json to square-dashed.json

* Add aliases

* Added icons/vector-square.svg

* Added icons/vector-square.json

* Update vector-square.svg

* Add contributor

* Update icons/vector-square.json

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-06-27 11:35:27 +02:00
Jamie Law
f6a7eadd99 fix(icons): Updates to clock-related icons (#2538)
* Update base `clock`-icons

Replace `<polyline/>`s with `<path/>`s, and adjust the angles of the 2-, 4-, 8- and 10 o'clock hour hands to better match the 1-, 5-, 7- and 11 o'clock hands

* Update clocks with symbols

Update `clock-arrow-down`, `clock-arrow-up` and `clock-alert` to match the hand angle of `clock`

Also align the exclamation mark of `clock-alert` to the pixel grid

* Update small clock faces

Make the clock faces in `calendar-clock`, `file-clock`, `folder-clock` and `watch` consistent with each other, and resolve some two-pixel-gap violations.

* Align the tips of all hours hands on large clocks to the grid

* Add self-attribution where appropriate

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-06-27 11:34:16 +02:00
Jamie Law
f51a01a7d0 Updated icons/ligature.svg (#3369) 2025-06-27 11:27:03 +02:00
Jakob Guddas
30ed887ad1 fix(icons): changed dessert icon (#3362)
* Updated icons/dessert.svg

* Updated icons/dessert.json
2025-06-27 10:08:12 +02:00
Karsa
a87ac013f7 docs(packages/lucide-static): update docs for better clarity (#3225)
* docs(packages/lucide-static): update docs for better clarity

* docs(packages/lucide-static): fix node.js heading level
2025-06-24 13:18:07 +02:00
Jakob Guddas
87a67cc314 feat(icons): added bottle-wine icon (#3265)
* Added icons/bottle-wine.svg

* Added icons/bottle-wine.json

* Updated icons/bottle-wine.svg

* Updated icons/bottle-wine.json

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-06-24 13:03:03 +02:00
Kyle Angelo Galendez
e418be64a1 feat(icons): add line-squiggle icon (#2393)
* Added icons/wavy-line.svg

* Added icons/wavy-line.json

* Updated icons/wavy-line.json

* Updated icons/wavy-line.json

* Rename wavy-line.json to line-curvy.json

* Rename wavy-line.svg to line-curvy.svg

* Rename line-curvy.json to line-swirly.json

* Rename line-curvy.svg to line-swirly.svg

* Rename line-swirly.json to line-squiggle.json

* Rename line-swirly.svg to line-squiggle.svg

* Align the icon in the center

* Update icon visually

* Circle the loop of the icon

* Add contributor

* Add `"annotate"` as tag

After seeing Google Chromebook the same icon, it says 'annotate'. I add it here as a tag.

* Update line-squiggle.json

* Update line-squiggle.json

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-06-21 08:50:54 +02:00
Jakob Guddas
0ca76a1de1 Updated icons/shopping-bag.svg (#2483)
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-06-21 08:48:24 +02:00
Jakob Guddas
be81f20a6b docs(design-guide): added bad examples for 2px rule (#2879) 2025-06-21 08:47:28 +02:00
Aram Soneson
41d32b8f40 feat(icons): added squircle-dashed icon (#3262)
* Added icons/squircle-dashed.svg

* Added icons/squircle-dashed.json

* Update squircle-dashed.svg
2025-06-21 08:47:07 +02:00
Muhammad Tayyab Sheikh
c543914fb0 fix(icons): ensure shaft overlaps arrowhead in circle-arrow-left and circle-arrow-right (#3269)
Reordered SVG paths so the shaft renders after the arrowhead, eliminating visual gaps due to stroke rendering.

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-06-21 08:32:36 +02:00
Vladimir Kirilenko
40d9e44b03 feat(icons): added georgian-lari icon (#3323)
* Added icons/georgian-lari.svg

* Added icons/georgian-lari.json
2025-06-21 08:31:53 +02:00
Karsa
5821c62880 feat(icons): added spool icon (#2926)
* [github] Added issue template forms

* [github] yaml => yml

* Syntax fixes

* Further syntax fixes

* Sort issue templates

* Update 02_bug_report.yml

* Added icons/spool.svg

* Added icons/spool.json

---------

Co-authored-by: Karsa <karsa@karsa.org>
2025-06-20 22:27:20 +02:00
Jakob Guddas
d15973e7ef fix(icons): changed blocks icon (#3267)
* Updated icons/blocks.svg

* Updated icons/blocks.json

* Updated icons/blocks.svg

* Updated icons/blocks.svg

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-06-19 22:26:53 +02:00
Eric Fennis
475439bca9 fix(docs): Add ts ext to files missing files 2025-06-19 16:46:10 +02:00
Eric Fennis
13fc99f80d fix(docs): Add TS extention to index file 2025-06-19 16:39:23 +02:00
Eric Fennis
6a49a51164 fix(docs): Fix site build 2025-06-19 16:31:36 +02:00
Eric Fennis
ba5a701faf ci(lint): Fix failing gh actions 2025-06-19 16:26:57 +02:00
shopped
9e74dbb617 feat(icons): added drone icon (#3247)
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
Co-authored-by: Karsa <contact@karsa.org>
2025-06-19 08:54:03 +02:00
Eric Fennis
3e644fda2d chore(scripts): Refactor scripts to typescript (#3316)
* Adjust typescript types

* adjust types

* fix types in all helper files

* Fix types

* Migrate js files to ts files

* Refactor to TS files

* Rename extentions

* Adjust imports

* Fix builds

* Update lockfile

* Fix last typescript migration

* Fix entry path @lucide/outline-svg

* Fix types

* add checkout step

* format files

* Format files
2025-06-18 15:47:24 +02:00
Jakob Guddas
7517894f2d fix(icons): renamed *-help and *-question icons to *-question-mark (#2540)
* fix(icons): renamed `*-help` icons

* fix(icons): renamed `*-question` icons

* Add EOF

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-06-18 10:01:41 +02:00
Jamie Law
5e363f48af Updated egg and egg-off icons (#3299)
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-06-18 09:27:59 +02:00
Karsa
537040bf81 feat(icons): adds barrel icon (#1955)
* feat(icons): adds barrel icon

* feat(icons): add maps category to barrel

* feat(icons): add brewery tags to barrel

* feat(icons): updates barrel design with 90deg rotated version, added jamiemlaw as a contributor

---------

Co-authored-by: Karsa <karsa@sztaki.hu>
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
Co-authored-by: Jakob Guddas <github@jguddas.de>
2025-06-18 09:19:20 +02:00
Luis David Lopera
308e738e0e feat(icons): added wifi-cog icon (#3133)
* Added icons/wifi-cog.svg

* Added icons/wifi-cog.json
2025-06-16 22:14:00 +02:00
Epifânio Francisco
e16e8b2783 fix: remove flex-direction CSS causing absolute stroke width toggle positioning issue (#3308) (#3317)
- Remove 'flex-direction: row-reverse' CSS rule for #absolute-stroke-width
- Fixes toggle moving out of bounds and being pseudo active

Closes #3308
2025-06-16 22:10:56 +02:00
Epifânio Francisco
e7f03df844 perf(docs): optimize IconsOverview initial render - 50% faster load times (#3282)
- Reduce initial icon render from 1600 to 120-200 icons
- Implement dynamic initialGridItems calculation based on viewport
- Use slice() instead of splice() to avoid array mutation
- Fix NoResults condition to use searchResults.length
- Load time improved from (50-60%+ improvement)
2025-06-16 22:08:44 +02:00
Pranav Nedungadi
8aed9b826d fix: some typos (#3319)
Signed-off-by: Pranav <pranav10121@gmail.com>
2025-06-14 11:58:54 +02:00
Jakob Guddas
e332d8b7ce fix(icons): changed radio icon (#3293)
* Updated icons/radio.svg

* Updated icons/radio.json

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-06-13 09:24:03 +02:00
Jamie Law
92aeac569f Updated icons/mic.svg (#3301)
Co-authored-by: Karsa <contact@karsa.org>
2025-06-13 09:22:50 +02:00
Jamie Law
53c16e191b Updated icons/pocket.svg (#3304) 2025-06-13 09:19:16 +02:00
Jamie Law
4791674365 Updated icons/stamp.svg (#3310) 2025-06-13 09:18:58 +02:00
Zefir
8527049827 feat(icons): added rectangle-circle icon (#3245)
* Added icons/rectangle-circle.svg

* Added icons/rectangle-circle.json

* reduce rectangle radius to 1px

Co-authored-by: Jakob Guddas <github@jguddas.de>

* added jguddas to rectangle-circle contributors

Co-authored-by: Jakob Guddas <github@jguddas.de>

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
Co-authored-by: Jakob Guddas <github@jguddas.de>
2025-06-13 09:12:16 +02:00
Jamie Law
8205ef68ed Updated icons/radiation.svg (#3303) 2025-06-11 14:55:06 +02:00
Jakob Guddas
f5c708adf2 Updated icons/house-plus.svg (#3238) 2025-06-10 20:08:48 +02:00
Domas Markevičius
d42539d2b4 feat(icons): added book-alert icon (#3249)
* Added icons/book-alert.json

* Added icons/book-alert.svg

* Updated icons/book-alert.svg

Indentation fix

* Updated icons/book-alert

* Updated icons/book-alert.svg

* Updated icons/book-alert.json

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-06-10 20:04:31 +02:00
Epifânio Francisco
3614b6b46e fix: absoluteStrokeWidth not resetting with Reset button (#3261)
- Fix Switch component to properly handle v-model binding
- Add absoluteStrokeWidth to customizingActive computed property
- Ensure absoluteStrokeWidth resets correctly in both Home and Sidebar customizers

Resolves issue where absoluteStrokeWidth toggle remained enabled
after clicking Reset button, causing confusion with bold icons.

Fixes #2984

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-06-10 19:50:23 +02:00
Jakob Guddas
86443054e2 fix(icons): changed settings-2 icon (#2974)
* Updated icons/settings-2.svg

* Updated icons/settings-2.json
2025-06-10 19:48:39 +02:00
Jamie Law
cc09c2640f Update bandage.json (#3300)
Added `patch` as a tag to `bandage` icon
2025-06-10 19:32:30 +02:00
Jakob Guddas
394399bfb8 fix(pull-request-icon-preview): pinned and --forced svgson installation 5.3.1 (#3289) 2025-06-10 19:32:06 +02:00
Jakob Guddas
af1b0e033e fix(icons): changed trophy icon (#2475)
* Updated icons/trophy.svg

* Updated icons/trophy.json

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-06-06 10:53:30 +02:00
Eric Fennis
bdb5f6c137 feat(icons): Add sim card icon from lab (#3275)
* Add sim card from lab

* Update icons/card-sim.svg

Co-authored-by: Karsa <contact@karsa.org>

* Update card-sim.json

---------

Co-authored-by: Karsa <contact@karsa.org>
2025-06-04 09:56:26 +02:00
Kuba Ratajczyk
d42a21610f feat(icons): added grid-3x2 icon (#3216)
* Added icons/grid-3x2.svg

* Added icons/grid-3x2.json

* Update grid-3x2.json

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-06-03 23:13:23 +02:00
Jakob Guddas
f5be7a9862 Updated icons/file-badge.svg (#2934) 2025-06-03 19:24:29 +02:00
Eric Fennis
52d408e6d0 Fixing vercel build 2025-06-02 21:06:33 +02:00
Jeevan
f69c760598 feat(angular): update peer dependencies for Angular to support version 20.x (#3273) 2025-06-01 08:22:28 +02:00
Nagesh Panchal
22a43cbdeb feat(icon): add id-card-lanyard icon (#2898)
* feat(icon): add id-card-with-lanyard icon

* Optimized svg

* Optimization and minor changes

* Renamed icon
2025-05-23 14:02:02 +02:00
Lennard Scheibel
1ca70df074 Add cloud-check icon (#2612) 2025-05-23 13:03:34 +02:00
Jakob Guddas
5514709fb3 fix(icons): changed eraser icon (#3076)
* Updated icons/eraser.svg

* Updated icons/eraser.json

* Updated icons/eraser.json

* Updated icons/eraser.svg

* Updated icons/eraser.svg

* Updated icons/eraser.svg
2025-05-23 13:03:21 +02:00
Jakob Guddas
34a8280ba8 fix(icons): changed paperclip icon (#2956)
* Updated icons/paperclip.svg

* Updated icons/paperclip.json

* Updated icons/paperclip.svg

* Updated icons/paperclip.svg

* Updated icons/paperclip.svg

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-05-23 13:02:05 +02:00
Jakob Guddas
f814c94672 fix(icons): changed battery-* icons (#3083)
* Updated icons/battery-full.svg

* Updated icons/battery-full.json

* Updated icons/battery.svg

* Updated icons/battery.json

* Updated icons/battery-charging.svg

* Updated icons/battery-charging.json

* Updated icons/battery-plus.svg

* Updated icons/battery-warning.svg

* Updated icons/battery-warning.json

* Updated icons/battery-medium.svg

* Updated icons/battery-medium.json

* Updated icons/battery-low.svg

* Updated icons/battery-low.json

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-05-23 12:35:36 +02:00
Jakob Guddas
e1cfbe14be fix(icons): changed list-collapse icon (#3081)
* Updated icons/list-collapse.svg

* Updated icons/list-collapse.json
2025-05-23 12:35:03 +02:00
Jakob Guddas
a549fd65ef fix(icons): changed square-check-big icon (#3156)
* Updated icons/square-check-big.svg

* Updated icons/square-check-big.json

* Updated icons/square-check-big.svg
2025-05-23 12:34:25 +02:00
dependabot[bot]
239bbd9a9a build(deps-dev): bump vite from 6.0.7 to 6.1.6 (#3236)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 6.0.7 to 6.1.6.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v6.1.6/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v6.1.6/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 6.1.6
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-23 12:32:16 +02:00
Brizen Britt
85b6de6684 docs(docs): added testing website locally instructions (#3124)
* added testing website

* added build command

* resolve suggestions
2025-05-23 12:11:20 +02:00
dependabot[bot]
19fa01b5fc build(deps-dev): bump vite from 6.3.2 to 6.3.4 (#3181)
* build(deps-dev): bump vite from 6.3.2 to 6.3.4

Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 6.3.2 to 6.3.4.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v6.3.4/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 6.3.4
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update vitest as well

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-05-16 14:25:07 +02:00
LieOnLion
e3e6dd9ccc feat(icons): added circle-pound-sterling icon (#2822)
* Added icons/circle-pound-sterling.svg

* Added icons/circle-pound-sterling.json

* Update icons/circle-pound-sterling.json

Co-authored-by: Jakob Guddas <github@jguddas.de>

---------

Co-authored-by: Jakob Guddas <github@jguddas.de>
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-05-16 14:06:04 +02:00
1427 changed files with 24936 additions and 11247 deletions

View File

@@ -9,9 +9,3 @@ strikethrough
touchpad
ungroup
toc
# Brands
codepen
codesandbox
dribbble
x.com

View File

@@ -7,8 +7,10 @@ body:
value: |
Before submitting an icon request check if it has already been requested. If there is an open request, please add a 👍.
**Important note**: No new brand logos are allowed, see https://github.com/lucide-icons/lucide/issues/670.
Existing brand icons will also be phased out. For brand icons, consider using https://simpleicons.org, which offers purpose-built SVGs that are also on a 24×24px grid.
> [!CAUTION]
> New brand logos are **not** allowed, see our official statement: https://github.com/lucide-icons/lucide/blob/main/BRAND_LOGOS_STATEMENT.md.
>
> Existing brand icons are being phased out. Consider using https://simpleicons.org, which offers purpose-built SVGs that are also on a 24×24px grid.
- type: input
id: name
attributes:
@@ -41,9 +43,9 @@ body:
required: true
- label: I have searched existing icons to make sure it does not already exist and I didn't find any.
required: true
- label: I am not requesting a brand logo and the art is not protected by copyright.
- label: I am not requesting a brand logo and the art is not protected by copyright, see more at https://github.com/lucide-icons/lucide/blob/main/BRAND_LOGOS_STATEMENT.md
required: true
- label: I am not requesting an icon that includes religious, political imagery or hate symbols.
- label: I am not requesting an icon that includes religious, war/violence related, political imagery or hate symbols.
required: true
- label: I have provided appropriate use cases for the icon(s) requested.
required: true

View File

@@ -25,10 +25,12 @@ Common scopes: icons, docs, studio, site, dev
### Concept <!-- ONLY for new icons -->
<!-- All of these requirements must be fulfilled. -->
<!-- IMPORTANT! Please read our official statement on brand logos in Lucide: -->
<!-- https://github.com/lucide-icons/lucide/blob/main/BRAND_LOGOS_STATEMENT.md -->
- [ ] I have provided valid use cases for each icon.
- [ ] I have not added any a brand or logo icon.
- [ ] I have [not added any brand or logo icon](https://github.com/lucide-icons/lucide/blob/main/BRAND_LOGOS_STATEMENT.md).
- [ ] I have not used any hate symbols.
- [ ] I have not included any religious or political imagery.
- [ ] I have not included any religious, war/violence related or political imagery.
### Author, credits & license<!-- ONLY for new icons. -->
<!-- Please choose one of the following, and put an "x" next to it. -->

View File

@@ -7,6 +7,10 @@ on:
paths:
- icons/**/*.svg
permissions:
id-token: write # Required for OIDC
contents: read
jobs:
create-release:
if: github.repository == 'lucide-icons/lucide' && startsWith(github.event.head_commit.message, 'feat(icons)')
@@ -15,9 +19,9 @@ jobs:
VERSION: ${{ steps.new-version.outputs.NEW_VERSION }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'
@@ -68,7 +72,7 @@ jobs:
if: github.repository == 'lucide-icons/lucide'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Semantic Release
id: semantic

View File

@@ -11,12 +11,12 @@ jobs:
issues: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Check for blocked phrases in issue title
run: |
ISSUE_TITLE=$(jq -r '.issue.title' "$GITHUB_EVENT_PATH")
BLOCKED_PHRASES=("twitter" "whatsapp" "logo" "google" "tiktok" "facebook" "slack" "discord" "bluesky" "spotify" "behance" "pix" "x.com")
BLOCKED_PHRASES=("twitter" "whatsapp" "logo" "google" "tiktok" "facebook" "slack" "discord" "bluesky" "spotify" "behance" "pix" "x.com" "telegram")
# Check title and body for blocked phrases
for PHRASE in "${BLOCKED_PHRASES[@]}"
@@ -24,7 +24,7 @@ jobs:
if echo "$ISSUE_TITLE" | grep -i "$PHRASE"; then
gh issue close ${{ github.event.issue.number }} --reason "not planned" --comment "This looks like a duplicate, use the [search](https://github.com/lucide-icons/lucide/issues?q=is%3Aissue+$PHRASE) to find similar issues.
Read more about brand guideline rules at [We're not accepting new Brand icons #670](https://github.com/lucide-icons/lucide/issues/670).
Read [our official statement about brand logos in Lucide](https://github.com/lucide-icons/lucide/blob/main/BRAND_LOGOS_STATEMENT.md).
Always happy to help on [Discord](https://discord.gg/EH6nSts)."
gh issue lock ${{ github.event.issue.number }} --reason spam

View File

@@ -9,4 +9,5 @@ jobs:
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/labeler@v5

View File

@@ -9,9 +9,9 @@ jobs:
lint-code:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'

View File

@@ -13,7 +13,10 @@ jobs:
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version-file: 'package.json'
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@v46
@@ -21,7 +24,7 @@ jobs:
files: icons/*
- name: Generate annotations
run: node ./scripts/lintFilenames.mjs
run: node ./scripts/lintFilenames.mts
env:
CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }}
@@ -31,6 +34,6 @@ jobs:
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Check Uniqueness of Aliases
run: "! cat <(printf \"%s\\n\" icons/*.json | while read -r name; do basename \"$name\" .json; done) <(jq -cr 'select(.aliases) | .aliases[] | if type==\"string\" then . else .name end' icons/*.json) | sort | uniq -c | grep -ve '^\\s*1 '"

View File

@@ -11,9 +11,9 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'
@@ -27,9 +27,9 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'

View File

@@ -12,11 +12,11 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v2
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
node-version: 18
node-version-file: 'package.json'
cache: 'pnpm'
- name: Install dependencies
@@ -32,7 +32,7 @@ jobs:
- uses: pnpm/action-setup@v2
- uses: actions/setup-node@v3.8.1
with:
node-version: 18
node-version-file: 'package.json'
cache: 'pnpm'
- name: Install dependencies

View File

@@ -10,9 +10,9 @@ jobs:
lucide-font:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'

View File

@@ -13,9 +13,9 @@ jobs:
lucide-preact:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'

View File

@@ -13,9 +13,9 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'
@@ -29,9 +29,9 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'

View File

@@ -14,9 +14,9 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'
@@ -30,9 +30,9 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'

View File

@@ -10,9 +10,9 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'

View File

@@ -13,9 +13,9 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'
@@ -29,9 +29,9 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'

View File

@@ -11,9 +11,9 @@ jobs:
lucide-static:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'

View File

@@ -13,9 +13,9 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'
@@ -29,9 +29,9 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'

View File

@@ -13,9 +13,9 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'
@@ -29,9 +29,9 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'

View File

@@ -13,9 +13,9 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'
@@ -29,9 +29,9 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'

43
.github/workflows/lucide-vue.yml vendored Normal file
View File

@@ -0,0 +1,43 @@
name: Lucide Vue checks
on:
pull_request:
paths:
- packages/lucide-vue/**
- packages/shared/**
- tools/build-icons/**
- tools/rollup-plugins/**
- pnpm-lock.yaml
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
cache: 'pnpm'
node-version-file: 'package.json'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build
run: pnpm --filter @lucide/vue build
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
cache: 'pnpm'
node-version-file: 'package.json'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Test
run: pnpm --filter @lucide/vue test

View File

@@ -12,9 +12,9 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'
@@ -28,9 +28,9 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'

View File

@@ -14,7 +14,10 @@ jobs:
pull-requests: write
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version-file: 'package.json'
- name: Get changed files
id: changed-files
@@ -22,9 +25,9 @@ jobs:
with:
files: icons/*.svg
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
- name: Install svgson for code preview (safer and faster than installing all deps)
run: npm install svgson
run: npm install svgson@5.3.1 --force
- name: Save PR number
run: |
@@ -32,7 +35,7 @@ jobs:
echo ${{ github.event.number }} > ./pr/NR
- name: Generate comment markup
run: node ./scripts/generateChangedIconsCommentMarkup.mjs >> ./pr/comment-markup.md
run: node ./scripts/generateChangedIconsCommentMarkup.mts >> ./pr/comment-markup.md
id: comment-markup
env:
CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }}

View File

@@ -0,0 +1,37 @@
name: Pull request tags suggestions
on:
pull_request_target:
paths:
- 'icons/*.json'
jobs:
pull-request-tags-suggestions:
name: Pull Request Tags Suggestions
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
# We checkout the main branch of main repository for security reasons.
# This is to prevent the workflow from running on a forked repository.
- uses: actions/checkout@v6
with:
repository: lucide-icons/lucide
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Generate comment markup
run: node ./scripts/suggestTags.mts
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
PULL_REQUEST_NUMBER: ${{ github.event.number }}
COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -18,9 +18,13 @@ on:
description: Version
required: true
permissions:
id-token: write # Required for OIDC
contents: read
jobs:
pre-release:
if: github.repository == 'lucide-icons/lucide' && contains('["ericfennis", "karsa-mistmere"]', github.actor)
if: github.repository == 'lucide-icons/lucide' && contains('["ericfennis", "karsa-mistmere", "jguddas"]', github.actor)
runs-on: ubuntu-latest
outputs:
VERSION: ${{ steps.get_version.outputs.VERSION }}
@@ -39,7 +43,8 @@ jobs:
runs-on: ubuntu-latest
needs: pre-release
permissions:
id-token: write
id-token: write # Required for OIDC
contents: read
strategy:
fail-fast: false
matrix:
@@ -48,7 +53,6 @@ jobs:
'lucide',
'lucide-react',
'lucide-react-native',
'lucide-vue',
'lucide-vue-next',
'lucide-angular',
'lucide-preact',
@@ -56,11 +60,12 @@ jobs:
'lucide-svelte',
'@lucide/astro',
'@lucide/svelte',
'@lucide/vue',
]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'
@@ -90,12 +95,14 @@ jobs:
runs-on: ubuntu-latest
needs: [pre-release, lucide-font]
permissions:
id-token: write
id-token: write # Required for OIDC
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: actions/download-artifact@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'
@@ -125,9 +132,9 @@ jobs:
runs-on: ubuntu-latest
needs: pre-release
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'
@@ -153,7 +160,7 @@ jobs:
needs: [pre-release, lucide-font]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: actions/download-artifact@v4
- name: Zip font and icons
run: |

View File

@@ -2,7 +2,8 @@ name: 'Request Review'
on:
pull_request_target:
types: [opened]
paths: icons/*.svg
paths:
- icons/*.svg
jobs:
request-review:
@@ -12,7 +13,7 @@ jobs:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
fetch-depth: 0
ref: refs/pull/${{ github.event.pull_request.number }}/merge

1
.gitignore vendored
View File

@@ -4,6 +4,7 @@
.obsidian
.now
.idea
.env
node_modules
dist
build

View File

@@ -2,5 +2,5 @@
"cSpell.words": ["devs", "preact", "Preact"],
"eslint.enable": true,
"eslint.validate": ["javascript", "svg"],
"svg.preview.background": "transparent"
"svg.preview.background": "editor"
}

View File

@@ -1,4 +1,5 @@
{
"$schema": "https://raw.githubusercontent.com/Yash-Singh1/vscode-snippets-json-schema/main/schema.json",
"Lucide SVG": {
"scope": "xml",
"description": "Base SVG with Lucide attributes.",
@@ -51,6 +52,16 @@
],
"body": "<circle cx=\"${2:12}\" cy=\"${3:$2}\" r=\"${1|10,2,.5\" fill=\"currentColor|}\" />"
},
"Squircle": {
"scope": "xml",
"description": "SVG `path` with Lucide defaults.",
"prefix": [
"squircle",
"path",
"<path"
],
"body": "<path d=\"M12 3c7.2 0 9 1.8 9 9s-1.8 9-9 9-9-1.8-9-9 1.8-9 9-9\" />"
},
"Ellipse": {
"scope": "xml",
"description": "SVG `ellipse`.",

71
BRAND_LOGOS_STATEMENT.md Normal file
View File

@@ -0,0 +1,71 @@
# Our Official Stance on Including Brand Logos in Lucide
## TL;DR
Lucide **does not accept** brand logos, and we do not plan to add them in the future.
This is due to a combination of **legal restrictions**, **design consistency concerns**, and **practical maintenance reasons**.
If you need brand logos, we recommend [Simple Icons](https://simpleicons.org/), which provides an extensive, legally safer collection of brand marks.
---
## 1. Historical Context
This is not a new debate — other icon sets have gone through the same discussion:
- **Material Design Icons** [deprecated all brand icons](https://github.com/Templarian/MaterialDesign/issues/6602) because they didn't fit the style, didn't work well in one color, and often looked out of place in a 24×24px grid.
- **Feather Icons** [came to the same conclusion](https://github.com/feathericons/feather/issues/763): brand logos have their own style, and forcing them into another inevitably leads to aesthetic compromises.
- **Lucide** learned from these examples — we'd rather focus on making a consistent set of non-brand icons that all work together.
## 2. Legal Considerations
Most brand logos:
- Are **protected by trademark or copyright**.
- Have **strict rules** for how they can be used (colors, spacing, proportions, etc.).
- **Don't allow modification** — but we'd have to change them to fit Lucide's style.
This means adding them could:
1. Break copyright or trademark law.
2. Make both you and the Lucide project legally responsible.
3. Force us to review every new request one by one for legal issues — something we simply can't do.
> **Note:** Simple Icons avoids this by keeping logos exactly as the brand provides them — though even they sometimes face [legal challenges](https://github.com/simple-icons/simple-icons/issues/11236).
## 3. Design & Consistency
Lucide is all about **visual consistency**.
Adding brand logos would:
- Break [our own design rules](https://lucide.dev/guide/design/icon-design-guide#icon-design-principles) for shapes, proportions, and stroke.
- Mix two fundamentally different categories of graphics (pictograms vs. corporate logos).
- Create a library where a subset of icons will always look "out of place".
If the logos are not in Lucide's style, why include them in Lucide at all? Better to use them from a dedicated brand icon source.
## 4. Maintenance Burden
Even with our current **"no brand icon requests"** policy, people still request them regularly.
Having any brand icons in the set:
- Makes people think we might add more in the future.
- Leads to repeated requests and the same conversations over and over.
- Wastes maintainer time redirecting people to the same explanation.
Removing them entirely solves this problem.
## 5. Recommended Alternatives
If you need brand icons, try:
- [Simple Icons](https://simpleicons.org/): offers a huge range of brands, in consistent SVG format, using a 24×24 viewBox, the same as ours.
- Official brand asset pages: most major companies provide downloadable SVGs.
You can use these alongside Lucide without bloating our core library.
## Final Words
Lucide is an **icon** set, not a **logo** set.
Logos belong in dedicated logo resources.
We're focusing on what Lucide does best: providing a clean, cohesive, and legally safe collection of open-source icons.

View File

@@ -161,6 +161,30 @@ lucide
The lucide.dev website is using [vitepress](https://vitepress.dev/) to generate the static website. The markdown files are located in the docs directory.
#### Running the Docs Website Locally
To test the docs website locally, follow these steps:
1. **Navigate to the docs directory**
```sh
cd docs
```
2. **Start the Local Development Server**
```sh
pnpm run docs:dev
```
3. **Open the Website Locally**
Vitepress should open with the following format:
VitePress dev server is running at:
- **Local**: `http://localhost:3000/`
- **Network**: `http://192.168.x.x:3000/`
### Guides
Detailed documentation about: installation, guides, packages, design guides etc.
@@ -173,8 +197,6 @@ All the icons of lucide in SVG format. These will be used as source for all the
Includes all the (npm) packages of lucide.
> Note: One package is not managed by pnpm: **lucide-flutter**, this package is written in Dart and uses pub for publishing.
### Scripts
Includes useful scripts to automate certain jobs. Big part of the scripts is the template generation, for example it generates icon components for all the packages. These scripts are usually executed from the "scripts" section in the package.json.

26
LICENSE
View File

@@ -1,6 +1,6 @@
ISC License
Copyright (c) for portions of Lucide are held by Cole Bemis 2013-2022 as part of Feather (MIT). All other copyright (c) for Lucide are held by Lucide Contributors 2022.
Copyright (c) for portions of Lucide are held by Cole Bemis 2013-2023 as part of Feather (MIT). All other copyright (c) for Lucide are held by Lucide Contributors 2025.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ -13,3 +13,27 @@ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---
The MIT License (MIT) (for portions derived from Feather)
Copyright (c) 2013-2023 Cole Bemis
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -57,6 +57,12 @@ For more info on how to contribute please see the [contribution guidelines](http
Caught a mistake or want to contribute to the documentation? [Edit this page on Github](https://github.com/lucide-icons/lucide/blob/main/README.md)
## About brand logos
Lucide **does not accept** brand logos, and we do not plan to add them in the future. This is due to a combination of **legal restrictions**, **design consistency concerns**, and **practical maintenance reasons**.
[Click here to read our official statement about brand logos in Lucide.](./BRAND_LOGOS_STATEMENT.md)
## Community
Join the community on our [Discord](https://discord.gg/EH6nSts) server!
@@ -84,5 +90,5 @@ Thank you to all the people who contributed to Lucide!
### Awesome backers 🍺
<a href="https://www.scipress.io?utm_source=lucide"><img src="docs/public/sponsors/scipress.svg" width="180" alt="Scipress sponsor badge" /></a>
<a href="https://github.com/pdfme/pdfme"><img src="docs/public/sponsors/pdfme.svg" width="180" alt="pdfme sponsor badge" /></a>
<a href="https://www.fina.money/"><img src="docs/public/sponsors/fina-money.png" width="180" alt="Fina Money sponsor badge" /></a>

View File

@@ -1,5 +0,0 @@
{
"$schema": "../category.schema.json",
"title": "Brands",
"icon": "facebook"
}

View File

@@ -1,5 +1,5 @@
{
"$schema": "../category.schema.json",
"title": "Navigation",
"title": "Navigation, Maps, and POIs",
"icon": "compass"
}

View File

@@ -13,10 +13,12 @@ export default eventHandler((event) => {
const data = pathData.at(-1).slice(0, -4);
const [name] = pathData;
const src = Buffer.from(data, 'base64')
.toString('utf8')
.replaceAll('\n', '')
.replace(/<svg[^>]*>|<\/svg>/g, '');
const src = Buffer.from(data, 'base64').toString('utf8').replaceAll('\n', '');
const width = parseInt((src.includes('<svg ') ? src.match(/width="(\d+)"/)?.[1] : null) ?? '24');
const height = parseInt(
(src.includes('<svg ') ? src.match(/height="(\d+)"/)?.[1] : null) ?? '24',
);
const children = [];
@@ -38,7 +40,7 @@ export default eventHandler((event) => {
children.push(
createElement(Backdrop, {
backdropString,
src,
src: src.replace(/<svg[^>]*>|<\/svg>/g, ''),
color: '#777',
}),
);
@@ -46,7 +48,18 @@ export default eventHandler((event) => {
const svg = Buffer.from(
// We can't use jsx here, is not supported here by nitro.
renderToString(createElement(SvgPreview, { src, showGrid: true }, children)),
renderToString(
createElement(
SvgPreview,
{
src: src.replace(/<svg[^>]*>|<\/svg>/g, ''),
height,
width,
showGrid: true,
},
children,
),
),
).toString('utf8');
defaultContentType(event, 'image/svg+xml');

View File

@@ -17,6 +17,13 @@ export default eventHandler((event) => {
.replaceAll('\n', '')
.replace(/<svg[^>]*>|<\/svg>/g, '');
const width = parseInt(
(newSrc.includes('<svg ') ? newSrc.match(/width="(\d+)"/)?.[1] : null) ?? '24',
);
const height = parseInt(
(newSrc.includes('<svg ') ? newSrc.match(/height="(\d+)"/)?.[1] : null) ?? '24',
);
const children = [];
const oldSrc = iconNodes[name]
@@ -27,7 +34,9 @@ export default eventHandler((event) => {
const svg = Buffer.from(
// We can't use jsx here, is not supported here by nitro.
renderToString(createElement(Diff, { oldSrc, newSrc, showGrid: true }, children)),
renderToString(
createElement(Diff, { oldSrc, newSrc, showGrid: true, height, width }, children),
),
).toString('utf8');
defaultContentType(event, 'image/svg+xml');

View File

@@ -1,6 +1,10 @@
import { eventHandler, setResponseHeader, defaultContentType } from 'h3';
import { Resvg, initWasm } from '@resvg/resvg-wasm';
import iconNodes from '../../../data/iconNodes';
import wasm from './loadWasm';
import { createElement } from 'react';
import { renderToStaticMarkup } from 'react-dom/server';
import createLucideIcon from 'lucide-react/src/createLucideIcon';
var initializedResvg = initWasm(wasm);
@@ -9,27 +13,37 @@ export default eventHandler(async (event) => {
await initializedResvg;
const imageSize = 96;
const [iconSizeString, svgData] = params.data.split('/');
const name = params.data.split('/').at(-3);
const iconSizeString = params.data.split('/').at(-2);
const svgData = params.data.split('/').at(-1);
const iconSize = parseInt(iconSizeString, 10);
const data = svgData.slice(0, -4);
const prevSvg = iconNodes[name]
? renderToStaticMarkup(createElement(createLucideIcon(name, iconNodes[name])))
: undefined;
const src = Buffer.from(data, 'base64').toString('utf8');
const svg = (src.includes('<svg') ? src : `<svg>${src}</svg>`)
.replace(/(\r\n|\n|\r)/gm, '')
.replace(
/<svg[^>]*/,
/<svg[^>]*>/,
`<svg
xmlns="http://www.w3.org/2000/svg"
width="${iconSize}"
height="${iconSize}"
viewBox="0 0 24 24"
height="${prevSvg ? iconSize * 2 : iconSize}"
viewBox="0 0 24 ${prevSvg ? 48 : 24}"
fill="none"
stroke="#fff"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
${prevSvg?.replaceAll('\n', '').replace(/<svg[^>]*>|<\/svg>/g, '')}
<g transform="translate(0, ${prevSvg ? 24 : 0})">
`,
);
)
.replace(/<\/svg>/, '</g></svg>');
const resvg = new Resvg(svg, { background: '#000' });
const pngData = resvg.render();
@@ -39,7 +53,7 @@ export default eventHandler(async (event) => {
setResponseHeader(event, 'Cache-Control', 'public,max-age=31536000');
return `
<svg xmlns="http://www.w3.org/2000/svg" width="${imageSize}" height="${imageSize}" viewBox="0 0 ${imageSize} ${imageSize}">
<svg xmlns="http://www.w3.org/2000/svg" width="${imageSize}" height="${prevSvg ? imageSize * 2 : imageSize}" viewBox="0 0 ${imageSize} ${prevSvg ? imageSize * 2 : imageSize}">
<style>
@media screen and (prefers-color-scheme: light) {
#fallback-background { fill: transparent; }
@@ -52,20 +66,20 @@ export default eventHandler(async (event) => {
<mask id="mask">
<image
width="${imageSize}"
height="${imageSize}"
height="${prevSvg ? imageSize * 2 : imageSize}"
href="data:image/png;base64,${pngBuffer.toString('base64')}"
image-rendering="pixelated"
/>
</mask>
<rect
id="fallback-background"
width="${imageSize}"
height="${imageSize}" ry="${imageSize / 24}"
width="100%"
height="100%" ry="${imageSize / 24}"
fill="#fff"
/>
<rect
width="${imageSize}"
height="${imageSize}"
width="100%"
height="100%"
fill="#000"
mask="url(#mask)"
/>

View File

@@ -9,6 +9,7 @@ if (process.env.NODE_ENV === 'development') {
wasm = fs.readFileSync(require.resolve('@resvg/resvg-wasm/index_bg.wasm'));
} else {
// @ts-ignore
wasm = resvg_wasm;
}

View File

@@ -1,6 +1,10 @@
import { fileURLToPath, URL } from 'node:url';
import { defineConfig } from 'vitepress';
import { groupIconMdPlugin, groupIconVitePlugin } from 'vitepress-plugin-group-icons'
import container from 'markdown-it-container';
import { renderSandbox } from 'vitepress-plugin-sandpack';
import sidebar from './sidebar';
import snackPlayer from './plugins/snackPlayer';
const title = 'Lucide';
const socialTitle = 'Lucide Icons';
@@ -13,6 +17,19 @@ export default defineConfig({
cleanUrls: true,
outDir: '.vercel/output/static',
srcExclude: ['**/README.md'],
markdown: {
config(md) {
md.use(groupIconMdPlugin);
md.use(snackPlayer);
md.use(container, 'sandbox', {
render (tokens, idx) {
console.log(tokens);
return renderSandbox(tokens, idx, 'sandbox');
},
});
},
},
vite: {
resolve: {
alias: [
@@ -34,8 +51,18 @@ export default defineConfig({
},
],
},
plugins: [
groupIconVitePlugin()
],
},
head: [
[
'link',
{
rel: 'preconnect',
href: 'https://analytics.lucide.dev',
},
],
[
'script',
{

View File

@@ -15,10 +15,6 @@
"name": "arrows",
"title": "Arrows"
},
{
"name": "brands",
"title": "Brands"
},
{
"name": "buildings",
"title": "Buildings"
@@ -101,7 +97,7 @@
},
{
"name": "navigation",
"title": "Navigation"
"title": "Navigation, Maps, and POIs"
},
{
"name": "notifications",

View File

@@ -7,6 +7,14 @@
"dark": "/company-logos/vercel-dark.svg"
}
},
{
"name": "MDN Web Docs",
"url": "https://developer.mozilla.org/",
"image": {
"light": "/company-logos/mdn-light.svg",
"dark": "/company-logos/mdn-dark.svg"
}
},
{
"name": "Supabase",
"url": "https://supabase.com",
@@ -23,6 +31,14 @@
"dark": "/company-logos/obsidian-dark.svg"
}
},
{
"name": "Nuxt",
"url": "https://nuxt.com/",
"image": {
"light": "/company-logos/nuxt-light.svg",
"dark": "/company-logos/nuxt-dark.svg"
}
},
{
"name": "Open Collective",
"url": "https://opencollective.com",

View File

@@ -31,20 +31,12 @@
}
]
},
"lucide-vue-next": {
"@lucide/vue": {
"order": 2,
"icon": "vue-next",
"icon": "vue",
"docsAlias": "lucide-vue",
"packageDirname": "vue",
"shields": [
{
"alt": "npm",
"src": "https://img.shields.io/npm/v/lucide-vue-next",
"href": "https://www.npmjs.com/package/lucide-vue-next"
},
{
"alt": "npm",
"src": "https://img.shields.io/npm/dw/lucide-vue-next",
"href": "https://www.npmjs.com/package/lucide-vue-next"
}
]
},
"lucide-svelte": {

View File

@@ -77,8 +77,8 @@
"source": "https://github.com/swisnl/nuxt-lucide-icons",
"documentation": "https://github.com/swisnl/nuxt-lucide-icons/blob/main/README.md"
},
{
"name": "lucide-lustre",
{
"name": "lucide_lustre",
"description": "A library providing https://lucide.dev icons to lustre.",
"icon": "/framework-logos/lustre.webp",
"shields": [
@@ -95,5 +95,81 @@
],
"source": "https://github.com/dinkelspiel/lucide_lustre",
"documentation": "https://github.com/dinkelspiel/lucide_lustre/blob/master/README.md"
},
{
"name": "lucide_icons_flutter",
"description": "A library providing https://lucide.dev icons to Flutter.",
"icon": "/framework-logos/flutter.svg",
"shields": [
{
"alt": "Latest Stable Version",
"src": "https://img.shields.io/pub/v/lucide_icons_flutter",
"href": "https://pub.dev/packages/lucide_icons_flutter"
},
{
"alt": "Total Downloads",
"src": "https://img.shields.io/pub/dm/lucide_icons_flutter",
"href": "https://pub.dev/packages/lucide_icons_flutter"
}
],
"source": "https://github.com/vqh2602/lucide-flutter-main",
"documentation": "https://pub.dev/documentation/lucide_icons_flutter/latest/"
},
{
"name": "lucide-slint",
"description": "Implementation of the lucide icon library for Slint.",
"icon": "/framework-logos/slint.svg",
"shields": [
{
"alt": "Latest Stable Version",
"src": "https://img.shields.io/crates/v/lucide-slint",
"href": "https://crates.io/crates/lucide-slint"
},
{
"alt": "Total Downloads",
"src": "https://img.shields.io/crates/d/lucide-slint",
"href": "https://crates.io/crates/lucide-slint"
}
],
"source": "https://github.com/cnlancehu/lucide-slint",
"documentation": "https://github.com/cnlancehu/lucide-slint/blob/main/README.md"
},
{
"name": "lucide-go",
"description": "Implementation of Lucide icons for Go's html/template package.",
"icon": "/framework-logos/go.svg",
"shields": [
{
"alt": "Latest Stable Version",
"src": "https://img.shields.io/github/v/release/kaugesaar/lucide-go",
"href": "https://github.com/kaugesaar/lucide-go/releases"
},
{
"alt": "Go Reference",
"src": "https://pkg.go.dev/badge/github.com/kaugesaar/lucide-go.svg",
"href": "https://pkg.go.dev/github.com/kaugesaar/lucide-go"
}
],
"source": "https://github.com/kaugesaar/lucide-go",
"documentation": "https://github.com/kaugesaar/lucide-go/blob/master/README.md"
},
{
"name": "lucide-rails",
"description": "Ruby on Rails views helper method for rendering Lucide icons.",
"icon": "/framework-logos/rails.svg",
"shields": [
{
"alt": "Latest Stable Version",
"src": "https://img.shields.io/gem/v/lucide-rails",
"href": "https://rubygems.org/gems/lucide-rails"
},
{
"alt": "Total Downloads",
"src": "https://img.shields.io/gem/dt/lucide-rails",
"href": "https://rubygems.org/gems/lucide-rails"
}
],
"source": "https://github.com/heyvito/lucide-rails",
"documentation": "https://github.com/heyvito/lucide-rails/blob/master/README.md"
}
]

View File

@@ -53,8 +53,8 @@ const Backdrop = ({
<rect
x="0"
y="0"
width="24"
height="24"
width="100%"
height="100%"
fill="#fff"
stroke="none"
/>
@@ -67,8 +67,8 @@ const Backdrop = ({
<rect
x="0"
y="0"
width="24"
height="24"
width="100%"
height="100%"
opacity={0.5}
fill={`url(#pattern-${id})`}
stroke="none"

View File

@@ -7,15 +7,17 @@ const SvgPreview = React.forwardRef<
{
oldSrc: string;
newSrc: string;
height: number;
width: number;
} & React.SVGProps<SVGSVGElement>
>(({ oldSrc, newSrc, children, ...props }, ref) => {
>(({ oldSrc, newSrc, children, height, width, ...props }, ref) => {
return (
<svg
ref={ref}
xmlns="http://www.w3.org/2000/svg"
width={24}
height={24}
viewBox="0 0 24 24"
width={width}
height={height}
viewBox={`0 0 ${width} ${height}`}
fill="none"
stroke="currentColor"
strokeWidth={2}
@@ -25,6 +27,8 @@ const SvgPreview = React.forwardRef<
>
<style>{darkModeCss}</style>
<Grid
width={width}
height={height}
strokeWidth={0.1}
stroke="#777"
strokeOpacity={0.3}
@@ -37,8 +41,8 @@ const SvgPreview = React.forwardRef<
<rect
x="0"
y="0"
width="24"
height="24"
width="100%"
height="100%"
fill="#000"
stroke="none"
/>

View File

@@ -0,0 +1,137 @@
import React from 'react';
import pathToPoints from './path-to-points';
import { Path, PathProps } from './types';
export const GapViolationHighlight = ({
radius,
stroke,
strokeWidth,
strokeOpacity,
paths,
...props
}: {
paths: Path[];
} & PathProps<'stroke' | 'strokeOpacity' | 'strokeWidth', 'd'>) => {
const id = React.useId();
const groupedPaths = Object.entries(
paths.reduce(
(groups, val) => {
const key = val.c.id;
groups[key] = [...(groups[key] || []), val];
return groups;
},
{} as Record<number, Path[]>,
),
);
const groups: Group[] = [];
for (const [, paths] of groupedPaths) {
const d = paths.map((path) => path.d).join(' ');
const points = paths.flatMap((path) => pathToPoints(path));
groups.push({ id: d, points });
}
const mergedGroups = mergeGroups(groups, 2);
return (
<g {...props}>
<defs xmlns="http://www.w3.org/2000/svg">
<pattern
id={`backdrop-pattern-${id}`}
width=".1"
height=".1"
patternUnits="userSpaceOnUse"
patternTransform="rotate(45 50 50)"
>
<line
stroke={stroke}
strokeWidth={0.1}
y2={1}
/>
<line
stroke={stroke}
strokeWidth={0.1}
y2={1}
/>
</pattern>
</defs>
{mergedGroups.flatMap((ds, idx, arr) =>
arr.slice(0, idx).map((val, i) => (
<g
strokeWidth={strokeWidth}
key={i}
>
<mask
id={`svg-preview-backdrop-mask-${id}-${i}`}
maskUnits="userSpaceOnUse"
>
<path
stroke="white"
d={val.join(' ')}
/>
</mask>
<path
d={ds.join(' ')}
stroke={`url(#backdrop-pattern-${id})`}
strokeWidth={strokeWidth}
strokeOpacity={strokeOpacity}
mask={`url(#svg-preview-backdrop-mask-${id}-${i})`}
/>
</g>
)),
)}
</g>
);
};
type Point = { x: number; y: number };
type Group = { id: string; points: Point[] };
// Euclidean distance
function distance(a: Point, b: Point): number {
return Math.hypot(a.x - b.x, a.y - b.y);
}
// Check if two groups should be merged based on minimum distance
function shouldMerge(a: Group, b: Group, minDistance: number): boolean {
for (const pa of a.points) {
for (const pb of b.points) {
if (distance(pa, pb) <= minDistance) {
return true;
}
}
}
return false;
}
// Merge groups and return arrays of merged group IDs
function mergeGroups(groups: Group[], minDistance: number): string[][] {
const mergedGroups: Group[][] = groups.map((g) => [g]);
let changed = true;
while (changed) {
changed = false;
outer: for (let i = 0; i < mergedGroups.length; i++) {
for (let j = i + 1; j < mergedGroups.length; j++) {
// Check if any group in mergedGroups[i] should merge with any in mergedGroups[j]
if (
mergedGroups[i].some((ga) =>
mergedGroups[j].some((gb) => shouldMerge(ga, gb, minDistance)),
)
) {
// Merge group j into group i
mergedGroups[i] = [...mergedGroups[i], ...mergedGroups[j]];
mergedGroups.splice(j, 1);
changed = true;
break outer;
}
}
}
}
// Return only arrays of IDs
return mergedGroups.map((groupList) => groupList.map((g) => g.id));
}

View File

@@ -1,6 +1,7 @@
import React from 'react';
import { PathProps, Path } from './types';
import { getPaths, assert } from './utils';
import getPaths, { assert } from './utils';
import { GapViolationHighlight } from './GapViolationHighlight.tsx';
export const darkModeCss = `
@media screen and (prefers-color-scheme: light) {
@@ -20,10 +21,16 @@ export const darkModeCss = `
export const Grid = ({
radius,
fill = '#fff',
fill,
height,
width,
subGridSize = 0,
...props
}: {
height: number;
width: number;
strokeWidth: number;
subGridSize?: number;
radius: number;
} & PathProps<'stroke', 'strokeWidth'>) => (
<g
@@ -33,43 +40,53 @@ export const Grid = ({
>
<rect
className="svg-preview-grid-rect"
width={24 - props.strokeWidth}
height={24 - props.strokeWidth}
width={width - props.strokeWidth}
height={height - props.strokeWidth}
x={props.strokeWidth / 2}
y={props.strokeWidth / 2}
rx={radius}
fill={fill}
/>
<path
strokeDasharray={'0 0.1 ' + '0.1 0.15 '.repeat(11) + '0 0.15'}
strokeDasharray={
'0 0.1 ' + '0.1 0.15 '.repeat(subGridSize ? subGridSize * 4 - 1 : 95) + '0 0.15'
}
strokeWidth={0.1}
d={
props.d ||
new Array(Math.floor(24 - 1))
.fill(null)
.map((_, i) => i)
.filter((i) => i % 3 !== 2)
.flatMap((i) => [
`M${props.strokeWidth} ${i + 1}h${24 - props.strokeWidth * 2}`,
`M${i + 1} ${props.strokeWidth}v${24 - props.strokeWidth * 2}`,
])
.join('')
}
/>
<path
d={
props.d ||
new Array(Math.floor(24 - 1))
.fill(null)
.map((_, i) => i)
.filter((i) => i % 3 === 2)
.flatMap((i) => [
`M${props.strokeWidth} ${i + 1}h${24 - props.strokeWidth * 2}`,
`M${i + 1} ${props.strokeWidth}v${24 - props.strokeWidth * 2}`,
])
.join('')
[
...new Array(Math.floor(width - 1))
.fill(null)
.map((_, i) => i)
.filter((i) => !subGridSize || i % subGridSize !== subGridSize - 1)
.flatMap((i) => [`M${i + 1} ${props.strokeWidth}v${height - props.strokeWidth * 2}`]),
...new Array(Math.floor(height - 1))
.fill(null)
.map((_, i) => i)
.filter((i) => !subGridSize || i % subGridSize !== subGridSize - 1)
.flatMap((i) => [`M${props.strokeWidth} ${i + 1}h${width - props.strokeWidth * 2}`]),
].join('')
}
/>
{!!subGridSize && (
<path
d={
props.d ||
[
...new Array(Math.floor(width - 1))
.fill(null)
.map((_, i) => i)
.filter((i) => i % subGridSize === subGridSize - 1)
.flatMap((i) => [`M${i + 1} ${props.strokeWidth}v${height - props.strokeWidth * 2}`]),
...new Array(Math.floor(height - 1))
.fill(null)
.map((_, i) => i)
.filter((i) => i % subGridSize === subGridSize - 1)
.flatMap((i) => [`M${props.strokeWidth} ${i + 1}h${width - props.strokeWidth * 2}`]),
].join('')
}
/>
)}
</g>
);
@@ -99,6 +116,7 @@ const Shadow = ({
>
{groupedPaths.map(([id, paths]) => (
<mask
key={`svg-preview-shadow-mask-${id}`}
id={`svg-preview-shadow-mask-${id}`}
maskUnits="userSpaceOnUse"
strokeOpacity="1"
@@ -108,8 +126,8 @@ const Shadow = ({
<rect
x={0}
y={0}
width={24}
height={24}
width="100%"
height="100%"
fill="#fff"
stroke="none"
rx={radius}
@@ -152,30 +170,34 @@ const ColoredPath = ({
colors,
paths,
...props
}: { paths: Path[]; colors: string[] } & PathProps<never, 'd' | 'stroke'>) => (
<g
className="svg-preview-colored-path-group"
{...props}
>
{paths.map(({ d, c }, i) => (
<path
key={i}
d={d}
stroke={colors[(c.name === 'path' ? i : c.id) % colors.length]}
/>
))}
</g>
);
}: { paths: Path[]; colors: string[] } & PathProps<never, 'd' | 'stroke'>) => {
let idx = 0;
return (
<g
className="svg-preview-colored-path-group"
{...props}
>
{paths.map(({ d, c }, i) => (
<path
key={i}
d={d}
stroke={colors[(c.name === 'path' ? idx++ : c.id) % colors.length]}
/>
))}
</g>
);
};
const ControlPath = ({
paths,
radius,
pointSize,
...props
}: { pointSize: number; paths: Path[]; radius: number } & PathProps<
'stroke' | 'strokeWidth',
'd'
>) => {
}: {
pointSize: number;
paths: Path[];
radius: number;
} & PathProps<'stroke' | 'strokeWidth', 'd'>) => {
const controlPaths = paths.map((path, i) => {
const element = paths.filter((p) => p.c.id === path.c.id);
const lastElement = element.at(-1)?.next;
@@ -207,8 +229,8 @@ const ControlPath = ({
<rect
x="0"
y="0"
width="24"
height="24"
width="100%"
height="100%"
fill="#fff"
stroke="none"
rx={radius}
@@ -243,7 +265,7 @@ const ControlPath = ({
)
.join('')}
/>
{controlPaths.map(({ d, prev, next, startMarker, endMarker }, i) => (
{controlPaths.map(({ prev, next, startMarker, endMarker }, i) => (
<React.Fragment key={i}>
{startMarker && (
<circle
@@ -279,11 +301,37 @@ const Radii = ({
{...props}
>
{paths.map(
({ c, prev, next, circle }, i) =>
({ circle, next, prev, c }, i) =>
circle && (
<React.Fragment key={i}>
{c.name !== 'circle' && (
<path d={`M${prev.x} ${prev.y} ${circle.x} ${circle.y} ${next.x} ${next.y}`} />
{circle.tangentIntersection && c.name === 'path' && (
<>
<circle
cx={next.x * 2 - circle.tangentIntersection.x}
cy={next.y * 2 - circle.tangentIntersection.y}
r={0.25}
/>
<circle
cx={prev.x * 2 - circle.tangentIntersection.x}
cy={prev.y * 2 - circle.tangentIntersection.y}
r={0.25}
/>
<path
d={`M${next.x * 2 - circle.tangentIntersection.x} ${
next.y * 2 - circle.tangentIntersection.y
}L${circle.tangentIntersection.x} ${circle.tangentIntersection.y}L${prev.x * 2 - circle.tangentIntersection.x} ${
prev.y * 2 - circle.tangentIntersection.y
}`}
/>
<circle
cx={circle.tangentIntersection.x}
cy={circle.tangentIntersection.y}
r={0.25}
/>
</>
)}
{c.name === 'path' && (
<path d={`M${next.x} ${next.y}L${circle.x} ${circle.y}L${prev.x} ${prev.y}`} />
)}
<circle
cy={circle.y}
@@ -313,55 +361,60 @@ const Radii = ({
const Handles = ({
paths,
...props
}: { paths: Path[] } & PathProps<
'strokeWidth' | 'stroke' | 'strokeDasharray' | 'strokeOpacity',
any
>) => {
return (
<g
className="svg-preview-handles-group"
{...props}
>
{paths.map(({ c, prev, next, cp1, cp2 }) => (
<>
{cp1 && <path d={`M${prev.x} ${prev.y} ${cp1.x} ${cp1.y}`} />}
{cp1 && (
<circle
cy={cp1.y}
cx={cp1.x}
r={0.25}
/>
)}
{cp2 && <path d={`M${next.x} ${next.y} ${cp2.x} ${cp2.y}`} />}
{cp2 && (
<circle
cy={cp2.y}
cx={cp2.x}
r={0.25}
/>
)}
</>
))}
</g>
);
};
}: { paths: Path[] } & PathProps<'strokeWidth' | 'stroke' | 'strokeOpacity', any>) => (
<g
className="svg-preview-handles-group"
{...props}
>
{paths.map(({ c, prev, next, cp1, cp2 }, i) => (
<React.Fragment key={i}>
{cp1 && <path d={`M${prev.x} ${prev.y} ${cp1.x} ${cp1.y}`} />}
{cp1 && (
<circle
cy={cp1.y}
cx={cp1.x}
r={0.25}
/>
)}
{cp2 && <path d={`M${next.x} ${next.y} ${cp2.x} ${cp2.y}`} />}
{cp2 && (
<circle
cy={cp2.y}
cx={cp2.x}
r={0.25}
/>
)}
</React.Fragment>
))}
</g>
);
const SvgPreview = React.forwardRef<
SVGSVGElement,
{
height?: number;
width?: number;
src: string | ReturnType<typeof getPaths>;
showGrid?: boolean;
} & React.SVGProps<SVGSVGElement>
>(({ src, children, showGrid = false, ...props }, ref) => {
>(({ src, children, height = 24, width = 24, showGrid = false, ...props }, ref) => {
const subGridSize =
Math.max(height, width) % 3 === 0
? Math.max(height, width) > 24
? 12
: 3
: Math.max(height, width) % 5 === 0
? 5
: 0;
const paths = typeof src === 'string' ? getPaths(src) : src;
return (
<svg
ref={ref}
xmlns="http://www.w3.org/2000/svg"
width={24}
height={24}
viewBox="0 0 24 24"
width={width}
height={height}
viewBox={`0 0 ${width} ${height}`}
fill="none"
stroke="currentColor"
strokeWidth={2}
@@ -372,8 +425,12 @@ const SvgPreview = React.forwardRef<
<style>{darkModeCss}</style>
{showGrid && (
<Grid
height={height}
width={width}
subGridSize={subGridSize}
strokeWidth={0.1}
stroke="#777"
mask="url(#svg-preview-bounding-box-mask)"
strokeOpacity={0.3}
radius={1}
/>
@@ -385,6 +442,12 @@ const SvgPreview = React.forwardRef<
radius={1}
strokeOpacity={0.15}
/>
<GapViolationHighlight
paths={paths}
stroke="red"
strokeOpacity={0.75}
strokeWidth={4}
/>
<Handles
paths={paths}
strokeWidth={0.12}
@@ -394,18 +457,7 @@ const SvgPreview = React.forwardRef<
<ColoredPath
paths={paths}
colors={[
'#1982c4',
'#4267AC',
'#6a4c93',
'#B55379',
'#FF595E',
'#FF7655',
'#ff924c',
'#FFAE43',
'#ffca3a',
'#C5CA30',
'#8ac926',
'#52A675',
'##dfdfd6',
]}
/>
<Radii
@@ -433,4 +485,6 @@ const SvgPreview = React.forwardRef<
);
});
SvgPreview.displayName = 'SvgPreview';
export default SvgPreview;

View File

@@ -0,0 +1,19 @@
import memoize from 'lodash/memoize';
import SVGPathCommander from 'svg-path-commander';
import { Path } from './types';
function pathToPoints({ d, prev, next }: Path, interval = 1) {
const commander = new SVGPathCommander(d);
const points = [];
try {
const totalLength = commander.getTotalLength();
points.push(prev);
for (let i = interval; i < totalLength - interval; i += interval) {
points.push(commander.getPointAtLength(i));
}
points.push(next);
} catch (err) {}
return points;
}
export default memoize(pathToPoints);

View File

@@ -1,7 +1,10 @@
import { INode, parseSync } from 'svgson';
// @ts-ignore
import toPath from 'element-to-path';
// @ts-ignore
import { SVGPathData, encodeSVGPath } from 'svg-pathdata';
import { Path, Point } from './types';
import memoize from 'lodash/memoize';
function assertNever(x: never): never {
throw new Error('Unknown type: ' + x['type']);
@@ -44,17 +47,21 @@ const extractNodes = (node: INode): INode[] => {
return [];
};
export const getNodes = (src: string) =>
extractNodes(parseSync(src.includes('<svg') ? src : `<svg>${src}</svg>`));
export const getNodes = memoize((src: string) =>
extractNodes(parseSync(src.includes('<svg') ? src : `<svg>${src}</svg>`)),
);
export const getCommands = (src: string) =>
getNodes(src)
.map(convertToPathNode)
.flatMap(({ d, name }, idx) =>
new SVGPathData(d).toAbs().commands.map((c, cIdx) => ({ ...c, id: idx, idx: cIdx, name })),
new SVGPathData(d)
.toAbs()
// @ts-ignore
.commands.map((c, cIdx) => ({ ...c, id: idx, idx: cIdx, name })),
);
export const getPaths = (src: string) => {
const getPaths = (src: string) => {
const commands = getCommands(src.includes('<svg') ? src : `<svg>${src}</svg>`);
const paths: Path[] = [];
let prev: Point | undefined = undefined;
@@ -237,6 +244,7 @@ export const getPaths = (src: string) => {
break;
}
default: {
// @ts-ignore
assertNever(c);
}
}
@@ -244,7 +252,7 @@ export const getPaths = (src: string) => {
return paths;
};
export const arcEllipseCenter = (
const arcEllipseCenter = (
x1: number,
y1: number,
rx: number,
@@ -296,5 +304,52 @@ export const arcEllipseCenter = (
M2[1][0] * Cp[0] + M2[1][1] * Cp[1] + V3[1],
];
return { x: C[0], y: C[1] };
return {
x: C[0],
y: C[1],
tangentIntersection: intersectTangents(
{ x: x1, y: y1 },
{ x: x2, y: y2 },
{ x: C[0], y: C[1] },
),
};
};
function getTangentDirection(p: Point, center: Point): Point {
// Tangent is perpendicular to the radius vector (rotate radius 90°)
const dx = p.x - center.x;
const dy = p.y - center.y;
return { x: -dy, y: dx }; // 90° rotation
}
function intersectTangents(start: Point, end: Point, center: Point): Point | null {
const t1 = getTangentDirection(start, center);
const t2 = getTangentDirection(end, center);
// Solve: start + λ * t1 = end + μ * t2
const A = [
[t1.x, -t2.x],
[t1.y, -t2.y],
];
const b = [end.x - start.x, end.y - start.y];
// Compute determinant
const det = A[0][0] * A[1][1] - A[0][1] * A[1][0];
if (Math.abs(det) < 1e-10) {
// Lines are parallel, no intersection
return null;
}
const invDet = 1 / det;
const lambda = (b[0] * A[1][1] - b[1] * A[0][1]) * invDet;
// Intersection point = start + lambda * t1
return {
x: start.x + lambda * t1.x,
y: start.y + lambda * t1.y,
};
}
export default memoize(getPaths);

View File

@@ -1,5 +1,4 @@
import { bundledLanguages, type ThemeRegistration } from 'shikiji';
import { getHighlighter } from 'shikiji';
import { bundledLanguages, createHighlighter, type ThemeRegistration } from 'shiki';
type CodeExampleType = {
title: string;
@@ -38,7 +37,7 @@ export default App;
language: 'vue',
title: 'Vue',
code: `<script setup>
import { $PascalCase } from 'lucide-vue-next';
import { $PascalCase } from '@lucide/vue';
</script>
<template>
@@ -102,13 +101,8 @@ import { LucideAngularModule, $PascalCase } from 'lucide-angular';
},
{
language: 'html',
title: 'Icon Font',
code: `<style>
@import ('~lucide-static/font/Lucide.css');
</style>
<div class="icon-$Name"></div>
`,
title: 'Icon font',
code: `<div class="icon-$Name"></div>`,
},
];
};
@@ -118,7 +112,7 @@ export type ThemeOptions =
| { light: ThemeRegistration; dark: ThemeRegistration };
const highLightCode = async (code: string, lang: string, active?: boolean) => {
const highlighter = await getHighlighter({
const highlighter = await createHighlighter({
themes: ['github-light', 'github-dark'],
langs: Object.keys(bundledLanguages),
});

View File

@@ -1,5 +1,5 @@
import { bundledLanguages, type ThemeRegistration } from 'shikiji';
import { getHighlighter } from 'shikiji';
import { bundledLanguages, type ThemeRegistration } from 'shiki';
import { createHighlighter } from 'shiki';
type CodeExampleType = {
title: string;
@@ -119,7 +119,7 @@ export type ThemeOptions =
| { light: ThemeRegistration; dark: ThemeRegistration };
const highLightCode = async (code: string, lang: string, active?: boolean) => {
const highlighter = await getHighlighter({
const highlighter = await createHighlighter({
themes: ['github-light', 'github-dark'],
langs: Object.keys(bundledLanguages),
});

View File

@@ -1,12 +1,12 @@
import { bundledLanguages, type ThemeRegistration } from 'shikiji';
import { getHighlighter } from 'shikiji';
import { bundledLanguages, type ThemeRegistration } from 'shiki';
import { createHighlighter } from 'shiki';
export type ThemeOptions =
| ThemeRegistration
| { light: ThemeRegistration; dark: ThemeRegistration };
const highLightCode = async (code: string, lang: string, active?: boolean) => {
const highlighter = await getHighlighter({
const highlighter = await createHighlighter({
themes: ['github-light', 'github-dark'],
langs: Object.keys(bundledLanguages),
});

View File

@@ -0,0 +1,110 @@
/**
* SnackPlayer markdown-it plugin
*
* Converts fenced code blocks like:
*
* ```SnackPlayer name=My%20Example&description=Nice%20demo&ext=tsx
* // code here
* ```
*
* into:
*
* <div
* class="snack-player"
* data-snack-name="My Example"
* ...
* />
*/
import type MarkdownIt from 'markdown-it';
type SnackParams = Record<string, string>;
function parseParams(paramString = ''): SnackParams {
const params = Object.fromEntries(
new URLSearchParams(paramString),
) as SnackParams;
if (!params.platform) {
params.platform = 'web';
}
return params;
}
export default function snackPlayerPlugin(md: MarkdownIt) {
const escapeHtml = md.utils.escapeHtml;
const defaultFence =
md.renderer.rules.fence ||
((tokens, idx, options, env, self) =>
self.renderToken(tokens, idx, options));
md.renderer.rules.fence = (tokens, idx, options, env, self) => {
const token = tokens[idx];
const info = (token.info || '').trim();
if (!info) {
return defaultFence(tokens, idx, options, env, self);
}
const [lang, ...rest] = info.split(/\s+/);
if (lang !== 'SnackPlayer') {
return defaultFence(tokens, idx, options, env, self);
}
const paramString = rest.join(' ');
const params = parseParams(paramString);
// Gather necessary params
const name = params.name
? decodeURIComponent(params.name)
: 'Example';
const description = params.description
? decodeURIComponent(params.description)
: 'Example usage';
const ext = params.ext ? decodeURIComponent(params.ext) : 'tsx';
const filename = `App.${ext}`;
const files = encodeURIComponent(
JSON.stringify({
[filename]: {
type: 'CODE',
contents: token.content,
},
}),
);
const dependencies =
'react-native-safe-area-context' +
(params.dependencies ? `,${params.dependencies}` : '');
const platform = params.platform ?? 'web';
const supportedPlatforms =
params.supportedPlatforms ?? 'ios,android,web';
const theme = params.theme ?? 'light';
const preview = params.preview ?? 'true';
const loading = params.loading ?? 'lazy';
const deviceAppearance = params.deviceAppearance ?? 'dark';
// Build the HTML output (escaping where appropriate)
return (
`<SnackPlayer` +
` class="snack-player"` +
` data-snack-name="${escapeHtml(name)}"` +
` data-snack-description="${escapeHtml(description)}"` +
` data-snack-files="${files}"` +
` data-snack-dependencies="${escapeHtml(dependencies)}"` +
` data-snack-platform="${escapeHtml(platform)}"` +
` data-snack-supported-platforms="${escapeHtml(
supportedPlatforms,
)}"` +
// ` data-snack-theme="${escapeHtml(theme)}"` +
` data-snack-preview="${escapeHtml(preview)}"` +
` data-snack-loading="${escapeHtml(loading)}"` +
` data-snack-device-appearance="${escapeHtml(
deviceAppearance,
)}"` +
` data-snack-device-frame="false"` +
`></SnackPlayer>`
);
};
}

View File

@@ -1,139 +0,0 @@
import { DefaultTheme, UserConfig } from 'vitepress';
const sidebar: UserConfig<DefaultTheme.Config>['themeConfig']['sidebar'] = {
guide: [
{
text: 'Introduction',
items: [
{ text: 'What is lucide?', link: '/guide/' },
{ text: 'Installation', link: '/guide/installation' },
{ text: 'Comparison', link: '/guide/comparison' },
],
},
{
text: 'Basics',
items: [
{
text: 'Color',
link: '/guide/basics/color',
},
{
text: 'Sizing',
link: '/guide/basics/sizing',
},
{
text: 'Stroke width',
link: '/guide/basics/stroke-width',
},
],
},
// TODO: Add this section
{
text: 'Advanced',
items: [
{
text: 'Accessibility',
link: '/guide/advanced/accessibility',
},
{
text: 'Global styling',
link: '/guide/advanced/global-styling',
},
// {
// text: 'Animations',
// },
{
text: 'Filled icons',
link: '/guide/advanced/filled-icons',
},
{
text: 'Aliased Names',
link: '/guide/advanced/aliased-names',
},
// {
// text: 'Combining icons',
// },
// {
// text: 'Dynamic imports'
// },
// {
// text: 'Auto importing'
// },
],
},
{
text: 'Packages',
items: [
{
text: 'Lucide',
link: '/guide/packages/lucide',
},
{
text: 'Lucide React',
link: '/guide/packages/lucide-react',
},
{
text: 'Lucide Vue',
link: '/guide/packages/lucide-vue-next',
},
{
text: 'Lucide Svelte',
link: '/guide/packages/lucide-svelte',
},
{
text: 'Lucide Solid',
link: '/guide/packages/lucide-solid',
},
{
text: 'Lucide React Native',
link: '/guide/packages/lucide-react-native',
},
{
text: 'Lucide Angular',
link: '/guide/packages/lucide-angular',
},
{
text: 'Lucide Preact',
link: '/guide/packages/lucide-preact',
},
{
text: 'Lucide Astro',
link: '/guide/packages/lucide-astro',
},
{
text: 'Lucide Static',
link: '/guide/packages/lucide-static',
},
],
},
{
text: 'Contributing',
items: [
{
text: 'Icon Design Principles',
link: '/guide/design/icon-design-guide',
},
{
text: 'Designing in Illustrator',
link: '/guide/design/illustrator-guide',
},
{
text: 'Designing in InkScape',
link: '/guide/design/inkscape-guide',
},
{
text: 'Designing in Figma',
link: '/guide/design/figma-guide',
},
{
text: 'Designing in Affinity Designer',
link: '/guide/design/affinity-designer-guide',
},
],
},
],
// This should be here to keep the sidebar shown on the icons page
icons: [{ text: '', link: '/' }],
};
export default sidebar;

View File

@@ -0,0 +1,66 @@
import { DefaultTheme, UserConfig } from 'vitepress';
import { reactSidebar } from './react';
import { vueSidebar } from './vue';
import { svelteSidebar } from './svelte';
import { lucideSidebar } from './lucide';
import { solidSidebar } from './solid';
import { preactSidebar } from './preact';
import { reactNativeSidebar } from './react-native';
type Sidebar = UserConfig<DefaultTheme.Config>['themeConfig']['sidebar']
export const guideSidebarTop: DefaultTheme.SidebarItem[] = [
{
text: 'Introduction',
items: [
{ text: 'What is lucide?', link: '/guide/' },
{ text: 'Installation', link: '/guide/installation' },
{ text: 'Comparison', link: '/guide/comparison' },
],
},
]
const sidebar: Sidebar = {
'/guide': [{ text: '', link: '/' }],
'/guide/lucide': lucideSidebar,
'/guide/react': reactSidebar,
'/guide/vue': vueSidebar,
'/guide/svelte': svelteSidebar,
'/guide/solid': solidSidebar,
'/guide/preact': preactSidebar,
'/guide/react-native/': reactNativeSidebar,
'/resources': [
{
text: "Community",
},
{
text: 'Designing icons',
items: [
{
text: 'Icon Design Principles',
link: '/guide/design/icon-design-guide',
},
{
text: 'Designing in Illustrator',
link: '/guide/design/illustrator-guide',
},
{
text: 'Designing in InkScape',
link: '/guide/design/inkscape-guide',
},
{
text: 'Designing in Figma',
link: '/guide/design/figma-guide',
},
{
text: 'Designing in Affinity Designer',
link: '/guide/design/affinity-designer-guide',
},
],
},
],
// This should be here to keep the sidebar shown on the icons page
icons: [{ text: '', link: '/' }],
};
export default sidebar;

View File

@@ -0,0 +1,68 @@
import { DefaultTheme } from "vitepress";
export const lucideSidebar = [
{
items: [
{
text: 'Overview',
link: '/guide/lucide/',
},
{
text: 'Getting started',
link: '/guide/lucide/getting-started',
},
],
},
{
text: 'Basics',
items: [
{
text: 'Color',
desc: 'Adjust the color of your icons',
link: '/guide/lucide/basics/color',
},
{
text: 'Sizing',
desc: 'Adjust the size of your icons',
link: '/guide/lucide/basics/sizing',
},
{
text: 'Stroke width',
desc: 'Adjust the stroke width of your icons',
link: '/guide/lucide/basics/stroke-width',
},
],
},
// TODO: Add this section
{
text: 'Advanced',
items: [
{
text: 'Typescript',
link: '/guide/lucide/advanced/typescript',
desc: 'All exported types and how to use them',
},
{
text: 'With shadow DOM',
link: '/guide/lucide/advanced/shadow-dom',
desc: 'All exported types and how to use them',
},
{
text: 'Accessibility',
link: '/guide/lucide/advanced/accessibility',
desc: 'Making your icons accessible',
},
{
text: 'With lucide lab',
link: '/guide/lucide/advanced/with-lucide-lab',
desc: 'Using lucide-lab with lucide',
},
{
text: 'Filled icons',
link: '/guide/lucide/advanced/filled-icons',
desc: 'Using filled icons in lucide',
},
],
},
] satisfies DefaultTheme.SidebarItem[] & { items: { desc?: string }[] }[];

View File

@@ -0,0 +1,97 @@
import { DefaultTheme } from "vitepress";
export const preactSidebar = [
{
items: [
{
text: 'Overview',
link: '/guide/preact/',
},
{
text: 'Getting started',
link: '/guide/preact/getting-started',
},
],
},
{
text: 'Basics',
items: [
{
text: 'Color',
desc: 'Adjust the color of your icons',
link: '/guide/preact/basics/color',
},
{
text: 'Sizing',
desc: 'Adjust the size of your icons',
link: '/guide/preact/basics/sizing',
},
{
text: 'Stroke width',
desc: 'Adjust the stroke width of your icons',
link: '/guide/preact/basics/stroke-width',
},
],
},
{
text: 'Advanced',
items: [
{
text: 'Typescript',
link: '/guide/preact/advanced/typescript',
desc: 'All exported types and how to use them',
},
{
text: 'Accessibility',
link: '/guide/preact/advanced/accessibility',
desc: 'Making your icons accessible',
},
{
text: 'Global styling',
link: '/guide/preact/advanced/global-styling',
desc: 'Apply global styles to all icons',
},
{
text: 'With lucide lab',
link: '/guide/preact/advanced/with-lucide-lab',
desc: 'Using lucide-lab with lucide-preact',
},
// {
// text: 'Animations',
// link: '/guide/preact/advanced/animations',
// desc: 'Add animations to your icons',
// },
{
text: 'Filled icons',
link: '/guide/preact/advanced/filled-icons',
desc: 'Using filled icons in lucide-preact',
},
{
text: 'Aliased Names',
link: '/guide/preact/advanced/aliased-names',
desc: 'Using aliased icon names',
},
{
text: 'Combining icons',
link: '/guide/preact/advanced/combining-icons',
desc: 'Combine multiple icons into one',
},
],
},
{
text: 'Resources',
items: [
{
text: 'Accessibility in depth',
link: '/guide/accessibility',
desc: 'Accessibility best practices',
},
{
text: 'VSCode',
link: '/guide/vscode',
desc: 'VSCode and Lucide',
},
],
}
] satisfies DefaultTheme.SidebarItem[] & { items: { desc?: string }[] }[];

View File

@@ -0,0 +1,82 @@
import { DefaultTheme } from "vitepress";
export const reactNativeSidebar = [
{
items: [
{
text: 'Overview',
link: '/guide/react-native/',
},
{
text: 'Getting started',
link: '/guide/react-native/getting-started',
},
],
},
{
text: 'Basics',
items: [
{
text: 'Color',
desc: 'Adjust the color of your icons',
link: '/guide/react-native/basics/color',
},
{
text: 'Sizing',
desc: 'Adjust the size of your icons',
link: '/guide/react-native/basics/sizing',
},
{
text: 'Stroke width',
desc: 'Adjust the stroke width of your icons',
link: '/guide/react-native/basics/stroke-width',
},
],
},
{
text: 'Advanced',
items: [
{
text: 'Typescript',
link: '/guide/react-native/advanced/typescript',
desc: 'All exported types and how to use them',
},
{
text: 'Global styling',
link: '/guide/react-native/advanced/global-styling',
desc: 'Apply global styles to all icons',
},
{
text: 'With lucide lab',
link: '/guide/react-native/advanced/with-lucide-lab',
desc: 'Using lucide-lab with lucide-react-native',
},
{
text: 'Filled icons',
link: '/guide/react-native/advanced/filled-icons',
desc: 'Using filled icons in lucide-react-native',
},
{
text: 'Aliased Names',
link: '/guide/react-native/advanced/aliased-names',
desc: 'Using aliased icon names',
},
{
text: 'Combining icons',
link: '/guide/react-native/advanced/combining-icons',
desc: 'Combine multiple icons into one',
},
],
},
{
text: 'Resources',
items: [
{
text: 'VSCode',
link: '/guide/vscode',
desc: 'VSCode and Lucide',
},
],
}
] satisfies DefaultTheme.SidebarItem[] & { items: { desc?: string }[] }[];

View File

@@ -0,0 +1,103 @@
import { DefaultTheme } from "vitepress";
export const reactSidebar = [
{
items: [
{
text: 'Overview',
link: '/guide/react/',
},
{
text: 'Getting started',
link: '/guide/react/getting-started',
},
],
},
{
text: 'Basics',
items: [
{
text: 'Color',
desc: 'Adjust the color of your icons',
link: '/guide/react/basics/color',
},
{
text: 'Sizing',
desc: 'Adjust the size of your icons',
link: '/guide/react/basics/sizing',
},
{
text: 'Stroke width',
desc: 'Adjust the stroke width of your icons',
link: '/guide/react/basics/stroke-width',
},
],
},
{
text: 'Advanced',
items: [
{
text: 'Typescript',
link: '/guide/react/advanced/typescript',
desc: 'All exported types and how to use them',
},
{
text: 'Accessibility',
link: '/guide/react/advanced/accessibility',
desc: 'Making your icons accessible',
},
{
text: 'Global styling',
link: '/guide/react/advanced/global-styling',
desc: 'Apply global styles to all icons',
},
{
text: 'With lucide lab',
link: '/guide/react/advanced/with-lucide-lab',
desc: 'Using lucide-lab with lucide-react',
},
// {
// text: 'Animations',
// link: '/guide/react/advanced/animations',
// desc: 'Add animations to your icons',
// },
{
text: 'Filled icons',
link: '/guide/react/advanced/filled-icons',
desc: 'Using filled icons in lucide-react',
},
{
text: 'Aliased Names',
link: '/guide/react/advanced/aliased-names',
desc: 'Using aliased icon names',
},
{
text: 'Combining icons',
link: '/guide/react/advanced/combining-icons',
desc: 'Combine multiple icons into one',
},
{
text: 'Dynamic icon component',
link: '/guide/react/advanced/dynamic-icon-component.md',
desc: 'Dynamically import icons as needed',
}
],
},
{
text: 'Resources',
items: [
{
text: 'Accessibility in depth',
link: '/guide/accessibility',
desc: 'Accessibility best practices',
},
{
text: 'VSCode',
link: '/guide/vscode',
desc: 'VSCode and Lucide',
},
],
}
] satisfies DefaultTheme.SidebarItem[] & { items: { desc?: string }[] }[];

View File

@@ -0,0 +1,98 @@
import { DefaultTheme } from "vitepress";
export const solidSidebar = [
{
items: [
{
text: 'Overview',
link: '/guide/solid/',
},
{
text: 'Getting started',
link: '/guide/solid/getting-started',
},
],
},
{
text: 'Basics',
items: [
{
text: 'Color',
desc: 'Adjust the color of your icons',
link: '/guide/solid/basics/color',
},
{
text: 'Sizing',
desc: 'Adjust the size of your icons',
link: '/guide/solid/basics/sizing',
},
{
text: 'Stroke width',
desc: 'Adjust the stroke width of your icons',
link: '/guide/solid/basics/stroke-width',
},
],
},
{
text: 'Advanced',
items: [
{
text: 'Typescript',
link: '/guide/solid/advanced/typescript',
desc: 'All exported types and how to use them',
},
{
text: 'Accessibility',
link: '/guide/solid/advanced/accessibility',
desc: 'Making your icons accessible',
},
{
text: 'Global styling',
link: '/guide/solid/advanced/global-styling',
desc: 'Apply global styles to all icons',
},
{
text: 'With lucide lab',
link: '/guide/solid/advanced/with-lucide-lab',
desc: 'Using lucide-lab with lucide-solid',
},
// {
// text: 'Animations',
// link: '/guide/solid/advanced/animations',
// desc: 'Add animations to your icons',
// },
{
text: 'Filled icons',
link: '/guide/solid/advanced/filled-icons',
desc: 'Using filled icons in lucide-solid',
},
{
text: 'Aliased Names',
link: '/guide/solid/advanced/aliased-names',
desc: 'Using aliased icon names',
},
{
text: 'Combining icons',
link: '/guide/solid/advanced/combining-icons',
desc: 'Combine multiple icons into one',
},
],
},
{
text: 'Resources',
items: [
{
text: 'Accessibility in depth',
link: '/guide/accessibility',
desc: 'Accessibility best practices',
},
{
text: 'VSCode',
link: '/guide/vscode',
desc: 'VSCode and Lucide',
},
],
}
] satisfies DefaultTheme.SidebarItem[] & { items: { desc?: string }[] }[];

View File

@@ -0,0 +1,87 @@
import { DefaultTheme } from "vitepress";
export const svelteSidebar = [
{
items: [
{
text: 'Overview',
link: '/guide/svelte/',
},
{
text: 'Getting started',
link: '/guide/svelte/getting-started',
},
],
},
{
text: 'Basics',
items: [
{
text: 'Color',
desc: 'Adjust the color of your icons',
link: '/guide/svelte/basics/color',
},
{
text: 'Sizing',
desc: 'Adjust the size of your icons',
link: '/guide/svelte/basics/sizing',
},
{
text: 'Stroke width',
desc: 'Adjust the stroke width of your icons',
link: '/guide/svelte/basics/stroke-width',
},
],
},
{
text: 'Advanced',
items: [
{
text: 'Typescript',
link: '/guide/svelte/advanced/typescript',
desc: 'All exported types and how to use them',
},
{
text: 'Accessibility',
link: '/guide/svelte/advanced/accessibility',
desc: 'Making your icons accessible',
},
{
text: 'Global styling',
link: '/guide/svelte/advanced/global-styling',
desc: 'Apply global styles to all icons',
},
{
text: 'With lucide lab',
link: '/guide/svelte/advanced/with-lucide-lab',
desc: 'Using lucide-lab with @lucide/svelte',
},
{
text: 'Filled icons',
link: '/guide/svelte/advanced/filled-icons',
desc: 'Using filled icons in @lucide/svelte',
},
{
text: 'Combining icons',
link: '/guide/svelte/advanced/combining-icons',
desc: 'Combine multiple icons into one',
},
],
},
{
text: 'Resources',
items: [
{
text: 'Accessibility in depth',
link: '/guide/accessibility',
desc: 'Accessibility best practices',
},
{
text: 'VSCode',
link: '/guide/vscode',
desc: 'VSCode and Lucide',
},
],
}
] satisfies DefaultTheme.SidebarItem[] & { items: { desc?: string }[] }[];

View File

@@ -0,0 +1,92 @@
import { DefaultTheme } from "vitepress";
export const vueSidebar = [
{
items: [
{
text: 'Overview',
link: '/guide/vue/',
},
{
text: 'Getting started',
link: '/guide/vue/getting-started',
},
],
},
{
text: 'Basics',
items: [
{
text: 'Color',
desc: 'Adjust the color of your icons',
link: '/guide/vue/basics/color',
},
{
text: 'Sizing',
desc: 'Adjust the size of your icons',
link: '/guide/vue/basics/sizing',
},
{
text: 'Stroke width',
desc: 'Adjust the stroke width of your icons',
link: '/guide/vue/basics/stroke-width',
},
],
},
{
text: 'Advanced',
items: [
{
text: 'Typescript',
link: '/guide/vue/advanced/typescript',
desc: 'All exported types and how to use them',
},
{
text: 'Accessibility',
link: '/guide/vue/advanced/accessibility',
desc: 'Making your icons accessible',
},
{
text: 'Global styling',
link: '/guide/vue/advanced/global-styling',
desc: 'Apply global styles to all icons',
},
{
text: 'With lucide lab',
link: '/guide/vue/advanced/with-lucide-lab',
desc: 'Using lucide-lab with @lucide/vue',
},
{
text: 'Filled icons',
link: '/guide/vue/advanced/filled-icons',
desc: 'Using filled icons in @lucide/vue',
},
{
text: 'Aliased Names',
link: '/guide/vue/advanced/aliased-names',
desc: 'Using aliased icon names',
},
{
text: 'Combining icons',
link: '/guide/vue/advanced/combining-icons',
desc: 'Combine multiple icons into one',
},
],
},
{
text: 'Resources',
items: [
{
text: 'Accessibility in depth',
link: '/guide/accessibility',
desc: 'Accessibility best practices',
},
{
text: 'VSCode',
link: '/guide/vscode',
desc: 'VSCode and Lucide',
},
],
}
] satisfies DefaultTheme.SidebarItem[] & { items: { desc?: string }[] }[];

View File

@@ -3,12 +3,20 @@ import createLucideIcon from 'lucide-vue-next/src/createLucideIcon'
import { search } from '../../../data/iconNodes'
const SearchIcon = createLucideIcon('search', search)
defineProps({
shortcut: {
type: String,
required: false
}
})
</script>
<template>
<button class="fake-input">
<component :is="SearchIcon" class="search-icon"/>
<slot/>
<kbd v-if="shortcut" class="shortcut">{{ shortcut }}</kbd>
</button>
</template>
@@ -33,4 +41,23 @@ const SearchIcon = createLucideIcon('search', search)
border-color: var(--vp-c-brand);
background: var(--vp-c-bg-alt);
}
.shortcut {
margin-left: auto;
padding: 2px 6px;
font-size: 12px;
font-family: inherit;
font-weight: 500;
line-height: 1.5;
color: var(--vp-c-text-3);
background: var(--vp-c-default-soft);
border: 1px solid var(--vp-c-divider);
border-radius: 4px;
}
@media (hover: none) {
.shortcut {
display: none;
}
}
</style>

View File

@@ -6,20 +6,35 @@ export default {
export interface InputProps {
type: string
modelValue: string
shortcut?: string
}
</script>
<script setup lang="ts">
import { ref } from 'vue'
import { ref, onMounted, nextTick, watch } from 'vue'
const props = withDefaults(defineProps<InputProps>(), {
type: 'text'
})
const input = ref()
const wrapperEl = ref()
const shortcutEl = ref()
defineEmits(['change', 'input', 'update:modelValue'])
const updateShortcutSpacing = () => {
nextTick(() => {
if (shortcutEl.value && wrapperEl.value) {
const shortcutWidth = shortcutEl.value.offsetWidth
wrapperEl.value.style.setProperty('--shortcut-width', `${shortcutWidth}px`)
}
})
}
onMounted(updateShortcutSpacing)
watch(() => props.shortcut, updateShortcutSpacing)
defineExpose({
focus: () => {
input.value.focus()
@@ -28,17 +43,18 @@ defineExpose({
</script>
<template>
<div class="input-wrapper">
<div class="input-wrapper" ref="wrapperEl">
<slot name="icon" class="icon" />
<input
:type="type"
class="input"
:class="{'has-icon': $slots.icon}"
:class="{'has-icon': $slots.icon, 'has-shortcut': shortcut}"
ref="input"
:value="modelValue"
v-bind="$attrs"
@input="$emit('update:modelValue', $event.target.value)"
/>
<kbd v-if="shortcut" class="shortcut" ref="shortcutEl">{{ shortcut }}</kbd>
</div>
</template>
@@ -57,6 +73,10 @@ defineExpose({
font-size: 14px;
}
.input.has-shortcut {
padding-right: calc(var(--shortcut-width, 40px) + 22px);
}
.input:hover, .input:focus {
border-color: var(--vp-c-brand);
background: var(--vp-c-bg-alt);
@@ -66,7 +86,28 @@ defineExpose({
padding-left: 52px;
}
.shortcut {
position: absolute;
right: 12px;
top: 50%;
transform: translateY(-50%);
padding: 2px 6px;
font-size: 12px;
font-family: inherit;
font-weight: 500;
line-height: 1.5;
color: var(--vp-c-text-3);
background: var(--vp-c-default-soft);
border: 1px solid var(--vp-c-divider);
border-radius: 4px;
pointer-events: none;
}
@media (hover: none) {
.shortcut {
display: none;
}
}
</style>
<style>

View File

@@ -14,6 +14,7 @@ const SearchIcon = createLucideIcon('search', search)
interface Props {
modelValue: string
shortcut?: string
}
const props = defineProps<Props>()
@@ -39,6 +40,7 @@ const value = computed({
ref="input"
type="search"
autofocus
:shortcut="shortcut"
v-bind="$attrs"
v-model="value"
class="input-wrapper"
@@ -71,5 +73,4 @@ const value = computed({
font-size: 14px;
height: 48px;
}
</style>

View File

@@ -5,8 +5,8 @@ export type IconNode = [elementName: string, attrs: Record<string, string>][]
const props = defineProps<{
name: string;
tags: string[];
categories: string[];
tags?: string[];
categories?: string[];
// contributors: Contributor[];
iconNode: IconNode;
}>()

View File

@@ -0,0 +1,61 @@
<script setup lang="ts">
import VPLink from 'vitepress/dist/client/theme-default/components/VPLink.vue'
defineProps<{
href?: string
title?: string
desc?: string
}>()
</script>
<template>
<VPLink
class="overview-link"
:href="href"
:aria-label="`${title} - ${desc}`"
>
<span class="title">
{{ title }}
</span>
<span
class="desc">
{{ desc }}
</span>
</VPLink>
</template>
<style scoped>
.overview-link {
display: block;
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
padding: 11px 16px 13px;
width: 100%;
height: 100%;
transition: border-color 0.25s;
text-decoration: none;
}
.overview-link:hover {
border-color: var(--vp-c-brand-1);
}
.desc {
display: block;
line-height: 20px;
font-size: 12px;
font-weight: 500;
color: var(--vp-c-text-2);
}
.title {
display: block;
line-height: 20px;
font-size: 14px;
font-weight: 500;
color: var(--vp-c-brand-1);
transition: color 0.25s;
margin-bottom: 2px;
}
</style>

View File

@@ -0,0 +1,31 @@
<template>
<div class="overview-link-grid">
<slot />
</div>
</template>
<style>
.overview-link-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 16px;
}
.overview-link-grid > * {
box-sizing: border-box;
padding: 8px;
}
@media (min-width: 960px) {
.overview-link-grid {
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
}
}
@media (min-width: 1280px) {
.overview-link-grid {
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
}
}
</style>

View File

@@ -0,0 +1,226 @@
<script setup lang="ts">
import { ref } from 'vue'
import {
Listbox,
ListboxLabel,
ListboxButton,
ListboxOptions,
ListboxOption,
} from '@headlessui/vue'
import { CheckIcon, ChevronsUpDownIcon } from 'lucide-vue-next'
defineProps<{
id?: string
items?: { name: string, icon: string }[]
}>()
const selected = defineModel<{ name: string, icon: string }>()
</script>
<template>
<Listbox v-model="selected">
<div class="select-wrapper">
<ListboxButton class="select-button" :id="id">
<img
:src="selected.icon"
:class="{ 'select-item-icon': true }"
:alt="`${selected.name} logo`"
loading="lazy"
/>
<span class="select-text">{{ selected.name }}</span>
<span class="select-icon">
<ChevronsUpDownIcon class="chevron-icon" aria-hidden="true" />
</span>
</ListboxButton>
<transition
leave-active-class="transition-leave"
leave-from-class="transition-leave-from"
leave-to-class="transition-leave-to"
>
<ListboxOptions class="select-options">
<ListboxOption
v-slot="{ active, selected }"
v-for="item in items"
:key="item.name"
:value="item"
as="template"
>
<li :class="['select-option', { active, selected }]">
<img
:src="item.icon"
:class="{ 'select-item-icon': true }"
:alt="`${item.name} logo`"
loading="lazy"
/>
<span :class="['option-text', { selected }]">{{ item.name }}</span>
<span v-if="selected" class="check-icon">
<CheckIcon class="check" aria-hidden="true" />
</span>
</li>
</ListboxOption>
</ListboxOptions>
</transition>
</div>
</Listbox>
</template>
<style scoped>
.select-wrapper {
position: relative;
}
.select-button {
background: var(--vp-sidebar-input);
border-radius: 8px;
color: var(--vp-c-text-1);
padding: 7px 14px;
height: auto;
font-size: 14px;
border: 1px solid transparent;
cursor: text;
display: flex;
gap: 12px;
transition: color 0.25s, border-color 0.25s, background-color 0.25s;
width: 100%;
align-items: center;
}
.select-button:focus {
border-color: var(--vp-c-brand);
}
.select-text {
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.select-icon {
pointer-events: none;
position: absolute;
top: 0;
bottom: 0;
right: 0;
display: flex;
align-items: center;
padding-right: 0.5rem;
}
.select-item-icon {
object-fit: contain;
width: 24px;
height: 24px;
}
.chevron-icon {
height: 1.25rem;
width: 1.25rem;
color: #9ca3af;
}
.transition-leave {
transition: opacity 100ms ease-in;
}
.transition-leave-from {
opacity: 1;
}
.transition-leave-to {
opacity: 0;
}
.select-options {
position: absolute;
display: flex;
flex-direction: column;
border-radius: 12px;
padding: 12px;
min-width: 128px;
border: 1px solid var(--vp-c-divider);
background-color: var(--vp-c-bg-elv);
box-shadow: var(--vp-shadow-3);
transition: background-color 0.5s;
max-height: calc(100vh - var(--vp-nav-height));
overflow-y: auto;
width: 100%;
z-index: 90;
right: 0;
top: 44px;
}
.select-option {
position: relative;
cursor: default;
user-select: none;
padding: 0px 4px;
text-align: left;
display: flex;
align-items: center;
gap: 12px;
border-radius: 6px;
line-height: 32px;
font-size: 14px;
font-weight: 500;
color: var(--vp-c-text-1);
white-space: nowrap;
transition: background-color .25s,color .25s;
list-style: none;
}
.select-option:hover, .select-option.active {
color: var(--vp-c-brand);
background-color: var(--vp-c-default-soft);
}
.select-option:active {
color: var(--vp-c-brand);
background-color: var(--vp-c-bg-elv);
}
.option-text {
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-weight: normal;
}
.option-text.selected {
font-weight: 500;
}
.check-icon {
position: absolute;
top: 0;
bottom: 0;
right: 12px;
display: flex;
align-items: center;
padding-left: 0.75rem;
color: var(--vp-c-brand);
}
.check {
height: 1.25rem;
width: 1.25rem;
}
@media (min-width: 640px) {
.select-button,
.select-options {
font-size: 0.875rem;
line-height: 1.25rem;
}
}
</style>

View File

@@ -1,15 +1,22 @@
<script setup>
import { ref } from 'vue'
import { Switch } from '@headlessui/vue'
const enabled = ref(false)
defineProps({
modelValue: {
type: Boolean,
default: false
}
})
const emit = defineEmits(['update:modelValue'])
</script>
<template>
<Switch
v-model="enabled"
:model-value="modelValue"
@update:model-value="emit('update:modelValue', $event)"
class="switch"
:class="{ enabled }"
:class="{ enabled: modelValue }"
>
<span class="thumb" />
</Switch>

View File

@@ -0,0 +1,136 @@
<script setup lang="ts">
import { getSandpackFiles, getCustomSetupFromProps, parsedBoolean, getSandpackOptions } from 'vitepress-plugin-sandpack';
import { Sandpack, type SandpackFiles } from 'sandpack-vue3';
import { computed, nextTick, onBeforeMount, onMounted, ref, useSlots, watch } from 'vue';
import styles from './styles.css?raw'
import sandpackTheme from '../../sandpackTheme.json'
import { sandboxProps } from 'vitepress-plugin-sandpack';
const props = defineProps({
...sandboxProps,
editorHeight: {
type: [String, Number],
default: undefined
},
editorWidthPercentage: {
type: [String, Number],
default: undefined
},
dependencies: {
type: String,
default: undefined
}
});
const files = ref<SandpackFiles>({});
const getOpt = (propName: string) => props[propName] ?? props?.options?.[propName];
const editorVisible = computed(() => parsedBoolean(getOpt('hideEditor')) ? 'none' : 'flex');
const previewHeight = computed(() => isNaN(Number(getOpt('previewHeight'))) ? undefined : Number(getOpt('previewHeight')));
const previewHeightStyle =
computed(() => previewHeight.value ? `${previewHeight.value}px` : 'var(--sp-layout-height)');
const coderHeight = computed(() => isNaN(Number(getOpt('coderHeight'))) ? undefined : Number(getOpt('coderHeight')));
const coderHeightStyle =
computed(() => coderHeight.value ? `${coderHeight.value}px` : 'var(--sp-layout-height)');
const slots = useSlots();
const isDark = ref(false);
const resolveFiles = async () => {
files.value = {
...await getSandpackFiles(props, slots),
'styles.css': {
code: styles,
hidden: true
},
};
};
watch(props, resolveFiles);
onBeforeMount(resolveFiles);
const dependencies = computed(() => {
if (props.dependencies) {
return props.dependencies.split(',').reduce((acc, dep) => {
const [name, version] = dep.split(':').map(s => s.trim());
acc[name] = version || 'latest';
return acc;
}, {} as Record<string, string>);
}
return undefined;
})
</script>
<template>
<Sandpack :theme="sandpackTheme" :template="template" :rtl="parsedBoolean(rtl)" :files="files" :options="{
...(getSandpackOptions(props) as any),
editorWidthPercentage: getOpt('editorWidthPercentage') ? Number(getOpt('editorWidthPercentage')) : undefined,
showConsoleButton: false,
}" :customSetup='{
dependencies: dependencies
}' />
</template>
<style>
.sp-wrapper+* {
margin-top: 24px;
}
.sp-wrapper .sp-layout {
border-radius: 8px;
}
.sp-wrapper .sp-tabs-scrollable-container {
border-radius: 8px 8px 0 0;
position: relative;
box-shadow: inset 0 -1px var(--vp-code-tab-divider);
margin-bottom: 0px;
margin-top: -1px;
height: 48px;
padding-bottom: 1px;
}
.sp-wrapper .sp-preview-container {
background-color: transparent;
}
.sp-wrapper .sp-tabs .sp-tab-button {
padding: 0 12px;
line-height: 48px;
height: 48px;
font-size: 14px;
font-weight: 500;
position: relative;
}
.sp-wrapper .sp-tabs .sp-tab-button:after {
position: absolute;
right: 8px;
left: 8px;
bottom: 0px;
z-index: 1;
height: 1px;
content: '';
background-color: transparent;
transition: background-color 0.25s;
}
.sp-wrapper .sp-tabs .sp-tab-button[data-active='true'] {
color: var(--vp-code-tab-active-text-color);
}
.sp-wrapper .sp-tabs .sp-tab-button[data-active='true']:after {
background-color: var(--vp-code-tab-active-bar-color);
}
.sp-wrapper .sp-button {
color: var(--sp-colors-clickable);
text-decoration: none;
}
</style>

View File

@@ -0,0 +1,48 @@
<script setup lang="ts">
import { useMutationObserver, useScriptTag } from '@vueuse/core';
import { useData } from 'vitepress';
import { onMounted, useTemplateRef, watchEffect } from 'vue';
const { isDark } = useData()
const el = useTemplateRef('el')
useScriptTag('https://snack.expo.dev/embed.js')
watchEffect(() => {
console.log(isDark.value);
})
useMutationObserver(el, (mutations) => {
const container = el.value;
if (mutations[0]) {
if ('ExpoSnack' in window) {
window?.ExpoSnack?.remove(container);
window?.ExpoSnack?.append(container);
}
}
}, {
attributes: true,
})
onMounted(() => {
const container = el.value;
if ('ExpoSnack' in window) {
window?.ExpoSnack?.append(container);
}
})
</script>
<template>
<div v-bind="$attrs" class="snack-player" ref="el" :data-snack-theme="isDark ? 'dark' : 'light'" />
</template>
<style>
.snack-player {
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
width: 100%;
height: 635px;
margin-bottom: 24px;
overflow: hidden;
}
</style>

View File

@@ -0,0 +1,50 @@
body {
font-family: sans-serif;
-webkit-font-smoothing: auto;
-moz-font-smoothing: auto;
-moz-osx-font-smoothing: grayscale;
font-smoothing: auto;
text-rendering: optimizeLegibility;
font-smooth: always;
-webkit-tap-highlight-color: transparent;
-webkit-touch-callout: none;
background: #202127;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
margin: 0;
color: #fff;
}
html,
body {
height: 100%;
min-height: 100%;
}
button {
display: flex;
align-items: center;
font-size: 18px;
padding: 10px 20px;
line-height: 24px;
gap: 8px;
border-radius: 24px;
outline: none;
border: none;
background: #111;
transition: all 0.3s ease;
}
button:hover {
background: #f56565;
}
.app {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 32px;
}

View File

@@ -0,0 +1,88 @@
<script setup lang="tsx">
import VPSidebarGroup from 'vitepress/dist/client/theme-default/components/VPSidebarGroup.vue';
import sidebar, { guideSidebarTop } from '../../../sidebar';
import { useData, useRouter } from 'vitepress';
import Select from '../base/Select.vue';
import { computed, ref, watch, watchEffect } from 'vue';
import { link, route } from '~/.vitepress/data/iconNodes';
import { useLocalStorage } from '@vueuse/core';
const { page } = useData()
const router = useRouter()
const frameworks = [
{ name: 'Vanilla', icon: '/framework-logos/js.svg', route: '/guide/lucide' },
{ name: 'React', icon: '/framework-logos/react.svg', route: '/guide/react' },
{ name: 'Vue', icon: '/framework-logos/vue.svg', route: '/guide/vue' },
{ name: 'Svelte', icon: '/framework-logos/svelte.svg', route: '/guide/svelte' },
{ name: 'Solid', icon: '/framework-logos/solid.svg', route: '/guide/solid' },
{ name: 'Angular', icon: '/framework-logos/angular.svg', route: '/guide/angular' },
{ name: 'Preact', icon: '/framework-logos/preact.svg', route: '/guide/preact' },
{ name: 'React Native', icon: '/framework-logos/react-native.svg', route: '/guide/react-native' },
{ name: 'Astro', icon: '/framework-logos/astro-dark.svg', route: '/guide/astro' },
]
const fallbackFramework = useLocalStorage('lucide-docs-fallback-framework', frameworks[1])
const selected = computed(() => {
const current = frameworks.find(({ route }) => {
return router.route.path.split('/').slice(0, 3).join('/') === route
})
return current || fallbackFramework.value
})
function onSelectFramework(item: { name: string, icon: string, route: string }) {
fallbackFramework.value = item
if (item.route !== router.route.path) {
const likeRoute = router.route.path.replace(selected.value.route, item.route);
const hasRoute = sidebar[item.route]?.some(section =>
section?.items?.some(({ link }) => link === likeRoute)
);
if (hasRoute) {
router.go(likeRoute)
return;
}
router.go(item.route)
}
}
</script>
<template>
<VPSidebarGroup :items="guideSidebarTop" v-if="page?.relativePath?.startsWith?.('guide')" />
<div class="framework-select" v-if="page?.relativePath?.startsWith?.('guide')">
<label for="framework-select">Framework</label>
<Select id="framework-select" :items="frameworks" @update:model-value="onSelectFramework" v-model="selected" />
</div>
<VPSidebarGroup :key="selected.route"
v-if="page?.relativePath?.startsWith?.('guide') && !page?.relativePath?.startsWith?.(selected.route.substring(1))"
:items="sidebar[selected.route]" />
</template>
<style scoped>
.framework-select {
font-size: 12px;
transition: border-color 0.5s, background-color 0.5s ease;
margin-bottom: 10px;
position: sticky;
top: -0.5px;
z-index: 10;
border-top: 1px solid var(--vp-c-divider);
padding-top: 10px;
margin-top: -10px;
}
label {
color: var(--vp-c-text-1);
padding: 4px 0;
line-height: 24px;
font-size: 14px;
transition: color 0.25s;
font-weight: bold;
margin-bottom: 4px;
display: block;
}
</style>

View File

@@ -0,0 +1,51 @@
<script setup lang="ts">
import { useRouter } from 'vitepress';
import Badge from '../base/Badge.vue';
import HomeContainer from './HomeContainer.vue';
import { data } from './HomeHeroIconsCard.data'
import FakeInput from '../base/FakeInput.vue'
import { nextTick, provide } from 'vue'
import useSearchShortcut from '../../utils/useSearchShortcut';
const { go } = useRouter()
const { shortcutText: kbdSearchShortcut } = useSearchShortcut(() => {
go('/icons/?focus')
})
const enableTransitions = () =>
'startViewTransition' in document &&
window.matchMedia('(prefers-reduced-motion: no-preference)').matches
async function handleClick(event: MouseEvent) {
if (!enableTransitions()) {
go('/icons/?focus')
return;
}
await document.startViewTransition(async () => {
await go('/icons/?focus');
await nextTick()
}).ready
}
</script>
<template>
<FakeInput
@click="go('/icons/?focus')"
:shortcut="kbdSearchShortcut"
class="search-box"
>
Search {{ data.iconsCount }} icons...
</FakeInput>
</template>
<style scoped>
.search-box {
view-transition-name: icons-search-box;
width: 100%;
margin-top: 24px;
}
</style>

View File

@@ -0,0 +1,428 @@
<script setup lang="ts">
import { ref, onMounted, shallowRef, onBeforeUnmount, watchEffect, computed } from 'vue';
import { data } from './HomeHeroIconsCard.data'
import { useRouter } from 'vitepress';
import { random } from 'lodash-es'
import FakeInput from '../base/FakeInput.vue'
import { motion, Variants, useScroll, useSpring, useTransform } from "motion-v"
import LucideIcon from '../base/LucideIcon.vue'
import { shrink } from '~/.vitepress/data/iconNodes';
const emit = defineEmits(['animation-complete'])
const MotionLucideIcon = motion.create(LucideIcon)
const COLUMNS = 8;
const SIZE = 2;
const GAP = 1;
const { scrollYProgress } = useScroll()
const opacity = useTransform(() => (1 - scrollYProgress.get() * 8))
const icons = ref(data.icons.slice(0, 64).map((icon, index) => {
const x = index % COLUMNS;
const y = Math.floor(index / COLUMNS);
if (index === 0) {
return {
...icon,
x: 9999,
y: 9999,
opacity: 0
}
}
return {
...icon,
x: x * (SIZE + GAP) + 0.5,
y: y * (SIZE + GAP) + 0.5
}
}))
const { go } = useRouter()
const intervalTime = shallowRef()
const showHandles = ref(true)
const scaleDownVariants: Variants = {
fullSize: {
scale: 1
},
riseUp: {
x: 0.5,
y: -0.5,
animationName: 'riseUp',
scale: 1,
transition: {
delay: 0.5,
duration: 1.5,
ease: [0.22, 1, 0.36, 1]
}
},
small: {
x: -10.5,
y: -10.5,
scale: 0.1,
animationName: 'small',
transition: {
delay: 1,
duration: 1,
ease: [0.22, 1, 0.36, 1]
}
}
}
const scaleDownAnimation = ref('fullSize')
const iconGridAnimation = ref('initial')
const drawAnimation = ref('visible')
const draw: Variants = {
hidden: { pathLength: 0, opacity: 0 },
visible: {
animationName: 'visible',
pathLength: 1,
opacity: 1,
transition: {
pathLength: { delay: 2.4, type: "spring", duration: 2.8, bounce: 0 },
opacity: { delay: 2.4, duration: 0.1 },
},
},
exit: (path) => ({
animationName: 'exit',
stroke: path ? 'var(--vp-c-text-1)' : 'var(--vp-c-brand)',
pathLength: 1,
opacity: 1,
transition: {
duration: 0.8,
},
}),
};
const onAnimationComplete = (item) => {
if (item.animationName === 'visible') {
drawAnimation.value = 'exit'
return
}
if (item.animationName === 'exit') {
showHandles.value = false
scaleDownAnimation.value = 'small'
}
if (item.animationName === 'small') {
iconGridAnimation.value = 'showIcons'
}
if (item.animationName === 'riseUp') {
scaleDownAnimation.value = 'small'
}
if (item.animationName === 'showIcons') {
shrinkIconAnimation.value = 'shrinkIcons'
}
if (item.animationName === 'shrinkIcons') {
iconGridAnimation.value = 'initial'
setTimeout(() => {
emit('animation-complete')
}, 2800)
}
}
const randomIndex = ref(
Math.floor(Math.random() * 64)
)
const iconAnimationVariants = {
initial: {
animationName: 'end',
opacity: 0,
x: 0,
y: 0,
transition: { duration: 1, delay: 1, ease: 'easeInOut' }
},
showIcons: (index) => ({
animationName: 'showIcons',
opacity: [0, 1, 1],
x: [0.5, 0, 0],
y: [-0.5, 0, 0],
strokeWidth: randomIndex.value === index ? [0, 2, 2] : undefined,
transition: { delay: index * 0.023, duration: 1.6, ease: 'easeInOut' }
}),
}
const shrinkIconAnimation = ref('initial')
const shrinkIconVariants = {
initial: { strokeWidth: 2 },
shrinkIcons: (index) => ({
animationName: 'shrinkIcons',
opacity: 1,
strokeWidth: 0,
transition: { delay: 1.8, duration: 1.5, ease: 'easeInOut' }
})
}
</script>
<template>
<div class="home-hero-animation-container">
<div class="home-hero-animation">
<motion.svg xmlns="http://www.w3.org/2000/svg" viewBox="-12 -12 48 48" fill="none" overflow="auto"
stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="hero-background"
:style="{ opacity }">
<g class="svg-preview-grid-group" stroke-linecap="butt" stroke-width="0.1" stroke="#777"
mask="url(#svg-preview-bounding-box-mask)" stroke-opacity="0.3">
<path
stroke-dasharray="0 0.1 0.1 0.15 0.1 0.15 0.1 0.15 0.1 0.15 0.1 0.15 0.1 0.15 0.1 0.15 0.1 0.15 0.1 0.15 0.1 0.15 0.1 0.15 0 0.15"
stroke-width="0.1"
d="M1 0.1v23.8M2 0.1v23.8M4 0.1v23.8M5 0.1v23.8M7 0.1v23.8M8 0.1v23.8M10 0.1v23.8M11 0.1v23.8M13 0.1v23.8M14 0.1v23.8M16 0.1v23.8M17 0.1v23.8M19 0.1v23.8M20 0.1v23.8M22 0.1v23.8M23 0.1v23.8M0.1 1h23.8M0.1 2h23.8M0.1 4h23.8M0.1 5h23.8M0.1 7h23.8M0.1 8h23.8M0.1 10h23.8M0.1 11h23.8M0.1 13h23.8M0.1 14h23.8M0.1 16h23.8M0.1 17h23.8M0.1 19h23.8M0.1 20h23.8M0.1 22h23.8M0.1 23h23.8">
</path>
<path
d="M3 0.1v23.8M6 0.1v23.8M9 0.1v23.8M12 0.1v23.8M15 0.1v23.8M18 0.1v23.8M21 0.1v23.8M0.1 3h23.8M0.1 6h23.8M0.1 9h23.8M0.1 12h23.8M0.1 15h23.8M0.1 18h23.8M0.1 21h23.8">
</path>
</g>
<!-- <rect fill="red" x="0" y="0" width="24" height="24" fill-opacity="0.1" stroke="none" /> -->
<motion.g initial="initial" :variants="shrinkIconVariants" :animate="shrinkIconAnimation"
@animation-complete="onAnimationComplete">
<MotionLucideIcon v-for="(icon, index) in icons" size="2" class="animated-icon" initial="initial"
:key="icon.name" :variants="iconAnimationVariants" :animate="iconGridAnimation" :custom="index"
strokeWidth="inherit" v-bind="icon" @animation-complete="onAnimationComplete" />
<motion.g class="svg-preview-colored-path-group" :variants="scaleDownVariants" :animate="scaleDownAnimation"
initial="hidden" @animation-complete="onAnimationComplete">
<motion.path
d="M14 12C14 9.79086 12.2091 8 10 8C7.79086 8 6 9.79086 6 12C6 16.4183 9.58172 20 14 20C18.4183 20 22 16.4183 22 12C22 8.446 20.455 5.25285 18 3.05557"
:style="{ stroke: 'var(--vp-c-gray-1)' }" :animate="drawAnimation" initial="hidden" :variants="draw"
:custom="1" @animation-complete="onAnimationComplete" />
<motion.path
d="M10 12C10 14.2091 11.7909 16 14 16C16.2091 16 18 14.2091 18 12C18 7.58172 14.4183 4 10 4C5.58172 4 2 7.58172 2 12C2 15.5841 3.57127 18.8012 6.06253 21"
:style="{ stroke: 'var(--vp-c-gray-1)' }" :animate="drawAnimation" initial="hidden" :variants="draw"
:custom="0" />
</motion.g>
</motion.g>
<motion.g class="svg-preview-control-path-marker-group" stroke="#fff" stroke-width="0.125"
:initial="{ opacity: 1 }" :animate="showHandles ? { opacity: 1 } : { opacity: 0 }"
:transition="{ delay: 0, duration: 0.2 }">
<motion.path
d="M14 12C14 9.79086 12.2091 8 10 8C7.79086 8 6 9.79086 6 12C6 16.4183 9.58172 20 14 20C18.4183 20 22 16.4183 22 12C22 8.446 20.455 5.25285 18 3.05557"
:initial="{ opacity: 0 }" :animate="{ opacity: 1 }" :transition="{ delay: 1.6, duration: 1.5 }" />
<motion.path
d="M10 12C10 14.2091 11.7909 16 14 16C16.2091 16 18 14.2091 18 12C18 7.58172 14.4183 4 10 4C5.58172 4 2 7.58172 2 12C2 15.5841 3.57127 18.8012 6.06253 21"
:initial="{ opacity: 0 }" :animate="{ opacity: 1 }" :transition="{ delay: 1.6, duration: 1.5 }" />
<motion.g :initial="{ opacity: 0 }" :animate="{ opacity: 1 }" :transition="{ delay: 0.2, duration: 0.3 }">
<path
d="M14 12h.01M10 8h.01M10 8h.01M6 12h.01M6 12h.01M14 20h.01M14 20h.01M22 12h.01M22 12h.01M18 3.05557h.01M10 12h.01M14 16h.01M14 16h.01M18 12h.01M18 12h.01M10 4h.01M10 4h.01M2 12h.01M2 12h.01M6.06253 21h.01">
</path>
</motion.g>
<motion.circle :initial="{ opacity: 0, scale: 0.2 }" :animate="{ opacity: 1, scale: 1 }"
:transition="{ delay: 0, duration: 0.8 }" cx="14" cy="12" r="0.5" />
<motion.circle :initial="{ opacity: 0, scale: 0.2 }" :animate="{ opacity: 1, scale: 1 }"
:transition="{ delay: 0, duration: 0.8 }" cx="14" cy="12" r="0.5" />
<motion.circle :initial="{ opacity: 0, scale: 0.2 }" :animate="{ opacity: 1, scale: 1 }"
:transition="{ delay: 0, duration: 0.8 }" cx="18" cy="3.05557" r="0.5" />
<motion.circle :initial="{ opacity: 0, scale: 0.2 }" :animate="{ opacity: 1, scale: 1 }"
:transition="{ delay: 0, duration: 0.8 }" cx="10" cy="12" r="0.5" />
<motion.circle :initial="{ opacity: 0, scale: 0.2 }" :animate="{ opacity: 1, scale: 1 }"
:transition="{ delay: 0, duration: 0.8 }" cx="6.06253" cy="21" r="0.5" />
</motion.g>
<motion.g class="svg-preview-handles-group" stroke-width="0.12" stroke="#FFF" stroke-opacity="0.3"
:initial="{ opacity: 1 }" :animate="showHandles ? { opacity: 1 } : { opacity: 0 }"
:transition="{ delay: 0, duration: 0.6 }">
<motion.g :initial="{ opacity: 0, scale: 0.2 }" :animate="{ opacity: 1, scale: 1 }"
:transition="{ delay: 0.2, duration: 0.3 }">
<path d="M14 12 14 9.79086"></path>
<circle cy="9.79086" cx="14" r="0.25"></circle>
</motion.g>
<motion.g :initial="{ opacity: 0, scale: 0.2 }" :animate="{ opacity: 1, scale: 1 }"
:transition="{ delay: 0.4, duration: 0.3 }">
<path d="M10 8 12.2091 8"></path>
<circle cy="8" cx="12.2091" r="0.25"></circle>
<path d="M10 8 7.79086 8"></path>
<circle cy="8" cx="7.79086" r="0.25"></circle>
</motion.g>
<motion.g :initial="{ opacity: 0, scale: 0.2 }" :animate="{ opacity: 1, scale: 1 }"
:transition="{ delay: 0.6, duration: 0.3 }">
<path d="M6 12 6 9.79086"></path>
<circle cy="9.79086" cx="6" r="0.25"></circle>
<path d="M6 12 6 16.4183"></path>
<circle cy="16.4183" cx="6" r="0.25"></circle>
</motion.g>
<motion.g :initial="{ opacity: 0, scale: 0.2 }" :animate="{ opacity: 1, scale: 1 }"
:transition="{ delay: 0.8, duration: 0.3 }">
<path d="M14 20 9.58172 20"></path>
<circle cy="20" cx="9.58172" r="0.25"></circle>
<path d="M14 20 18.4183 20"></path>
<circle cy="20" cx="18.4183" r="0.25"></circle>
</motion.g>
<motion.g :initial="{ opacity: 0, scale: 0.2 }" :animate="{ opacity: 1, scale: 1 }"
:transition="{ delay: 1, duration: 0.3 }">
<path d="M22 12 22 16.4183"></path>
<circle cy="16.4183" cx="22" r="0.25"></circle>
<path d="M22 12 22 8.446"></path>
<circle cy="8.446" cx="22" r="0.25"></circle>
</motion.g>
<motion.g :initial="{ opacity: 0, scale: 0.2 }" :animate="{ opacity: 1, scale: 1 }"
:transition="{ delay: 1.2, duration: 0.3 }">
<path d="M18 3.05557 20.455 5.25285"></path>
<circle cy="5.25285" cx="20.455" r="0.25"></circle>
</motion.g>
<motion.g :initial="{ opacity: 0, scale: 0.2 }" :animate="{ opacity: 1, scale: 1 }"
:transition="{ delay: 0.2, duration: 0.3 }">
<path d="M10 12 10 14.2091"></path>
<circle cy="14.2091" cx="10" r="0.25"></circle>
</motion.g>
<motion.g :initial="{ opacity: 0, scale: 0.2 }" :animate="{ opacity: 1, scale: 1 }"
:transition="{ delay: 0.4, duration: 0.3 }">
<path d="M14 16 11.7909 16"></path>
<circle cy="16" cx="11.7909" r="0.25"></circle>
<path d="M14 16 16.2091 16"></path>
<circle cy="16" cx="16.2091" r="0.25"></circle>
</motion.g>
<motion.g :initial="{ opacity: 0, scale: 0.2 }" :animate="{ opacity: 1, scale: 1 }"
:transition="{ delay: 0.6, duration: 0.3 }">
<path d="M18 12 18 14.2091"></path>
<circle cy="14.2091" cx="18" r="0.25"></circle>
<path d="M18 12 18 7.58172"></path>
<circle cy="7.58172" cx="18" r="0.25"></circle>
</motion.g>
<motion.g :initial="{ opacity: 0, scale: 0.2 }" :animate="{ opacity: 1, scale: 1 }"
:transition="{ delay: 0.8, duration: 0.3 }">
<path d="M10 4 14.4183 4"></path>
<circle cy="4" cx="14.4183" r="0.25"></circle>
<path d="M10 4 5.58172 4"></path>
<circle cy="4" cx="5.58172" r="0.25"></circle>
</motion.g>
<motion.g :initial="{ opacity: 0, scale: 0.2 }" :animate="{ opacity: 1, scale: 1 }"
:transition="{ delay: 1, duration: 0.3 }">
<path d="M2 12 2 7.58172"></path>
<circle cy="7.58172" cx="2" r="0.25"></circle>
<path d="M2 12 2 15.5841"></path>
<circle cy="15.5841" cx="2" r="0.25"></circle>
</motion.g>
<motion.g :initial="{ opacity: 0, scale: 0.2 }" :animate="{ opacity: 1, scale: 1 }"
:transition="{ delay: 1.2, duration: 0.3 }">
<path d="M6.06253 21 3.57127 18.8012"></path>
<circle cy="18.8012" cx="3.57127" r="0.25"></circle>
</motion.g>
</motion.g>
</motion.svg>
</div>
</div>
</template>
<style scoped>
.home-hero-animation-container {
margin: -48px -24px 0;
display: flex;
}
.home-hero-animation {
height: 250px;
width: 396px;
overflow: hidden;
margin: auto;
margin-left: calc(((396px - 100vw) / 2)* -1);
}
@media (min-width: 396px) {
.home-hero-animation {
margin-left: auto;
}
}
.hero-background {
transform: rotateX(-51deg) rotateZ(-43deg);
transform-style: preserve-3d;
will-change: transform, opacity;
position: relative;
top: -155px;
left: -82px;
width: 560px;
height: 560px;
}
@media (min-width: 640px) {
.hero-background {
width: 680px;
height: 680px;
left: -100px;
top: -188px;
}
.home-hero-animation {
height: 305px;
width: 480px;
}
}
@media (min-width: 768px) {
.hero-background {
width: 760px;
height: 760px;
left: -110px;
top: -200px;
}
.home-hero-animation {
height: 360px;
width: 540px;
}
.home-hero-animation-container {
margin-top: -60px;
}
}
@media (min-width: 960px) {
.hero-background {
top: -20vw;
right: 20vw;
width: 80vw;
height: 80vw;
}
.home-hero-animation {
height: 415px;
width: 620px;
}
.home-hero-animation-container {
margin: -48px -48px 0 -64px;
}
}
@media (min-width: 1160px) {
.home-hero-animation-container {
margin-right: -64px;
margin-bottom: -180px;
}
.home-hero-animation {
width: auto;
height: calc(((1152px/2)));
top: -20px;
}
.hero-background {
top: -20vw;
}
}
@media (min-width: 1280px) {
.home-hero-animation-container {
margin-right: calc(((((100vw - 1152px) / 2)) * -1) + 24px);
margin-left: -128px
}
.hero-background {
width: 1024px;
height: 1024px;
top: -280px;
}
}
</style>

View File

@@ -1,153 +1,13 @@
<script setup lang="ts">
import { ref, onMounted, shallowRef, onBeforeUnmount} from 'vue';
import { data } from './HomeHeroIconsCard.data'
import LucideIcon from '../base/LucideIcon.vue'
import { useRouter } from 'vitepress';
import { random } from 'lodash-es'
import FakeInput from '../base/FakeInput.vue'
const { go } = useRouter()
const intervalTime = shallowRef()
const getInitialItems = () => data.icons.slice(0, 48)
const items = ref(getInitialItems())
let id = items.value.length + 1
function getRandomNewIcon() {
const randomIndex = random(0, 200)
const newRandomIcon = data.icons[randomIndex]
if (items.value.some((item) => item.name === newRandomIcon.name)) {
return getRandomNewIcon()
}
return newRandomIcon
}
function insert() {
const replaceIndex = random(0, 48)
const newIcon = getRandomNewIcon()
items.value[replaceIndex] = newIcon
}
function startInterval() {
intervalTime.value = setInterval(() => {
insert()
}, 2000)
}
// TODO: Try maybe something else for better pref performance
onMounted(() => {
window.addEventListener('mousemove', startInterval, { once: true })
})
onBeforeUnmount(() => {
clearInterval(intervalTime.value)
})
import { ref } from 'vue';
import HomeHeroIconsAnimation from './HomeHeroIconsAnimation.vue'
const animationRun = ref(1)
</script>
<template>
<div class="card-wrapper">
<div class="icons-card">
<div class="card-grid">
<TransitionGroup name="list" mode="out-in">
<div
v-for="icon in items"
:key="icon.name"
class="random-icon"
>
<LucideIcon
v-bind="icon"
/>
</div>
</TransitionGroup>
</div>
<FakeInput @click="go('/icons/?focus')" class="search-box">
Search {{ data.iconsCount }} icons...
</FakeInput>
</div>
</div>
<HomeHeroIconsAnimation
:key="animationRun"
@animation-complete="animationRun++"
/>
</template>
<style scoped>
.card-wrapper {
margin-left: auto;
margin-bottom: auto;
margin-top: 48px;
}
.icons-card {
background: var(--vp-c-bg-alt);
padding: 24px;
border-radius: 8px;
width: 100%;
height:100%;
max-height: 220px;
max-width: 560px;
margin: 0 auto;
position: relative;
}
.card-grid {
display: grid;
gap: 8px;
grid-template-columns: repeat(auto-fill, minmax(36px, 1fr));
grid-template-rows: repeat(auto-fill, minmax(36px, 1fr));
width: 100%;
height:100%;
max-height: 168px;
max-width: 512px;
overflow: hidden;
position: relative;
}
.list-enter-active {
transition: all 0.5s cubic-bezier(.85,.85,.25,1.1);
}
.list-enter-from,
.list-leave-to {
opacity: 0;
transform: scale(0.01);
}
.list-leave-active {
position: absolute;
opacity: 0;
display: none;
}
.search-box {
position: absolute;
width: 100%;
left: 0;
top: -64px;
}
.random-icon {
display: inline-flex;
justify-content: center;
align-items: center;
}
@media (min-width: 960px) {
.search-box {
top: unset;
bottom: -24px;
left: -24px;
box-shadow: var(--vp-shadow-3);
background: var(--vp-c-bg);
}
.dark .search-box {
background: var(--vp-c-bg-soft);
}
.card-wrapper {
margin-top: 8px;
}
}
</style>

View File

@@ -1,25 +1,22 @@
<script setup lang="ts">
import Badge from '../base/Badge.vue';
import HomeContainer from './HomeContainer.vue';
import { data } from './HomeHeroBefore.data'
import { data } from './HomeHeroInfoBefore.data'
</script>
<template>
<HomeContainer class="container">
<Badge
:href="`https://github.com/lucide-icons/lucide/releases/tag/${data.version}`"
>v{{ data.version }}</Badge>
</HomeContainer>
<Badge :href="`https://github.com/lucide-icons/lucide/releases/tag/${data.version}`">v{{ data.version }}</Badge>
</template>
<style scoped>
.container {
margin-block: 0;;
margin-block: 0;
margin-top: 37px;
margin-bottom: -96px;
display: flex;
justify-content: center;
}
.badge {
display: inline-block;
}
@@ -34,9 +31,9 @@ import { data } from './HomeHeroBefore.data'
.container {
justify-content: flex-start;
}
.badge {
display: inline-block;
}
}
</style>

View File

@@ -48,6 +48,7 @@ function resetStyle () {
color.value = 'currentColor'
strokeWidth.value = 2
size.value = 24
absoluteStrokeWidth.value = false
}
watch(absoluteStrokeWidth, (enabled) => {

View File

@@ -13,9 +13,9 @@ export default {
label: 'Lucide documentation for React',
},
{
name: 'lucide-vue-next',
name: 'lucide-vue',
logo: '/framework-logos/vue.svg',
label: 'Lucide documentation for Vue 3',
label: 'Lucide documentation for Vue',
},
{
name: 'lucide-svelte',

View File

@@ -9,7 +9,7 @@ import {useData, useRouter} from 'vitepress';
import { computed } from 'vue';
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon';
import { diamond } from '../../../data/iconNodes'
import deprecationReasonTemplate from '../../../../../tools/build-icons/utils/deprecationReasonTemplate.mjs';
import deprecationReasonTemplate from '../../../../../tools/build-icons/utils/deprecationReasonTemplate.ts';
const props = defineProps<{

View File

@@ -178,6 +178,8 @@ const DiamondIcon = createLucideIcon('Diamond', diamond)
stroke-width: var(--customize-strokeWidth, 2);
width: calc(var(--customize-size, 24) * 1px);
height: calc(var(--customize-size, 24) * 1px);
max-width: 3rem;
max-height: 3rem;
}
html.absolute-stroke-width .lucide-icon.customizable {

View File

@@ -26,10 +26,9 @@ const iconComponent = computed(() => {
<component
ref="previewIcon"
:is="iconComponent"
:width="size"
:height="size"
:stroke="color"
:stroke-width="absoluteStrokeWidth ? Number(strokeWidth) * 24 / Number(size) : strokeWidth"
:size="size"
:color="color"
:strokeWidth="absoluteStrokeWidth ? Number(strokeWidth) * 24 / Number(size) : strokeWidth"
/>
<svg class="icon-grid" :viewBox="`0 0 ${size} ${size}`" fill="none" stroke-width="0.1" xmlns="http://www.w3.org/2000/svg">
<g :key="`grid-${i}`" v-for="(_, i) in gridLines">

View File

@@ -29,7 +29,12 @@ const props = defineProps<{
const iconComponent = computed(() => {
if (!props.name || !props.iconNode) return null;
return createLucideIcon(props.name, props.iconNode);
try {
return createLucideIcon(props.name, props.iconNode);
} catch (error) {
console.warn(`Icon ${props.name} not found, using fallback`);
return null;
}
});
const CalendarIcon = createLucideIcon('calendar', Calendar.iconNode);
@@ -61,7 +66,7 @@ const prettyName = props.name
</script>
<template>
<section class="showcase">
<section class="showcase" v-if="iconComponent">
<h2 class="title">See this icon in action</h2>
<div class="showcase-grid">
<div class="showcase-item column">

View File

@@ -4,6 +4,7 @@ import type { IconEntity, Category } from '../../types';
import useSearch from '../../composables/useSearch';
import InputSearch from '../base/InputSearch.vue';
import useSearchInput from '../../composables/useSearchInput';
import useSearchShortcut from '../../utils/useSearchShortcut';
import StickyBar from './StickyBar.vue';
import IconsCategory from './IconsCategory.vue';
import useFetchTags from '../../composables/useFetchTags';
@@ -27,6 +28,10 @@ const activeIconName = ref(null);
const { searchInput, searchQuery, searchQueryDebounced } = useSearchInput();
const isSearching = computed(() => !!searchQuery.value);
const { shortcutText: kbdSearchShortcut } = useSearchShortcut(() => {
searchInput.value?.focus();
});
function setActiveIconName(name: string) {
activeIconName.value = name;
}
@@ -154,6 +159,7 @@ watchEffect(() => {
<InputSearch
:placeholder="`Search ${icons.length} icons ...`"
v-model="searchQuery"
:shortcut="kbdSearchShortcut"
class="input-wrapper"
ref="searchInput"
@focus="onFocusSearchInput"

View File

@@ -1,11 +1,13 @@
<script setup lang="ts">
import { ref, computed, defineAsyncComponent, onMounted, watch } from 'vue';
import { ref, computed, defineAsyncComponent, onMounted, onBeforeUnmount, watch } from 'vue';
import type { IconEntity } from '../../types';
import { useElementSize, useEventListener, useVirtualList } from '@vueuse/core';
import { useRoute } from 'vitepress';
import IconGrid from './IconGrid.vue';
import InputSearch from '../base/InputSearch.vue';
import useSearch from '../../composables/useSearch';
import useSearchInput from '../../composables/useSearchInput';
import useSearchShortcut from '../../utils/useSearchShortcut';
import StickyBar from './StickyBar.vue';
import useFetchTags from '../../composables/useFetchTags';
import useFetchCategories from '../../composables/useFetchCategories';
@@ -14,7 +16,15 @@ import CarbonAdOverlay from './CarbonAdOverlay.vue';
const ICON_SIZE = 56;
const ICON_GRID_GAP = 8;
const DEFAULT_GRID_ITEMS = 10 * 160;
const initialGridItems = computed(() => {
if (containerWidth.value === 0) return 120;
const itemsPerRow = columnSize.value || 10;
const visibleRows = Math.ceil(window.innerHeight / (ICON_SIZE + ICON_GRID_GAP));
return Math.min(itemsPerRow * (visibleRows + 2), 200);
});
const props = defineProps<{
icons: IconEntity[];
@@ -50,6 +60,11 @@ const mappedIcons = computed(() => {
});
const { searchInput, searchQuery, searchQueryDebounced } = useSearchInput();
const { shortcutText: kbdSearchShortcut } = useSearchShortcut(() => {
searchInput.value?.focus();
});
const searchResults = useSearch(searchQueryDebounced, mappedIcons, [
{ name: 'name', weight: 3 },
{ name: 'aliases', weight: 3 },
@@ -72,8 +87,13 @@ const { list, containerProps, wrapperProps, scrollTo } = useVirtualList(
onMounted(() => {
containerProps.ref.value = document.documentElement;
useEventListener(window, 'scroll', containerProps.onScroll)
})
// Check if we should focus the search input from URL parameter
const route = useRoute()
if (route.data?.relativePath && window.location.search.includes('focus')) {
searchInput.value?.focus()
}
})
function setActiveIconName(name: string) {
activeIconName.value = name;
@@ -110,20 +130,20 @@ function handleCloseDrawer() {
:placeholder="`Search ${icons.length} icons ...`"
v-model="searchQuery"
ref="searchInput"
:shortcut="kbdSearchShortcut"
class="input-wrapper"
@focus="onFocusSearchInput"
/>
</StickyBar>
<NoResults
v-if="list.length === 0 && searchQuery !== ''"
v-if="searchResults.length === 0 && searchQuery !== ''"
:searchQuery="searchQuery"
@clear="searchQuery = ''"
/>
<IconGrid
v-else-if="list.length === 0"
:key="index"
overlayMode
:icons="[...searchResults].splice(0, DEFAULT_GRID_ITEMS)"
:icons="searchResults.slice(0, initialGridItems)"
:activeIcon="activeIconName"
@setActiveIcon="setActiveIconName"
/>
@@ -162,6 +182,7 @@ function handleCloseDrawer() {
.input-wrapper {
width: 100%;
view-transition-name: icons-search-box;
}
.overview-container {

View File

@@ -47,6 +47,7 @@ function resetStyle () {
color.value = STYLE_DEFAULTS.color
strokeWidth.value = STYLE_DEFAULTS.strokeWidth
size.value = STYLE_DEFAULTS.size
absoluteStrokeWidth.value = STYLE_DEFAULTS.absoluteStrokeWidth
}
watch(absoluteStrokeWidth, (enabled) => {
@@ -59,9 +60,8 @@ const customizingActive = computed(() => {
return color.value !== STYLE_DEFAULTS.color
|| strokeWidth.value !== STYLE_DEFAULTS.strokeWidth
|| size.value !== STYLE_DEFAULTS.size
|| absoluteStrokeWidth.value !== STYLE_DEFAULTS.absoluteStrokeWidth
})
</script>
<template>
@@ -110,7 +110,7 @@ const customizingActive = computed(() => {
name="size"
v-model="size"
:min="16"
:max="48"
:max="256"
:step="4"
/>
</InputField>
@@ -120,8 +120,8 @@ const customizingActive = computed(() => {
label="Absolute Stroke width"
>
<Switch
id="size"
name="size"
id="absolute-stroke-width"
name="absolute-stroke-width"
v-model="absoluteStrokeWidth"
/>
</InputField>
@@ -161,9 +161,4 @@ const customizingActive = computed(() => {
.color-picker {
margin-left: auto;
}
#absolute-stroke-width {
flex-direction: row-reverse;
}
</style>

View File

@@ -0,0 +1,379 @@
<script setup lang="ts">
import VPHero from 'vitepress/dist/client/theme-default/components/VPHero.vue'
import { useData } from 'vitepress/dist/client/theme-default/composables/data'
import FakeInput from '../base/FakeInput.vue';
import { useRouter } from 'vitepress';
import { data } from '../home/HomeHeroIconsCard.data'
import { useScroll } from '@vueuse/core';
import { computed } from 'vue';
const { go } = useRouter()
const { frontmatter: fm } = useData()
const { x, y, isScrolling, arrivedState, directions } = useScroll(window)
const opacity = computed(() => {
if (y.value < 0) return 1
if (y.value > 300) return 0
return 1 - (y.value / 300)
})
</script>
<template>
<VPHero v-if="fm.hero" class="VPHomeHero" :name="fm.hero.name" :text="fm.hero.text" :tagline="fm.hero.tagline"
:image="undefined" :actions="fm.hero.actions">
<template #home-hero-image></template>
<template #home-hero-actions-after>
<FakeInput @click="go('/icons/?focus')" class="search-box">
Search {{ data.iconsCount }} icons...
</FakeInput>
</template>
</VPHero>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-12 -12 48 48" fill="none" overflow="auto"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="hero-background" :style="{ opacity: opacity }">
<g class="svg-preview-grid-group" stroke-linecap="butt" stroke-width="0.1" stroke="#777"
stroke-opacity="0.3">
<!-- <rect class="svg-preview-grid-rect" width="23.9" height="23.9" x="0.05" y="0.05" rx="1"></rect> -->
<path
stroke-dasharray="0 0.1 0.1 0.15 0.1 0.15 0.1 0.15 0.1 0.15 0.1 0.15 0.1 0.15 0.1 0.15 0.1 0.15 0.1 0.15 0.1 0.15 0.1 0.15 0 0.15"
stroke-width="0.1"
d="M1 0.1v23.8M2 0.1v23.8M4 0.1v23.8M5 0.1v23.8M7 0.1v23.8M8 0.1v23.8M10 0.1v23.8M11 0.1v23.8M13 0.1v23.8M14 0.1v23.8M16 0.1v23.8M17 0.1v23.8M19 0.1v23.8M20 0.1v23.8M22 0.1v23.8M23 0.1v23.8M0.1 1h23.8M0.1 2h23.8M0.1 4h23.8M0.1 5h23.8M0.1 7h23.8M0.1 8h23.8M0.1 10h23.8M0.1 11h23.8M0.1 13h23.8M0.1 14h23.8M0.1 16h23.8M0.1 17h23.8M0.1 19h23.8M0.1 20h23.8M0.1 22h23.8M0.1 23h23.8">
</path>
<path
d="M3 0.1v23.8M6 0.1v23.8M9 0.1v23.8M12 0.1v23.8M15 0.1v23.8M18 0.1v23.8M21 0.1v23.8M0.1 3h23.8M0.1 6h23.8M0.1 9h23.8M0.1 12h23.8M0.1 15h23.8M0.1 18h23.8M0.1 21h23.8">
</path>
</g>
<g class="svg-preview-shadow-mask-group" stroke-width="4" stroke="#777" stroke-opacity="0.15">
<mask id="svg-preview-shadow-mask-0" maskUnits="userSpaceOnUse" stroke-opacity="1" stroke-width="4" stroke="#000">
<rect x="0" y="0" width="100%" height="100%" fill="#fff" stroke="none" rx="1"></rect>
<path d="M4.9 16.1h.01M4.9 1.9h.01"></path>
</mask>
<mask id="svg-preview-shadow-mask-1" maskUnits="userSpaceOnUse" stroke-opacity="1" stroke-width="4" stroke="#000">
<rect x="0" y="0" width="100%" height="100%" fill="#fff" stroke="none" rx="1"></rect>
<path d="M7.8 4.7h.01M7 12.2h.01"></path>
</mask>
<mask id="svg-preview-shadow-mask-2" maskUnits="userSpaceOnUse" stroke-opacity="1" stroke-width="4" stroke="#000">
<rect x="0" y="0" width="100%" height="100%" fill="#fff" stroke="none" rx="1"></rect>
<path d="M12 7h.01M14 9h.01M12 11h.01M10 9h.01"></path>
</mask>
<mask id="svg-preview-shadow-mask-3" maskUnits="userSpaceOnUse" stroke-opacity="1" stroke-width="4" stroke="#000">
<rect x="0" y="0" width="100%" height="100%" fill="#fff" stroke="none" rx="1"></rect>
<path d="M16.2 4.8h.01M17 12.27h.01"></path>
</mask>
<mask id="svg-preview-shadow-mask-4" maskUnits="userSpaceOnUse" stroke-opacity="1" stroke-width="4" stroke="#000">
<rect x="0" y="0" width="100%" height="100%" fill="#fff" stroke="none" rx="1"></rect>
<path d="M19.1 1.9h.01M19.1 16h.01"></path>
</mask>
<mask id="svg-preview-shadow-mask-5" maskUnits="userSpaceOnUse" stroke-opacity="1" stroke-width="4" stroke="#000">
<rect x="0" y="0" width="100%" height="100%" fill="#fff" stroke="none" rx="1"></rect>
<path d="M9.5 18h.01M14.5 18h.01"></path>
</mask>
<mask id="svg-preview-shadow-mask-6" maskUnits="userSpaceOnUse" stroke-opacity="1" stroke-width="4" stroke="#000">
<rect x="0" y="0" width="100%" height="100%" fill="#fff" stroke="none" rx="1"></rect>
<path d="M8 22h.01M12 11h.01M16 22h.01"></path>
</mask>
</g>
<!-- <g class="svg-preview-shadow-group" stroke-width="4" stroke="#777" stroke-opacity="0.15">
<path mask="url(#svg-preview-shadow-mask-0)" d="M 4.9 16.1 C1 12.2 1 5.8 4.9 1.9"></path>
<path mask="url(#svg-preview-shadow-mask-1)" d="M 7.8 4.7 A6.14 6.14 0 0 0 7 12.2"></path>
<path mask="url(#svg-preview-shadow-mask-2)" d="M 12 7 A2 2 0 0 1 14 9"></path>
<path mask="url(#svg-preview-shadow-mask-2)" d="M 14 9 A2 2 0 0 1 12 11"></path>
<path mask="url(#svg-preview-shadow-mask-2)" d="M 12 11 A2 2 0 0 1 10 9"></path>
<path mask="url(#svg-preview-shadow-mask-2)" d="M 10 9 A2 2 0 0 1 12 7"></path>
<path mask="url(#svg-preview-shadow-mask-3)" d="M 16.2 4.8 C18.2 6.8 18.46 9.91 17 12.27"></path>
<path mask="url(#svg-preview-shadow-mask-4)" d="M 19.1 1.9 A9.96 9.96 0 0 1 19.1 16"></path>
<path mask="url(#svg-preview-shadow-mask-5)" d="M 9.5 18 L 14.5 18"></path>
<path mask="url(#svg-preview-shadow-mask-6)" d="M 8 22 L 12 11"></path>
<path mask="url(#svg-preview-shadow-mask-6)" d="M 12 11 L 16 22"></path>
<path
d="M4.9 16.1h.01M4.9 1.9h.01M7.8 4.7h.01M7 12.2h.01M12 7h.01M14 9h.01M12 11h.01M10 9h.01M16.2 4.8h.01M17 12.27h.01M19.1 1.9h.01M19.1 16h.01M9.5 18h.01M14.5 18h.01M8 22h.01M16 22h.01">
</path>
</g> -->
<g>
<defs xmlns="http://www.w3.org/2000/svg">
<pattern id="backdrop-pattern-:R4:" width=".1" height=".1" patternUnits="userSpaceOnUse"
patternTransform="rotate(45 50 50)">
<line stroke="red" stroke-width="0.1" y2="1"></line>
<line stroke="red" stroke-width="0.1" y2="1"></line>
</pattern>
</defs>
<g stroke-width="4">
<mask id="svg-preview-backdrop-mask-:R4:-0" maskUnits="userSpaceOnUse">
<path stroke="white" d="M 4.9 16.1 C1 12.2 1 5.8 4.9 1.9"></path>
</mask>
<path d="M 7.8 4.7 A6.14 6.14 0 0 0 7 12.2" stroke="url(#backdrop-pattern-:R4:)" stroke-width="4"
stroke-opacity="0.75" mask="url(#svg-preview-backdrop-mask-:R4:-0)"></path>
</g>
<g stroke-width="4">
<mask id="svg-preview-backdrop-mask-:R4:-0" maskUnits="userSpaceOnUse">
<path stroke="white" d="M 4.9 16.1 C1 12.2 1 5.8 4.9 1.9"></path>
</mask>
<path
d="M 12 7 A2 2 0 0 1 14 9 M 14 9 A2 2 0 0 1 12 11 M 12 11 A2 2 0 0 1 10 9 M 10 9 A2 2 0 0 1 12 7 M 8 22 L 12 11 M 12 11 L 16 22 M 9.5 18 L 14.5 18"
stroke="url(#backdrop-pattern-:R4:)" stroke-width="4" stroke-opacity="0.75"
mask="url(#svg-preview-backdrop-mask-:R4:-0)"></path>
</g>
<g stroke-width="4">
<mask id="svg-preview-backdrop-mask-:R4:-1" maskUnits="userSpaceOnUse">
<path stroke="white" d="M 7.8 4.7 A6.14 6.14 0 0 0 7 12.2"></path>
</mask>
<path
d="M 12 7 A2 2 0 0 1 14 9 M 14 9 A2 2 0 0 1 12 11 M 12 11 A2 2 0 0 1 10 9 M 10 9 A2 2 0 0 1 12 7 M 8 22 L 12 11 M 12 11 L 16 22 M 9.5 18 L 14.5 18"
stroke="url(#backdrop-pattern-:R4:)" stroke-width="4" stroke-opacity="0.75"
mask="url(#svg-preview-backdrop-mask-:R4:-1)"></path>
</g>
<g stroke-width="4">
<mask id="svg-preview-backdrop-mask-:R4:-0" maskUnits="userSpaceOnUse">
<path stroke="white" d="M 4.9 16.1 C1 12.2 1 5.8 4.9 1.9"></path>
</mask>
<path d="M 16.2 4.8 C18.2 6.8 18.46 9.91 17 12.27" stroke="url(#backdrop-pattern-:R4:)" stroke-width="4"
stroke-opacity="0.75" mask="url(#svg-preview-backdrop-mask-:R4:-0)"></path>
</g>
<g stroke-width="4">
<mask id="svg-preview-backdrop-mask-:R4:-1" maskUnits="userSpaceOnUse">
<path stroke="white" d="M 7.8 4.7 A6.14 6.14 0 0 0 7 12.2"></path>
</mask>
<path d="M 16.2 4.8 C18.2 6.8 18.46 9.91 17 12.27" stroke="url(#backdrop-pattern-:R4:)" stroke-width="4"
stroke-opacity="0.75" mask="url(#svg-preview-backdrop-mask-:R4:-1)"></path>
</g>
<g stroke-width="4">
<mask id="svg-preview-backdrop-mask-:R4:-2" maskUnits="userSpaceOnUse">
<path stroke="white"
d="M 12 7 A2 2 0 0 1 14 9 M 14 9 A2 2 0 0 1 12 11 M 12 11 A2 2 0 0 1 10 9 M 10 9 A2 2 0 0 1 12 7 M 8 22 L 12 11 M 12 11 L 16 22 M 9.5 18 L 14.5 18">
</path>
</mask>
<path d="M 16.2 4.8 C18.2 6.8 18.46 9.91 17 12.27" stroke="url(#backdrop-pattern-:R4:)" stroke-width="4"
stroke-opacity="0.75" mask="url(#svg-preview-backdrop-mask-:R4:-2)"></path>
</g>
<g stroke-width="4">
<mask id="svg-preview-backdrop-mask-:R4:-0" maskUnits="userSpaceOnUse">
<path stroke="white" d="M 4.9 16.1 C1 12.2 1 5.8 4.9 1.9"></path>
</mask>
<path d="M 19.1 1.9 A9.96 9.96 0 0 1 19.1 16" stroke="url(#backdrop-pattern-:R4:)" stroke-width="4"
stroke-opacity="0.75" mask="url(#svg-preview-backdrop-mask-:R4:-0)"></path>
</g>
<g stroke-width="4">
<mask id="svg-preview-backdrop-mask-:R4:-1" maskUnits="userSpaceOnUse">
<path stroke="white" d="M 7.8 4.7 A6.14 6.14 0 0 0 7 12.2"></path>
</mask>
<path d="M 19.1 1.9 A9.96 9.96 0 0 1 19.1 16" stroke="url(#backdrop-pattern-:R4:)" stroke-width="4"
stroke-opacity="0.75" mask="url(#svg-preview-backdrop-mask-:R4:-1)"></path>
</g>
<g stroke-width="4">
<mask id="svg-preview-backdrop-mask-:R4:-2" maskUnits="userSpaceOnUse">
<path stroke="white"
d="M 12 7 A2 2 0 0 1 14 9 M 14 9 A2 2 0 0 1 12 11 M 12 11 A2 2 0 0 1 10 9 M 10 9 A2 2 0 0 1 12 7 M 8 22 L 12 11 M 12 11 L 16 22 M 9.5 18 L 14.5 18">
</path>
</mask>
<path d="M 19.1 1.9 A9.96 9.96 0 0 1 19.1 16" stroke="url(#backdrop-pattern-:R4:)" stroke-width="4"
stroke-opacity="0.75" mask="url(#svg-preview-backdrop-mask-:R4:-2)"></path>
</g>
<g stroke-width="4">
<mask id="svg-preview-backdrop-mask-:R4:-3" maskUnits="userSpaceOnUse">
<path stroke="white" d="M 16.2 4.8 C18.2 6.8 18.46 9.91 17 12.27"></path>
</mask>
<path d="M 19.1 1.9 A9.96 9.96 0 0 1 19.1 16" stroke="url(#backdrop-pattern-:R4:)" stroke-width="4"
stroke-opacity="0.75" mask="url(#svg-preview-backdrop-mask-:R4:-3)"></path>
</g>
</g>
<g class="svg-preview-handles-group" stroke-width="0.12" stroke="#777" stroke-opacity="0.6">
<path d="M4.9 16.1 1 12.2"></path>
<circle cy="12.2" cx="1" r="0.25"></circle>
<path d="M4.9 1.9 1 5.8"></path>
<circle cy="5.8" cx="1" r="0.25"></circle>
<path d="M16.2 4.8 18.2 6.8"></path>
<circle cy="6.8" cx="18.2" r="0.25"></circle>
<path d="M17 12.27 18.46 9.91"></path>
<circle cy="9.91" cx="18.46" r="0.25"></circle>
</g>
<g class="svg-preview-colored-path-group">
<path d="M 4.9 16.1 C1 12.2 1 5.8 4.9 1.9" stroke="##dfdfd6"></path>
<path d="M 7.8 4.7 A6.14 6.14 0 0 0 7 12.2" stroke="##dfdfd6"></path>
<path d="M 12 7 A2 2 0 0 1 14 9" stroke="##dfdfd6"></path>
<path d="M 14 9 A2 2 0 0 1 12 11" stroke="##dfdfd6"></path>
<path d="M 12 11 A2 2 0 0 1 10 9" stroke="##dfdfd6"></path>
<path d="M 10 9 A2 2 0 0 1 12 7" stroke="##dfdfd6"></path>
<path d="M 16.2 4.8 C18.2 6.8 18.46 9.91 17 12.27" stroke="##dfdfd6"></path>
<path d="M 19.1 1.9 A9.96 9.96 0 0 1 19.1 16" stroke="##dfdfd6"></path>
<path d="M 9.5 18 L 14.5 18" stroke="##dfdfd6"></path>
<path d="M 8 22 L 12 11" stroke="##dfdfd6"></path>
<path d="M 12 11 L 16 22" stroke="##dfdfd6"></path>
</g>
<g class="svg-preview-radii-group" stroke-width="0.12" stroke-dasharray="0 0.25 0.25" stroke="#777"
stroke-opacity="0.3">
<circle cx="9.518750780437157" cy="16.261333416579962" r="0.25"></circle>
<circle cx="11.118750780437157" cy="1.261333416579964" r="0.25"></circle>
<path
d="M9.518750780437157 16.261333416579962L4.481249219562843 8.138666583420036L11.118750780437157 1.261333416579964">
</path>
<circle cx="4.481249219562843" cy="8.138666583420036" r="0.25"></circle>
<path d="M7 12.2L12.217985863765243 8.963918492134958L7.8 4.7"></path>
<circle cy="8.963918492134958" cx="12.217985863765243" r="0.25" stroke-dasharray="0" stroke="red"></circle>
<circle cy="8.963918492134958" cx="12.217985863765243" r="6.14" stroke="red"></circle>
<circle cy="9" cx="12" r="0.25" stroke-dasharray="0"></circle>
<circle cy="9" cx="12" r="2"></circle>
<circle cy="9" cx="12" r="0.25" stroke-dasharray="0"></circle>
<circle cy="9" cx="12" r="2"></circle>
<circle cy="9" cx="12" r="0.25" stroke-dasharray="0"></circle>
<circle cy="9" cx="12" r="2"></circle>
<circle cy="9" cx="12" r="0.25" stroke-dasharray="0"></circle>
<circle cy="9" cx="12" r="2"></circle>
<circle cx="12.035530040810755" cy="23.05" r="0.25"></circle>
<circle cx="12.035530040810755" cy="-5.1499999999999995" r="0.25"></circle>
<path d="M12.035530040810755 23.05L26.164469959189248 8.95L12.035530040810755 -5.1499999999999995"></path>
<circle cx="26.164469959189248" cy="8.95" r="0.25"></circle>
<path d="M19.1 16L12.064440320770494 8.95L19.1 1.9"></path>
<circle cy="8.95" cx="12.064440320770494" r="0.25" stroke-dasharray="0" stroke="red"></circle>
<circle cy="8.95" cx="12.064440320770494" r="9.96" stroke="red"></circle>
</g>
<g class="svg-preview-control-path-marker-mask-group" stroke-width="1" stroke="#000">
<mask id="svg-preview-control-path-marker-mask-0" maskUnits="userSpaceOnUse">
<rect x="0" y="0" width="100%" height="100%" fill="#ccc" stroke="none" rx="1"></rect>
<path d="M4.9 16.1h.01"></path>
<path d="M4.9 1.9h.01"></path>
</mask>
<mask id="svg-preview-control-path-marker-mask-1" maskUnits="userSpaceOnUse">
<rect x="0" y="0" width="100%" height="100%" fill="#ccc" stroke="none" rx="1"></rect>
<path d="M7.8 4.7h.01"></path>
<path d="M7 12.2h.01"></path>
</mask>
<mask id="svg-preview-control-path-marker-mask-6" maskUnits="userSpaceOnUse">
<rect x="0" y="0" width="100%" height="100%" fill="#ccc" stroke="none" rx="1"></rect>
<path d="M16.2 4.8h.01"></path>
<path d="M17 12.27h.01"></path>
</mask>
<mask id="svg-preview-control-path-marker-mask-7" maskUnits="userSpaceOnUse">
<rect x="0" y="0" width="100%" height="100%" fill="#ccc" stroke="none" rx="1"></rect>
<path d="M19.1 1.9h.01"></path>
<path d="M19.1 16h.01"></path>
</mask>
<mask id="svg-preview-control-path-marker-mask-8" maskUnits="userSpaceOnUse">
<rect x="0" y="0" width="100%" height="100%" fill="#ccc" stroke="none" rx="1"></rect>
<path d="M9.5 18h.01"></path>
<path d="M14.5 18h.01"></path>
</mask>
<mask id="svg-preview-control-path-marker-mask-9" maskUnits="userSpaceOnUse">
<rect x="0" y="0" width="100%" height="100%" fill="#ccc" stroke="none" rx="1"></rect>
<path d="M8 22h.01"></path>
<path d="M12 11h.01"></path>
</mask>
<mask id="svg-preview-control-path-marker-mask-10" maskUnits="userSpaceOnUse">
<rect x="0" y="0" width="100%" height="100%" fill="#ccc" stroke="none" rx="1"></rect>
<path d="M12 11h.01"></path>
<path d="M16 22h.01"></path>
</mask>
</g>
<g class="svg-preview-control-path-group" stroke="#fff" stroke-width="0.125">
<path mask="url(#svg-preview-control-path-marker-mask-0)" d="M 4.9 16.1 C1 12.2 1 5.8 4.9 1.9"></path>
<path mask="url(#svg-preview-control-path-marker-mask-1)" d="M 7.8 4.7 A6.14 6.14 0 0 0 7 12.2"></path>
<path d="M 12 7 A2 2 0 0 1 14 9"></path>
<path d="M 14 9 A2 2 0 0 1 12 11"></path>
<path d="M 12 11 A2 2 0 0 1 10 9"></path>
<path d="M 10 9 A2 2 0 0 1 12 7"></path>
<path mask="url(#svg-preview-control-path-marker-mask-6)" d="M 16.2 4.8 C18.2 6.8 18.46 9.91 17 12.27"></path>
<path mask="url(#svg-preview-control-path-marker-mask-7)" d="M 19.1 1.9 A9.96 9.96 0 0 1 19.1 16"></path>
<path mask="url(#svg-preview-control-path-marker-mask-8)" d="M 9.5 18 L 14.5 18"></path>
<path mask="url(#svg-preview-control-path-marker-mask-9)" d="M 8 22 L 12 11"></path>
<path mask="url(#svg-preview-control-path-marker-mask-10)" d="M 12 11 L 16 22"></path>
</g>
<g class="svg-preview-control-path-marker-group" stroke="#fff" stroke-width="0.125">
<path
d="M4.9 16.1h.01M4.9 1.9h.01M7.8 4.7h.01M7 12.2h.01M16.2 4.8h.01M17 12.27h.01M19.1 1.9h.01M19.1 16h.01M9.5 18h.01M14.5 18h.01M8 22h.01M12 11h.01M12 11h.01M16 22h.01">
</path>
<circle cx="4.9" cy="16.1" r="0.5"></circle>
<circle cx="4.9" cy="1.9" r="0.5"></circle>
<circle cx="7.8" cy="4.7" r="0.5"></circle>
<circle cx="7" cy="12.2" r="0.5"></circle>
<circle cx="16.2" cy="4.8" r="0.5"></circle>
<circle cx="17" cy="12.27" r="0.5"></circle>
<circle cx="19.1" cy="1.9" r="0.5"></circle>
<circle cx="19.1" cy="16" r="0.5"></circle>
<circle cx="9.5" cy="18" r="0.5"></circle>
<circle cx="14.5" cy="18" r="0.5"></circle>
<circle cx="8" cy="22" r="0.5"></circle>
<circle cx="16" cy="22" r="0.5"></circle>
</g>
<g class="svg-preview-handles-group" stroke-width="0.12" style="stroke: var(--vp-c-brand)" stroke-opacity="0.3">
<path d="M4.9 16.1 1 12.2"></path>
<circle cy="12.2" cx="1" r="0.25"></circle>
<path d="M4.9 1.9 1 5.8"></path>
<circle cy="5.8" cx="1" r="0.25"></circle>
<path d="M16.2 4.8 18.2 6.8"></path>
<circle cy="6.8" cx="18.2" r="0.25"></circle>
<path d="M17 12.27 18.46 9.91"></path>
<circle cy="9.91" cx="18.46" r="0.25"></circle>
</g>
</svg>
</template>
<style>
.hero {
overflow: hidden;
height: 60vh;
display: flex;
position: relative;
/* align-items: center;
justify-content: center; */
}
.hero-background {
transform: rotateX(-51deg) rotateZ(-43deg);
transform-style: preserve-3d;
position: fixed;
top: -240px;
right: -320px;
width: 112vw;
height: 112vh;
}
.hero-title {
font-size: 3.2rem;
line-height: 1.2;
font-weight: 700;
text-align: center;
max-width: 800px;
}
/* .VPHomeHero .image {
display: none;
}
.VPHomeHero .container {
justify-content: center;
text-align: center;
}
.VPHomeHero .main {
text-align: center;
} */
/* .VPHomeHero .container .actions {
justify-content: center;
} */
/*
@media (min-width: 960px) {
.VPHomeHero :deep(.actions) {
justify-content: center;
}
} */
@media screen and (prefers-color-scheme: light) {
.svg-preview-grid-rect { fill: none }
}
@media screen and (prefers-color-scheme: dark) {
.svg-preview-grid-rect { fill: none }
.svg
.svg-preview-grid-group,
.svg-preview-radii-group,
.svg-preview-shadow-mask-group,
.svg-preview-shadow-group {
stroke: #fff;
}
}
.search-box {
/* width: calc(100vw - 272px); */
width: 100%;
margin-top: 24px;
}
</style>

View File

@@ -0,0 +1,248 @@
<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, type Component } from 'vue';
import {
heart,
star,
zap,
code,
feather,
cloud,
sun,
moon,
camera,
music,
video,
globe,
layers,
package as packageIcon,
compass,
command,
terminal,
database,
server,
cpu,
lock,
key,
shield,
wifi,
download,
upload,
search,
settings,
users,
mail,
bell,
calendar,
clock,
gitBranch,
funnel,
bookmark,
tag,
sparkles,
} from '../../../data/iconNodes';
import createElement from 'lucide/src/createElement';
import { IconNode } from 'lucide';
const icons: Component[] = [
heart, star, zap, code, feather, cloud, sun, moon, camera, music,
video, globe, layers, packageIcon, compass, command, terminal, database,
server, cpu, lock, key, shield, wifi, download, upload, search,
settings, users, mail, bell, calendar, clock, gitBranch, funnel,
bookmark, tag, sparkles,
];
const svgs = icons.map(icon => {
const element = createElement(icon as IconNode, {
stroke: 'white',
opacity: '0.2',
})
return element.outerHTML;
});
const highlightedSvgs = icons.map(icon => {
const element = createElement(icon as IconNode, {
stroke: 'white',
})
return element.outerHTML;
});
const images = svgs.map(svg => {
const img = new Image();
img.src = `data:image/svg+xml;base64,${btoa(svg)}`;
return img;
});
const highlightedImages = highlightedSvgs.map(svg => {
const img = new Image();
img.src = `data:image/svg+xml;base64,${btoa(svg)}`;
return img;
});
const canvas = ref<HTMLCanvasElement | null>(null);
// Setting up the columns
const fontSize = 16;
const gap = 40;
const gapY = 8;
const intervalTime = ref<number | null>(null);
// Store individual drops with their positions
let individualDrops: Array<{x: number, y: number, active: boolean}> = [];
// Random integer between min and max
function randInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
onMounted(() => {
if (!canvas.value) return;
const ctx = canvas.value.getContext('2d');
if (!ctx) return;
const width = window.innerWidth;
const height = window.innerHeight * 0.6;
canvas.value.width = width;
canvas.value.height = height;
let columns = Math.floor(width / 120);
var rows = Math.floor(canvas.value.height / fontSize);
var midStart = Math.floor(rows * 0.25);
var midEnd = Math.floor(rows * 0.85);
let drops = Array.from({ length: columns }, () => randInt(midStart, midEnd));
let fps, fpsInterval, startTime, now, then, elapsed;
// Add click event listener
function handleCanvasClick(event: MouseEvent) {
const rect = canvas.value?.getBoundingClientRect();
if (!rect) return;
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
individualDrops.push({
x: x,
y: y,
active: true
});
}
canvas.value.addEventListener('click', handleCanvasClick);
function draw() {
if (!ctx) return;
ctx.fillStyle = 'rgba(27, 27, 31, 0.50)';
ctx.fillRect(0, 0, width, height);
ctx.fillStyle = 'rgba(255, 255, 255, 1)';
// Draw regular drops
for (var i = drops.length; i > 0; i--) {
var img = images[Math.floor(Math.random() * images.length)];
ctx.drawImage(img, i * fontSize * 2 + gap, drops[i] * fontSize, (fontSize / 2) + gapY, (fontSize / 2) + gapY);
drops[i]++;
if (Math.random() > .98) {
drops[i] = randInt(midStart, midEnd);
}
}
// Draw and update individual drops
individualDrops = individualDrops.filter(drop => {
if (!drop.active) return false;
var img = highlightedImages[Math.floor(Math.random() * images.length)];
ctx.drawImage(img, drop.x - fontSize/2, drop.y, fontSize, fontSize);
drop.y += fontSize
// Remove if off screen
if (drop.y > height) {
drop.active = false;
return false;
}
return true;
});
}
function startAnimating(fps) {
fpsInterval = 1000 / fps;
then = Date.now();
startTime = then;
animate();
}
function animate() {
// request another frame
requestAnimationFrame(animate);
// calc elapsed time since last loop
now = Date.now();
elapsed = now - then;
// if enough time has elapsed, draw the next frame
if (elapsed > fpsInterval) {
// Get ready for next frame by setting then=now, but also adjust for your
// specified fpsInterval not being a multiple of RAF's interval (16.7ms)
then = now - (elapsed % fpsInterval);
// Put your drawing code here
draw();
}
}
startAnimating(12);
window.addEventListener('resize', function() {
canvas.value.width = window.innerWidth;
canvas.value.height = window.innerHeight;
rows = Math.floor(canvas.value.height / fontSize);
columns = Math.floor(canvas.value.width / fontSize);
var oldDrops = drops.slice();
drops = [];
for (var i = 0; i < columns; i++) {
drops[i] = oldDrops[i % oldDrops.length] ?? randInt(midStart, midEnd);
}
});
// Cleanup event listener
onBeforeUnmount(() => {
canvas.value?.removeEventListener('click', handleCanvasClick);
});
});
</script>
<template>
<div class="hero">
<canvas ref="canvas" class="hero-canvas" />
</div>
</template>
<style scoped>
.hero {
overflow: hidden;
height: 60vh;
}
.hero-canvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 60vh;
filter: blur(0px);
}
</style>

View File

@@ -2,7 +2,7 @@
import { ref, inject, Ref } from 'vue';
export const ICON_STYLE_CONTEXT = Symbol('size');
export const ICON_STYLE_CONTEXT = Symbol('style');
interface IconSizeContext {
size: Ref<number>;

View File

@@ -1,27 +1,36 @@
import { h } from 'vue';
import { h, nextTick, onMounted, watch } from 'vue';
import DefaultTheme from 'vitepress/theme';
import './style.css';
import { Theme } from 'vitepress';
import 'virtual:group-icons.css'
import { Theme, useRouter } from 'vitepress';
import IconsSidebarNavAfter from './layouts/IconsSidebarNavAfter.vue';
import HomeHeroIconsCard from './components/home/HomeHeroIconsCard.vue';
import HomeHeroBefore from './components/home/HomeHeroBefore.vue';
import HomeHeroAfter from './components/home/HomeHeroAfter.vue';
import HomeHeroInfoBefore from './components/home/HomeHeroInfoBefore.vue';
import { ICON_STYLE_CONTEXT, iconStyleContext } from './composables/useIconStyle';
import { CATEGORY_VIEW_CONTEXT, categoryViewContext } from './composables/useCategoryView';
import { EXTERNAL_LIBS_CONTEXT, externalLibContext } from './composables/useExternalLibs';
import FrameworkSelect from './components/guide/FrameworkSelect.vue';
import SnackPlayer from './components/editors/SnackPlayer.vue';
import Sandbox from './components/editors/Sandbox.vue';
const theme: Partial<Theme> = {
extends: DefaultTheme,
Layout() {
return h(DefaultTheme.Layout, null, {
'home-hero-before': () => h(HomeHeroBefore),
'sidebar-nav-before': () => h(FrameworkSelect),
'home-hero-info-before': () => h(HomeHeroInfoBefore),
'sidebar-nav-after': () => h(IconsSidebarNavAfter),
'home-hero-image': () => h(HomeHeroIconsCard),
'home-hero-actions-after': () => h(HomeHeroAfter),
});
},
enhanceApp({ app }) {
app.provide(ICON_STYLE_CONTEXT, iconStyleContext);
app.provide(CATEGORY_VIEW_CONTEXT, categoryViewContext);
app.provide(EXTERNAL_LIBS_CONTEXT, externalLibContext);
app.component('SnackPlayer', SnackPlayer)
app.component('Sandbox', Sandbox);
},
};

View File

@@ -25,6 +25,13 @@
--vp-code-editor-string: #032f62;
--vp-c-text-4: rgba(60, 60, 67, 0.32);
--vp-home-hero-name-color: var(--vp-c-text);
--vp-a11y-danger: var(--vp-c-danger-3);
--vp-a11y-success: var(--vp-c-success-3);
--vp-a11y-warning: #da9200;
--vp-sidebar-input: var(--vp-c-gray-3);
}
.dark {
@@ -42,8 +49,23 @@
--vp-code-editor-string: #9ecbff;
--vp-c-text-4: rgba(235, 235, 245, 0.16);
--vp-a11y-danger: var(--vp-c-danger-2);
--vp-a11y-success: var(--vp-c-success-2);
--vp-a11y-warning: var(--vp-c-warning-2);
--vp-sidebar-input: var(--vp-c-bg-soft);
}
@view-transition {
navigation: auto;
}
/* ::view-transition-old(icons-search-box),
::view-transition-new(icons-search-box) {
animation: .3s transition 0s ease;
} */
.VPNavBarTitle .logo {
height: 36px;
width: 36px;
@@ -60,37 +82,16 @@
.VPHomeHero .image-container {
transform: none;
width: 100%;
/* padding: 0 24px; */
}
/* .VPHomeHero .container {
flex-direction: column-reverse;
} */
.VPHomeHero .container .main {
/* flex:1; */
flex-shirk: 0;
}
.VPHomeHero .container .main h1.name {
color: var(--vp-c-text);
}
.VPHomeHero .container .main h1.name .clip {
color: inherit;
-webkit-text-fill-color: unset;
color: var(--vp-c-text);
font-size: 36px;
}
.VPHomeHero .container .main h1::first-line {
.VPHomeHero .name::first-line {
color: var(--vp-c-brand);
}
/* */
.VPHomeHero .container .image {
margin: 0;
order: 2;
/* flex: 1; */
margin-top: 32px;
/* margin-top: 32px; */
}
.VPHomeHero .container .image-container {
@@ -98,10 +99,6 @@
justify-content: flex-end;
}
.VPHomeHero .container .image-bg {
display: none;
}
.VPFeature .icon {
background-color: var(--vp-c-bg);
}
@@ -115,10 +112,8 @@
padding-left: 0;
}
@media (min-width: 640px) {
.VPHomeHero .container .main h1.name .clip {
font-size: unset;
}
.VPHomeHero .container {
flex-direction: column-reverse;
}
@media (min-width: 960px) {
@@ -141,67 +136,11 @@
display: block;
}
.VPHomeHero .container .main h1.name {
.VPHomeHero .container {
flex-direction: row;
}
}
.VPNavBarHamburger .container > span {
border-radius: 2px;
}
/*
html:has(* .outline-link:target) {
scroll-behavior: smooth;
} */
.sp-wrapper + * {
margin-top: 24px;
}
.sp-wrapper .sp-layout {
border-radius: 8px;
}
.sp-wrapper .sp-tabs-scrollable-container {
border-radius: 8px 8px 0 0;
position: relative;
box-shadow: inset 0 -1px var(--vp-code-tab-divider);
margin-bottom: 0px;
margin-top: -1px;
height: 48px;
padding-bottom: 1px;
}
.sp-wrapper .sp-preview-container {
background-color: transparent;
}
.sp-wrapper .sp-tabs .sp-tab-button {
padding: 0 12px;
line-height: 48px;
height: 48px;
font-size: 14px;
font-weight: 500;
position: relative;
/* box-sizing: content-box; */
}
.sp-wrapper .sp-tabs .sp-tab-button:after {
position: absolute;
right: 8px;
left: 8px;
bottom: 0px;
z-index: 1;
height: 1px;
content: '';
background-color: transparent;
transition: background-color 0.25s;
}
.sp-wrapper .sp-tabs .sp-tab-button[data-active='true'] {
color: var(--vp-code-tab-active-text-color);
}
.sp-wrapper .sp-tabs .sp-tab-button[data-active='true']:after {
background-color: var(--vp-code-tab-active-bar-color);
}

View File

@@ -0,0 +1,35 @@
import { ref, onMounted, onBeforeUnmount } from 'vue';
/**
* Composable for handling search keyboard shortcuts.
* Listens for Cmd/Ctrl+K and "/" keys to trigger a search action.
*
* @param callback - Function to execute when shortcut is triggered
* @returns Object containing the platform-specific shortcut display text
*/
export default function useSearchShortcut(callback: () => void) {
const shortcutText = ref('');
function handleKeydown(event: KeyboardEvent) {
// Check for Cmd+K (Mac), Ctrl+K (Windows/Linux), or forward slash
if (((event.metaKey || event.ctrlKey) && event.key === 'k') || event.key === '/') {
event.preventDefault();
callback();
}
}
onMounted(() => {
// Detect platform and set appropriate keyboard shortcut for search focus
const isMac = /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform);
shortcutText.value = isMac ? '⌘K' : 'Ctrl+K';
// Add keyboard shortcut listener
window.addEventListener('keydown', handleKeydown);
});
onBeforeUnmount(() => {
window.removeEventListener('keydown', handleKeydown);
});
return { shortcutText };
}

View File

@@ -11,6 +11,12 @@ declare module '*.data.ts' {
export { data };
}
declare module '*.data' {
const data: any;
export { data };
}
declare module '*.wasm' {}
declare const resvg_wasm: RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
@@ -22,3 +28,23 @@ declare module 'node:module' {
declare module '*.node.json' {
export default IconNode;
}
declare global {
interface Window {
ExpoSnack?: {
/**
* Initialize all snack players on the page
*/
initialize(): void;
/**
* Remove a snack player container
*/
remove(container: Element): void;
/**
* Append/add a snack player container
*/
append(container: Element): void;
};
}
}

View File

@@ -2,7 +2,7 @@
title: Accessibility
---
# Accessibility
# Accessibility in dept
Icons are pictures that show what something means without using words. They can be very helpful
because they can quickly give information.
@@ -17,7 +17,7 @@ Icons are a helpful tool to improve perception, but they aren't a replacement fo
In most cases, it is probably a good idea to also provide a textual representation of your icon's
function.
![In short: Dont rely on communicating the function of elements by icons alone. Do also provide a written description of the your interactive elements. For example: write out "On this page" on your on-page navigation element.](../../images/a11y/visible-labels.svg?raw=true)
<!--@include: ../../docs/images/a11y/visible-labels.svg -->
## Contrast
@@ -27,21 +27,21 @@ with low vision or color vision deficiencies.
We recommend
following [WCAG 2.1 SC 1.4.3](https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html).
![In short: use a contrast ratio of at least 4.5:1](../../images/a11y/contrast.svg?raw=true)
<!--@include: ../../docs/images/a11y/contrast.svg -->
## Use of color
Avoid relying solely on color to convey meaning in icons, as some users may have color blindness.
Instead, use additional visual cues like shape, shading or text.
![For example: Dont mark state with color, mark it with distinct visuals.](../../images/a11y/use-of-color.svg?raw=true)
<!--@include: ../../docs/images/a11y/use-of-color.svg -->
## Interactivity
Ensure that interactive icons are accessible via keyboard navigation and provide clear feedback when
activated.
![](../../images/a11y/interactivity.svg?raw=true)
<!--@include: ../../docs/images/a11y/interactivity.svg -->
In most cases this is easily done by wrapping them in icon buttons.
@@ -50,7 +50,7 @@ In most cases this is easily done by wrapping them in icon buttons.
Small targets can be difficult to click or touch, if your icon is interactive, we recommend that it
should have a minimum target size of 44×44 pixels.
![](../../images/a11y/target-size.svg?raw=true)
<!--@include: ../../docs/images/a11y/target-size.svg -->
In practice, this doesn't necessarily mean that the icon itself should be this large, only its
interactive wrapper element.
@@ -60,14 +60,14 @@ interactive wrapper element.
Icons should represent concepts or actions in a universally understandable way. Avoid using abstract
or ambiguous, or culture-specific symbols that might confuse some users.
![For example: Use universally understandable symbols and don't base your choice of icon on puns.](../../images/a11y/meaningfulness.svg?raw=true)
<!--@include: ../../docs/images/a11y/meaningfulness.svg -->
## Consistency
Maintain consistency in icon design and usage across your interface to help users learn and
understand their meanings more easily.
![For example: Dont use the same icon for multiple distinct purposes or meanings. Dont use different icons for the same purpose or function.](../../images/a11y/consistency.svg?raw=true)
<!--@include: ../../docs/images/a11y/consistency.svg -->
## Text Alternatives
@@ -89,7 +89,7 @@ In case some of your icons stand alone, and they serve a non-decorative function
provide the appropriate accessible label for them.
:::
![In short: provide accessible label for semantic icons, but not for decorative icons.](../../images/a11y/alttext-standalone.svg?raw=true)
<!--@include: ../../docs/images/a11y/alttext-standalone.svg -->
In general try to avoid using functional icons with no interactivity, we recommend that:
@@ -104,7 +104,8 @@ elements (badges, buttons, nav items etc.) only, _not_ the icons themselves.
Do not provide an accessible label to icons when used on a button, as this label will be read out by
screen readers, leading to nonsensical text.
![](../../images/a11y/alttext-buttons.svg?raw=true)
<!--@include: ../../docs/images/a11y/alttext-buttons.svg -->
::: details Code examples
@@ -132,7 +133,7 @@ the close button of a dialog for example).
As previously stated, you should provide your accessible label on the icon button itself, not the
contained icon.
![](../../images/a11y/alttext-iconbuttons.svg?raw=true)
<!--@include: ../../docs/images/a11y/alttext-iconbuttons.svg -->
::: details Code examples
@@ -215,3 +216,16 @@ We also recommend checking out the following resources about accessibility:
- [A11yTalks](https://www.a11ytalks.com/)
- [A11y automation tracker](https://a11y-automation.dev/)
- [The A11Y Project](https://www.a11yproject.com/)
<style>
svg.a11y-example {
max-width: calc(100% + 48px);
margin: 0 -24px;
}
@media (min-width: 480px) {
svg.a11y-example {
margin: 0;
max-width: 100%;
}
}
</style>

View File

@@ -42,7 +42,11 @@ Here are rules that should be followed to keep quality and consistency when maki
### 8. Distinct elements must have **2 pixels of spacing between each other**
![2px-element-spacing](../../images/2px-element-spacing.svg?raw=true "2px-element-spacing")
![2px-element-spacing](../../images/2px-element-spacing.svg?raw=true '2px-element-spacing')
![2px-element-spacing-connected](../../images/2px-element-spacing-connected.svg?raw=true '2px-element-spacing-connected')
![2px-element-spacing-abrupt-cut](../../images/2px-element-spacing-abrupt-cut.svg?raw=true '2px-element-spacing-abrupt-cut')
### 9. Icons should have a similar optical volume to `circle` and `square`.

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

View File

@@ -7,7 +7,7 @@ nextPage:
# What is Lucide?
Lucide is an open-source icon library that provides 1000+ vector (svg) files for displaying icons and symbols in digital and non-digital projects. The library aims to make it easier for designers and developers to incorporate icons into their projects by providing several official [packages](/packages) to make it easier to use these icons in your project.
Lucide is an open-source icon library that provides 1000+ vector (svg) files for displaying icons and symbols in digital and non-digital projects. The library aims to make it easier for designers and developers to incorporate icons into their projects by providing several official [packages](/packages).
## Available Icons

Some files were not shown because too many files have changed in this diff Show More