Compare commits

...
123 Commits
Author SHA1 Message Date
849d10eda4 feat(icons): added mouse-right icon (#2787)
* Added icons/mouse-right.svg

* Added icons/mouse-right.json

* Update icons/mouse-right.svg

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

---------

Co-authored-by: Karsa <contact@karsa.org>
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2026-02-17 13:35:39 +01:00
2b9cacc5ae feat(icons): added mouse-left icon (#2788)
* Added icons/mouse-left.svg

* Added icons/mouse-left.json

* Update icons/mouse-left.svg

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

---------

Co-authored-by: Karsa <contact@karsa.org>
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2026-02-17 13:35:29 +01:00
Jakob Guddasgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>Eric FennisKarsa
cc9c5d2731 feat(icons): added x-line-top icon (#2838)
* Added icons/x-bar.svg

* Added icons/x-bar.json

* Rename x-bar.svg to x-line-top.svg

* Add x-line-top.json icon file

* Update icons/x-line-top.json

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>
Co-authored-by: Karsa <contact@karsa.org>
2026-02-17 13:35:19 +01:00
Jakob Guddas 019d3b9792 fix(icons): flipped coins icon (#3158)
* Updated icons/coins.svg

* Updated icons/coins.json

* Updated icons/coins.svg
2026-02-17 13:23:31 +01:00
Jamie Law feca9eaab5 fix(icons): changed rocking-chair icon (#3445)
* Updated icons/rocking-chair.svg

* Updated icons/rocking-chair.json
2026-02-17 12:51:52 +01:00
Henry WilkinsonandEric Fennis 88b972059f feat(icons): added message-circle-check icon (#3770)
* Added icons/message-circle-check.svg

* Added icons/message-circle-check.json

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2026-02-17 12:32:36 +01:00
KarsaandKarsa 9d86a86a99 feat(icons): added shelving-unit icon (#3041)
* [github] Added issue template forms

* [github] yaml => yml

* Syntax fixes

* Further syntax fixes

* Sort issue templates

* Update 02_bug_report.yml

* Added icons/shelf.svg

* Added icons/shelf.json

* Update shelf.json

* Update shelf.json

* Update and rename shelf.json to shelving-unit.json

* Rename shelf.svg to shelving-unit.svg

---------

Co-authored-by: Karsa <karsa@karsa.org>
2026-02-17 12:11:12 +01:00
Adam LindqvistandKarsa 727e29183f fix(icons): rearange circle-icons path and circle order (#3746)
* Rearrange svg element order

* Rearrange svg element order

* Revert package.json update

---------

Co-authored-by: Karsa <contact@karsa.org>
2026-02-17 10:56:57 +01:00
603df50ddd feat(icons): added towel-rack icon (#3350)
* Added icons/towel-rack.svg

* Added icons/towel-rack.json

* Updated icons/towel-rack.svg

* Updated icons/towel-rack.svg

* Updated icons/towel-rack.svg

* Updated icons/towel-rack.json

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
Co-authored-by: Karsa <contact@karsa.org>
2026-02-17 10:25:44 +01:00
Muhammad Aqib BashirandKarsa c79a0ecbda feat(icons): add mirror-round and mirror-rectangular (#3832)
* feat(icon): add mirror-round icon

* feat(icon): add mirror-rectangular icon

* Update icons/mirror-rectangular.json: remove part of name from tages

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

* Update icons/mirror-round.json: remove part of name from tags

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

* Update icons/mirror-round.json: add vanity and makeup tags

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

* fix(size): increase to make it a bit larger from `14 x 18` to `16 x 20`

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

* Apply suggestion from @karsa-mistmere

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

* Update mirror-round.json

---------

Co-authored-by: Karsa <contact@karsa.org>
2026-02-17 09:44:18 +01:00
SpleefiesandKarsa 8939deb430 fix(icons): changed clipboard-pen icon (#4006)
* Updated icons/clipboard-pen.svg

* Updated icons/clipboard-pen.json

---------

Co-authored-by: Karsa <contact@karsa.org>
2026-02-17 09:44:06 +01:00
Karsa 54abe52318 fix(icons): fix linting issues in git-merge-conflict.json 2026-02-17 09:22:57 +01:00
137034105a feat(icons): Add git-merge-conflict icon (#3008)
* Add icons/git-merge-conflict-icon

* updated git-merge-conflict.svg and git-merge-conflict.json

* added more contributors to git-merge-conflict.json

---------

Co-authored-by: timmy471 <timilehin.ayantunji@etranzact.com>
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2026-02-17 08:45:50 +01:00
Owen FosterandJakob Guddas 9d9fb2c30b fix(icons): adjusted clapperboard so slash is no longer protruding (#3764)
* Adjusted second slash on clapperboard icon so it no longer protrudes.

* Update icons/clapperboard.svg

* Modify SVG paths in clapperboard icon

* Add 'torfmuer' to contributors in clapperboard.json

---------

Co-authored-by: Jakob Guddas <github@jguddas.de>
2026-02-17 08:34:00 +01:00
Nathan De PachtereandEric Fennis caa50f5543 feat(icons): added line-dot-right-horizontal icon (#3742)
* Added icons/last-commit.svg

* Added icons/last-commit.json

* Rename to line-dot-right-horizontal

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2026-02-17 08:19:51 +01:00
Jakob Guddas b5fc65eed5 fix(icons): changed gift icon (#3977)
* Updated icons/gift.svg

* Updated icons/gift.svg

* Updated icons/gift.svg

* Updated icons/gift.svg

* Updated icons/gift.json
2026-02-17 07:56:09 +01:00
Jamie Law 153c35f5dd chore(tags): added tags to info (#4047)
* Update tags for `info` icon

* Add extra tags from Chat GPT
2026-02-17 07:54:41 +01:00
Timofey Kochetovgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>KarsaEric Fennis
07744ea48f feat(icons): added globe-off icon (#4051)
* Added icons/globe-off.svg

* Added icons/globe-off.json

* Apply suggestion from @github-actions[bot]

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

* Update icons/globe-off.svg

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

* Update globe-off.json

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Karsa <contact@karsa.org>
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2026-02-17 07:34:06 +01:00
Jakob Guddas 0846e2c6ef fix(icons): changed rocket icon (#4067)
* Updated icons/rocket.svg

* Updated icons/rocket.json
2026-02-17 07:30:29 +01:00
Jakob Guddasgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>Eric Fennis
dea4936a72 fix(icons): changed forklift icon (#4069)
* Updated icons/forklift.svg

* Updated icons/forklift.json

* 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>
2026-02-17 07:29:19 +01:00
Muhammad Aqib BashirandEric Fennis b76860a2b8 feat(icons): add lens-concave and lens-convex (#3831)
* feat(icons): add lens-convex icon

* feat(icon): add lens-concave icon

* fix(icon): replace approximate curve segments with 14-unit

* fix(icon): undo changes of making convex icon into concave

* fix(lens-concave): round path values from 13.9 to 14 to improve icon math

* Add 'shapes' category to lens-convex.json

* Add 'shapes' category to lens-concave.json

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2026-02-17 07:25:57 +01:00
Karsa de9b2d39b7 feat(icons): adds user-key and user-round-key, updates other -key icons to match (#4044)
* Updated icons/file-key.svg

* Updated icons/book-key.svg

* Updated icons/folder-key.svg

* Updated icons/rotate-ccw-key.svg

* Added icons/user-round-key.svg

* Added icons/user-round-key.json

* Added icons/user-key.svg

* Added icons/user-key.json

* Update user-key.json

* Update user-round-key.json
2026-02-13 12:28:24 +01:00
Jakob Guddas 08488f4809 fix(icons): changed cloud-alert and cloud-check icon (#3976)
* Updated icons/cloud-alert.svg

* Updated icons/cloud-alert.json

* Updated icons/cloud-check.svg
2026-02-13 12:24:21 +01:00
Jakob Guddas 3574e224e0 fix(icons): changed file-cog icon (#3965)
* Updated icons/file-cog.svg

* Updated icons/file-cog.json

* Updated icons/file-cog.svg

* Updated icons/file-cog.svg
2026-02-13 12:12:31 +01:00
Jakob Guddas 9a8e023754 fix(icons): changed git-branch icon (#3938)
* Updated icons/git-branch.svg

* Updated icons/git-branch.json
2026-02-10 08:23:39 +01:00
Jakob GuddasandEric Fennis 88fd3dadcb fix(icons): changed hard-drive icon (#3622)
* Updated icons/hard-drive.svg

* Updated icons/hard-drive.svg

* Updated icons/hard-drive.json

* Add keywords for storage and hardware in hard-drive.json

Added new keywords related to storage and hardware.

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2026-02-03 08:28:23 +01:00
Jakob Guddas bc446a83b2 fix(icons): changed bandage icon (#3967)
* Updated icons/bandage.svg

* Updated icons/bandage.json
2026-02-03 08:24:10 +01:00
Jakob Guddas 39fa345658 fix(icons): changed bell-dot icon (#3973)
* Updated icons/bell-dot.svg

* Updated icons/bell-dot.json
2026-02-03 08:23:15 +01:00
Jakob Guddas fb31e5f616 fix(icons): changed bug-off icon (#3972)
* Updated icons/bug-off.svg

* Updated icons/bug-off.json
2026-02-03 08:21:35 +01:00
Jakob Guddas dcd95a3b90 fix(icons): changed user-lock icon (#3971)
* Updated icons/user-lock.svg

* Updated icons/user-lock.json
2026-02-03 08:18:47 +01:00
SpleefiesandJakob Guddas a1a729555b feat(icons): added database-search icon (#4003)
* Added icons/database-search.svg

* Added icons/database-search.json

* Refactor SVG paths in database-search icon

---------

Co-authored-by: Jakob Guddas <github@jguddas.de>
2026-02-02 21:30:51 +01:00
Eric Fennis a28631e2a4 chore(docs): Improve SEO icon detail pages (#4040)
* improve-seo-website

* Add some description texts
2026-01-29 16:19:32 +01:00
Eric Fennis cbaaf4e21e fix(@lucide/svelte): Fix build in (#4026)
* Fix build

* Adds extra typechecking

* Format code
2026-01-23 14:06:15 +01:00
lt25106 e9e060a851 feat(icons): added printer-x icon (#3941)
* Added icons/printer-cross.svg

* Added icons/printer-cross.json

* Add error-related tags to printer-cross.json

Added new tags for error handling.

* Rename printer-cross.json to printer-x.json

* Update and rename printer-cross.svg to printer-x.svg
2026-01-23 11:43:01 +01:00
Epifânio Francisco 0371baabcb fix(site): hide native search input clear "X" icon (#3933)
* fix(site): hide native search input clear "X" icon

Hide the browser's default clear "X" icon for search inputs to prevent duplicate icons when using the custom clear button. This resolves issue #3925

* style(site): adjust clear button position in input component for mobile

* fix(site): adjust clear button position for Mac in search input
2026-01-23 07:51:00 +01:00
Karsa 67c0485457 feat(scripts): added helper script to automatically update OpenCollective backers in all readmes (#4014)
feat(docs): upgraded backers as per the recurring contributions in the last 3 months
2026-01-22 11:27:39 +01:00
drago1520 c47876e5a6 Suggest anchoring to a specific lucide version when using cnd (#3727)
My whole app crashed because the newest version had a critical bug. I want to help other people avoid my mistake by anchoring to a specific version that worked for them
2026-01-22 09:07:52 +01:00
dependabot[bot]dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>Eric Fennis
96859ba947 chore(deps): bump lodash-es from 4.17.21 to 4.17.23 (#4019)
Bumps [lodash-es](https://github.com/lodash/lodash) from 4.17.21 to 4.17.23.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.21...4.17.23)

---
updated-dependencies:
- dependency-name: lodash-es
  dependency-version: 4.17.23
  dependency-type: direct:production
...

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>
2026-01-22 09:06:37 +01:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 7dfa7d9fc6 chore(deps): bump lodash from 4.17.21 to 4.17.23 (#4020)
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.21 to 4.17.23.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.21...4.17.23)

---
updated-dependencies:
- dependency-name: lodash
  dependency-version: 4.17.23
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-22 08:58:17 +01:00
b6ed43d48c feat(packages): Added aria-hidden fallback for decorative icons to all packages (#3604)
* Add aria hidden to lucide package

* Add aria-hidden to astro package

* Add tests for shared package

* Fix test for props

* add aria prop to lucide-solid

* Add aria-hidden to lucide-vue-next

* Add aria-hidden prop to angular package

* Fix builds

* Add notice about aria-label in docs

* Format code

* Update packages/svelte/tests/Icon.spec.ts

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

* Update packages/lucide-svelte/tests/Icon.spec.ts

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

* Update packages/shared/src/utils/hasA11yProp.ts

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

* Adjusted docs

* Fix import

* update tests

* test(shared): fix hasA11yProp unit test

* fix(packages/lucide-angular): fix hasA11yProp import path (non-relative import path will not get properly resolved by ng-packagr)

* Format code

* Adjust aria props to root element

* Format code

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Karsa <contact@karsa.org>
2026-01-20 15:55:02 +01:00
Alexander Wondwossen 9b10d1d9bb Update LICENSE (#4009) 2026-01-20 08:13:05 +01:00
Jakob Guddas 442ddbc43c fix: resolved spelling mistake in gh issue close command (#4000) 2026-01-19 13:08:57 +01:00
Jakob Guddas ce5cab371f fix(icons): changed cloudy icon (#3966)
* Updated icons/cloudy.svg

* Updated icons/cloudy.json

* Updated icons/cloudy.svg
2026-01-15 14:48:23 +01:00
Jakob Guddas 69239462bb fix(icons): changed message-square-dashed icon (#3959)
* Updated icons/message-square-dashed.svg

* Updated icons/message-square-dashed.json

* Updated icons/message-square-dashed.svg
2026-01-15 14:35:18 +01:00
Jakob Guddasgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>KarsaEric Fennis
f8e04958d8 fix(icons): changed bookmark icon (#2906)
* Updated icons/bookmark.svg

* Updated icons/bookmark.json

* Updated icons/bookmark.svg

* Updated icons/bookmark.svg

* Update icons/bookmark.json

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

* Updated icons/bookmark-check.svg

* Updated icons/bookmark-check.json

* Updated icons/bookmark-minus.svg

* Updated icons/bookmark-minus.json

* Updated icons/bookmark-plus.svg

* Updated icons/bookmark-plus.json

* Updated icons/bookmark-x.svg

* Updated icons/bookmark-x.json

---------

Co-authored-by: Karsa <contact@karsa.org>
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-01-15 14:15:40 +01:00
Karsa e466a104ff fix(icons): changed waypoints icon (#3990)
* Updated icons/waypoints.svg

* Updated icons/waypoints.json
2026-01-15 13:30:47 +01:00
Muhammad Aqib BashirandKarsa 516917c527 feat(icon): add globe-x icon with metadata (#3827)
* feat(icon): add globe-x icon with metadata

* style(icon): redesign icon to make X mark 5 by 5

---------

Co-authored-by: Karsa <contact@karsa.org>
2026-01-12 10:34:21 +01:00
dependabot[bot]dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>Eric Fennis
3cec234405 chore(deps-dev): bump preact from 10.27.2 to 10.27.3 (#3955)
Bumps [preact](https://github.com/preactjs/preact) from 10.27.2 to 10.27.3.
- [Release notes](https://github.com/preactjs/preact/releases)
- [Commits](https://github.com/preactjs/preact/compare/10.27.2...10.27.3)

---
updated-dependencies:
- dependency-name: preact
  dependency-version: 10.27.3
  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>
2026-01-10 17:34:30 +01:00
a258509bbb docs(site): added lucide-web-components third-party package (#3948)
* Add Lucide Web Components implementation

* restore lucide-rails

* Update docs/.vitepress/data/packageData.thirdParty.json

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

---------

Co-authored-by: midesweb <miguel@escuela.it>
Co-authored-by: Karsa <contact@karsa.org>
2026-01-10 17:34:16 +01:00
Jakob Guddas 10918eba1f Updated icons/cloud-off.svg (#3942) 2026-01-10 12:38:15 +01:00
Jakob Guddas 5f4f58cba8 fix(icons): changed lasso icon (#3961)
* Updated icons/lasso.svg

* Updated icons/lasso.json

* Updated icons/lasso.svg
2026-01-10 12:37:19 +01:00
Jakob Guddas 511d7fabf7 Updated icons/monitor-off.svg (#3962) 2026-01-10 12:36:16 +01:00
Jakob Guddas 77a67819e4 Updated icons/tickets-plane.svg (#3928) 2026-01-08 12:54:13 +01:00
Jakob Guddas edc588125f Updated icons/star-off.svg (#3952) 2026-01-07 09:20:42 +01:00
taimar 0c4ac91ba5 fix(site): unify and improve the styling of input fields (#3919)
* Unify and improve the styling of input fields

* InputSearch: use ellipsis character, remove redundant space
2026-01-02 08:48:03 +01:00
taimar 450acd617d Add missing titles and unify title template (#3920) 2026-01-02 08:46:32 +01:00
Karsa d391bda369 feat: add Android to brand stopwords (#3895) 2025-12-22 07:54:08 +01:00
David Castilla Ortiz 69bf052ee5 Enable ligatures in font build configuration (#3876) 2025-12-18 12:17:28 +01:00
Karsa 6b4075b89b feat(icons): added toolbox icon (#3871)
* Added icons/toolbox.svg

* Added icons/toolbox.json
2025-12-18 11:44:47 +01:00
Jacek TomaszewskiandEric Fennis 7a68e10b12 fix(lucide-react-native): remove icons namespace export to enable tree-shaking (#3868)
* fix(lucide-react-native): remove icons namespace export to enable tree-shaking

The `export * as icons from './icons'` statement defeats tree-shaking
because bundlers cannot determine which exports from the namespace are
actually used at build time. This causes all 1600+ icons to be included
in the final bundle even when only a few are imported.

This change removes the namespace re-export while keeping all individual
icon exports available via `export * from './icons'`.

BREAKING CHANGE: The `icons` namespace export is no longer available.
Users should import icons directly: `import { Activity } from 'lucide-react-native'`
instead of `import { icons } from 'lucide-react-native'; icons.Activity`.

* Add icons entry file to improve treeshaking

* Format code

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-12-18 11:44:26 +01:00
Jakob Guddas a4531a9985 fix(react-native-web): only add className prop to parent Icon component (#3892) 2025-12-18 11:43:31 +01:00
taimar 3edcd9e0c3 fix and unify color-picker font-size (#3889) 2025-12-15 14:59:14 +01:00
Jakob Guddas 0b8f99326c fix(icons): changed paint-bucket icon (#3880)
* Updated icons/paint-bucket.svg

* Updated icons/paint-bucket.svg

* Updated icons/paint-bucket.svg

* Updated icons/paint-bucket.svg

* Updated icons/paint-bucket.json

* Updated icons/paint-bucket.json
2025-12-12 13:27:37 +01:00
Alexandru Portangithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>Jakob Guddas
7abb61630e feat(icons): added stone icon (#3850)
* Added icons/stone.svg

* Added icons/stone.json

* Update icons/stone.json

Added suggested tags

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

* Update stone.svg

Updated based on suggested changes

* Update icons/stone.json

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Jakob Guddas <github@jguddas.de>
2025-12-12 09:26:54 +01:00
Eric FennisandKarsa 3b0d158ea1 fix(site): Small adjustments color picker and add clear button search bar (#3851)
* Small adjustments

* Format code

* format code

* Remove default value

* format code

* update yml file

* Format code

* Update docs/.vitepress/theme/components/base/Input.vue

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

* Add extra check if null or undefined

---------

Co-authored-by: Karsa <contact@karsa.org>
2025-12-12 09:25:39 +01:00
124572c83b feat(icons): added cannabis-off icon (#3748)
* Added icons/cannabis-off.svg

* Added icons/cannabis-off.json

* fix: applied optimization by jguddas

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
Co-authored-by: Karsa <contact@karsa.org>
2025-12-11 09:15:49 +01:00
7endergithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>Eric Fennis
4fcfb6a4d1 feat(icons): added fishing-hook icon (#3837)
* Added icons/fishing-hook.svg

* Added icons/fishing-hook.json

* Update icons/fishing-hook.json

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

* Update fishing-hook.svg

upd, Big thanks to  @jguddas, @karsa-mistmere and @jamiemlaw

* Update fishing-hook.json

+ contributors

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-12-10 14:06:51 +01:00
Jamie LawandEric Fennis 0f732b411d feat(icons): added hd icon (#2958)
* Add `hd` icon

* Update hd.svg

* Update hd.json

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-12-10 13:56:10 +01:00
Eric Fennis ce09c31f08 Merge branch 'main' of https://github.com/lucide-icons/lucide 2025-12-10 13:12:58 +01:00
Eric Fennis c2b059fb60 ci(release.yml): Remove deprecated token assignment 2025-12-10 13:12:55 +01:00
b3c80d027a feat(icons): added balloon icon (#2519)
* Added icons/balloon.svg

* Added icons/balloon.json

* Updated icons/balloon.svg

* Optimize paths

* Update tags

---------

Co-authored-by: peteruithoven <peter@metabolic.nl>
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-12-10 13:07:01 +01:00
Eric Fennis 20f30bb5ea Merge branch 'main' of https://github.com/lucide-icons/lucide 2025-12-10 13:05:38 +01:00
Eric Fennis c47ae67a3b ci(ci.yml): Apply env variables 2025-12-10 13:05:34 +01:00
Nathan De Pachtere 7866a5a5c6 feat(icons): added circle-pile icon (#3681)
* Added icons/circle-pile.svg

* Added icons/circle-pile.json

* Add tags

* Reduce high
2025-12-10 12:58:51 +01:00
Eric Fennis 92bc88b001 ci(ci.yml): Fix latest tag from steps and remove superfluous steps 2025-12-10 12:58:06 +01:00
Eric FennisandKarsa e75fbcdec4 feat(icons): Add cloud-sync and cloud-backup (#3466)
* Add icons

* Update cloud-backup.json

* Update cloud-sync.json

* Format code

---------

Co-authored-by: Karsa <contact@karsa.org>
2025-12-10 12:49:56 +01:00
Eric Fennis 4cef8283a7 ci(ci.yml): Fix LATEST_TAG assignment 2025-12-10 12:49:24 +01:00
330f4b37db feat(icons): add search-error icon (#3292)
* feat(icons): add `search-error` icon

* Update icons/search-error.svg

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

* Update icons/search-error.json

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

* chore(icons): rename `search-error` to `search-alert`

* Fix indentation contributors in search-alert.json

* Format code

---------

Co-authored-by: Jakob Guddas <github@jguddas.de>
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-12-10 11:33:12 +01:00
Sage FennelandKarsa fd31cb44a8 docs(dev): Fix code sample for vanilla JS (#3836)
* Fix code sample for vanilla JS

The .append method of elements takes plain text, not HTML. This updates the code to use .insertAdjacentHTML instead.

* Update createLabCodeExamples.ts

* fix(site): update createCodeExamples.ts vanilla JS code example

* Update createLabCodeExamples.ts

* Update createCodeExamples.ts

* Update createCodeExamples.ts

---------

Co-authored-by: Karsa <contact@karsa.org>
2025-12-10 11:18:57 +01:00
Juan Isidoro García CifuentesandEric Fennis 790d30dbfa feat(icons): added layers-plus icon (#3367)
* Added icons/layers-plus.svg

* Added icons/layers-plus.json

* Update layers-plus.json

fix: updated contributors to reflect actual author

* fix(icon): update layers-plus icon to follow size and spacing guidelines

* fix(icon): adjust layers-plus plus sign alignment and slope

* fix(icon): apply optimized version from reviewer to align on grid and spacing

* chore: add karsa-mistmere as contributor

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-12-10 11:13:25 +01:00
Karsa e7c075785f fix(icons): changed tickets icon (#3859)
* Updated icons/tickets.svg

* Updated icons/tickets.json

* Updated icons/tickets-plane.svg

* Updated icons/tickets-plane.json
2025-12-10 11:10:26 +01:00
Ian Jones 6d4c91707d fix(icons): Swap thumbs-up thumbs-down paths to fix fill issue (#3873)
* Swap thumbs-up.svg paths

* Swap thumbs-down.svg paths
2025-12-10 11:09:27 +01:00
Jakob Guddas c0ea92ebe7 fix(icons): changed brush-cleaning icon (#3863)
* Updated icons/brush-cleaning.svg

* Updated icons/brush-cleaning.json
2025-12-10 11:08:55 +01:00
Jakob Guddas 42dc5508dd fix(icons): changed paint-bucket icon (#3865)
* Updated icons/paint-bucket.svg

* Updated icons/paint-bucket.svg
2025-12-10 11:07:50 +01:00
Eric Fennis 4dda432471 chore(repo): Update Node version and overal cleanup (#3861)
* update ci script

* Update ci workflow

* Update node version
2025-12-10 11:06:48 +01:00
Karsa 0775d8647e fix(site): only show search placeholder if there aren't any results 2025-12-10 08:50:17 +01:00
KarsaandKarsa 83ef8fc98d fix(icons): changed microchip icon (#3018)
* [github] Added issue template forms

* [github] yaml => yml

* Syntax fixes

* Further syntax fixes

* Sort issue templates

* Update 02_bug_report.yml

* Updated icons/microchip.svg

---------

Co-authored-by: Karsa <karsa@karsa.org>
2025-12-09 15:47:57 +01:00
5b56ef705d fix(icons): changed memory-stick icon (#3017)
* [github] Added issue template forms

* [github] yaml => yml

* Syntax fixes

* Further syntax fixes

* Sort issue templates

* Update 02_bug_report.yml

* Updated icons/memory-stick.svg

* Update memory-stick.svg

---------

Co-authored-by: Karsa <karsa@karsa.org>
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-12-09 15:46:35 +01:00
Karsa dafe529892 fix: fixed linting issues introduced in c4e5730bc4 (#3858) 2025-12-05 15:21:00 +01:00
151c5b145c feat(icons): added van icon (#3821)
* Add van icon

* update tags

* edit icon

* edit contributors

* Update icons/van.json

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

---------

Co-authored-by: Karsa <contact@karsa.org>
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-12-05 14:35:58 +01:00
Eric Fennis d6f9043096 style(ci): Format ci.yml 2025-12-05 14:32:32 +01:00
b4405f05ab feat(site): add brand stop words to icon search (#3824)
* feat(site): added extended no results placeholder with brand icon stop words

* feat(site): fix grammatical error

* feat: extract brand stopwords & update github action to use these stopwords

* Apply suggestions from code review

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

* feat: only use icon name section for closing brand request issues

* feat: added mcp brand stopword

---------

Co-authored-by: Jakob Guddas <github@jguddas.de>
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-12-05 14:18:46 +01:00
Jakob Guddas 9076da5f1b fix(icons): changed bubbles icon (#3774)
* Updated icons/bubbles.svg

* Updated icons/bubbles.svg

* Updated icons/bubbles.json
2025-12-05 13:46:42 +01:00
Jamie Law 2e7d806282 fix(icons): changed flashlight icons (#3843)
* Update flashlight.svg

Arcified ends and adjusted the tapering

* Update flashlight-off.svg

Update to match `flashlight.svg`

* Update flashlight.json

Added attribution and tags

* Update flashlight-off.json

* Update flashlight.svg

Correctly round bottom corners

* Update flashlight-off.svg
2025-12-05 09:53:31 +01:00
Eric Fennis c4e5730bc4 fix(workflow): Fix permissions release worklfow 2025-12-05 09:27:34 +01:00
Nathan De Pachteregithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>Karsa
02b35e2518 feat(icons): added estimated-weight icon (#3822)
* Added icons/estimated-weight.svg

* Added icons/estimated-weight.json

* Update icons/estimated-weight.json

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

* Change name + reshape tilde

* Change name to weight-tilde

* Update weight-tilde.json

removed weight and tilde, since they're part of the name

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Karsa <contact@karsa.org>
2025-12-05 09:18:42 +01:00
Jakob Guddas f183c3ba20 feat(preview-comment): add symmetry preview (#3823) 2025-12-05 09:11:17 +01:00
Eden YeminiandKarsa 67e9efb801 fix(icons): Shrink square-scissors icons to match optical volume (#3603)
* Update square-scissors to match optical volume

* Update square-bottom-dashed-scissors to match optical volume

---------

Co-authored-by: Karsa <contact@karsa.org>
2025-12-03 13:26:43 +01:00
Jakob Guddas 2e4c9a65be fix(icons): changed thermometer-sun icon (#3773)
* Updated icons/thermometer-sun.svg

* Updated icons/thermometer-sun.json

* Modify SVG paths in thermometer-sun icon
2025-12-03 13:01:26 +01:00
Jamie Law de4e8d0acd fix(icons): changed plug icon (#3841)
* Updated icons/plug.svg

* Updated icons/plug.json
2025-12-03 12:45:33 +01:00
1f113d4274 feat(icons): added scooter icon (#3818)
* Add scooter icon

* edit tags

* edit icon

* edit icon

* deit icon

* edit icon

* edit contributors

* Update scooter.json

---------

Co-authored-by: Karsa <contact@karsa.org>
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-11-28 14:42:51 +01:00
Eric Fennis dffffc7aff fix: Reverts vercel output path 2025-11-27 11:06:47 +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 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 Guddasgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>Eric Fennis
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 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
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 LawandEric Fennis 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
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
KarsaandKarsa 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 Pgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>Jakob Guddas
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
452 changed files with 8767 additions and 6364 deletions
+14 -48
View File
@@ -7,6 +7,10 @@ on:
paths:
- icons/**/*.svg
permissions:
id-token: write # Required for OIDC
contents: write
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'
@@ -32,25 +36,19 @@ jobs:
id: latest-tag
run: echo "LATEST_TAG=$(git describe --tags `git rev-list --tags --max-count=1`)" >> $GITHUB_OUTPUT
- name: Log latest tag
run: echo '${{ steps.latest-tag.outputs.LATEST_TAG }}'
- name: Check if we can patch
run: .github/workflows/version-up.sh --minor
run: pnpm semver $LATEST_TAG -i minor
env:
LATEST_TAG: ${{ steps.latest-tag.outputs.LATEST_TAG }}
- name: Create new version
id: new-version
run: echo "NEW_VERSION=$(.github/workflows/version-up.sh --minor)" >> $GITHUB_OUTPUT
- name: Create change log
id: change-log
run: |
CHANGE_LOG=$(pnpm run generate:changelog --old-tag=${{ steps.latest-tag.outputs.LATEST_TAG }})
CHANGE_LOG=$(tail -n +5 <<< $CHANGE_LOG)
echo $CHANGE_LOG
EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64)
echo "CHANGE_LOG<<$EOF" >> $GITHUB_OUTPUT
echo "$CHANGE_LOG" >> $GITHUB_OUTPUT
echo "$EOF" >> $GITHUB_OUTPUT
run: echo "NEW_VERSION=$(pnpm semver $LATEST_TAG -i minor)" >> $GITHUB_OUTPUT
env:
GITHUB_API_KEY: ${{ secrets.GITHUB_TOKEN }}
LATEST_TAG: ${{ steps.latest-tag.outputs.LATEST_TAG }}
- name: Check output
run: |
@@ -64,38 +62,6 @@ jobs:
name: Version ${{ steps.new-version.outputs.NEW_VERSION }}
generate_release_notes: true
test-semantic-release:
if: github.repository == 'lucide-icons/lucide'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Semantic Release
id: semantic
uses: cycjimmy/semantic-release-action@v4
with:
tag_format: ${version}
branches: |
['new-release-workflow']
extends: |
semantic-release-monorepo
extra_plugins: |
@semantic-release/github
@semantic-release/git
@semantic-release/release-notes-generator
conventional-changelog-conventionalcommits
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Log output
if: steps.semantic.outputs.new_release_published == 'true'
run: |
echo ${{ steps.semantic.outputs.new_release_version }}
echo ${{ steps.semantic.outputs.new_release_major_version }}
echo ${{ steps.semantic.outputs.new_release_minor_version }}
echo ${{ steps.semantic.outputs.new_release_patch_version }}
start-release:
if: github.repository == 'lucide-icons/lucide'
needs: create-release
@@ -1,4 +1,4 @@
name: Close Issue with Banned Phrases
name: Close Icon Requests with Brand Terms
on:
issues:
@@ -11,25 +11,38 @@ jobs:
issues: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Check for blocked phrases in issue title
- name: Load stopwords from JSON & check issue title & body
if: contains(github.event.issue.labels.*.name, '🙌 icon request')
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" "telegram")
ISSUE_BODY=$(jq -r '.issue.body // ""' "$GITHUB_EVENT_PATH")
ICON_NAME_SECTION=$(printf '%s\n' "$ISSUE_BODY" | awk '/### Icon name/{flag=1; next} /^### /{flag=0} flag')
# Check title and body for blocked phrases
for PHRASE in "${BLOCKED_PHRASES[@]}"
do
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.
jq -r 'to_entries[] | "\(.key) \(.value)"' brand-stopwords.json | while read -r KEY VALUE; do
SAFE_KEY=$(printf '%s\n' "$KEY" | sed 's/[][\.^$*]/\\&/g')
SAFE_VALUE=$(printf '%s\n' "$VALUE" | sed 's/[][\.^$*]/\\&/g')
Read [our official statement about brand logos in Lucide](https://github.com/lucide-icons/lucide/blob/main/BRAND_LOGOS_STATEMENT.md).
if echo "$ISSUE_TITLE" | grep -iqE "$SAFE_KEY|$SAFE_VALUE" || \
{ [ -n "$ICON_NAME_SECTION" ] && echo "$ICON_NAME_SECTION" | grep -iqE "$SAFE_KEY|$SAFE_VALUE"; }; then
gh issue close ${{ github.event.issue.number }} \
--reason "not planned" \
--comment "It looks like this request is about **${VALUE}**, which is a brand logo.
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)
You can [search for similar issues.](https://github.com/lucide-icons/lucide/issues?q=is%3Aissue+${VALUE})
We’re always happy to help on [Discord](https://discord.gg/EH6nSts)."
Always happy to help on [Discord](https://discord.gg/EH6nSts)."
gh issue lock ${{ github.event.issue.number }} --reason spam
exit 1
exit 0
fi
done
env:
GH_TOKEN: ${{ github.token }}
env:
GH_TOKEN: ${{ github.token }}
+1 -1
View File
@@ -9,5 +9,5 @@ jobs:
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: actions/labeler@v5
+2 -2
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'
+3 -3
View File
@@ -13,8 +13,8 @@ jobs:
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version-file: 'package.json'
- name: Get changed files
@@ -34,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 '"
+4 -4
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'
+2 -2
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@v2
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
node-version-file: 'package.json'
cache: 'pnpm'
+2 -2
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'
+2 -2
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'
+4 -4
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'
+4 -4
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'
+2 -2
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'
+4 -4
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'
+2 -2
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'
+20 -4
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'
@@ -41,3 +41,19 @@ jobs:
- name: Test
run: pnpm --filter @lucide/svelte test
typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- 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: Test
run: pnpm --filter @lucide/svelte typecheck
+4 -4
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'
+4 -4
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'
+4 -4
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'
@@ -14,8 +14,8 @@ jobs:
pull-requests: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version-file: 'package.json'
@@ -25,7 +25,7 @@ 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@5.3.1 --force
@@ -16,11 +16,11 @@ jobs:
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@v4
- uses: actions/checkout@v6
with:
repository: lucide-icons/lucide
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'
+20 -17
View File
@@ -18,9 +18,13 @@ on:
description: Version
required: true
permissions:
id-token: write # Required for OIDC
contents: write
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:
@@ -57,9 +62,9 @@ jobs:
'@lucide/svelte',
]
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'
@@ -67,9 +72,6 @@ jobs:
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Set Auth Token
run: npm config set //registry.npmjs.org/:_authToken ${{ inputs.NPM_TOKEN || secrets.NPM_TOKEN }}
- name: Set new version
run: pnpm --filter ${{ matrix.package }} version --new-version ${{ needs.pre-release.outputs.VERSION }} --no-git-tag-version
@@ -89,12 +91,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'
@@ -102,9 +106,6 @@ jobs:
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Set Auth Token
run: npm config set //registry.npmjs.org/:_authToken ${{ secrets.NPM_TOKEN }}
- name: Set new version
run: pnpm --filter lucide-static version --new-version ${{ needs.pre-release.outputs.VERSION }} --no-git-tag-version
@@ -124,9 +125,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'
@@ -150,9 +151,11 @@ jobs:
if: github.repository == 'lucide-icons/lucide'
runs-on: ubuntu-latest
needs: [pre-release, lucide-font]
permissions:
id-token: write # Required for OIDC
contents: write
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: actions/download-artifact@v4
- name: Zip font and icons
run: |
+3 -2
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
-284
View File
@@ -1,284 +0,0 @@
#!/usr/bin/env bash
## Copyright (C) 2017, Oleksandr Kucherenko
## Last revisit: 2017-09-29
## get highest version tag for all branches
function highest_tag(){
local TAG=$(git describe --tags `git rev-list --tags --max-count=1`)
echo "$TAG"
}
## extract current branch name
function current_branch(){
## expected: heads/{branch_name}
## expected: {branch_name}
local BRANCH=$(git rev-parse --abbrev-ref HEAD | cut -d"/" -f2)
echo "$BRANCH"
}
## get latest/head commit hash number
function head_hash(){
local COMMIT_HASH=$(git rev-parse --verify HEAD)
echo "$COMMIT_HASH"
}
## extract tag commit hash code, tag name provided by argument
function tag_hash(){
local TAG_HASH=$(git log -1 --format=format:"%H" $1 2>/dev/null | tail -n1)
echo "$TAG_HASH"
}
## get latest revision number
function latest_revision(){
local REV=$(git rev-list --count HEAD 2>/dev/null)
echo "$REV"
}
## parse last found tag, extract it PARTS
function parse_last(){
local position=$(($1-1))
# two parts found only
local SUBS=( ${PARTS[$position]//-/ } )
#echo ${SUBS[@]}, size: ${#SUBS}
# found NUMBER
PARTS[$position]=${SUBS[0]}
#echo ${PARTS[@]}
# found SUFFIX
if [[ ${#SUBS} -ge 1 ]]; then
PARTS[4]=${SUBS[1],,} #lowercase
#echo ${PARTS[@]}, ${SUBS[@]}
fi
}
## increment REVISION part, don't touch STAGE
function increment_revision(){
PARTS[3]=$(( PARTS[3] + 1 ))
IS_DIRTY=1
}
## increment PATCH part, reset all other lower PARTS, don't touch STAGE
function increment_patch(){
PARTS[2]=$(( PARTS[2] + 1 ))
PARTS[3]=0
IS_DIRTY=1
}
## increment MINOR part, reset all other lower PARTS, don't touch STAGE
function increment_minor(){
PARTS[1]=$(( PARTS[1] + 1 ))
PARTS[2]=0
PARTS[3]=0
IS_DIRTY=1
}
## increment MAJOR part, reset all other lower PARTS, don't touch STAGE
function incremet_major(){
PARTS[0]="v$(( PARTS[0] + 1 ))"
PARTS[1]=0
PARTS[2]=0
PARTS[3]=0
IS_DIRTY=1
}
## increment the number only of last found PART: REVISION --> PATCH --> MINOR. don't touch STAGE
function increment_last_found(){
if [[ "${#PARTS[3]}" == 0 || "${PARTS[3]}" == "0" ]]; then
if [[ "${#PARTS[2]}" == 0 || "${PARTS[2]}" == "0" ]]; then
increment_minor
else
increment_patch
fi
else
increment_revision
fi
# stage part is not EMPTY
if [[ "${#PARTS[4]}" != 0 ]]; then
IS_SHIFT=1
fi
}
## compose version from PARTS
function compose(){
MAJOR="${PARTS[0]}"
MINOR=".${PARTS[1]}"
PATCH=".${PARTS[2]}"
REVISION=".${PARTS[3]}"
SUFFIX="-${PARTS[4]}"
if [[ "${#PATCH}" == 1 ]]; then # if empty {PATCH}
PATCH=""
fi
if [[ "${#REVISION}" == 1 ]]; then # if empty {REVISION}
REVISION=""
fi
if [[ "${PARTS[3]}" == "0" ]]; then # if revision is ZERO
REVISION=""
fi
# shrink patch and revision
if [[ -z "${REVISION// }" ]]; then
if [[ "${PARTS[2]}" == "0" ]]; then
PATCH=".0"
fi
else # revision is not EMPTY
if [[ "${#PATCH}" == 0 ]]; then
PATCH=".0"
fi
fi
# remove suffix if we don't have a alpha/beta/rc
if [[ "${#SUFFIX}" == 1 ]]; then
SUFFIX=""
fi
echo "${MAJOR}${MINOR}${PATCH}${REVISION}${SUFFIX}" #full format
}
# initial version used for repository without tags
INIT_VERSION=0.0.0.0-alpha
# do GIT data extracting
TAG=$(highest_tag)
REVISION=$(latest_revision)
BRANCH=$(current_branch)
TAG_HASH=$(tag_hash $TAG)
HEAD_HASH=$(head_hash)
# if tag and branch commit hashes are different, than print info about that
#echo $HEAD_HASH vs $TAG_HASH
if [[ "$@" == "" ]]; then
if [[ "$TAG_HASH" == "$HEAD_HASH" ]]; then
echo "Tag $TAG and HEAD are aligned. We will stay on the TAG version."
echo ""
NO_ARGS_VALUE='--stay'
else
PATTERN="^[0-9]+.[0-9]+(.[0-9]+)*(-(alpha|beta|rc))*$"
if [[ "$BRANCH" =~ $PATTERN ]]; then
echo "Detected version branch '$BRANCH'. We will auto-increment the last version PART."
echo ""
NO_ARGS_VALUE='--default'
else
echo "Detected branch name '$BRANCH' than does not match version pattern. We will increase MINOR."
echo ""
NO_ARGS_VALUE='--minor'
fi
fi
fi
#
# {MAJOR}.{MINOR}[.{PATCH}[.{REVISION}][-(.*)]
#
# Suffix: alpha, beta, rc
# No Suffix --> {NEW_VERSION}-alpha
# alpha --> beta
# beta --> rc
# rc --> {VERSION}
#
PARTS=( ${TAG//./ } )
parse_last ${#PARTS[@]} # array size as argument
#echo ${PARTS[@]}
# if no parameters than emulate --default parameter
if [[ "$@" == "" ]]; then
set -- $NO_ARGS_VALUE
fi
# parse input parameters
for i in "$@"
do
key="$i"
case $key in
-a|--alpha) # switched to ALPHA
PARTS[4]="alpha"
IS_SHIFT=1
;;
-b|--beta) # switched to BETA
PARTS[4]="beta"
IS_SHIFT=1
;;
-c|--release-candidate) # switched to RC
PARTS[4]="rc"
IS_SHIFT=1
;;
-r|--release) # switched to RELEASE
PARTS[4]=""
IS_SHIFT=1
;;
-p|--patch) # increment of PATCH
increment_patch
;;
-e|--revision) # increment of REVISION
increment_revision
;;
-g|--git-revision) # use git revision number as a revision part§
PARTS[3]=$(( REVISION ))
IS_DIRTY=1
;;
-i|--minor) # increment of MINOR by default
increment_minor
;;
--default) # stay on the same stage, but increment only last found PART of version code
increment_last_found
;;
-m|--major) # increment of MAJOR
incremet_major
;;
-s|--stay) # extract version info
IS_DIRTY=1
NO_APPLY_MSG=1
;;
-t|--tag-only) # extract version info
TAG_ONLY=1
;;
--apply)
DO_APPLY=1
;;
-h|--help)
help
;;
esac
shift
done
# detected shift, but no increment
if [[ "$IS_SHIFT" == "1" ]]; then
# temporary disable stage shift
stage=${PARTS[4]}
PARTS[4]=''
# detect first run on repository, INIT_VERSION was used
if [[ "$(compose)" == "0.0" ]]; then
increment_minor
fi
PARTS[4]=$stage
fi
# no increment applied yet and no shift of state, do minor increase
if [[ "$IS_DIRTY$IS_SHIFT" == "" ]]; then
increment_minor
fi
compose
# is proposed tag in conflict with any other TAG
PROPOSED_HASH=$(tag_hash $(compose))
if [[ "${#PROPOSED_HASH}" -gt 0 && "$NO_APPLY_MSG" == "" ]]; then
echo -e "\033[31mERROR:\033[0m "
echo -e "\033[31mERROR:\033[0m Found conflict with existing tag \033[32m$(compose)\033[0m / $PROPOSED_HASH"
echo -e "\033[31mERROR:\033[0m Only manual resolving is possible now."
echo -e "\033[31mERROR:\033[0m "
echo -e "\033[31mERROR:\033[0m To Resolve try to add --revision or --patch modifier."
echo -e "\033[31mERROR:\033[0m "
echo ""
exit 1
fi
+3 -1
View File
@@ -20,6 +20,8 @@ packages/**/src/icons/*.tsx
packages/**/src/aliases/*.ts
packages/**/src/aliases.ts
!packages/**/src/aliases/index.ts
packages/**/src/utils/*.ts
!packages/shared/src/utils/*.ts
packages/**/src/dynamicIconImports.ts
packages/**/DynamicIcon.d.ts
packages/**/dynamicIconImports.js
@@ -44,7 +46,7 @@ docs/.vitepress/data/releaseMetaData
docs/.vitepress/data/categoriesData.json
docs/.vitepress/data/iconDetails
docs/.vitepress/data/relatedIcons.json
docs/.vitepress/data/brandStopwords.json
docs/.vercel
docs/.nitro
.gitignore
-1
View File
@@ -1 +0,0 @@
auto-install-peers=true
+2 -2
View File
@@ -1,6 +1,6 @@
ISC License
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.
Copyright (c) for portions of Lucide are held by Cole Bemis 2013-2026 as part of Feather (MIT). All other copyright (c) for Lucide are held by Lucide Contributors 2026.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ -18,7 +18,7 @@ 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
Copyright (c) 2013-2026 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
+11 -2
View File
@@ -88,7 +88,16 @@ Thank you to all the people who contributed to Lucide!
<a href="https://www.digitalocean.com/?refcode=b0877a2caebd&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge"><img src="docs/public/digitalocean.svg" width="200" alt="DigitalOcean Referral Badge" /></a>
[//]: <> (Open Collective backers)
### Awesome backers 🍺
<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>
<a href="https://github.com/pdfme/pdfme"><img src="docs/public/sponsors/pdfme.svg" width="180" alt="pdfme – Open-source PDF generation library built with TypeScript and React." /></a>
<a href="https://www.paxhistoria.co/"><img src="docs/public/sponsors/paxhistoria.svg?" width="180" alt="Pax Historia – An alternate history sandbox game" /></a>
### Backers ☕
<a href="https://www.fina.money/"><img src="docs/public/sponsors/fina-money.png" width="180" alt="Fina Money – Modular Finance Tracker" /></a>
### Other contributors 💸
You can find all our past and non-recurring financial contributors at [our Open Collective page](https://opencollective.com/lucide-icons).
+150
View File
@@ -0,0 +1,150 @@
{
"adobe": "Adobe",
"airplay": "AirPlay",
"amazon": "Amazon",
"android": "Android",
"angular": "Angular",
"aws": "AWS",
"azure": "Azure",
"bandcamp": "Bandcamp",
"behance": "Behance",
"bitbucket": "Bitbucket",
"blender": "Blender",
"bluesky": "BlueSky",
"bootstrap": "Bootstrap",
"brave": "Brave",
"chakra": "Chakra UI",
"chrome": "Chrome",
"codepen": "Codepen",
"codesandbox": "CodeSandbox",
"csharp": "C#",
"cypress": "Cypress",
"dart": "Dart",
"deezer": "Deezer",
"deno": "Deno",
"discord": "Discord",
"docker": "Docker",
"dribbble": "Dribbble",
"dropbox": "Dropbox",
"edge": "Edge",
"ember": "Ember",
"epic": "Epic Games",
"erlang": "Erlang",
"esbuild": "esbuild",
"eslint": "ESLint",
"facebook": "Facebook",
"figjam": "FigJam",
"figma": "Figma",
"firebase": "Firebase",
"firefox": "Firefox",
"framer": "Framer",
"gatsby": "Gatsby",
"gcp": "Google Cloud",
"github": "GitHub",
"gitlab": "GitLab",
"golang": "GoLang",
"google": "Google",
"gmail": "Gmail",
"gravatar": "Gravatar",
"haskell": "Haskell",
"instagram": "Instagram",
"java": "Java",
"javascript": "JavaScript",
"jest": "Jest",
"jira": "Jira",
"kotlin": "Kotlin",
"kubernetes": "Kubernetes",
"less": "Less",
"leetcode": "LeetCode",
"leet-code": "LeetCode",
"line": "LINE",
"linkedin": "LinkedIn",
"lua": "Lua",
"mariadb": "MariaDB",
"mcp": "MCP",
"messenger": "Messenger",
"microsoft": "Microsoft",
"mongodb": "MongoDB",
"mui": "Material UI",
"mysql": "MySQL",
"nestjs": "NestJS",
"netflix": "Netflix",
"netlify": "Netlify",
"next": "Next.js",
"nodejs": "Node.js",
"notion": "Notion",
"nostr": "Nostr",
"npm": "npm",
"nuxt": "Nuxt",
"opera": "Opera",
"oracle": "Oracle",
"patreon": "Patreon",
"paypal": "PayPal",
"perl": "Perl",
"php": "PHP",
"pinterest": "Pinterest",
"pix": "PiX",
"playstation": "PlayStation",
"playwright": "Playwright",
"pnpm": "pnpm",
"postcss": "PostCSS",
"postgresql": "PostgreSQL",
"prettier": "Prettier",
"prisma": "Prisma",
"python": "Python",
"qwik": "Qwik",
"react": "React",
"reddit": "Reddit",
"redis": "Redis",
"rollup": "Rollup",
"rust": "Rust",
"safari": "Safari",
"sass": "Sass",
"scala": "Scala",
"scss": "Sass",
"semantic": "Semantic UI",
"shopify": "Shopify",
"skype": "Skype",
"slack": "Slack",
"solid": "SolidJS",
"soundcloud": "SoundCloud",
"spotify": "Spotify",
"sqlite": "SQLite",
"squarespace": "Squarespace",
"steam": "Steam",
"stripe": "Stripe",
"substack": "Substack",
"supabase": "Supabase",
"surge": "Surge",
"svelte": "Svelte",
"swift": "Swift",
"tailwind": "Tailwind CSS",
"telegram": "Telegram",
"terraform": "Terraform",
"tesla": "Tesla",
"tidal": "Tidal",
"tiktok": "TikTok",
"trello": "Trello",
"twitch": "Twitch",
"twitter": "Twitter",
"typescript": "TypeScript",
"unity": "Unity",
"unreal": "Unreal Engine",
"vercel": "Vercel",
"vimeo": "Vimeo",
"vite": "Vite",
"vitest": "Vitest",
"vue": "Vue",
"webpack": "Webpack",
"wechat": "WeChat",
"whatsapp": "WhatsApp",
"windows": "Windows",
"wix": "Wix",
"x.com": "X.com",
"x-social": "X.com",
"xbox": "Xbox",
"yarn": "Yarn",
"youtube": "YouTube",
"zig": "Zig",
"zoom": "Zoom"
}
+1 -1
View File
@@ -1,5 +1,5 @@
{
"$schema": "../category.schema.json",
"title": "Navigation",
"title": "Navigation, Maps, and POIs",
"icon": "compass"
}
@@ -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;
}
@@ -0,0 +1,55 @@
import { eventHandler, setResponseHeader, defaultContentType } from 'h3';
import { renderToString } from 'react-dom/server';
import { createElement } from 'react';
import Diff from '../../../lib/SvgPreview/Diff.tsx';
export default eventHandler((event) => {
const { params } = event.context;
const pathData = params.data.split('/');
const data = pathData.at(-1).slice(0, -4);
const [operation] = pathData;
const newSrc = Buffer.from(data, 'base64')
.toString('utf8')
.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 = [];
let oldSrc = '';
if (operation.startsWith('rotate-')) {
const degrees = parseInt(operation.replace('rotate-', ''));
if (isNaN(degrees)) return '';
oldSrc = `<g transform="rotate(${degrees} ${width / 2} ${height / 2})">${newSrc}</g>`;
} else if (operation === 'flip-horizontal') {
oldSrc = `<g transform="scale(1, -1) translate(0, -${height})">${newSrc}</g>`;
} else if (operation === 'flip-vertical') {
oldSrc = `<g transform="scale(-1, 1) translate(-${width}, 0)">${newSrc}</g>`;
} else if (operation === 'flip-backslash') {
oldSrc = `<g transform="rotate(90, ${width / 2}, ${height / 2}) scale(1, -1) translate(0, -${height})">${newSrc}</g>`;
} else if (operation === 'flip-slash') {
oldSrc = `<g transform="rotate(90, ${width / 2}, ${height / 2}) scale(-1, 1) translate(-${width}, 0)">${newSrc}</g>`;
} else {
return '';
}
const svg = Buffer.from(
// We can't use jsx here, is not supported here by nitro.
renderToString(
createElement(Diff, { oldSrc, newSrc, showGrid: true, height, width }, children),
),
).toString('utf8');
defaultContentType(event, 'image/svg+xml');
setResponseHeader(event, 'Cache-Control', 'public,max-age=31536000');
return svg;
});
@@ -22,6 +22,7 @@ export default eventHandler((event) => {
const height = getQuery(event).height || undefined;
const color = getQuery(event).color || undefined;
const strokeWidth = getQuery(event).strokeWidth || undefined;
const background = getQuery(event).background || undefined;
const LucideIcon = createLucideIcon(params.iconName, iconNode);
@@ -32,6 +33,7 @@ export default eventHandler((event) => {
height,
color: color ? `#${color}` : undefined,
strokeWidth,
style: background ? { background } : undefined,
}),
),
).toString('utf8');
+31 -1
View File
@@ -1,6 +1,7 @@
import { fileURLToPath, URL } from 'node:url';
import { defineConfig } from 'vitepress';
import sidebar from './sidebar';
import getStructuredData from './getStructuredData';
const title = 'Lucide';
const socialTitle = 'Lucide Icons';
@@ -9,6 +10,7 @@ const description = 'Beautiful & consistent icon toolkit made by the community.'
// https://vitepress.dev/reference/site-config
export default defineConfig({
title,
titleTemplate: ':title \u2013 Lucide',
description,
cleanUrls: true,
outDir: '.vercel/output/static',
@@ -150,8 +152,36 @@ export default defineConfig({
},
],
],
async transformPageData(pageData) {
if (
pageData.relativePath.startsWith('icons/') &&
!pageData.relativePath.startsWith('icons/lab/') &&
pageData.params?.name
) {
const iconName = pageData.params.name;
pageData.title = `${iconName} icon details`;
const taggedAs = pageData.params?.tags?.length
? `Tagged as: ${pageData.params.tags.join(', ')}.`
: '';
const categorizedIn = pageData.params?.category?.length
? `Categorized in: ${pageData.params.category.join(', ')}.`
: '';
pageData.description =
`Details and related icons for ${iconName} icon. ${taggedAs} ${categorizedIn}`.trim();
const structuredData = await getStructuredData(iconName, pageData);
pageData.frontmatter.head ??= [];
pageData.frontmatter.head.push([
'script',
{ type: 'application/ld+json' },
JSON.stringify(structuredData),
]);
}
},
themeConfig: {
// https://vitepress.dev/reference/default-theme-config
logo: {
light: '/logo.light.svg',
dark: '/logo.dark.svg',
+1 -1
View File
@@ -101,7 +101,7 @@
},
{
"name": "navigation",
"title": "Navigation"
"title": "Navigation, Maps, and POIs"
},
{
"name": "notifications",
@@ -78,7 +78,7 @@
"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": [
@@ -98,7 +98,7 @@
},
{
"name": "lucide_icons_flutter",
"description": "A library providing https://lucide.dev icons to lustre.",
"description": "A library providing https://lucide.dev icons to Flutter.",
"icon": "/framework-logos/flutter.svg",
"shields": [
{
@@ -152,5 +152,43 @@
],
"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"
},
{
"name": "lucide-web-components",
"description": "Implementation of Lucide icons in Web Components custom elements.",
"icon": "/framework-logos/web-components.svg",
"shields": [
{
"alt": "Latest Stable Version",
"src": "https://img.shields.io/npm/v/@dile/iconlib",
"href": "https://dile-components.com/icons/lucide-icons/"
},
{
"alt": "Total Downloads",
"src": "https://img.shields.io/npm/dt/@dile/iconlib",
"href": "https://dile-components.com/icons/lucide-icons/"
}
],
"source": "https://github.com/Polydile/dile-components",
"documentation": "https://github.com/Polydile/dile-components/blob/master/packages/iconlib/README.md"
}
]
+46
View File
@@ -0,0 +1,46 @@
import { PageData } from 'vitepress';
export default async function getStructuredData(iconName: string, pageData: PageData) {
const url = `https://lucide.dev/icons/${iconName}`;
return {
'@context': 'https://schema.org',
'@type': 'WebPage',
'@id': url,
name: pageData.title,
description: pageData.description,
url,
inLanguage: 'en',
isPartOf: {
'@type': 'WebSite',
name: 'Lucide Icons',
url: 'https://lucide.dev',
},
breadcrumb: {
'@type': 'BreadcrumbList',
itemListElement: [
{ '@type': 'ListItem', position: 1, name: 'Icons', item: 'https://lucide.dev/icons' },
{ '@type': 'ListItem', position: 2, name: iconName, item: url },
],
},
mainEntity: {
'@type': 'ImageObject',
'@id': `${url}#icon`,
name: iconName,
// TODO: Add support for description extraction from icon metadata
// description: 'An ...SVG icon from the Lucide icon set.',
contentUrl: `https://lucide.dev/api/icons/${iconName}}?width=24&height=24&background=white`,
thumbnailUrl: `https://lucide.dev/api/icons/${iconName}}?width=256&height=256&background=white`,
encodingFormat: 'image/svg+xml',
keywords: pageData.params?.tags,
width: 24,
height: 24,
creator: {
'@type': 'Organization',
name: 'Lucide Icons',
},
datePublished: pageData?.params?.createdRelease?.date,
dateModified: pageData?.params?.changedRelease?.date,
},
};
}
@@ -1,4 +1,4 @@
import { bundledLanguages, getHighlighter, type ThemeRegistration } from 'shikiji';
import { bundledLanguages, createHighlighter, type ThemeRegistration } from 'shiki';
type CodeExampleType = {
title: string;
@@ -9,14 +9,20 @@ type CodeExampleType = {
const getIconCodes = (): CodeExampleType => {
return [
{
language: 'js',
language: 'html',
title: 'Vanilla',
code: `\
import { createIcons, icons } from 'lucide';
<script>
import { createIcons, $CamelCase } from 'lucide';
createIcons({ icons });
createIcons({
icons: {
$CamelCase
}
});
</script>
document.body.append('<i data-lucide="$Name"></i>');\
<i data-lucide="$Name"></i>\
`,
},
{
@@ -112,7 +118,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),
});
@@ -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;
@@ -10,10 +10,11 @@ type CodeExampleType = {
const getIconCodes = (): CodeExampleType => {
return [
{
language: 'js',
language: 'html',
title: 'Vanilla',
code: `\
import { createIcons, icons } from 'lucide';
<script>
import { createIcons } from 'lucide';
import { $CamelCase } from '@lucide/lab';
createIcons({
@@ -21,8 +22,9 @@ createIcons({
$CamelCase
}
});
</script>
document.body.append('<i data-lucide="$Name"></i>');\
<i data-lucide="$Name"></i>\
`,
},
{
@@ -119,7 +121,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),
});
@@ -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),
});
+1 -1
View File
@@ -1,5 +1,5 @@
import { createLucideIcon } from 'lucide-react/src/lucide-react';
import { type LucideProps, type IconNode } from 'lucide-react/src/createLucideIcon';
import { type LucideProps, type IconNode } from 'lucide-react/src/types';
import { IconEntity } from '../theme/types';
import { renderToStaticMarkup } from 'react-dom/server';
import { IconContent } from './generateZip';
@@ -1,23 +1,31 @@
<script setup lang="ts">
import { useData } from 'vitepress';
import { computed } from 'vue';
const props = defineProps<{
modelValue: string
id: string
}>()
modelValue: string;
id: string;
}>();
const emit = defineEmits(['update:modelValue'])
const { isDark } = useData();
const emit = defineEmits(['update:modelValue']);
const value = computed({
get: () => props.modelValue,
set: (val) => emit('update:modelValue', val)
})
get: () => {
if (props.modelValue == null || props.modelValue === 'currentColor') {
return isDark.value ? '#ffffff' : '#000000';
}
return props.modelValue;
},
set: (val) => emit('update:modelValue', val),
});
</script>
<template>
<div class="color-picker">
<div class="color-input-wrapper">
<!-- TODO: Add currentColor div if value is currentColor -->
<input
type="color"
:id="id"
@@ -33,6 +41,7 @@ const value = computed({
class="color-input-text"
aria-label="Color picker input"
v-model="value"
placeholder="[default]"
/>
</div>
</template>
@@ -45,27 +54,33 @@ const value = computed({
top: -5px;
left: -5px;
}
.color-input-wrapper {
height: 24px;
width: 24px;
overflow: hidden;
position: relative;
border-radius: 12px;
border-radius: 4px;
flex-shrink: 0;
}
.color-picker {
background: var(--color-picker-bg, var(--vp-c-bg-alt));
border-radius: 8px;
color: var(--vp-c-text-2);
padding: 4px 8px;
padding: 3px 8px 3px 3px;
height: auto;
font-size: 14px;
font-size: 13px;
text-align: left;
border: 1px solid transparent;
cursor: text;
display: flex;
align-items: center;
gap: 2px;
transition:
color 0.25s,
border-color 0.25s,
background-color 0.25s;
}
.color-input-text {
@@ -75,19 +90,24 @@ const value = computed({
border: none;
background: transparent;
color: var(--vp-c-text-1);
font-size: 14px;
font-size: 13px;
text-align: left;
border-radius: 8px;
cursor: text;
transition: border-color 0.25s, background 0.4s ease;
transition:
border-color 0.25s,
background 0.4s ease;
letter-spacing: 1px;
}
.color-picker:hover, .color-picker:focus {
.color-picker:hover,
.color-picker:focus-within {
border-color: var(--vp-c-brand);
background: var(--vp-c-bg-alt);
}
.color-picker:focus-within {
background-color: var(--vp-c-bg);
}
.color-input[value="currentColor"] {
.color-input[value='currentColor'] {
}
</style>
@@ -1,14 +1,27 @@
<script setup>
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon'
import { search } from '../../../data/iconNodes'
import Icon from 'lucide-vue-next/src/Icon';
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/>
<Icon
:iconNode="search"
class="search-icon"
/>
<slot />
<kbd
v-if="shortcut"
class="shortcut"
>{{ shortcut }}</kbd
>
</button>
</template>
@@ -26,11 +39,33 @@ const SearchIcon = createLucideIcon('search', search)
cursor: text;
display: flex;
gap: 12px;
transition: color 0.25s, border-color 0.25s, background-color 0.25s;
transition:
color 0.25s,
border-color 0.25s,
background-color 0.25s;
}
.fake-input:hover, .fake-input:focus {
.fake-input:hover,
.fake-input:focus {
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>
@@ -5,7 +5,6 @@
</template>
<style scoped>
.icon-button {
display: inline-flex;
border: 1px solid transparent;
@@ -30,9 +29,9 @@
}
.icon-button:active {
border-color: var(--vp-button-alt-active-border);
color: var(--vp-button-alt-active-text);
background-color: var(--vp-button-alt-active-bg);
border-color: var(--vp-button-alt-active-border);
color: var(--vp-button-alt-active-text);
background-color: var(--vp-button-alt-active-bg);
}
.icon-button.active {
+132 -17
View File
@@ -1,44 +1,93 @@
<script lang="ts">
export default {
inheritAttrs: false,
}
};
export interface InputProps {
type: string
modelValue: string
type: string;
modelValue: string;
shortcut?: string;
}
</script>
<script setup lang="ts">
import { ref } from 'vue'
import { ref, onMounted, nextTick, watch } from 'vue';
import Icon from 'lucide-vue-next/src/Icon';
import { x } from '../../../data/iconNodes';
import IconButton from './IconButton.vue';
const props = withDefaults(defineProps<InputProps>(), {
type: 'text'
})
type: 'text',
});
const input = ref()
const input = ref();
const wrapperEl = ref();
const shortcutEl = ref();
defineEmits(['change', 'input', 'update:modelValue'])
const emit = 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`);
}
});
};
const isMac = /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform);
onMounted(updateShortcutSpacing);
watch(() => props.shortcut, updateShortcutSpacing);
function onClear() {
emit('update:modelValue', '');
input.value.focus();
}
defineExpose({
focus: () => {
input.value.focus()
}
})
input.value.focus();
},
});
</script>
<template>
<div class="input-wrapper">
<slot name="icon" class="icon" />
<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)"
/>
<IconButton
@click="onClear"
v-if="type === 'search' && modelValue"
class="clear-button"
aria-label="Clear input"
:style="{ right: isMac ? '50px' : '68px' }"
>
<Icon
:iconNode="x"
:size="20"
/>
</IconButton>
<kbd
v-if="shortcut"
class="shortcut"
ref="shortcutEl"
>{{ shortcut }}</kbd
>
</div>
</template>
@@ -46,6 +95,7 @@ defineExpose({
.input-wrapper {
position: relative;
}
.input {
justify-content: flex-start;
border: 1px solid transparent;
@@ -55,22 +105,84 @@ defineExpose({
height: 40px;
background-color: var(--vp-c-bg-alt);
font-size: 14px;
transition:
color 0.25s,
border-color 0.25s,
background-color 0.25s;
}
.input:hover, .input:focus {
.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);
}
.input:focus {
background-color: var(--vp-c-bg);
}
.input.has-icon {
padding-left: 52px;
}
.clear-button {
position: absolute;
right: 68px;
top: 9px;
padding: 4px;
transition: background-color .25s;
}
.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;
}
.input[type="search"]::-webkit-search-cancel-button {
-webkit-appearance: none;
appearance: none;
display: none;
}
.input[type="search"]::-ms-clear {
display: none;
}
.input[type="search"]::-o-clear {
display: none;
}
.input[type="search"]::-moz-clear {
display: none;
}
@media (hover: none) {
.shortcut {
display: none;
}
.clear-button {
right: 16px !important;
}
}
</style>
<style>
.input-wrapper svg {
.input-wrapper > svg {
position: absolute;
left: 16px;
top: 12px;
@@ -78,4 +190,7 @@ defineExpose({
color: var(--vp-c-text-2);
pointer-events: none;
}
.input-wrapper > svg:has(+ .input.has-icon:focus) {
color: var(--vp-c-text-1);
}
</style>
@@ -1,37 +1,36 @@
<script lang="ts">
export default {
inheritAttrs: false,
}
};
</script>
<script setup lang="ts">
import { computed, ref } from 'vue'
import Input from './Input.vue'
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon'
import { search } from '../../../data/iconNodes'
const SearchIcon = createLucideIcon('search', search)
import { computed, ref } from 'vue';
import Input from './Input.vue';
import Icon from 'lucide-vue-next/src/Icon';
import { search } from '../../../data/iconNodes';
interface Props {
modelValue: string
modelValue: string;
shortcut?: string;
}
const props = defineProps<Props>()
const props = defineProps<Props>();
const input = ref()
const input = ref();
const emit = defineEmits(['update:modelValue'])
const emit = defineEmits(['update:modelValue']);
defineExpose({
focus: () => {
input.value.focus()
}
})
input.value.focus();
},
});
const value = computed({
get: () => props.modelValue,
set: (val) => emit('update:modelValue', val)
})
set: (val) => emit('update:modelValue', val),
});
</script>
<template>
@@ -39,12 +38,16 @@ const value = computed({
ref="input"
type="search"
autofocus
:shortcut="shortcut"
v-bind="$attrs"
v-model="value"
class="input-wrapper"
>
<template #icon>
<component :is="SearchIcon" class="search-icon" />
<Icon
:iconNode="search"
class="search-icon"
/>
</template>
</Input>
</template>
@@ -60,7 +63,8 @@ const value = computed({
background-color: var(--vp-c-bg-alt);
}
.input:hover, .input:focus {
.input:hover,
.input:focus {
border-color: var(--vp-c-brand);
background: var(--vp-c-bg-alt);
}
@@ -71,5 +75,4 @@ const value = computed({
font-size: 14px;
height: 48px;
}
</style>
@@ -1,14 +1,15 @@
<script setup lang="ts">
import { rotateCw } from '../../../data/iconNodes'
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon'
import IconButton from "./IconButton.vue";
const RotateIcon = createLucideIcon('RotateIcon', rotateCw)
import { rotateCw } from '../../../data/iconNodes';
import Icon from 'lucide-vue-next/src/Icon';
import IconButton from './IconButton.vue';
</script>
<template>
<IconButton class="reset-button">
<RotateIcon :size="20"/>
<Icon
:size="20"
:iconNode="rotateCw"
/>
</IconButton>
</template>
@@ -32,6 +33,7 @@ const RotateIcon = createLucideIcon('RotateIcon', rotateCw)
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(359deg);
}
@@ -5,10 +5,15 @@ import LucideIcon from '../base/LucideIcon.vue'
import { useRouter } from 'vitepress';
import { random } from 'lodash-es'
import FakeInput from '../base/FakeInput.vue'
import useSearchShortcut from '../../utils/useSearchShortcut'
const { go } = useRouter()
const intervalTime = shallowRef()
const { shortcutText: kbdSearchShortcut } = useSearchShortcut(() => {
go('/icons/?focus')
})
const getInitialItems = () => data.icons.slice(0, 48)
const items = ref(getInitialItems())
let id = items.value.length + 1
@@ -64,7 +69,11 @@ onBeforeUnmount(() => {
</div>
</TransitionGroup>
</div>
<FakeInput @click="go('/icons/?focus')" class="search-box">
<FakeInput
@click="go('/icons/?focus')"
:shortcut="kbdSearchShortcut"
class="search-box"
>
Search {{ data.iconsCount }} icons...
</FakeInput>
</div>
@@ -22,16 +22,16 @@ export default {
logo: '/framework-logos/svelte.svg',
label: 'Lucide documentation for Svelte',
},
{
name: 'lucide-preact',
logo: '/framework-logos/preact.svg',
label: 'Lucide documentation for Preact',
},
{
name: 'lucide-solid',
logo: '/framework-logos/solid.svg',
label: 'Lucide documentation for Solid',
},
{
name: 'lucide-preact',
logo: '/framework-logos/preact.svg',
label: 'Lucide documentation for Preact',
},
{
name: 'lucide-angular',
logo: '/framework-logos/angular.svg',
@@ -48,11 +48,6 @@ export default {
logo: '/framework-logos/react-native.svg',
label: 'Lucide documentation for React Native',
},
{
name: 'lucide-flutter',
logo: '/framework-logos/flutter.svg',
label: 'Lucide documentation for Flutter',
},
],
};
},
@@ -2,45 +2,48 @@
import { useData } from 'vitepress';
import { useSessionStorage } from '@vueuse/core';
import IconButton from '../base/IconButton.vue';
import VPDocAsideCarbonAds from 'vitepress/dist/client/theme-default/components/VPDocAsideCarbonAds.vue'
import { x } from '../../../data/iconNodes'
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon';
import VPDocAsideCarbonAds from 'vitepress/dist/client/theme-default/components/VPDocAsideCarbonAds.vue';
import { x } from '../../../data/iconNodes';
import Icon from 'lucide-vue-next/src/Icon';
import { onMounted, ref } from 'vue';
const { theme } = useData()
const showAd = useSessionStorage('show-carbon-ads', true)
const carbonLoaded = ref(true)
const { theme } = useData();
const showAd = useSessionStorage('show-carbon-ads', true);
const carbonLoaded = ref(true);
defineProps<{
drawerOpen: boolean
}>()
const CloseIcon = createLucideIcon('Close', x)
drawerOpen: boolean;
}>();
onMounted(() => {
setTimeout(() => {
if (window?._carbonads == null) {
carbonLoaded.value = false
carbonLoaded.value = false;
}
}, 5000)
})
}, 5000);
});
</script>
<template>
<div
:class="{
'drawer-open': drawerOpen,
'hide-ad': !(showAd && carbonLoaded)
'hide-ad': !(showAd && carbonLoaded),
}"
class="floating-ad"
v-if="theme.carbonAds"
>
<IconButton @click="showAd = false" class="hide-button">
<component :is="CloseIcon" :size="20" absoluteStrokeWidth />
<IconButton
@click="showAd = false"
class="hide-button"
>
<Icon
:iconNode="x"
:size="20"
absoluteStrokeWidth
/>
</IconButton>
<VPDocAsideCarbonAds
:carbon-ads="theme.carbonAds"
/>
<VPDocAsideCarbonAds :carbon-ads="theme.carbonAds" />
</div>
</template>
@@ -51,7 +54,9 @@ onMounted(() => {
bottom: 32px;
width: 224px;
right: 32px;
transition: opacity 0.5s, transform 0.25s ease;
transition:
opacity 0.5s,
transform 0.25s ease;
}
.floating-ad.drawer-open {
@@ -67,8 +72,11 @@ onMounted(() => {
transform: translateY(-288px) translateX(224px);
}
.floating-ad.drawer-open, .floating-ad.hide-ad {
transition: opacity 0.25s, transform 0.5s cubic-bezier(0.19, 1, 0.22, 1);
.floating-ad.drawer-open,
.floating-ad.hide-ad {
transition:
opacity 0.25s,
transform 0.5s cubic-bezier(0.19, 1, 0.22, 1);
}
@media (min-width: 1280px) {
@@ -1,70 +1,68 @@
<script setup lang="ts">
import { ref } from 'vue';
import ButtonMenu from '../base/ButtonMenu.vue'
import ButtonMenu from '../base/ButtonMenu.vue';
import { useIconStyleContext } from '../../composables/useIconStyle';
import useConfetti from '../../composables/useConfetti';
import getSVGIcon from '../../utils/getSVGIcon';
import downloadData from '../../utils/downloadData';
const downloadText = 'Download!'
const copiedText = 'Copied!'
const confettiText = ref(copiedText)
const downloadText = 'Download!';
const copiedText = 'Copied!';
const confettiText = ref(copiedText);
const props = defineProps<{
name: string
popoverPosition?: 'top' | 'bottom'
}>()
name: string;
popoverPosition?: 'top' | 'bottom';
}>();
const { size } = useIconStyleContext()
const { size } = useIconStyleContext();
const { animate, confetti } = useConfetti()
const { animate, confetti } = useConfetti();
function copySVG() {
confettiText.value = copiedText
const svgString = getSVGIcon()
confettiText.value = copiedText;
const svgString = getSVGIcon();
navigator.clipboard.writeText(svgString)
navigator.clipboard.writeText(svgString);
confetti()
confetti();
}
function copyDataUrl() {
confettiText.value = copiedText
const svgString = getSVGIcon()
confettiText.value = copiedText;
const svgString = getSVGIcon();
// Create SVG data url
const dataUrl = `data:image/svg+xml;base64,${btoa(svgString)}`
navigator.clipboard.writeText(dataUrl)
const dataUrl = `data:image/svg+xml;base64,${btoa(svgString)}`;
navigator.clipboard.writeText(dataUrl);
confetti()
confetti();
}
function downloadSVG() {
confettiText.value = downloadText
const svgString = getSVGIcon()
confettiText.value = downloadText;
const svgString = getSVGIcon();
downloadData(`${props.name}.svg`, `data:image/svg+xml;base64,${btoa(svgString)}`)
confetti()
downloadData(`${props.name}.svg`, `data:image/svg+xml;base64,${btoa(svgString)}`);
confetti();
}
function downloadPNG() {
confettiText.value = downloadText
const svgString = getSVGIcon()
confettiText.value = downloadText;
const svgString = getSVGIcon();
const canvas = document.createElement('canvas');
canvas.width = size.value;
canvas.height = size.value;
const ctx = canvas.getContext("2d");
const ctx = canvas.getContext('2d');
const image = new Image();
image.src = `data:image/svg+xml;base64,${btoa(svgString)}`;
image.onload = function() {
image.onload = function () {
ctx.drawImage(image, 0, 0);
downloadData(`${props.name}.png`, canvas.toDataURL('image/png'))
confetti()
}
downloadData(`${props.name}.png`, canvas.toDataURL('image/png'));
confetti();
};
}
</script>
<template>
@@ -75,10 +73,10 @@ function downloadPNG() {
:data-confetti-text="confettiText"
:popoverPosition="popoverPosition"
:options="[
{ text: 'Copy SVG' , onClick: copySVG },
{ text: 'Copy Data URL' , onClick: copyDataUrl },
{ text: 'Download SVG' , onClick: downloadSVG },
{ text: 'Download PNG' , onClick: downloadPNG },
{ text: 'Copy SVG', onClick: copySVG },
{ text: 'Copy Data URL', onClick: copyDataUrl },
{ text: 'Download SVG', onClick: downloadSVG },
{ text: 'Download PNG', onClick: downloadPNG },
]"
/>
</template>
@@ -1,41 +1,44 @@
<script setup lang="ts">
import { computed, useSlots } from 'vue';
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon'
import { copy } from '../../../data/iconNodes'
import { copy } from '../../../data/iconNodes';
import useConfetti from '../../composables/useConfetti';
const { animate, confetti } = useConfetti()
const slots = useSlots()
import Icon from 'lucide-vue-next/src/Icon';
const { animate, confetti } = useConfetti();
const slots = useSlots();
const copiedText = computed(() => slots.default?.()[0].children)
const copiedText = computed(() => slots.default?.()[0].children);
function copyText() {
navigator.clipboard.writeText(copiedText.value)
navigator.clipboard.writeText(copiedText.value);
confetti()
confetti();
}
const Copy = createLucideIcon('ChevronUp', copy)
</script>
<template>
<h1
class="icon-name confetti-button"
:class="{animate}"
:class="{ animate }"
data-confetti-text="Copied!"
@click="copyText"
>
<slot />
<Copy :size="20" class="copy-icon"/>
<Icon
:iconNode="copy"
:size="20"
class="copy-icon"
/>
</h1>
</template>
<style scoped>
@import './confetti.css';
.icon-name {
font-size: 24px;
font-weight: 500;
line-height: 32px;
transition: background ease-in .15s;;
transition: background ease-in 0.15s;
padding: 2px 8px;
border-radius: 8px;
width: auto;
@@ -48,7 +51,7 @@ const Copy = createLucideIcon('ChevronUp', copy)
}
.icon-name:hover .copy-icon {
opacity: .9;
opacity: 0.9;
}
.icon-name:before,
@@ -65,10 +68,10 @@ const Copy = createLucideIcon('ChevronUp', copy)
opacity: 0;
margin-left: 12px;
margin-top: 6px;
transition:ease .3s opacity;
transition: ease 0.3s opacity;
}
.icon-name:hover .copy-icon:hover {
opacity: .6;
opacity: 0.6;
}
</style>
@@ -1,18 +1,19 @@
<script setup lang="ts">
import { ref, computed, defineAsyncComponent, onMounted, watch, watchEffect } from 'vue';
import { ref, computed, defineAsyncComponent, onMounted } from 'vue';
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 IconsCategory, { CategoryRow } from './IconsCategory.vue';
import useFetchTags from '../../composables/useFetchTags';
import useFetchCategories from '../../composables/useFetchCategories';
import { useElementSize, useEventListener, useVirtualList } from '@vueuse/core';
import chunkArray from '../../utils/chunkArray';
import { CategoryRow } from './IconsCategory.vue';
import useScrollToCategory from '../../composables/useScrollToCategory';
import CarbonAdOverlay from './CarbonAdOverlay.vue';
import useSearchPlaceholder from '../../utils/useSearchPlaceholder.ts';
const ICON_SIZE = 56;
const ICON_GRID_GAP = 8;
@@ -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;
}
@@ -35,10 +40,10 @@ const { execute: fetchTags, data: tags } = useFetchTags();
const { execute: fetchCategories, data: categoriesMap } = useFetchCategories();
const overviewEl = ref<HTMLElement | null>(null);
const { width: containerWidth } = useElementSize(overviewEl)
const { width: containerWidth } = useElementSize(overviewEl);
const columnSize = computed(() => {
return Math.floor((containerWidth.value) / ((ICON_SIZE + ICON_GRID_GAP)));
return Math.floor(containerWidth.value / (ICON_SIZE + ICON_GRID_GAP));
});
const mappedIcons = computed(() => {
@@ -66,26 +71,27 @@ const searchResults = useSearch(searchQueryDebounced, mappedIcons, [
const categories = computed(() => {
if (!props.categories?.length || !props.icons?.length) return [];
return props.categories
.map(({ name, title }) => {
const categoryIcons = props.icons.filter((icon) => {
const iconCategories = icon?.externalLibrary ? icon.categories : props.iconCategories[icon.name]
return props.categories.map(({ name, title }) => {
const categoryIcons = props.icons.filter((icon) => {
const iconCategories = icon?.externalLibrary
? icon.categories
: props.iconCategories[icon.name];
return iconCategories?.includes(name);
});
return iconCategories?.includes(name);
});
const searchedCategoryIcons = isSearching
? categoryIcons.filter((icon) =>
searchResults.value.some((item) => item?.name === icon?.name)
)
: categoryIcons;
const searchedCategoryIcons = isSearching
? categoryIcons.filter((icon) =>
searchResults.value.some((item) => item?.name === icon?.name),
)
: categoryIcons;
return {
title,
name,
icons: searchedCategoryIcons,
};
})
return {
title,
name,
icons: searchedCategoryIcons,
};
});
});
const categoriesList = computed(() => {
@@ -102,26 +108,24 @@ const categoriesList = computed(() => {
return acc;
}, []);
});
const searchPlaceholder = useSearchPlaceholder(searchQuery, searchResults);
const { list, containerProps, wrapperProps, scrollTo } = useVirtualList(
categoriesList,
{
itemHeight: ICON_SIZE + ICON_GRID_GAP,
overscan: 10
},
)
const { list, containerProps, wrapperProps, scrollTo } = useVirtualList(categoriesList, {
itemHeight: ICON_SIZE + ICON_GRID_GAP,
overscan: 10,
});
useScrollToCategory({
categories,
categoriesList,
scrollTo,
searchQueryDebounced,
})
});
onMounted(() => {
containerProps.ref.value = document.documentElement;
useEventListener(window, 'scroll', containerProps.onScroll)
})
useEventListener(window, 'scroll', containerProps.onScroll);
});
function onFocusSearchInput() {
if (tags.value == null) {
@@ -140,28 +144,27 @@ function handleCloseDrawer() {
window.history.pushState({}, '', '/icons/categories');
}
watchEffect(() => {
console.log(props.icons.find((icon) => icon.name === 'burger'));
});
</script>
<template>
<div ref="overviewEl" class="overview-container">
<div
ref="overviewEl"
class="overview-container"
>
<StickyBar class="category-search">
<InputSearch
:placeholder="`Search ${icons.length} icons ...`"
:placeholder="`Search ${icons.length} icons…`"
v-model="searchQuery"
:shortcut="kbdSearchShortcut"
class="input-wrapper"
ref="searchInput"
@focus="onFocusSearchInput"
/>
</StickyBar>
<NoResults
v-if="categories.length === 0"
:searchQuery="searchQuery"
v-if="searchPlaceholder.isNoResults"
:searchQuery="searchPlaceholder.query"
:isBrandSearch="searchPlaceholder.isBrand"
@clear="searchQuery = ''"
/>
<div v-bind="wrapperProps">
@@ -202,8 +205,4 @@ watchEffect(() => {
.icons {
margin-bottom: 8px;
}
.overview-container {
padding-bottom: 288px;
}
</style>
@@ -2,26 +2,29 @@
import { ref, computed, defineAsyncComponent, onMounted, 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';
import chunkArray from '../../utils/chunkArray';
import CarbonAdOverlay from './CarbonAdOverlay.vue';
import useSearchPlaceholder from '../../utils/useSearchPlaceholder.ts';
const ICON_SIZE = 56;
const ICON_GRID_GAP = 8;
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);
return Math.min(itemsPerRow * (visibleRows + 2), 200);
});
const props = defineProps<{
@@ -34,10 +37,10 @@ const { execute: fetchTags, data: tags } = useFetchTags();
const { execute: fetchCategories, data: categories } = useFetchCategories();
const overviewEl = ref<HTMLElement | null>(null);
const { width: containerWidth } = useElementSize(overviewEl)
const { width: containerWidth } = useElementSize(overviewEl);
const columnSize = computed(() => {
return Math.floor((containerWidth.value) / ((ICON_SIZE + ICON_GRID_GAP)));
return Math.floor(containerWidth.value / (ICON_SIZE + ICON_GRID_GAP));
});
const mappedIcons = computed(() => {
@@ -58,30 +61,38 @@ 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 },
{ name: 'tags', weight: 2 },
{ name: 'categories', weight: 1 },
]);
const searchPlaceholder = useSearchPlaceholder(searchQuery, searchResults);
const chunkedIcons = computed(() => {
return chunkArray(searchResults.value, columnSize.value);
});
const { list, containerProps, wrapperProps, scrollTo } = useVirtualList(
chunkedIcons,
{
itemHeight: ICON_SIZE + ICON_GRID_GAP,
overscan: 10
},
)
const { list, containerProps, wrapperProps, scrollTo } = useVirtualList(chunkedIcons, {
itemHeight: ICON_SIZE + ICON_GRID_GAP,
overscan: 10,
});
onMounted(() => {
containerProps.ref.value = document.documentElement;
useEventListener(window, 'scroll', containerProps.onScroll)
})
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;
@@ -101,8 +112,8 @@ const NoResults = defineAsyncComponent(() => import('./NoResults.vue'));
const IconDetailOverlay = defineAsyncComponent(() => import('./IconDetailOverlay.vue'));
watch(searchQueryDebounced, () => {
scrollTo(0)
})
scrollTo(0);
});
function handleCloseDrawer() {
setActiveIconName('');
@@ -112,19 +123,24 @@ function handleCloseDrawer() {
</script>
<template>
<div ref="overviewEl" class="overview-container">
<div
ref="overviewEl"
class="overview-container"
>
<StickyBar>
<InputSearch
:placeholder="`Search ${icons.length} icons ...`"
:placeholder="`Search ${icons.length} icons…`"
v-model="searchQuery"
ref="searchInput"
:shortcut="kbdSearchShortcut"
class="input-wrapper"
@focus="onFocusSearchInput"
/>
</StickyBar>
<NoResults
v-if="searchResults.length === 0 && searchQuery !== ''"
:searchQuery="searchQuery"
v-if="searchPlaceholder.isNoResults"
:searchQuery="searchPlaceholder.query"
:isBrandSearch="searchPlaceholder.isBrand"
@clear="searchQuery = ''"
/>
<IconGrid
@@ -170,8 +186,4 @@ function handleCloseDrawer() {
.input-wrapper {
width: 100%;
}
.overview-container {
padding-bottom: 288px;
}
</style>
@@ -1,56 +1,218 @@
<script setup lang="ts">
import { ref, onMounted, computed } from 'vue'
import { bird, squirrel, rabbit } from '../../../data/iconNodes'
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon'
import {useEventListener} from '@vueuse/core'
import VPButton from 'vitepress/dist/client/theme-default/components/VPButton.vue'
import { IconNode } from '../../types'
import { ref, onMounted, computed, markRaw, shallowReadonly, watch } from 'vue';
import {
bird,
squirrel,
rabbit,
ghost,
castle,
drama,
dog,
cat,
wandSparkles,
save,
snowflake,
cake,
fish,
turtle,
rat,
worm,
testTubeDiagonal,
sword,
} from '../../../data/iconNodes';
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon';
import { useEventListener } from '@vueuse/core';
import VPButton from 'vitepress/dist/client/theme-default/components/VPButton.vue';
import { IconNode } from '../../types';
defineProps<{
searchQuery: string
}>()
const { searchQuery, isBrandSearch } = defineProps<{
searchQuery: string;
isBrandSearch: boolean;
}>();
defineEmits(['clear'])
defineEmits(['clear']);
const animalIcon = ref<HTMLElement>()
const randomAnimal = computed<IconNode>(() => {
return Math.random() > 0.5 ? squirrel : Math.random() > 0.5 ? rabbit : bird
})
const animalComponent = computed(() => createLucideIcon('animal', randomAnimal.value))
const flip = ref(false)
interface Placeholder {
title: string;
message: string;
icon: IconNode;
finePrint?: string;
}
const brandPlaceholders: Placeholder[] = shallowReadonly([
{
title: 'Boooo! What a scary brand logo!',
message:
'[name] and its friends often haunt this search box, but you won’t ever find them here.',
icon: markRaw(ghost),
},
{
title: 'Thank You Mario!',
message: 'But [name] is in another castle!',
icon: markRaw(castle),
},
{
title: '[name] did audition for our icon set',
message: '...but didn’t make the callback.',
icon: markRaw(drama),
},
{
title: 'Such Search. Very [name].',
message: 'Much not here. So Wow.',
icon: markRaw(dog),
},
{
title: 'I Can Has [name]?',
message: 'No [name] for you in here.',
icon: markRaw(cat),
},
{
title: 'Loading [name]...',
message: 'Fatal error: our cartridge contains only open-source pixels.',
icon: markRaw(save),
},
{
title: '[name] Shall Not Pass',
message: 'Do not look to its coming at first light of any day.',
icon: markRaw(wandSparkles),
},
{
title: 'Winter is coming',
message: 'But [name] sure isn’t.',
icon: markRaw(snowflake),
},
{
title: 'The cake is a lie',
message: 'And so is the promise of an icon for [name] at Lucide.',
icon: markRaw(cake),
},
{
title: 'It’s not a bug',
message: 'Having no [name] icon around is a feature.',
icon: markRaw(worm),
},
{
title: 'The lab exploded',
message: 'We tried mixing [name] with open-source icons.',
icon: markRaw(testTubeDiagonal),
},
{
title: 'It’s Dangerous to Go Alone',
message: 'Take this icon instead — it’s not [name], but it might help.',
icon: markRaw(sword),
},
]);
const notFoundPlaceholders: Omit<Placeholder, 'title'>[] = shallowReadonly([
{
message: 'We’ve looked for this icon for a bird’s eye view, but could not find it.',
icon: markRaw(bird),
},
{
message: 'We checked every tree. Only acorns, no results.',
icon: markRaw(squirrel),
},
{
message: 'You’ve gone too deep into the rabbit hole — this icon doesn’t exist.',
icon: markRaw(rabbit),
},
{
message: 'This icon seems to have slipped through the net.',
icon: markRaw(fish),
},
{
message: 'This icon might exist in the future… but it hasn’t arrived yet.',
icon: markRaw(turtle),
},
{
message: 'Rats! This icon seems to have slipped through the cracks.',
icon: markRaw(rat),
},
]);
function randomItem<T>(arr: T[]): T {
return arr[Math.floor(Math.random() * arr.length)];
}
const placeholderIcon = ref<HTMLElement>();
const placeholder = ref<Placeholder>();
watch(
() => isBrandSearch,
() => {
placeholder.value = isBrandSearch
? {
...randomItem(brandPlaceholders),
finePrint:
'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.',
}
: {
title: `No results for “[name]”`,
finePrint:
'This icon doesn’t seem to exist… yet. Try searching similar terms, browsing existing requests, or opening a new one.',
...randomItem(notFoundPlaceholders),
};
},
{ immediate: true },
);
const iconComponent = computed(() => createLucideIcon('placeholder', placeholder.value.icon));
const flip = ref(false);
onMounted(() => {
useEventListener(document, 'mousemove', (mouseEvent) => {
const {width, height, x, y} = animalIcon.value.getBoundingClientRect()
const { width, x } = placeholderIcon.value.getBoundingClientRect();
const centerX = (width / 2) + x
flip.value = mouseEvent.x < centerX
})
})
const centerX = width / 2 + x;
flip.value = !isBrandSearch && mouseEvent.x < centerX;
});
});
</script>
<template>
<div class="no-results">
<component
:is="animalComponent"
class="animal-icon"
ref="animalIcon"
:is="iconComponent"
class="placeholder-icon"
ref="placeholderIcon"
:class="{ flip }"
:strokeWidth="1"
/>
<h2 class="no-results-text">
No icons found for '{{ searchQuery }}'
</h2>
<h2 class="no-results-text">{{ placeholder.title.replace('[name]', searchQuery) }}</h2>
<p class="no-results-message">
{{ placeholder.message.replace('[name]', searchQuery) }}
</p>
<div class="divider"></div>
<p
v-if="placeholder.finePrint"
class="no-results-fine-print"
>
{{ placeholder.finePrint }}
</p>
<VPButton
text="Clear your search and try again"
theme="alt"
v-if="isBrandSearch"
text="Head over to Simple Icons"
theme="brand"
:href="`https://simpleicons.org/?q=${searchQuery}`"
target="_blank"
/>
<VPButton
v-else
text="Clear search & try again"
theme="brand"
@click="$emit('clear')"
/>
<span class="text-divider">or</span>
<VPButton
text="Search on Github issues"
v-if="isBrandSearch"
text="Read our statement on brand logos"
theme="alt"
href="https://github.com/lucide-icons/lucide/blob/main/BRAND_LOGOS_STATEMENT.md"
target="_blank"
/>
<VPButton
v-else
text="Search GitHub issues"
theme="alt"
:href="`https://github.com/lucide-icons/lucide/issues?q=is%3Aopen+${searchQuery}`"
target="_blank"
@@ -63,33 +225,38 @@ onMounted(() => {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
padding-block: 48px;
}
.animal-icon {
width: 160px;
height: 160px;
color: var(--vp-c-neutral);
opacity: 0.8;
margin-top: 72px;
.placeholder-icon {
width: 96px;
height: 96px;
color: var(--vp-c-text-1);
}
.animal-icon.flip {
.placeholder-icon.flip {
transform: rotateY(180deg);
}
@media (min-width: 960px) {
.animal-icon {
width: 240px;
height: 240px;
}
}
.no-results-text {
line-height: 40px;
line-height: 1.35;
font-size: 24px;
margin-top: 24px;
margin-bottom: 8px;
text-wrap: balance;
}
.no-results-message {
text-wrap: balance;
}
.no-results-fine-print {
max-inline-size: 60ch;
font-size: 14px;
margin-bottom: 32px;
text-align: center;
color: var(--vp-c-text-2);
text-wrap: balance;
}
.text-divider {
@@ -97,4 +264,10 @@ onMounted(() => {
font-size: 16px;
color: var(--vp-c-neutral);
}
.divider {
margin: 24px auto 18px;
width: 64px;
height: 1px;
background-color: var(--vp-c-divider);
}
</style>
@@ -1,75 +1,72 @@
<script setup lang="ts">
import { shallowRef, type Ref, watch, computed } from 'vue'
import { useCssVar, syncRef } from '@vueuse/core'
import { STYLE_DEFAULTS, useIconStyleContext } from '../../composables/useIconStyle'
import RangeSlider from '../base/RangeSlider.vue'
import InputField from '../base/InputField.vue'
import ColorPicker from '../base/ColorPicker.vue'
import ResetButton from '../base/ResetButton.vue'
import Switch from '../base/Switch.vue'
import { shallowRef, type Ref, watch, computed } from 'vue';
import { useCssVar, syncRef } from '@vueuse/core';
import { STYLE_DEFAULTS, useIconStyleContext } from '../../composables/useIconStyle';
import RangeSlider from '../base/RangeSlider.vue';
import InputField from '../base/InputField.vue';
import ColorPicker from '../base/ColorPicker.vue';
import ResetButton from '../base/ResetButton.vue';
import Switch from '../base/Switch.vue';
const props = defineProps<{
rootEl?: Ref<HTMLElement>
}>()
rootEl?: Ref<HTMLElement>;
}>();
const { color, strokeWidth, size, absoluteStrokeWidth } = useIconStyleContext()
const documentRef = shallowRef<HTMLElement | undefined>(typeof document !== 'undefined' ? document?.documentElement : undefined)
const { color, strokeWidth, size, absoluteStrokeWidth } = useIconStyleContext();
const documentRef = shallowRef<HTMLElement | undefined>(
typeof document !== 'undefined' ? document?.documentElement : undefined,
);
const colorCssVar = useCssVar(
'--customize-color',
props.rootEl?.value ?? documentRef.value,
{
initialValue: `${STYLE_DEFAULTS.color}`
}
)
const colorCssVar = useCssVar('--customize-color', props.rootEl?.value ?? documentRef.value, {
initialValue: `${STYLE_DEFAULTS.color}`,
});
const strokeWidthCssVar = useCssVar(
'--customize-strokeWidth',
props.rootEl?.value ?? documentRef.value,
{
initialValue: `${STYLE_DEFAULTS.strokeWidth}`
}
)
initialValue: `${STYLE_DEFAULTS.strokeWidth}`,
},
);
const sizeCssVar = useCssVar(
'--customize-size',
props.rootEl?.value ?? documentRef.value,
{
initialValue: `${STYLE_DEFAULTS.size}`
}
)
const sizeCssVar = useCssVar('--customize-size', props.rootEl?.value ?? documentRef.value, {
initialValue: `${STYLE_DEFAULTS.size}`,
});
syncRef(color, colorCssVar, { direction: 'ltr' })
syncRef(strokeWidth, strokeWidthCssVar, { direction: 'ltr' })
syncRef(size, sizeCssVar, { direction: 'ltr' })
syncRef(color, colorCssVar, { direction: 'ltr' });
syncRef(strokeWidth, strokeWidthCssVar, { direction: 'ltr' });
syncRef(size, sizeCssVar, { direction: 'ltr' });
function resetStyle () {
color.value = STYLE_DEFAULTS.color
strokeWidth.value = STYLE_DEFAULTS.strokeWidth
size.value = STYLE_DEFAULTS.size
absoluteStrokeWidth.value = STYLE_DEFAULTS.absoluteStrokeWidth
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) => {
const htmlEl = document.documentElement
const htmlEl = document.documentElement;
htmlEl.classList.toggle('absolute-stroke-width', enabled)
})
htmlEl.classList.toggle('absolute-stroke-width', enabled);
});
const customizingActive = computed(() => {
return color.value !== STYLE_DEFAULTS.color
|| strokeWidth.value !== STYLE_DEFAULTS.strokeWidth
|| size.value !== STYLE_DEFAULTS.size
|| absoluteStrokeWidth.value !== STYLE_DEFAULTS.absoluteStrokeWidth
})
return (
color.value !== STYLE_DEFAULTS.color ||
strokeWidth.value !== STYLE_DEFAULTS.strokeWidth ||
size.value !== STYLE_DEFAULTS.size ||
absoluteStrokeWidth.value !== STYLE_DEFAULTS.absoluteStrokeWidth
);
});
</script>
<template>
<div class="customizer-card" :class="{ customized: customizingActive }">
<div
class="customizer-card"
:class="{ customized: customizingActive }"
>
<div class="card-header">
<h2 class="card-title">
Customizer
</h2>
<h2 class="card-title">Customizer</h2>
<ResetButton @click="resetStyle"></ResetButton>
</div>
<InputField
@@ -77,7 +74,11 @@ const customizingActive = computed(() => {
label="Color"
>
<template #display>
<ColorPicker v-model="color" id="icon-color" class="color-picker"/>
<ColorPicker
v-model="color"
id="icon-color"
class="color-picker"
/>
</template>
</InputField>
@@ -117,7 +118,7 @@ const customizingActive = computed(() => {
<InputField
id="absolute-stroke-width"
label="Absolute Stroke width"
label="Absolute stroke width"
>
<Switch
id="absolute-stroke-width"
@@ -143,6 +144,7 @@ const customizingActive = computed(() => {
font-size: 16px;
/* margin-bottom: 12px; */
}
.customizer-card {
background: var(--vp-c-bg);
padding: 12px 24px 24px;
@@ -151,7 +153,7 @@ const customizingActive = computed(() => {
position: relative;
z-index: 0;
border: 1px solid transparent;
transition: border-color .4s ease-in-out;
transition: border-color 0.4s ease-in-out;
}
.customizer-card.customized {
@@ -0,0 +1,41 @@
import { ref, Ref, watch } from 'vue';
import BRAND_STOPWORDS from '../../data/brandStopwords.json' with { type: 'json' };
export default function useSearchPlaceholder(
searchQuery: Ref<string, string>,
results: Ref<{ name: string }[]>,
) {
const state = ref({
isNoResults: false,
isBrand: false,
query: '',
});
watch(
results,
() => {
const query = searchQuery.value;
const searchResults = results.value;
if (query.length > 0 && searchResults.length === 0) {
for (const stopword of Object.keys(BRAND_STOPWORDS)) {
if (stopword.startsWith(query)) {
state.value = {
isNoResults: true,
isBrand: true,
query: BRAND_STOPWORDS[stopword],
};
return;
}
}
}
state.value = {
isNoResults: query in BRAND_STOPWORDS && searchResults.length === 0 && query !== '',
isBrand: query in BRAND_STOPWORDS,
query: BRAND_STOPWORDS[query] ?? query,
};
},
{ immediate: true },
);
return state;
}
@@ -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 };
}
@@ -1,4 +1,4 @@
import { IconNode } from 'lucide-vue-next/src/createLucideIcon';
import { type IconNode } from 'lucide-vue-next/src/types';
import Vue from 'vue';
declare module '*.vue' {
@@ -20,5 +20,6 @@ declare module 'node:module' {
}
declare module '*.node.json' {
export default IconNode;
const value: IconNode;
export default value;
}
+2
View File
@@ -1,5 +1,7 @@
---
aside: false
title: Contributing to Lucide
description: Guidelines and instructions for contributing to the Lucide project.
---
<!--@include: ../CONTRIBUTING.md -->
+5
View File
@@ -10,6 +10,11 @@ because they can quickly give information.
However, not everyone can understand them easily. When using icons it is very important to consider
the following aspects of accessibility.
::: info
By default, we hide icons from screen readers using `aria-hidden="true"`.
You can make them accessible yourself by following the guidelines below.
:::
## Provide visible labels
Icons are a helpful tool to improve perception, but they aren't a replacement for text.
+12
View File
@@ -167,3 +167,15 @@ import { coconut } from '@lucide/lab';
})
export class AppModule { }
```
## Accessibility
By default, we hide icons from screen readers using `aria-hidden="true"`.
You can add accessibility attributes using aria-labels.
```html
<lucide-icon name="check" aria-label="Task completed"></lucide-icon>
```
For best practices on accessibility, please see our [accessibility guide](../advanced/accessibility.md).
+16
View File
@@ -192,3 +192,19 @@ import LucideIcon from './LucideIcon.astro';
<LucideIcon name="Menu" />
```
## Accessibility
By default, we hide icons from screen readers using `aria-hidden="true"`.
You can add accessibility attributes using aria-labels.
```jsx
---
import { Check } from '@lucide/astro';
---
<Check aria-label="Task completed" />
```
For best practices on accessibility, please see our [accessibility guide](../advanced/accessibility.md).
+16
View File
@@ -131,3 +131,19 @@ const App = () => {
export default App;
```
## Accessibility
By default, we hide icons from screen readers using `aria-hidden="true"`.
You can add accessibility attributes using aria-labels.
```jsx
import { Check } from 'lucide-preact';
const App = () => {
return <Check aria-label="Task completed" />;
};
```
For best practices on accessibility, please see our [accessibility guide](../advanced/accessibility.md).
+11 -5
View File
@@ -102,10 +102,16 @@ The example below imports all ES Modules, so exercise caution when using it. Imp
### Icon Component Example
```jsx
import { icons } from 'lucide-react-native';
```tsx
import * as icons from 'lucide-react-native/icons';
const Icon = ({ name, color, size }) => {
interface IconProps {
name: keyof typeof icons;
color?: string;
size?: number;
}
const Icon = ({ name, color, size }: IconProps) => {
const LucideIcon = icons[name];
return <LucideIcon color={color} size={size} />;
@@ -116,11 +122,11 @@ export default Icon;
#### Using the Icon Component
```jsx
```tsx
import Icon from './Icon';
const App = () => {
return <Icon name="house" />;
return <Icon name="House" />;
};
export default App;
+16
View File
@@ -109,3 +109,19 @@ const App = () => (
<DynamicIcon name="camera" color="red" size={48} />
);
```
## Accessibility
By default, we hide icons from screen readers using `aria-hidden="true"`.
You can add accessibility attributes using aria-labels.
```jsx
import { Check } from 'lucide-react';
const App = () => {
return <Check aria-label="Task completed" />;
};
```
For best practices on accessibility, please see our [accessibility guide](../advanced/accessibility.md).
+16
View File
@@ -144,3 +144,19 @@ const App = () => {
export default App;
```
## Accessibility
By default, we hide icons from screen readers using `aria-hidden="true"`.
You can add accessibility attributes using aria-labels.
```jsx
import { Check } from 'lucide-solid';
const App = () => {
return <Check aria-label="Task completed" />;
};
```
For best practices on accessibility, please see our [accessibility guide](../advanced/accessibility.md).
+16
View File
@@ -331,3 +331,19 @@ The example below imports all ES Modules, so exercise caution when using it. Imp
<LucideIcon name="Menu" />
```
## Accessibility
By default, we hide icons from screen readers using `aria-hidden="true"`.
You can add accessibility attributes using aria-labels.
```svelte
<script>
import { Check } from '@lucide/svelte';
</script>
<Check aria-label="Task completed" />
```
For best practices on accessibility, please see our [accessibility guide](../advanced/accessibility.md).
+22
View File
@@ -146,3 +146,25 @@ All other props listed above also work on the `Icon` Component.
</div>
</template>
```
## Accessibility
By default, we hide icons from screen readers using `aria-hidden="true"`.
You can add accessibility attributes using aria-labels.
```vue
<script setup>
import { Check } from 'lucide-vue-next';
</script>
<template>
<Check
color="red"
:size="32"
aria-label="Task completed"
/>
</template>
```
For best practices on accessibility, please see our [accessibility guide](../advanced/accessibility.md).
+21
View File
@@ -62,6 +62,7 @@ Here is a complete example with unpkg
</script>
</body>
```
We strongly suggest you anchor to a specific version, such as `https://unpkg.com/lucide@x.xxx.x/dist/umd/lucide.min.js`, rather than using `@latest`.
### With ESModules
@@ -203,3 +204,23 @@ createIcons({
}
});
```
## Accessibility
By default, we hide icons from screen readers using `aria-hidden="true"`.
You can add accessibility attributes using aria-labels.
```html
<!DOCTYPE html>
<body>
<i data-lucide="house" aria-label="Home icon"></i>
<script src="https://unpkg.com/lucide@latest"></script>
<script>
lucide.createIcons();
</script>
</body>
```
For best practices on accessibility, please see our [accessibility guide](../advanced/accessibility.md).
+2
View File
@@ -1,4 +1,6 @@
---
title: Categories
description: Explore Lucide icons organized into various categories for easier browsing.
layout: page
outline: 2
outlineTitle: Categories
+2
View File
@@ -1,4 +1,6 @@
---
title: Icons
description: Browse all Lucide icons.
layout: page
outline: 2
outlineTitle: Categories
+16 -11
View File
@@ -2,18 +2,23 @@ import { IconEntity } from '../../.vitepress/theme/types';
export default {
paths: async () => {
const iconDetailsResponse = await fetch('https://lab.lucide.dev/api/icon-details');
const iconDetails = (await iconDetailsResponse.json()) as Record<string, IconEntity>;
try {
const iconDetailsResponse = await fetch('https://lab.lucide.dev/api/icon-details');
const iconDetails = (await iconDetailsResponse.json()) as Record<string, IconEntity>;
return Object.values(iconDetails).map((iconEntity) => {
const params = {
externalLibrary: 'lab',
...iconEntity,
};
return Object.values(iconDetails).map((iconEntity) => {
const params = {
externalLibrary: 'lab',
...iconEntity,
};
return {
params,
};
});
return {
params,
};
});
} catch (error) {
console.error('Error fetching icon details:', error);
return [];
}
},
};
+1
View File
@@ -2,6 +2,7 @@
# https://vitepress.dev/reference/default-theme-home-page
layout: home
markdownStyles: false
titleTemplate: false
head:
- - link
+2
View File
@@ -1,4 +1,6 @@
---
title: License
description: Review the licensing terms for using Lucide icons and resources.
aside: false
editLink: false
---
+28 -27
View File
@@ -9,13 +9,14 @@
"docs:build": "pnpm run /^prebuild:.*/ && vitepress build",
"docs:preview": "vitepress preview",
"build:docs": "vitepress build",
"prebuild:iconNodes": "node --experimental-strip-types ./scripts/writeIconNodes.mjs",
"prebuild:metaJson": "node --experimental-strip-types ./scripts/writeIconMetaIndex.mjs",
"prebuild:releaseJson": "node --experimental-strip-types ./scripts/writeReleaseMetadata.mjs",
"prebuild:categoriesJson": "node --experimental-strip-types ./scripts/writeCategoriesMetadata.mjs",
"prebuild:relatedIcons": "node --experimental-strip-types ./scripts/writeIconRelatedIcons.mjs",
"prebuild:iconDetails": "node --experimental-strip-types ./scripts/writeIconDetails.mjs",
"postbuild:vercelJson": "node --experimental-strip-types ./scripts/writeVercelOutput.mjs",
"prebuild:iconNodes": "node ./scripts/writeIconNodes.mjs",
"prebuild:metaJson": "node ./scripts/writeIconMetaIndex.mjs",
"prebuild:releaseJson": "node ./scripts/writeReleaseMetadata.mjs",
"prebuild:categoriesJson": "node ./scripts/writeCategoriesMetadata.mjs",
"prebuild:relatedIcons": "node ./scripts/writeIconRelatedIcons.mjs",
"prebuild:iconDetails": "node ./scripts/writeIconDetails.mjs",
"prebuild:brandStopwords": "node ./scripts/writeBrandStopwords.mjs",
"postbuild:vercelJson": "node ./scripts/writeVercelOutput.mjs",
"dev": "npx nitropack dev",
"prebuild:api": "npx nitropack prepare",
"build:api": "npx nitropack build",
@@ -28,35 +29,35 @@
"@lucide/build-icons": "workspace:*",
"@lucide/helpers": "workspace:*",
"@lucide/shared": "workspace:*",
"@rollup/plugin-replace": "^6.0.2",
"@types/semver": "^7.5.3",
"@rollup/plugin-replace": "^6.0.3",
"@types/semver": "^7.7.1",
"nitropack": "2.8.1",
"rollup-plugin-copy": "^3.5.0",
"vitepress": "^1.6.3",
"svg-path-commander": "^2.1.11"
"svg-path-commander": "^2.1.11",
"vitepress": "^1.6.4"
},
"dependencies": {
"@floating-ui/vue": "^1.0.3",
"@headlessui/vue": "^1.7.17",
"@floating-ui/vue": "^1.1.9",
"@headlessui/vue": "^1.7.23",
"@resvg/resvg-wasm": "^2.6.2",
"@vueuse/components": "^12.0.0",
"@vueuse/core": "^12.0.0",
"@vueuse/components": "^14.0.0",
"@vueuse/core": "^14.0.0",
"element-to-path": "^1.2.1",
"fuse.js": "^6.5.3",
"jszip": "^3.7.0",
"lodash": "^4.17.20",
"lodash-es": "^4.17.21",
"fuse.js": "^7.1.0",
"jszip": "^3.10.1",
"lodash": "^4.17.23",
"lodash-es": "^4.17.23",
"lucide-react": "workspace:*",
"lucide-vue-next": "workspace:*",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"sandpack-vue3": "3.1.11",
"semver": "^7.5.2",
"shikiji": "^0.7.4",
"simple-git": "^3.18.0",
"sitemap": "^7.1.1",
"semver": "^7.7.3",
"shiki": "^3.15.0",
"simple-git": "^3.30.0",
"sitemap": "^7.1.2",
"svg-pathdata": "^6.0.3",
"svgson": "^5.2.1",
"vue": "^3.5.18"
"svgson": "^5.3.1",
"vue": "^3.5.24"
}
}
+2
View File
@@ -1,4 +1,6 @@
---
title: Packages
description: Discover the various packages available in the Lucide ecosystem.
layout: page
outline: 2
outlineTitle: Packages
+10
View File
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 -166 512 512" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
<g fill="#CC0000">
<path d="M219.115365,150.633753 C219.115365,150.633753 241.684635,148.699244 241.684635,119.552645 C241.684635,90.4060453 214.343577,87.697733 214.343577,87.697733 L165.078086,87.697733 L165.078086,179.651385 L189.839798,179.651385 L189.839798,157.469018 L211.248363,179.651385 L247.875063,179.651385 L219.115365,150.633753 Z M209.571788,131.804534 L189.839798,131.804534 L189.839798,110.782872 L209.700756,110.782872 C209.700756,110.782872 215.246348,112.846348 215.246348,121.229219 C215.246348,129.612091 209.571788,131.804534 209.571788,131.804534 Z M302.815113,88.3425693 L277.666499,88.3425693 C259.74005,88.3425693 253.678589,104.592443 253.678589,112.330479 L253.678589,179.651385 L278.827204,179.651385 L278.827204,163.530479 L302.428212,163.530479 L302.428212,179.651385 L326.803023,179.651385 L326.803023,112.330479 C326.803023,92.7274559 309.005542,88.3425693 302.815113,88.3425693 Z M302.428212,137.479093 L278.698237,137.479093 L278.698237,115.167758 C278.698237,115.167758 278.698237,110.138035 286.565239,110.138035 L295.206045,110.138035 C302.170277,110.138035 302.299244,115.167758 302.299244,115.167758 L302.299244,137.479093 L302.428212,137.479093 Z M338.281108,88.3425693 L364.461461,88.3425693 L364.461461,179.651385 L338.281108,179.651385 L338.281108,88.3425693 Z M401.217128,156.050378 L401.217128,88.3425693 L375.165743,88.3425693 L375.165743,156.050378 L375.165743,179.651385 L401.217128,179.651385 L436.425189,179.651385 L436.425189,156.050378 L401.217128,156.050378 Z M445.065995,156.308312 L445.065995,179.651385 L487.238287,179.651385 C495.879093,179.651385 510.710327,173.33199 511.226196,155.663476 L511.226196,146.635768 C511.226196,131.546599 498.84534,122.647859 487.238287,122.647859 L466.216625,122.647859 L466.216625,111.81461 L507.873048,111.81461 L507.873048,88.3425693 L467.893199,88.3425693 C457.575819,88.3425693 443.776322,96.8544081 443.776322,112.71738 L443.776322,120.842317 C443.776322,136.70529 457.446851,144.830227 467.893199,144.830227 C496.910831,144.959194 460.928967,144.830227 487.754156,144.830227 L487.754156,156.179345 L445.065995,156.308312 Z">
</path>
<path d="M9.02770781,179.522418 L110.911839,179.522418 C110.911839,179.522418 91.4377834,90.6639798 155.921411,54.6821159 C169.978841,47.8468514 214.730479,22.311335 287.983879,76.4775819 C290.30529,74.543073 292.497733,72.995466 292.497733,72.995466 C292.497733,72.995466 225.434761,6.06146096 150.76272,13.5415617 C113.233249,16.8947103 67.0629723,51.0710327 39.9798489,96.2095718 C12.8967254,141.348111 9.02770781,179.522418 9.02770781,179.522418 Z M221.307809,20.763728 L221.823678,12.1229219 C220.662972,11.4780856 217.438791,9.93047859 209.313854,7.60906801 L208.797985,16.1209068 C213.053904,17.5395466 217.180856,19.0871537 221.307809,20.763728 Z M209.055919,48.1047859 L208.54005,56.2297229 C212.79597,56.3586902 217.051889,56.8745592 221.307809,57.77733 L221.823678,49.7813602 C217.438791,48.8785894 213.182872,48.3627204 209.055919,48.1047859 Z M161.338035,7.86700252 L162.627708,7.86700252 L160.048363,-7.10542736e-15 C156.050378,-7.10542736e-15 151.923426,0.257934509 147.667506,0.773803526 L150.117884,8.38287154 C153.857935,7.99596977 157.597985,7.86700252 161.338035,7.86700252 Z M167.528463,55.3269521 L170.49471,64.2256927 C174.234761,62.4201511 177.974811,60.8725441 181.714861,59.7118388 L178.877582,51.2 C174.492695,52.4896725 170.752645,53.9083123 167.528463,55.3269521 Z M108.97733,21.4085642 L103.173804,12.5098237 C99.9496222,14.186398 96.5964736,15.9919395 93.1143577,18.0554156 L99.0468514,27.0831234 C102.4,25.0196474 105.624181,23.0851385 108.97733,21.4085642 Z M135.415617,79.4438287 L141.606045,88.729471 C143.798489,85.5052897 146.377834,82.5390428 149.215113,79.572796 L143.411587,70.8030227 C140.44534,73.511335 137.737028,76.4775819 135.415617,79.4438287 Z M116.715365,120.971285 L127.161713,129.225189 C127.677582,124.195466 128.580353,119.165743 129.870025,114.13602 L120.584383,106.784887 C118.907809,111.556675 117.747103,116.328463 116.715365,120.971285 Z M60.2277078,59.7118388 L51.0710327,51.715869 C47.7178841,54.9400504 44.4937028,58.1642317 41.5274559,61.3884131 L51.4579345,69.9002519 C54.1662469,66.418136 57.1324937,62.9360202 60.2277078,59.7118388 Z M21.279597,117.360202 L6.44836272,111.943577 C3.99798489,117.489169 1.28967254,123.937531 0,127.419647 L14.8312343,132.836272 C16.5078086,128.451385 19.2161209,122.13199 21.279597,117.360202 Z M114.780856,153.728967 C115.038791,160.564232 115.683627,166.109824 116.328463,169.978841 L131.804534,175.524433 C130.643829,170.49471 129.483123,164.820151 128.70932,158.75869 L114.780856,153.728967 Z">
</path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.8 KiB

@@ -0,0 +1 @@
<svg width="161" height="132" viewBox="0 0 161 132" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient x1="0%" y1="50%" y2="50%" id="a"><stop stop-color="#2A3B8F" offset="0%"/><stop stop-color="#29ABE2" offset="100%"/></linearGradient><linearGradient x1="100%" y1="50%" x2="0%" y2="50%" id="c"><stop stop-color="#B4D44E" offset="0%"/><stop stop-color="#E7F716" offset="100%"/></linearGradient></defs><g fill="none" fill-rule="evenodd"><path fill="#166DA5" d="M160.6 65.9l-17.4 29.3-24.4-29.7 24.4-28.9z"/><path fill="#8FDB69" d="M141.3 100.2l-26.5-31.7-15.9 26.6 24.7 36.1z"/><path fill="#166DA5" d="M141 31.4l-26.2 31.8-15.9-26.6L123.6.9z"/><path fill="url(#a)" opacity=".95" d="M61.1 31.4H141L123.4.7H78.7z M114.8 63.3H159l-15.9-26.8H98.8"/><path fill="url(#c)" opacity=".95" d="M141.3 100.3H61l17.6 30.5h45z M114.8 68.4H159l-15.9 26.8H98.8"/><path fill="#010101" d="M78.6 130.8L41 65.8 79.1.8H37.9L.4 65.8l37.5 65z"/></g></svg>

After

Width:  |  Height:  |  Size: 936 B

+7
View File
@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" width="138" height="42" fill="none" viewBox="0 0 138 42">
<path fill="#fff" fill-rule="evenodd" d="M138 4a3.997 3.997 0 0 0-4-4H4a4 4 0 0 0-4 4v34a4 4 0 0 0 4 4h130a4 4 0 0 0 4-4z" clip-rule="evenodd"/>
<path fill="#000" fill-rule="evenodd" d="M27.963 16.943q1.08 0 1.864.43.783.428 1.212 1.154v-1.454h3.653V27.53H31.04v-1.454q-.429.727-1.23 1.156-.783.43-1.846.43-1.248 0-2.274-.653-1.006-.653-1.603-1.864-.578-1.23-.578-2.852t.578-2.834q.597-1.211 1.604-1.863a4.15 4.15 0 0 1 2.273-.653m1.175 3.188q-.82 0-1.36.577-.542.56-.542 1.585 0 1.006.541 1.602.54.579 1.36.579.802 0 1.343-.579.558-.578.56-1.602-.001-1.007-.56-1.585a1.76 1.76 0 0 0-1.342-.577" clip-rule="evenodd"/>
<path fill="#000" d="M75.361 16.943q2.05 0 3.225 1.006t1.361 2.648h-3.3q-.092-.504-.447-.765-.335-.28-.913-.28-.447 0-.69.169a.55.55 0 0 0-.223.465q0 .373.429.579.428.186 1.379.39 1.137.243 1.882.541.765.3 1.324.969.578.653.578 1.79 0 .914-.541 1.64-.522.727-1.51 1.156-.969.41-2.293.41-1.417 0-2.535-.466-1.1-.485-1.752-1.304a3.47 3.47 0 0 1-.708-1.883h3.542q.075.503.466.782.39.28.968.28.448 0 .71-.186a.57.57 0 0 0 .26-.485q0-.39-.428-.578-.43-.185-1.417-.41-1.12-.223-1.865-.503a3.25 3.25 0 0 1-1.304-.914q-.54-.633-.54-1.715 0-.93.502-1.677.504-.765 1.473-1.211.989-.448 2.367-.448"/>
<path fill="#000" fill-rule="evenodd" d="M94.305 16.943q1.564 0 2.796.653a4.8 4.8 0 0 1 1.957 1.863q.726 1.212.727 2.834 0 1.621-.727 2.852a4.8 4.8 0 0 1-1.957 1.864q-1.25.652-2.815.652-1.566 0-2.814-.652a4.8 4.8 0 0 1-1.939-1.864q-.709-1.212-.709-2.852 0-1.622.71-2.834a4.95 4.95 0 0 1 1.956-1.863q1.248-.653 2.815-.653m0 3.168q-.746 0-1.25.56-.502.56-.502 1.622 0 1.08.484 1.64.485.559 1.25.56.763-.001 1.267-.56.52-.578.521-1.64 0-1.063-.521-1.622a1.61 1.61 0 0 0-1.25-.56M118.921 16.943q1.081 0 1.864.43.783.428 1.212 1.154v-1.454h3.653V27.53h-3.653v-1.454q-.429.727-1.23 1.156-.784.43-1.846.43-1.248 0-2.274-.653-1.006-.653-1.603-1.864-.577-1.23-.577-2.852t.577-2.834q.597-1.211 1.603-1.863a4.15 4.15 0 0 1 2.274-.653m1.175 3.188q-.82 0-1.361.577-.54.56-.541 1.585 0 1.006.541 1.602.541.579 1.361.579.8 0 1.342-.579.558-.578.559-1.602 0-1.007-.559-1.585a1.77 1.77 0 0 0-1.342-.577M17.81 14.39q1.584 0 2.683.558 1.119.56 1.678 1.548.56.988.56 2.274 0 1.193-.56 2.18-.54.97-1.66 1.567-1.099.577-2.701.577H16v4.436h-3.653V14.39zM16 20.186h1.454q1.548 0 1.547-1.416 0-1.436-1.547-1.436h-1.454z" clip-rule="evenodd"/>
<path fill="#000" d="m41.658 19.702 1.51-2.629h4.008l-3.56 5.331 3.69 5.126h-4.1l-1.884-2.628-1.49 2.628h-4.009l3.487-5.424-3.635-5.033h4.1zM55.42 19.33h4.456v-4.94h3.653v13.14h-3.653v-5.274h-4.455v5.274h-3.653V14.39h3.653zM69.18 27.53h-3.654V17.073h3.654zM85.919 17.073h1.9v3.038h-1.9v3.505q0 .429.186.615.204.187.671.187h1.063v3.112h-1.585q-4.008 0-4.008-3.97v-3.449H80.96v-3.038h1.286v-2.535h3.673zM108.205 20.876h-1.024q-1.194 0-1.772.466-.577.465-.577 1.622v4.566h-3.654V17.073h3.654v1.846a4.76 4.76 0 0 1 1.491-1.417 3.6 3.6 0 0 1 1.882-.521zM113.075 27.53h-3.653V17.073h3.653zM67.372 12.339q.95 0 1.528.54.597.542.597 1.361 0 .801-.597 1.342-.577.523-1.528.523-.97 0-1.565-.523a1.76 1.76 0 0 1-.579-1.342q0-.82.579-1.36.596-.54 1.565-.541M111.268 12.339q.95 0 1.528.54.596.542.597 1.361 0 .801-.597 1.342-.578.522-1.528.523-.97 0-1.567-.523a1.76 1.76 0 0 1-.577-1.342q0-.82.577-1.36.597-.54 1.567-.541"/>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

+15
View File
@@ -0,0 +1,15 @@
import fs from 'fs/promises';
import path from 'path';
const currentDir = process.cwd();
const dataDirectory = path.resolve(currentDir, '.vitepress/data');
const stopwordsSource = path.resolve(currentDir, `../brand-stopwords.json`);
const stopwordsFile = path.resolve(dataDirectory, `brandStopwords.json`);
fs.copyFile(stopwordsSource, stopwordsFile)
.then(() => {
console.log('Successfully copied brandStopwords.json file');
})
.catch((error) => {
throw new Error(`Something went wrong generating the brandStopwords.json file,\n ${error}`);
});
+1
View File
@@ -1,4 +1,5 @@
---
title: Showcase
layout: page
outline: 2
outlineTitle: Showcase
+2 -1
View File
@@ -2,7 +2,8 @@
"$schema": "../icon.schema.json",
"contributors": [
"danielbayley",
"karsa-mistmere"
"karsa-mistmere",
"jguddas"
],
"tags": [
"and",
+1 -1
View File
@@ -9,6 +9,6 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M17.5 12c0 4.4-3.6 8-8 8A4.5 4.5 0 0 1 5 15.5c0-6 8-4 8-8.5a3 3 0 1 0-6 0c0 3 2.5 8.5 12 13" />
<path d="M16 12h3" />
<path d="M17.5 12a8 8 0 0 1-8 8A4.5 4.5 0 0 1 5 15.5c0-6 8-4 8-8.5a3 3 0 1 0-6 0c0 3 2.5 8.5 12 13" />
</svg>

Before

Width:  |  Height:  |  Size: 339 B

After

Width:  |  Height:  |  Size: 337 B

+2 -1
View File
@@ -3,7 +3,8 @@
"contributors": [
"colebemis",
"csandman",
"ericfennis"
"ericfennis",
"karsa-mistmere"
],
"tags": [
"ship"
+4 -3
View File
@@ -9,7 +9,8 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M12 22V8" />
<path d="M5 12H2a10 10 0 0 0 20 0h-3" />
<circle cx="12" cy="5" r="3" />
<path d="M12 6v16" />
<path d="m19 13 2-1a9 9 0 0 1-18 0l2 1" />
<path d="M9 11h6" />
<circle cx="12" cy="4" r="2" />
</svg>

Before

Width:  |  Height:  |  Size: 309 B

After

Width:  |  Height:  |  Size: 334 B

-1
View File
@@ -15,7 +15,6 @@
],
"categories": [
"arrows",
"navigation",
"gaming",
"files"
]
-1
View File
@@ -15,7 +15,6 @@
],
"categories": [
"arrows",
"navigation",
"gaming"
]
}
-1
View File
@@ -15,7 +15,6 @@
],
"categories": [
"arrows",
"navigation",
"gaming"
]
}
-1
View File
@@ -17,7 +17,6 @@
],
"categories": [
"arrows",
"navigation",
"gaming"
]
}
-1
View File
@@ -15,7 +15,6 @@
],
"categories": [
"arrows",
"navigation",
"gaming"
]
}
-1
View File
@@ -17,7 +17,6 @@
],
"categories": [
"arrows",
"navigation",
"gaming"
]
}
-1
View File
@@ -20,7 +20,6 @@
],
"categories": [
"arrows",
"navigation",
"text",
"development",
"gaming"
-1
View File
@@ -20,7 +20,6 @@
],
"categories": [
"arrows",
"navigation",
"text",
"development",
"gaming"
-1
View File
@@ -16,7 +16,6 @@
],
"categories": [
"arrows",
"navigation",
"files"
]
}
+1 -2
View File
@@ -10,7 +10,6 @@
"diagonal"
],
"categories": [
"arrows",
"navigation"
"arrows"
]
}
+1 -2
View File
@@ -10,7 +10,6 @@
"diagonal"
],
"categories": [
"arrows",
"navigation"
"arrows"
]
}
+1 -2
View File
@@ -12,7 +12,6 @@
"into"
],
"categories": [
"arrows",
"navigation"
"arrows"
]
}
-1
View File
@@ -19,7 +19,6 @@
],
"categories": [
"arrows",
"navigation",
"files",
"development"
]
+1 -2
View File
@@ -19,7 +19,6 @@
"move"
],
"categories": [
"arrows",
"navigation"
"arrows"
]
}
+1 -2
View File
@@ -11,7 +11,6 @@
"south"
],
"categories": [
"arrows",
"navigation"
"arrows"
]
}

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