Compare commits

..
169 Commits
Author SHA1 Message Date
Rodrigo Rodrigues 4b02cfdc8b feat(icons): added cctv-off icon (#4162)
* Added icons/cctv-off.svg

* Added icons/cctv-off.json
2026-03-23 14:43:40 +01:00
Antonin Kadrmas 59cf8104ef Update ICON_GUIDELINES link in CONTRIBUTING.md (#4187)
The old link for the guidelines link to the 404 page change the link to the working [link](https://lucide.dev/contribute/icon-design-guide).
2026-03-23 09:49:03 +01:00
Lukas Kalbertodt f9406068fb Fix formatting of <svg> element in two icons that were inconsistent (#4166)
These two icons were the only ones that didn't start with exactly

<svg
  xmlns="http://www.w3.org/2000/svg"
  width="24"
  height="24"
  viewBox="0 0 24 24"
  fill="none"
  stroke="currentColor"
  stroke-width="2"
  stroke-linecap="round"
  stroke-linejoin="round"
>

So I brought them in line.
2026-03-20 16:28:00 +01:00
d51c26ea9a fix(lucide-fonts): correct icon mappings in index.html and unicode.html (#4160)
Fixes lucide-icons/lucide#4154

The font mapping files were incorrectly mapping icon names to unicode values.
For example, message-circle-check was incorrectly pointing to database-search.

This was introduced in version 0.576 by commit efa1f252 which moved the
endCodePoint calculation inside the Promise.all callback, causing race
conditions where multiple icons could receive the same codepoint.

The fix moves endCodePoint calculation outside the Promise.all block
so it's calculated once before processing all icons, ensuring each icon
gets a unique codepoint.

Co-authored-by: Clawdbot <bot@clawdbot.com>
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2026-03-20 15:43:27 +01:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 6c00c2e74a chore(deps-dev): bump h3 from 1.15.4 to 1.15.6 (#4163)
Bumps [h3](https://github.com/h3js/h3) from 1.15.4 to 1.15.6.
- [Release notes](https://github.com/h3js/h3/releases)
- [Changelog](https://github.com/h3js/h3/blob/v1.15.6/CHANGELOG.md)
- [Commits](https://github.com/h3js/h3/compare/v1.15.4...v1.15.6)

---
updated-dependencies:
- dependency-name: h3
  dependency-version: 1.15.6
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-19 16:24:45 +01:00
Jakob Guddas 4af2f7e78b Updated icons/gpu.svg (#4147) 2026-03-14 20:58:03 +01:00
Jakob Guddas f8ee8d7287 docs: add rule about consistent use of shapes (#3975) 2026-03-14 20:55:52 +01:00
Sorin Hadjiu f910edc007 docs(site): add strapi lucide icons package (#4112) 2026-03-14 20:49:14 +01:00
Bhav Beri 4d44278d3e docs(svelte): clarify Svelte 4 vs Svelte 5 Lucide packages (#4107)
* Clarify package usage for Svelte 4 and 5

@lucide/svelte is specified for Svelte 5, with a note for Svelte 4 users to use lucide-svelte instead.

* Add message in lucide-svelte README as well

* Update the docs installation page to match the svelte docs page
2026-03-12 13:36:55 +01:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> e2d1445f5b chore(deps): bump simple-git from 3.30.0 to 3.32.3 (#4133)
Bumps [simple-git](https://github.com/steveukx/git-js/tree/HEAD/simple-git) from 3.30.0 to 3.32.3.
- [Release notes](https://github.com/steveukx/git-js/releases)
- [Changelog](https://github.com/steveukx/git-js/blob/main/simple-git/CHANGELOG.md)
- [Commits](https://github.com/steveukx/git-js/commits/simple-git@3.32.3/simple-git)

---
updated-dependencies:
- dependency-name: simple-git
  dependency-version: 3.32.3
  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-03-12 13:36:20 +01:00
Jakob Guddas a6136d3e47 fix(icons): changed school icon (#4124)
* Updated icons/school.svg

* Updated icons/school.json

* Updated icons/school.svg

* Updated icons/school.svg

* Updated icons/school.json

* Updated icons/school.json
2026-03-11 08:38:45 +01:00
Eric Fennis 785b2c6376 fix(redirects): Fixes icon alias redirects on site (#4122)
* Fix redirect issues

* turn off vercel output

* Revert "turn off vercel output"

This reverts commit 8cac049e24.

* Fix matching paths
2026-03-06 10:45:21 +01:00
Eric Fennis ed6ba4bf0c Merge branch 'main' of https://github.com/lucide-icons/lucide 2026-03-06 09:15:50 +01:00
Bhav Beri 5e460ce606 Fixes https://github.com/lucide-icons/lucide-lab/issues/6 (#4101) 2026-03-06 09:12:49 +01:00
Curtis Lowderandgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> 0a2312d260 chore(icons): Add 'crypto' tag to bitcoin.json (#4120)
* Add 'crypto' tag to bitcoin.json

* Apply suggestion from @github-actions[bot]

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

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-03-05 18:41:45 +01:00
Eric Fennis e6c7d7d495 docs(api): Update nitro to latest version (#4102)
* update nitro

* try this?

* Try this

* update vercel output

* Add await

* Fix endpoints

* Adjust 404 catch all

* Fix output

* Adjust react in api

* Fix types

* Remove @resvg/resvg-wasm

* Format code

* Fix failing check

* Apply feedback

* Format code
2026-03-05 14:03:27 +01:00
Eric Fennis e8e800f98d ci(build-font): Fix env var GH actions 2026-03-04 10:39:55 +01:00
KISHORE KUMAR Sandgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> 0ea8780434 feat(icons): added ellipse icon (#3749)
* Added icons/ellipse.svg

* Added icons/ellipse.json

* Updated icons/ellipse.json

* Update icons/ellipse.json

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

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-03-04 10:23:48 +01:00
Bhav Beri d88dca3732 Fix gitignore to correctly ignore the releaseMetadata folder of docs (#4100) 2026-03-04 10:22:12 +01:00
Eric Fennis efa1f25251 fix(font-build): Fix code points allocations 2026-03-04 10:17:52 +01:00
Eric Fennis 4201f70d7b ci(build-font): Use Vercel blob storage for font codepoints allocations 2026-03-02 08:30:29 +01:00
Eric Fennis d54247b637 ci: Update concurrency group name 2026-03-02 07:33:09 +01:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> f6c0d0603a chore(deps): bump rollup from 4.53.3 to 4.59.0 (#4106)
Bumps [rollup](https://github.com/rollup/rollup) from 4.53.3 to 4.59.0.
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.53.3...v4.59.0)

---
updated-dependencies:
- dependency-name: rollup
  dependency-version: 4.59.0
  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-03-02 07:22:10 +01:00
6ee46b39a8 feat(icons): added fishing-rod icon (#3839)
* Added icons/fishing-rod.svg

* Added icons/fishing-rod.json

* Update fishing-rod.svg

upd: thanks to @karsa-mistmere & @jguddas

* Update icons/fishing-rod.json

++ contributors

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

* Update fishing-rod.svg

 arc properly placed

---------

Co-authored-by: Jakob Guddas <github@jguddas.de>
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2026-02-27 16:32:47 +01:00
Eric FennisandCopilot de6ddfe381 fix(lucide-font, lucide-static): Fixing stable code points (#3894)
* Refactor font building

* Add lucide-font to gitignore

* Adjust workflow

* update lockfile

* Fixing code points build

* Adds next solution after build

* adds addLigatures to config

* Make code point consistent

* Format code

* Add todo

* Add comment in ci file

* Update tools/build-font/src/allocateCodepoints.ts

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

* Update tools/build-font/src/outlineSVGs.ts

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

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-27 16:29:52 +01:00
Jamie LawandKarsa fdcaa659e4 Updated icons/cuboid.svg (#4098)
Co-authored-by: Karsa <contact@karsa.org>
2026-02-26 11:02:02 +01:00
Karsa 1e3018fea6 fix(icons): updated cuboid icon tags and categories (#4095)
* Updated icons/cuboid.svg

* Updated icons/cuboid.json

* Updated icons/cuboid.json
2026-02-26 11:00:48 +01:00
Karsa cf1c2ef2e4 fix(icons): changed receipt icon (#4075)
* Updated icons/receipt.svg

* Updated icons/receipt-cent.svg

* Updated icons/receipt-cent.json

* Updated icons/receipt-euro.svg

* Updated icons/receipt-euro.json

* Updated icons/receipt-indian-rupee.svg

* Updated icons/receipt-indian-rupee.json

* Updated icons/receipt-japanese-yen.svg

* Updated icons/receipt-japanese-yen.json

* Updated icons/receipt-pound-sterling.svg

* Updated icons/receipt-pound-sterling.json

* Updated icons/receipt-russian-ruble.svg

* Updated icons/receipt-russian-ruble.json

* Updated icons/receipt-swiss-franc.svg

* Updated icons/receipt-swiss-franc.json

* Updated icons/receipt-turkish-lira.svg

* Updated icons/receipt-turkish-lira.json
2026-02-24 09:06:42 +01:00
Karsa 89bb4f356c fix(icons): fixes guideline violations in package-* icons. (#4074)
* Updated icons/package-plus.svg

* Updated icons/package-x.svg

* Updated icons/package-check.svg

* Updated icons/package-minus.svg

* Updated icons/package-search.svg
2026-02-23 10:05:53 +01:00
1a8a7567eb Added zodiac signs (#712)
* Added zodiac signs

* Update cancer.svg

* Update capricorn.svg

* Update icons/gemini.svg

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

* Update leo.svg

* Update libra.svg

* Update ophiuchus.svg

* Update pisces.svg

* Update scorpio.svg

* Update virgo.svg

* Update Aquarius SVG paths for new design

* Update scorpio.svg

* fix(icons/aquarius): fix off-grid paths introduced in https://github.com/lucide-icons/lucide/pull/712/changes/186e4d6e960d2593d71cc5b55f5a1870524ec478

* feat(icons): add zodiac prefix & add metadata json to zodiac icons

---------

Co-authored-by: Karsa <karsa@karsa.org>
Co-authored-by: Jakob Guddas <github@jguddas.de>
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2026-02-23 09:47:51 +01:00
Karsa bca7e9cf43 fix(github/workflows): reformat ci.yml with prettier 2026-02-23 09:45:56 +01:00
Eric Fennis 80923a08b3 ci(ci.yml): Adds workflow_dispatch option to trigger manual release 2026-02-19 13:16:14 +01:00
Jakob Guddas 10f6d5f8c6 fix(icons): renamed flip-* to square-centerline-dashed-* (#3945)
* fix(icons): renamed flip-* to square-split-dashed-*

* Rename square-split-dashed-horizontal.json to square-centerline-dashed-horizontal.json

* Add square-centerline-dashed-vertical.json file

* Rename square-split-dashed-horizontal.svg to square-centerline-dashed-horizontal.svg

* Rename square-split-dashed-vertical.svg to square-centerline-dashed-vertical.svg

* Apply suggestion from @jguddas

* Apply suggestion from @jguddas

* Apply suggestion from @jguddas

* Apply suggestion from @jguddas
2026-02-19 12:13:36 +01:00
Ethan Hazel e77a037d9a feat(icons): added square-arrow-right-exit and square-arrow-right-enter icons (#3958)
* add(icons): added square-arrow-in and square-arrow-out icons

* fix(icons): renamed square-arrow-in/out to have left/right in name

* fix(icons): updated square-arrow-exit-right and square-arrow-enter-left

* fix(square-arrow-enter-right, square-arrow-exit-right): Renamed icons & edited metadata
2026-02-19 12:12:34 +01:00
Jakob GuddasandKarsa 3f53744316 Updated icons/file-pen-line.svg (#3970)
Co-authored-by: Karsa <contact@karsa.org>
2026-02-19 12:11:30 +01:00
DuckAfire 09574d820e fix(icons): remove execution permission of SVG files (#4053) 2026-02-19 12:10:31 +01:00
edwloef aeef1017fe feat(icons): added metronome icon (#4063) 2026-02-19 10:12:32 +01:00
Eric Fennis 267f22dced fix(lucide): Fix ESM Module output path in build (#4084)
* Fix module output path

* Format code
2026-02-19 10:11:52 +01:00
Karsa 391b0f6fc6 feat(icons): added message-square-check icon (#4076)
* Added icons/message-square-check.svg

* Added icons/message-square-check.json
2026-02-17 23:16:53 +01:00
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
Usama KhanandEric Fennis 265770857f feat(icons): added solar-panel icon (#2780)
* Added icons/solar-panel.svg

* Added icons/solar-panel.json

* Updated icons/solar-panel.svg

* Updated icons/solar-panel.json

* Update solar-panel.svg

* Update solar-panel.json

---------

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

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

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

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

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

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

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

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

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

* Update file-scan.svg

---------

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

* Added icons/clock-check.json

* Updated icons/clock-check.svg

* Updated icons/clock-check.json

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-10-31 10:21:50 +01:00
liloudreamsKarsagithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>Eric Fennis
6432dddfe0 feat(icons): added helicopter icon (#2760)
* Added icons/helicopter.svg

* Added icons/helicopter.json

* Update helicopter.svg

* Update helicopter.svg

Review gap and stroke violations

* Update icons/helicopter.json

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

* Update icons/helicopter.json

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

* Update icons/helicopter.svg

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

---------

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

* Added icons/git-branch-minus.json

* chore: optimise with circles

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

* Update git-branch-minus.json

* Update git-branch.json

* Update git-branch-plus.json

---------

Co-authored-by: Karsa <contact@karsa.org>
2025-10-31 10:15:31 +01:00
Victor Kaugesaar 2e794a296a feat(docs): add new package for Go (#3736) 2025-10-31 09:54:31 +01:00
/Aljoscha be59e647c1 Replace pnpm install with pnpm add across documentation. (#3735) 2025-10-26 17:01:12 +01:00
658 changed files with 11523 additions and 7773 deletions
+32 -50
View File
@@ -7,17 +7,30 @@ on:
paths:
- icons/**/*.svg
workflow_dispatch:
inputs:
version:
description: Version
required: true
permissions:
id-token: write # Required for OIDC
contents: write
# This is added to make sure we wait until last release is done, this is needed for the font-build process
concurrency:
group: continuous-integration
jobs:
create-release:
if: github.repository == 'lucide-icons/lucide' && startsWith(github.event.head_commit.message, 'feat(icons)')
if: github.repository == 'lucide-icons/lucide' && startsWith(github.event.head_commit.message, 'feat(icons)') && github.event_name != 'workflow_dispatch'
runs-on: ubuntu-latest
outputs:
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 +45,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 +71,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
@@ -103,3 +78,10 @@ jobs:
secrets: inherit
with:
version: ${{ needs.create-release.outputs.VERSION }}
dispatch-release:
if: github.event_name == 'workflow_dispatch'
uses: './.github/workflows/release.yml'
secrets: inherit
with:
version: ${{ inputs.version }}
@@ -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 -5
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'
@@ -20,9 +20,6 @@ jobs:
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Outline svg Icons
run: pnpm build:outline-icons
- name: Create font in ./lucide-font
run: pnpm build:font
+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'
+25 -26
View File
@@ -12,15 +12,16 @@ on:
description: Version
type: string
workflow_dispatch:
inputs:
version:
description: Version
required: true
permissions:
id-token: write # Required for OIDC
contents: write
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
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 +40,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 +59,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 +69,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 +88,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 +103,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 +122,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'
@@ -134,11 +132,10 @@ jobs:
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Outline svg Icons
run: pnpm build:outline-icons
- name: Create font in ./lucide-font
run: pnpm build:font
run: pnpm build:font --saveCodePoints
env:
BLOB_READ_WRITE_TOKEN: ${{ secrets.BLOB_READ_WRITE_TOKEN }}
- name: 'Upload to Artifacts'
uses: actions/upload-artifact@v4
@@ -150,9 +147,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
+5 -2
View File
@@ -14,12 +14,15 @@ coverage
stats
*.log
outlined
lucide-font
packages/**/src/icons/*.js
packages/**/src/icons/*.ts
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
@@ -40,11 +43,11 @@ docs/.vitepress/.temp
docs/.vitepress/data/iconNodes
docs/.vitepress/data/iconMetaData.ts
docs/.vitepress/data/releaseMetaData.json
docs/.vitepress/data/releaseMetaData
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
+1 -1
View File
@@ -25,7 +25,7 @@ Guidelines for pull requests:
Please make sure you follow the icon guidelines, that should be followed to keep quality and consistency when making icons for Lucide.
Read it here: [ICON_GUIDELINES](https://lucide.dev/docs/icon-design-guide).
Read it here: [ICON_GUIDELINES](https://lucide.dev/contribute/icon-design-guide).
#### Lucide Studio
+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"
}
@@ -1,3 +1,4 @@
import { eventHandler, setResponseHeader } from 'h3';
import iconMetaData from '../../data/iconMetaData';
export default eventHandler((event) => {
+1
View File
@@ -1,3 +1,4 @@
import { eventHandler, setResponseHeader } from 'h3';
import iconNodes from '../../data/iconNodes/index.ts';
import { IconNodeWithKeys } from '../../theme/types';
import iconMetaData from '../../data/iconMetaData';
@@ -1,6 +1,6 @@
import { eventHandler, setResponseHeader, defaultContentType } from 'h3';
import { renderToString, renderToStaticMarkup } from 'react-dom/server';
import { createElement } from 'react';
import React from 'react';
import SvgPreview from '../../lib/SvgPreview/index.tsx';
import iconNodes from '../../data/iconNodes';
import createLucideIcon from 'lucide-react/src/createLucideIcon';
@@ -34,36 +34,29 @@ export default eventHandler((event) => {
const iconNode = iconNodes[backdropName];
const LucideIcon = createLucideIcon(backdropName, iconNode);
const svg = renderToStaticMarkup(createElement(LucideIcon));
const svg = renderToStaticMarkup(<LucideIcon />);
const backdropString = svg.replaceAll('\n', '').replace(/<svg[^>]*>|<\/svg>/g, '');
children.push(
createElement(Backdrop, {
backdropString,
src: src.replace(/<svg[^>]*>|<\/svg>/g, ''),
color: '#777',
}),
<Backdrop
backdropString={backdropString}
src={src.replace(/<svg[^>]*>|<\/svg>/g, '')}
color="#777"
/>,
);
}
const svg = Buffer.from(
// We can't use jsx here, is not supported here by nitro.
renderToString(
createElement(
SvgPreview,
{
src: src.replace(/<svg[^>]*>|<\/svg>/g, ''),
height,
width,
showGrid: true,
},
children,
),
),
).toString('utf8');
defaultContentType(event, 'image/svg+xml');
setResponseHeader(event, 'Cache-Control', 'public,max-age=31536000');
return svg;
return renderToString(
<SvgPreview
src={src.replace(/<svg[^>]*>|<\/svg>/g, '')}
showGrid
height={height}
width={width}
>
{children}
</SvgPreview>,
);
});
@@ -1,6 +1,6 @@
import { eventHandler, setResponseHeader, defaultContentType } from 'h3';
import { renderToString, renderToStaticMarkup } from 'react-dom/server';
import { createElement } from 'react';
import React from 'react';
import Diff from '../../../lib/SvgPreview/Diff.tsx';
import iconNodes from '../../../data/iconNodes';
import createLucideIcon from 'lucide-react/src/createLucideIcon';
@@ -26,21 +26,26 @@ export default eventHandler((event) => {
const children = [];
const Icon = createLucideIcon(name, iconNodes[name]);
const oldSrc = iconNodes[name]
? renderToStaticMarkup(createElement(createLucideIcon(name, iconNodes[name])))
? renderToStaticMarkup(<Icon />)
.replaceAll('\n', '')
.replace(/<svg[^>]*>|<\/svg>/g, '')
: '';
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;
return renderToString(
<Diff
oldSrc={oldSrc}
newSrc={newSrc}
showGrid
height={height}
width={width}
>
{children}
</Diff>,
);
});
@@ -1,17 +1,12 @@
import { eventHandler, setResponseHeader, defaultContentType } from 'h3';
import { Resvg, initWasm } from '@resvg/resvg-wasm';
import iconNodes from '../../../data/iconNodes';
import wasm from './loadWasm';
import { createElement } from 'react';
import { renderToStaticMarkup } from 'react-dom/server';
import createLucideIcon from 'lucide-react/src/createLucideIcon';
var initializedResvg = initWasm(wasm);
import sharp from 'sharp';
export default eventHandler(async (event) => {
const { params = {} } = event.context;
await initializedResvg;
const imageSize = 96;
const name = params.data.split('/').at(-3);
const iconSizeString = params.data.split('/').at(-2);
@@ -45,9 +40,7 @@ export default eventHandler(async (event) => {
)
.replace(/<\/svg>/, '</g></svg>');
const resvg = new Resvg(svg, { background: '#000' });
const pngData = resvg.render();
const pngBuffer = Buffer.from(pngData.asPng());
const pngBuffer = await sharp(Buffer.from(svg)).png().toBuffer();
defaultContentType(event, 'image/svg+xml');
setResponseHeader(event, 'Cache-Control', 'public,max-age=31536000');
@@ -67,7 +60,7 @@ export default eventHandler(async (event) => {
<image
width="${imageSize}"
height="${prevSvg ? imageSize * 2 : imageSize}"
href="data:image/png;base64,${pngBuffer.toString('base64')}"
href="data:image/png;base64,${pngBuffer.toString('base64')}"
image-rendering="pixelated"
/>
</mask>
@@ -1,15 +0,0 @@
import fs from 'fs';
import module from 'node:module';
/* WASM_IMPORT */
let wasm;
if (process.env.NODE_ENV === 'development') {
const require = module.createRequire(import.meta.url);
wasm = fs.readFileSync(require.resolve('@resvg/resvg-wasm/index_bg.wasm'));
} else {
wasm = resvg_wasm;
}
export default wasm;
@@ -1,44 +0,0 @@
import { eventHandler, setResponseHeader, defaultContentType } from 'h3';
import { renderToString } from 'react-dom/server';
import { createElement } from 'react';
import SvgPreview from '../../../lib/SvgPreview/index.tsx';
import createLucideIcon, { IconNode } from 'lucide-react/src/createLucideIcon';
import { parseSync } from 'svgson';
export default eventHandler((event) => {
const { params } = event.context;
const [strokeWidth, svgData] = params.data.split('/');
const data = svgData.slice(0, -4);
const src = Buffer.from(data, 'base64').toString('utf8');
const Icon = createLucideIcon(
'icon',
parseSync(src.includes('<svg') ? src : `<svg>${src}</svg>`).children.map(
({ name, attributes }) => [name, attributes],
) as IconNode,
);
const svg = Buffer.from(
// We can't use jsx here, is not supported here by nitro.
renderToString(createElement(Icon, { strokeWidth }))
.replace(/fill\="none"/, 'fill="#fff"')
.replace(
/>/,
`><style>
@media screen and (prefers-color-scheme: light) {
svg { fill: transparent !important; }
}
@media screen and (prefers-color-scheme: dark) {
svg { stroke: #fff; fill: transparent !important; }
}
</style>`,
),
).toString('utf8');
defaultContentType(event, 'image/svg+xml');
setResponseHeader(event, 'Cache-Control', 'public,max-age=31536000');
return svg;
});
@@ -0,0 +1,37 @@
import { eventHandler, setResponseHeader, defaultContentType } from 'h3';
import { renderToString } from 'react-dom/server';
import React from 'react';
import Icon from 'lucide-react/src/Icon';
import type { IconNode } from 'lucide-react/src/types';
import { parseSync } from 'svgson';
export default eventHandler((event) => {
const { params } = event.context;
const [strokeWidth, svgData] = params.data.split('/');
const data = svgData.slice(0, -4);
const src = Buffer.from(data, 'base64').toString('utf8');
const iconNode = parseSync(src.includes('<svg') ? src : `<svg>${src}</svg>`).children.map(
({ name, attributes }) => [name, attributes],
) as IconNode;
defaultContentType(event, 'image/svg+xml');
setResponseHeader(event, 'Cache-Control', 'public,max-age=31536000');
return renderToString(
<Icon
iconNode={iconNode}
strokeWidth={strokeWidth}
>
<style>
{`@media screen and (prefers-color-scheme: light) {
svg { fill: transparent !important; }
}
@media screen and (prefers-color-scheme: dark) {
svg { stroke: #fff; fill: transparent !important; }
}`}
</style>
</Icon>,
);
});
@@ -0,0 +1,58 @@
import { eventHandler, setResponseHeader, defaultContentType } from 'h3';
import { renderToString } from 'react-dom/server';
import React 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 '';
}
defaultContentType(event, 'image/svg+xml');
setResponseHeader(event, 'Cache-Control', 'public,max-age=31536000');
return renderToString(
<Diff
oldSrc={oldSrc}
newSrc={newSrc}
showGrid
height={height}
width={width}
>
{children}
</Diff>,
);
});
@@ -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');
+1
View File
@@ -1,3 +1,4 @@
import { eventHandler, setResponseHeader } from 'h3';
import iconMetaData from '../../data/iconMetaData';
export default eventHandler((event) => {
-3
View File
@@ -1,3 +0,0 @@
export default eventHandler(() => {
return { nitro: 'Is Awesome! asda' };
});
+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": [
{
@@ -133,5 +133,81 @@
],
"source": "https://github.com/cnlancehu/lucide-slint",
"documentation": "https://github.com/cnlancehu/lucide-slint/blob/main/README.md"
},
{
"name": "lucide-go",
"description": "Implementation of Lucide icons for Go's html/template package.",
"icon": "/framework-logos/go.svg",
"shields": [
{
"alt": "Latest Stable Version",
"src": "https://img.shields.io/github/v/release/kaugesaar/lucide-go",
"href": "https://github.com/kaugesaar/lucide-go/releases"
},
{
"alt": "Go Reference",
"src": "https://pkg.go.dev/badge/github.com/kaugesaar/lucide-go.svg",
"href": "https://pkg.go.dev/github.com/kaugesaar/lucide-go"
}
],
"source": "https://github.com/kaugesaar/lucide-go",
"documentation": "https://github.com/kaugesaar/lucide-go/blob/master/README.md"
},
{
"name": "lucide-rails",
"description": "Ruby on Rails views helper method for rendering Lucide icons.",
"icon": "/framework-logos/rails.svg",
"shields": [
{
"alt": "Latest Stable Version",
"src": "https://img.shields.io/gem/v/lucide-rails",
"href": "https://rubygems.org/gems/lucide-rails"
},
{
"alt": "Total Downloads",
"src": "https://img.shields.io/gem/dt/lucide-rails",
"href": "https://rubygems.org/gems/lucide-rails"
}
],
"source": "https://github.com/heyvito/lucide-rails",
"documentation": "https://github.com/heyvito/lucide-rails/blob/master/README.md"
},
{
"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"
},
{
"name": "strapi-lucide-icons",
"description": "Lucide icon picker custom field for Strapi.",
"icon": "/framework-logos/strapi.svg",
"shields": [
{
"alt": "Latest Stable Version",
"src": "https://img.shields.io/npm/v/strapi-lucide-icons",
"href": "https://www.npmjs.com/package/strapi-lucide-icons"
},
{
"alt": "Total Downloads",
"src": "https://img.shields.io/npm/dw/strapi-lucide-icons",
"href": "https://www.npmjs.com/package/strapi-lucide-icons"
}
],
"source": "https://github.com/shx08/strapi-lucide-icons",
"documentation": "https://github.com/shx08/strapi-lucide-icons/blob/main/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 -1
View File
@@ -1,4 +1,4 @@
import React from 'react';
import React, { Fragment } from 'react';
interface BackdropProps {
src: string;
+14 -11
View File
@@ -1,4 +1,4 @@
import React from 'react';
import React, { Fragment } from 'react';
import { PathProps, Path } from './types';
import getPaths, { assert } from './utils';
import { GapViolationHighlight } from './GapViolationHighlight.tsx';
@@ -389,15 +389,18 @@ const Handles = ({
</g>
);
const SvgPreview = React.forwardRef<
SVGSVGElement,
{
height?: number;
width?: number;
src: string | ReturnType<typeof getPaths>;
showGrid?: boolean;
} & React.SVGProps<SVGSVGElement>
>(({ src, children, height = 24, width = 24, showGrid = false, ...props }, ref) => {
interface SvgPreviewProps extends React.SVGProps<SVGSVGElement> {
height?: number;
width?: number;
src: string | ReturnType<typeof getPaths>;
showGrid?: boolean;
children?: React.ReactNode;
}
const SvgPreview = (
{ src, children, height = 24, width = 24, showGrid = false, ...props }: SvgPreviewProps,
ref,
) => {
const subGridSize =
Math.max(height, width) % 3 === 0
? Math.max(height, width) > 24
@@ -494,7 +497,7 @@ const SvgPreview = React.forwardRef<
{children}
</svg>
);
});
};
SvgPreview.displayName = 'SvgPreview';
@@ -1,4 +1,4 @@
import memoize from 'lodash/memoize';
import { memoize } from 'lodash-es';
import SVGPathCommander from 'svg-path-commander';
import { Path } from './types';
+1 -1
View File
@@ -8,7 +8,7 @@ export type Path = {
prev: Point;
next: Point;
isStart: boolean;
circle?: { x: number; y: number; r: number };
circle?: { x: number; y: number; r: number; tangentIntersection?: Point };
cp1?: Point;
cp2?: Point;
c: ReturnType<typeof getCommands>[number];
+1 -3
View File
@@ -1,10 +1,8 @@
import { INode, parseSync } from 'svgson';
// @ts-ignore
import toPath from 'element-to-path';
// @ts-ignore
import { SVGPathData, encodeSVGPath } from 'svg-pathdata';
import { Path, Point } from './types';
import memoize from 'lodash/memoize';
import { memoize } from 'lodash-es';
function assertNever(x: never): never {
throw new Error('Unknown type: ' + x['type']);
@@ -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>\
`,
},
{
@@ -107,16 +113,16 @@ import { LucideAngularModule, $PascalCase } from 'lucide-angular';
];
};
const highlighter = await createHighlighter({
themes: ['github-light', 'github-dark'],
langs: Object.keys(bundledLanguages),
});
export type ThemeOptions =
| ThemeRegistration
| { light: ThemeRegistration; dark: ThemeRegistration };
const highLightCode = async (code: string, lang: string, active?: boolean) => {
const highlighter = await getHighlighter({
themes: ['github-light', 'github-dark'],
langs: Object.keys(bundledLanguages),
});
export const highLightCode = async (code: string, lang: string, active?: boolean) => {
const highlightedCode = highlighter
.codeToHtml(code, {
lang,
@@ -1,5 +1,4 @@
import { bundledLanguages, type ThemeRegistration } from 'shikiji';
import { getHighlighter } from 'shikiji';
import { highLightCode } from './createCodeExamples';
type CodeExampleType = {
title: string;
@@ -10,10 +9,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 +21,9 @@ createIcons({
$CamelCase
}
});
</script>
document.body.append('<i data-lucide="$Name"></i>');\
<i data-lucide="$Name"></i>\
`,
},
{
@@ -103,45 +104,17 @@ import { $CamelCase } from '@lucide/lab';
@NgModule({
imports: [
LucideAngularModule.pick({ $CamelCase })
LucideAngularModule.pick({ $PascalCase: $CamelCase })
],
})
// app.component.html
<lucide-icon name="$CamelCase"></lucide-icon>
<lucide-icon name="$PascalCase"></lucide-icon>
`,
},
];
};
export type ThemeOptions =
| ThemeRegistration
| { light: ThemeRegistration; dark: ThemeRegistration };
const highLightCode = async (code: string, lang: string, active?: boolean) => {
const highlighter = await getHighlighter({
themes: ['github-light', 'github-dark'],
langs: Object.keys(bundledLanguages),
});
const highlightedCode = highlighter
.codeToHtml(code, {
lang,
themes: {
light: 'github-light',
dark: 'github-dark',
},
defaultColor: false,
})
.replace('shiki-themes', 'shiki-themes vp-code');
return `<div class="language-${lang} ${active ? 'active' : ''}">
<button title="Copy Code" class="copy"></button>
<span class="lang">${lang}</span>
${highlightedCode}
</div>`;
};
export default async function createCodeExamples() {
const codes = getIconCodes();
@@ -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.
+9
View File
@@ -90,6 +90,15 @@ Here are rules that should be followed to keep quality and consistency when maki
**Tip:** whenever possible align elements and arc centers to the grid.
### 14. Icons should share common shapes
You should try to create consistent groups and variants, reuse and try to create uniformity.
Consistency inside groups and variants has a lower priority than the rules above.
**Example:** All `-off` icons should look the same unless it for example violates the optical volume rule.
**Tip:** Try to not move the base shape to enable better use in a toggle context.
## Naming conventions
1. Icon names use lower kebab case.\
+13 -12
View File
@@ -11,7 +11,7 @@ Implementation of the lucide icon library for web applications.
::: code-group
```sh [pnpm]
pnpm install lucide
pnpm add lucide
```
```sh [yarn]
@@ -37,7 +37,7 @@ Implementation of the lucide icon library for React applications.
::: code-group
```sh [pnpm]
pnpm install lucide-react
pnpm add lucide-react
```
```sh [yarn]
@@ -64,7 +64,7 @@ Implementation of the lucide icon library for Vue applications.
::: code-group
```sh [pnpm]
pnpm install lucide-vue-next
pnpm add lucide-vue-next
```
```sh [yarn]
@@ -91,22 +91,23 @@ Implementation of the lucide icon library for Svelte applications.
::: code-group
```sh [pnpm]
pnpm install lucide-svelte
pnpm add @lucide/svelte
```
```sh [yarn]
yarn add lucide-svelte
yarn add @lucide/svelte
```
```sh [npm]
npm install lucide-svelte
npm install @lucide/svelte
```
```sh [bun]
bun add lucide-svelte
bun add @lucide/svelte
```
:::
> `@lucide/svelte` is only for Svelte 5, for Svelte 4 use the `lucide-svelte` package.
For more details, see the [documentation](packages/lucide-svelte.md).
@@ -117,7 +118,7 @@ Implementation of the lucide icon library for Solid applications.
::: code-group
```sh [pnpm]
pnpm install lucide-solid
pnpm add lucide-solid
```
```sh [yarn]
@@ -143,7 +144,7 @@ Implementation of the lucide icon library for Angular applications.
::: code-group
```sh [pnpm]
pnpm install lucide-angular
pnpm add lucide-angular
```
```sh [yarn]
@@ -169,7 +170,7 @@ Implementation of the lucide icon library for preact applications.
::: code-group
```sh [pnpm]
pnpm install lucide-preact
pnpm add lucide-preact
```
```sh [yarn]
@@ -196,7 +197,7 @@ Implementation of the lucide icon library for Astro applications.
::: code-group
```sh [pnpm]
pnpm install @lucide/astro
pnpm add @lucide/astro
```
```sh [yarn]
@@ -222,7 +223,7 @@ Implementation of the lucide icon library for multiple usages that like to use:
::: code-group
```sh [pnpm]
pnpm install lucide-static
pnpm add lucide-static
```
```sh [yarn]
+13 -1
View File
@@ -14,7 +14,7 @@ Angular components and services for Lucide icons that integrate with Angular's d
::: code-group
```sh [pnpm]
pnpm install lucide-angular
pnpm add lucide-angular
```
```sh [yarn]
@@ -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).
+17 -1
View File
@@ -18,7 +18,7 @@ Preact components for Lucide icons that provide React-like development experienc
::: code-group
```sh [pnpm]
pnpm install lucide-preact
pnpm add lucide-preact
```
```sh [yarn]
@@ -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).
+12 -6
View File
@@ -16,7 +16,7 @@ First, ensure that you have `react-native-svg` (version between 12 and 15) insta
::: code-group
```sh [pnpm]
pnpm install lucide-react-native
pnpm add lucide-react-native
```
```sh [yarn]
@@ -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;
+17 -1
View File
@@ -14,7 +14,7 @@ React components for Lucide icons that integrate seamlessly into your React appl
::: code-group
```sh [pnpm]
pnpm install lucide-react
pnpm add lucide-react
```
```sh [yarn]
@@ -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).
+17 -1
View File
@@ -14,7 +14,7 @@ SolidJS components for Lucide icons that leverage Solid's fine-grained reactivit
::: code-group
```sh [pnpm]
pnpm install lucide-solid
pnpm add lucide-solid
```
```sh [yarn]
@@ -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).
+1 -1
View File
@@ -45,7 +45,7 @@ For production environments, we recommend using a bundler with tree-shaking supp
::: code-group
```sh [pnpm]
pnpm install lucide-static
pnpm add lucide-static
```
```sh [yarn]
+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).
+23 -1
View File
@@ -14,7 +14,7 @@ Vue 3 components for Lucide icons that leverage the Composition API and modern V
::: code-group
```sh [pnpm]
pnpm install lucide-vue-next
pnpm add lucide-vue-next
```
```sh [yarn]
@@ -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).
+1 -1
View File
@@ -18,7 +18,7 @@ This package is deprecated. Vue 2 is EOF See [Announcement](https://v2.vuejs.or
::: code-group
```sh [pnpm]
pnpm install lucide-vue
pnpm add lucide-vue
```
```sh [yarn]
+22 -1
View File
@@ -16,7 +16,7 @@ The core Lucide package for vanilla JavaScript applications. This package allows
::: code-group
```sh [pnpm]
pnpm install lucide
pnpm add lucide
```
```sh [yarn]
@@ -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
---
+2 -24
View File
@@ -1,32 +1,10 @@
import copy from 'rollup-plugin-copy';
import replace from '@rollup/plugin-replace';
export default defineNitroConfig({
compatibilityDate: '2025-07-30',
preset: 'vercel_edge',
compatibilityDate: '2026-02-26',
preset: 'vercel',
srcDir: '.vitepress',
routeRules: {
'/api/**': { cors: false },
},
rollupConfig: {
external: ['@resvg/resvg-wasm/index_bg.wasm', './index_bg.wasm?module'],
plugins: [
copy({
targets: [
{
src: './node_modules/@resvg/resvg-wasm/index_bg.wasm',
dest: './.vercel/output/functions/__nitro.func',
},
],
}),
replace({
include: ['./**/*.ts'],
'/* WASM_IMPORT */': 'import resvg_wasm from "./index_bg.wasm?module";',
delimiters: ['', ''],
preventAssignment: false,
}),
],
},
esbuild: {
options: {
jsxFactory: 'React.createElement',
+31 -30
View File
@@ -9,17 +9,18 @@
"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",
"build": "pnpm run /^prebuild:.*/ && pnpm run /^build:.*/ && pnpm postbuild:vercelJson",
"build": "pnpm run /^prebuild:.*/ && pnpm run build:api && pnpm run build:docs && pnpm postbuild:vercelJson",
"preview": "node .output/server/index.mjs"
},
"author": "Eric Fennis",
@@ -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",
"nitropack": "2.8.1",
"@rollup/plugin-replace": "^6.0.3",
"@types/semver": "^7.7.1",
"h3": "^1.15.6",
"nitropack": "^2.12.4",
"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",
"@resvg/resvg-wasm": "^2.6.2",
"@vueuse/components": "^12.0.0",
"@vueuse/core": "^12.0.0",
"@floating-ui/vue": "^1.1.9",
"@headlessui/vue": "^1.7.23",
"@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-es": "^4.17.23",
"lucide-react": "workspace:*",
"lucide-vue-next": "workspace:*",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"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",
"sharp": "^0.34.5",
"shiki": "^3.15.0",
"simple-git": "^3.32.3",
"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
+55
View File
@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 205.4 76.7" style="enable-background:new 0 0 205.4 76.7;" xml:space="preserve">
<style type="text/css">
.st0{fill:#00ACD7;}
</style>
<g>
<g>
<g>
<g>
<path class="st0" d="M15.5,23.2c-0.4,0-0.5-0.2-0.3-0.5l2.1-2.7c0.2-0.3,0.7-0.5,1.1-0.5h35.7c0.4,0,0.5,0.3,0.3,0.6l-1.7,2.6
c-0.2,0.3-0.7,0.6-1,0.6L15.5,23.2z"/>
</g>
</g>
</g>
<g>
<g>
<g>
<path class="st0" d="M0.4,32.4c-0.4,0-0.5-0.2-0.3-0.5l2.1-2.7c0.2-0.3,0.7-0.5,1.1-0.5h45.6c0.4,0,0.6,0.3,0.5,0.6l-0.8,2.4
c-0.1,0.4-0.5,0.6-0.9,0.6L0.4,32.4z"/>
</g>
</g>
</g>
<g>
<g>
<g>
<path class="st0" d="M24.6,41.6c-0.4,0-0.5-0.3-0.3-0.6l1.4-2.5c0.2-0.3,0.6-0.6,1-0.6h20c0.4,0,0.6,0.3,0.6,0.7L47.1,41
c0,0.4-0.4,0.7-0.7,0.7L24.6,41.6z"/>
</g>
</g>
</g>
<g>
<g id="CXHf1q_3_">
<g>
<g>
<path class="st0" d="M128.4,21.4c-6.3,1.6-10.6,2.8-16.8,4.4c-1.5,0.4-1.6,0.5-2.9-1c-1.5-1.7-2.6-2.8-4.7-3.8
c-6.3-3.1-12.4-2.2-18.1,1.5c-6.8,4.4-10.3,10.9-10.2,19c0.1,8,5.6,14.6,13.5,15.7c6.8,0.9,12.5-1.5,17-6.6
c0.9-1.1,1.7-2.3,2.7-3.7c-3.6,0-8.1,0-19.3,0c-2.1,0-2.6-1.3-1.9-3c1.3-3.1,3.7-8.3,5.1-10.9c0.3-0.6,1-1.6,2.5-1.6
c5.1,0,23.9,0,36.4,0c-0.2,2.7-0.2,5.4-0.6,8.1c-1.1,7.2-3.8,13.8-8.2,19.6c-7.2,9.5-16.6,15.4-28.5,17
c-9.8,1.3-18.9-0.6-26.9-6.6c-7.4-5.6-11.6-13-12.7-22.2c-1.3-10.9,1.9-20.7,8.5-29.3c7.1-9.3,16.5-15.2,28-17.3
c9.4-1.7,18.4-0.6,26.5,4.9c5.3,3.5,9.1,8.3,11.6,14.1C130,20.6,129.6,21.1,128.4,21.4z"/>
</g>
<g>
<path class="st0" d="M161.5,76.7c-9.1-0.2-17.4-2.8-24.4-8.8c-5.9-5.1-9.6-11.6-10.8-19.3c-1.8-11.3,1.3-21.3,8.1-30.2
c7.3-9.6,16.1-14.6,28-16.7c10.2-1.8,19.8-0.8,28.5,5.1c7.9,5.4,12.8,12.7,14.1,22.3c1.7,13.5-2.2,24.5-11.5,33.9
c-6.6,6.7-14.7,10.9-24,12.8C166.8,76.3,164.1,76.4,161.5,76.7z M185.3,36.3c-0.1-1.3-0.1-2.3-0.3-3.3
c-1.8-9.9-10.9-15.5-20.4-13.3c-9.3,2.1-15.3,8-17.5,17.4c-1.8,7.8,2,15.7,9.2,18.9c5.5,2.4,11,2.1,16.3-0.6
C180.5,51.3,184.8,44.9,185.3,36.3z"/>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

+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

+7
View File
@@ -0,0 +1,7 @@
<svg width="800" height="800" viewBox="0 0 800 800" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M39 282C39 164 39 105.1 75.6 68.5C112.2 32 171.1 32 288.9 32H510.1C627.9 32 686.8 32 723.4 68.6C760 105.2 760 164.1 760 281.9V503.1C760 620.9 760 679.8 723.4 716.4C686.8 753 627.9 753 510.1 753H288.9C171.1 753 112.2 753 75.6 716.4C39 679.8 39 620.9 39 503.1V281.9Z" fill="#4945FF"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M536.4 250.7H293.7V374.5H417.5V498.2H541.3V255.5C541.3 252.9 539.1 250.7 536.4 250.7Z" fill="white"/>
<path d="M412.7 374.5H417.5V379.3H412.7V374.5Z" fill="white"/>
<path d="M293.8 374.5H412.8C415.4 374.5 417.6 376.6 417.6 379.3V498.3H298.6C295.9 498.3 293.8 496.1 293.8 493.4V374.5Z" fill="#9593FF"/>
<path d="M417.5 498.2H541.3L421.6 618C420 619.6 417.5 618.5 417.5 616.3V498.2ZM293.8 374.5H175.8C173.6 374.5 172.5 371.9 174.1 370.4L293.8 250.7V374.5Z" fill="#9593FF"/>
</svg>

After

Width:  |  Height:  |  Size: 930 B

@@ -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

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