Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
93e81c1a3b | ||
|
|
ded620ad88 | ||
|
|
cc8ccddc6d | ||
|
|
cbaaf4e21e | ||
|
|
e9e060a851 | ||
|
|
0371baabcb | ||
|
|
693b5f7210 | ||
|
|
e8d6205535 | ||
|
|
67c0485457 | ||
|
|
c47876e5a6 | ||
|
|
96859ba947 | ||
|
|
7dfa7d9fc6 | ||
|
|
b6ed43d48c | ||
|
|
9b10d1d9bb | ||
|
|
442ddbc43c | ||
|
|
ce5cab371f | ||
|
|
69239462bb | ||
|
|
f8e04958d8 | ||
|
|
e466a104ff | ||
|
|
516917c527 | ||
|
|
3cec234405 | ||
|
|
a258509bbb | ||
|
|
10918eba1f | ||
|
|
5f4f58cba8 | ||
|
|
511d7fabf7 | ||
|
|
77a67819e4 | ||
|
|
edc588125f | ||
|
|
0c4ac91ba5 | ||
|
|
450acd617d | ||
|
|
d391bda369 |
@@ -28,7 +28,7 @@ jobs:
|
||||
{ [ -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" \
|
||||
--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**.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -20,6 +20,8 @@ packages/**/src/icons/*.tsx
|
||||
packages/**/src/aliases/*.ts
|
||||
packages/**/src/aliases.ts
|
||||
!packages/**/src/aliases/index.ts
|
||||
packages/**/src/utils/*.ts
|
||||
!packages/shared/src/utils/*.ts
|
||||
packages/**/src/dynamicIconImports.ts
|
||||
packages/**/DynamicIcon.d.ts
|
||||
packages/**/dynamicIconImports.js
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"adobe": "Adobe",
|
||||
"airplay": "AirPlay",
|
||||
"amazon": "Amazon",
|
||||
"android": "Android",
|
||||
"angular": "Angular",
|
||||
"aws": "AWS",
|
||||
"azure": "Azure",
|
||||
|
||||
@@ -9,6 +9,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',
|
||||
|
||||
@@ -171,5 +171,24 @@
|
||||
],
|
||||
"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"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -101,9 +101,11 @@ const value = computed({
|
||||
}
|
||||
|
||||
.color-picker:hover,
|
||||
.color-picker:focus {
|
||||
.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'] {
|
||||
|
||||
@@ -48,7 +48,6 @@ defineProps({
|
||||
.fake-input:hover,
|
||||
.fake-input:focus {
|
||||
border-color: var(--vp-c-brand);
|
||||
background: var(--vp-c-bg-alt);
|
||||
}
|
||||
|
||||
.shortcut {
|
||||
|
||||
@@ -35,6 +35,8 @@ const updateShortcutSpacing = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const isMac = /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform);
|
||||
|
||||
onMounted(updateShortcutSpacing);
|
||||
watch(() => props.shortcut, updateShortcutSpacing);
|
||||
|
||||
@@ -73,6 +75,7 @@ defineExpose({
|
||||
v-if="type === 'search' && modelValue"
|
||||
class="clear-button"
|
||||
aria-label="Clear input"
|
||||
:style="{ right: isMac ? '50px' : '68px' }"
|
||||
>
|
||||
<Icon
|
||||
:iconNode="x"
|
||||
@@ -115,7 +118,9 @@ defineExpose({
|
||||
.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 {
|
||||
@@ -124,7 +129,7 @@ defineExpose({
|
||||
|
||||
.clear-button {
|
||||
position: absolute;
|
||||
right: 56px;
|
||||
right: 68px;
|
||||
top: 9px;
|
||||
padding: 4px;
|
||||
transition: background-color .25s;
|
||||
@@ -147,10 +152,32 @@ defineExpose({
|
||||
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>
|
||||
|
||||
@@ -163,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>
|
||||
|
||||
@@ -5,11 +5,10 @@ import IconButton from '../base/IconButton.vue';
|
||||
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';
|
||||
import { onMounted } from 'vue';
|
||||
|
||||
const { theme } = useData();
|
||||
const showAd = useSessionStorage('show-carbon-ads', true);
|
||||
const carbonLoaded = ref(true);
|
||||
|
||||
defineProps<{
|
||||
drawerOpen: boolean;
|
||||
@@ -18,23 +17,29 @@ defineProps<{
|
||||
onMounted(() => {
|
||||
setTimeout(() => {
|
||||
if (window?._carbonads == null) {
|
||||
carbonLoaded.value = false;
|
||||
showAd.value = false;
|
||||
}
|
||||
}, 5000);
|
||||
});
|
||||
|
||||
function handleHideAd() {
|
||||
console.log('CLOSE');
|
||||
|
||||
showAd.value = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
:class="{
|
||||
'drawer-open': drawerOpen,
|
||||
'hide-ad': !(showAd && carbonLoaded),
|
||||
'hide-ad': !showAd,
|
||||
}"
|
||||
class="floating-ad"
|
||||
v-if="theme.carbonAds"
|
||||
>
|
||||
<IconButton
|
||||
@click="showAd = false"
|
||||
@click="handleHideAd"
|
||||
class="hide-button"
|
||||
>
|
||||
<Icon
|
||||
@@ -49,7 +54,6 @@ onMounted(() => {
|
||||
|
||||
<style scoped>
|
||||
.floating-ad {
|
||||
display: none;
|
||||
position: fixed;
|
||||
bottom: 32px;
|
||||
width: 224px;
|
||||
@@ -79,18 +83,6 @@ onMounted(() => {
|
||||
transform 0.5s cubic-bezier(0.19, 1, 0.22, 1);
|
||||
}
|
||||
|
||||
@media (min-width: 1280px) {
|
||||
.floating-ad {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1440px) {
|
||||
.floating-ad {
|
||||
right: calc(((100% - (var(--vp-layout-max-width) - var(--vp-sidebar-width))) - 272px) / 2);
|
||||
}
|
||||
}
|
||||
|
||||
.hide-button {
|
||||
padding: 4px;
|
||||
position: absolute;
|
||||
|
||||
@@ -153,7 +153,7 @@ function handleCloseDrawer() {
|
||||
>
|
||||
<StickyBar class="category-search">
|
||||
<InputSearch
|
||||
:placeholder="`Search ${icons.length} icons ...`"
|
||||
:placeholder="`Search ${icons.length} icons…`"
|
||||
v-model="searchQuery"
|
||||
:shortcut="kbdSearchShortcut"
|
||||
class="input-wrapper"
|
||||
|
||||
@@ -129,7 +129,7 @@ function handleCloseDrawer() {
|
||||
>
|
||||
<StickyBar>
|
||||
<InputSearch
|
||||
:placeholder="`Search ${icons.length} icons ...`"
|
||||
:placeholder="`Search ${icons.length} icons…`"
|
||||
v-model="searchQuery"
|
||||
ref="searchInput"
|
||||
:shortcut="kbdSearchShortcut"
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
---
|
||||
title: Categories
|
||||
layout: page
|
||||
outline: 2
|
||||
outlineTitle: Categories
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
---
|
||||
title: Icons
|
||||
layout: page
|
||||
outline: 2
|
||||
outlineTitle: Categories
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# https://vitepress.dev/reference/default-theme-home-page
|
||||
layout: home
|
||||
markdownStyles: false
|
||||
titleTemplate: false
|
||||
|
||||
head:
|
||||
- - link
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
---
|
||||
title: License
|
||||
aside: false
|
||||
editLink: false
|
||||
---
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
"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",
|
||||
"dev": "nitropack dev",
|
||||
"prebuild:api": "nitropack prepare",
|
||||
"build:api": "nitropack build",
|
||||
"build": "pnpm run /^prebuild:.*/ && pnpm run /^build:.*/ && pnpm postbuild:vercelJson",
|
||||
"preview": "node .output/server/index.mjs"
|
||||
},
|
||||
@@ -40,13 +40,12 @@
|
||||
"@floating-ui/vue": "^1.1.9",
|
||||
"@headlessui/vue": "^1.7.23",
|
||||
"@resvg/resvg-wasm": "^2.6.2",
|
||||
"@vueuse/components": "^14.0.0",
|
||||
"@vueuse/core": "^14.0.0",
|
||||
"@vueuse/components": "^14.1.0",
|
||||
"@vueuse/core": "^14.1.0",
|
||||
"element-to-path": "^1.2.1",
|
||||
"fuse.js": "^7.1.0",
|
||||
"jszip": "^3.10.1",
|
||||
"lodash": "^4.17.21",
|
||||
"lodash-es": "^4.17.21",
|
||||
"lodash-es": "^4.17.23",
|
||||
"lucide-react": "workspace:*",
|
||||
"lucide-vue-next": "workspace:*",
|
||||
"react": "^18.3.1",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
---
|
||||
title: Packages
|
||||
layout: page
|
||||
outline: 2
|
||||
outlineTitle: Packages
|
||||
|
||||
@@ -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 |
@@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="138" height="42" fill="none" viewBox="0 0 138 42">
|
||||
<path fill="#fff" fill-rule="evenodd" d="M138 4a3.997 3.997 0 0 0-4-4H4a4 4 0 0 0-4 4v34a4 4 0 0 0 4 4h130a4 4 0 0 0 4-4z" clip-rule="evenodd"/>
|
||||
<path fill="#000" fill-rule="evenodd" d="M27.963 16.943q1.08 0 1.864.43.783.428 1.212 1.154v-1.454h3.653V27.53H31.04v-1.454q-.429.727-1.23 1.156-.783.43-1.846.43-1.248 0-2.274-.653-1.006-.653-1.603-1.864-.578-1.23-.578-2.852t.578-2.834q.597-1.211 1.604-1.863a4.15 4.15 0 0 1 2.273-.653m1.175 3.188q-.82 0-1.36.577-.542.56-.542 1.585 0 1.006.541 1.602.54.579 1.36.579.802 0 1.343-.579.558-.578.56-1.602-.001-1.007-.56-1.585a1.76 1.76 0 0 0-1.342-.577" clip-rule="evenodd"/>
|
||||
<path fill="#000" d="M75.361 16.943q2.05 0 3.225 1.006t1.361 2.648h-3.3q-.092-.504-.447-.765-.335-.28-.913-.28-.447 0-.69.169a.55.55 0 0 0-.223.465q0 .373.429.579.428.186 1.379.39 1.137.243 1.882.541.765.3 1.324.969.578.653.578 1.79 0 .914-.541 1.64-.522.727-1.51 1.156-.969.41-2.293.41-1.417 0-2.535-.466-1.1-.485-1.752-1.304a3.47 3.47 0 0 1-.708-1.883h3.542q.075.503.466.782.39.28.968.28.448 0 .71-.186a.57.57 0 0 0 .26-.485q0-.39-.428-.578-.43-.185-1.417-.41-1.12-.223-1.865-.503a3.25 3.25 0 0 1-1.304-.914q-.54-.633-.54-1.715 0-.93.502-1.677.504-.765 1.473-1.211.989-.448 2.367-.448"/>
|
||||
<path fill="#000" fill-rule="evenodd" d="M94.305 16.943q1.564 0 2.796.653a4.8 4.8 0 0 1 1.957 1.863q.726 1.212.727 2.834 0 1.621-.727 2.852a4.8 4.8 0 0 1-1.957 1.864q-1.25.652-2.815.652-1.566 0-2.814-.652a4.8 4.8 0 0 1-1.939-1.864q-.709-1.212-.709-2.852 0-1.622.71-2.834a4.95 4.95 0 0 1 1.956-1.863q1.248-.653 2.815-.653m0 3.168q-.746 0-1.25.56-.502.56-.502 1.622 0 1.08.484 1.64.485.559 1.25.56.763-.001 1.267-.56.52-.578.521-1.64 0-1.063-.521-1.622a1.61 1.61 0 0 0-1.25-.56M118.921 16.943q1.081 0 1.864.43.783.428 1.212 1.154v-1.454h3.653V27.53h-3.653v-1.454q-.429.727-1.23 1.156-.784.43-1.846.43-1.248 0-2.274-.653-1.006-.653-1.603-1.864-.577-1.23-.577-2.852t.577-2.834q.597-1.211 1.603-1.863a4.15 4.15 0 0 1 2.274-.653m1.175 3.188q-.82 0-1.361.577-.54.56-.541 1.585 0 1.006.541 1.602.541.579 1.361.579.8 0 1.342-.579.558-.578.559-1.602 0-1.007-.559-1.585a1.77 1.77 0 0 0-1.342-.577M17.81 14.39q1.584 0 2.683.558 1.119.56 1.678 1.548.56.988.56 2.274 0 1.193-.56 2.18-.54.97-1.66 1.567-1.099.577-2.701.577H16v4.436h-3.653V14.39zM16 20.186h1.454q1.548 0 1.547-1.416 0-1.436-1.547-1.436h-1.454z" clip-rule="evenodd"/>
|
||||
<path fill="#000" d="m41.658 19.702 1.51-2.629h4.008l-3.56 5.331 3.69 5.126h-4.1l-1.884-2.628-1.49 2.628h-4.009l3.487-5.424-3.635-5.033h4.1zM55.42 19.33h4.456v-4.94h3.653v13.14h-3.653v-5.274h-4.455v5.274h-3.653V14.39h3.653zM69.18 27.53h-3.654V17.073h3.654zM85.919 17.073h1.9v3.038h-1.9v3.505q0 .429.186.615.204.187.671.187h1.063v3.112h-1.585q-4.008 0-4.008-3.97v-3.449H80.96v-3.038h1.286v-2.535h3.673zM108.205 20.876h-1.024q-1.194 0-1.772.466-.577.465-.577 1.622v4.566h-3.654V17.073h3.654v1.846a4.76 4.76 0 0 1 1.491-1.417 3.6 3.6 0 0 1 1.882-.521zM113.075 27.53h-3.653V17.073h3.653zM67.372 12.339q.95 0 1.528.54.597.542.597 1.361 0 .801-.597 1.342-.577.523-1.528.523-.97 0-1.565-.523a1.76 1.76 0 0 1-.579-1.342q0-.82.579-1.36.596-.54 1.565-.541M111.268 12.339q.95 0 1.528.54.596.542.597 1.361 0 .801-.597 1.342-.578.522-1.528.523-.97 0-1.567-.523a1.76 1.76 0 0 1-.577-1.342q0-.82.577-1.36.597-.54 1.567-.541"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.3 KiB |
@@ -1,4 +1,5 @@
|
||||
---
|
||||
title: Showcase
|
||||
layout: page
|
||||
outline: 2
|
||||
outlineTitle: Showcase
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"$schema": "../icon.schema.json",
|
||||
"contributors": [
|
||||
"danielbayley"
|
||||
"danielbayley",
|
||||
"jguddas"
|
||||
],
|
||||
"tags": [
|
||||
"read",
|
||||
|
||||
@@ -9,6 +9,6 @@
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="m19 21-7-4-7 4V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2Z" />
|
||||
<path d="M17 3a2 2 0 0 1 2 2v15a1 1 0 0 1-1.496.868l-4.512-2.578a2 2 0 0 0-1.984 0l-4.512 2.578A1 1 0 0 1 5 20V5a2 2 0 0 1 2-2z" />
|
||||
<path d="m9 10 2 2 4-4" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 301 B After Width: | Height: | Size: 371 B |
@@ -2,7 +2,8 @@
|
||||
"$schema": "../icon.schema.json",
|
||||
"contributors": [
|
||||
"siarie",
|
||||
"ericfennis"
|
||||
"ericfennis",
|
||||
"jguddas"
|
||||
],
|
||||
"tags": [
|
||||
"delete",
|
||||
|
||||
@@ -9,6 +9,6 @@
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="m19 21-7-4-7 4V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v16z" />
|
||||
<line x1="15" x2="9" y1="10" y2="10" />
|
||||
<path d="M15 10H9" />
|
||||
<path d="M17 3a2 2 0 0 1 2 2v15a1 1 0 0 1-1.496.868l-4.512-2.578a2 2 0 0 0-1.984 0l-4.512 2.578A1 1 0 0 1 5 20V5a2 2 0 0 1 2-2z" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 317 B After Width: | Height: | Size: 366 B |
@@ -2,7 +2,8 @@
|
||||
"$schema": "../icon.schema.json",
|
||||
"contributors": [
|
||||
"siarie",
|
||||
"ericfennis"
|
||||
"ericfennis",
|
||||
"jguddas"
|
||||
],
|
||||
"tags": [
|
||||
"add"
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="m19 21-7-4-7 4V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v16z" />
|
||||
<line x1="12" x2="12" y1="7" y2="13" />
|
||||
<line x1="15" x2="9" y1="10" y2="10" />
|
||||
<path d="M12 7v6" />
|
||||
<path d="M15 10H9" />
|
||||
<path d="M17 3a2 2 0 0 1 2 2v15a1 1 0 0 1-1.496.868l-4.512-2.578a2 2 0 0 0-1.984 0l-4.512 2.578A1 1 0 0 1 5 20V5a2 2 0 0 1 2-2z" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 359 B After Width: | Height: | Size: 389 B |
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"$schema": "../icon.schema.json",
|
||||
"contributors": [
|
||||
"danielbayley"
|
||||
"danielbayley",
|
||||
"jguddas"
|
||||
],
|
||||
"tags": [
|
||||
"read",
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="m19 21-7-4-7 4V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2Z" />
|
||||
<path d="m14.5 7.5-5 5" />
|
||||
<path d="M17 3a2 2 0 0 1 2 2v15a1 1 0 0 1-1.496.868l-4.512-2.578a2 2 0 0 0-1.984 0l-4.512 2.578A1 1 0 0 1 5 20V5a2 2 0 0 1 2-2z" />
|
||||
<path d="m9.5 7.5 5 5" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 329 B After Width: | Height: | Size: 399 B |
@@ -4,9 +4,18 @@
|
||||
"colebemis",
|
||||
"csandman",
|
||||
"siarie",
|
||||
"ericfennis"
|
||||
"ericfennis",
|
||||
"jguddas"
|
||||
],
|
||||
"tags": [
|
||||
"save",
|
||||
"favorite",
|
||||
"mark",
|
||||
"label",
|
||||
"attachment",
|
||||
"file",
|
||||
"stick",
|
||||
"pin",
|
||||
"read",
|
||||
"clip",
|
||||
"marker",
|
||||
|
||||
@@ -9,5 +9,5 @@
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="m19 21-7-4-7 4V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v16z" />
|
||||
<path d="M17 3a2 2 0 0 1 2 2v15a1 1 0 0 1-1.496.868l-4.512-2.578a2 2 0 0 0-1.984 0l-4.512 2.578A1 1 0 0 1 5 20V5a2 2 0 0 1 2-2z" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 275 B After Width: | Height: | Size: 342 B |
@@ -9,7 +9,7 @@
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M10.94 5.274A7 7 0 0 1 15.71 10h1.79a4.5 4.5 0 0 1 4.222 6.057" />
|
||||
<path d="M18.796 18.81A4.5 4.5 0 0 1 17.5 19H9A7 7 0 0 1 5.79 5.78" />
|
||||
<path d="m2 2 20 20" />
|
||||
<path d="M5.782 5.782A7 7 0 0 0 9 19h8.5a4.5 4.5 0 0 0 1.307-.193" />
|
||||
<path d="M21.532 16.5A4.5 4.5 0 0 0 17.5 10h-1.79A7.008 7.008 0 0 0 10 5.07" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 388 B After Width: | Height: | Size: 385 B |
@@ -2,7 +2,8 @@
|
||||
"$schema": "../icon.schema.json",
|
||||
"contributors": [
|
||||
"ericfennis",
|
||||
"karsa-mistmere"
|
||||
"karsa-mistmere",
|
||||
"jguddas"
|
||||
],
|
||||
"tags": [
|
||||
"weather",
|
||||
|
||||
@@ -9,6 +9,6 @@
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M17.5 21H9a7 7 0 1 1 6.71-9h1.79a4.5 4.5 0 1 1 0 9Z" />
|
||||
<path d="M22 10a3 3 0 0 0-3-3h-2.207a5.502 5.502 0 0 0-10.702.5" />
|
||||
<path d="M17.5 12a1 1 0 1 1 0 9H9.006a7 7 0 1 1 6.702-9z" />
|
||||
<path d="M21.832 9A3 3 0 0 0 19 7h-2.207a5.5 5.5 0 0 0-10.72.61" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 345 B After Width: | Height: | Size: 341 B |
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"$schema": "../icon.schema.json",
|
||||
"contributors": [
|
||||
"karsa-mistmere",
|
||||
"Muhammad-Aqib-Bashir"
|
||||
],
|
||||
"tags": [
|
||||
"globe",
|
||||
"internet",
|
||||
"offline",
|
||||
"disconnected",
|
||||
"network",
|
||||
"connection",
|
||||
"world",
|
||||
"no connection",
|
||||
"network failure",
|
||||
"signal off"
|
||||
],
|
||||
"categories": [
|
||||
"connectivity",
|
||||
"devices",
|
||||
"navigation"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<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"
|
||||
>
|
||||
<path d="m16 3 5 5" />
|
||||
<path d="M2 12h20A10 10 0 1 1 12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 4-10" />
|
||||
<path d="m21 3-5 5" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 341 B |
@@ -9,7 +9,7 @@
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M3.704 14.467A10 8 0 0 1 2 10a10 8 0 0 1 20 0 10 8 0 0 1-10 8 10 8 0 0 1-5.181-1.158" />
|
||||
<path d="M3.704 14.467a10 8 0 1 1 3.115 2.375" />
|
||||
<path d="M7 22a5 5 0 0 1-2-3.994" />
|
||||
<circle cx="5" cy="16" r="2" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 381 B After Width: | Height: | Size: 333 B |
@@ -9,17 +9,14 @@
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M12 19h.01" />
|
||||
<path d="M12 3h.01" />
|
||||
<path d="M16 19h.01" />
|
||||
<path d="M16 3h.01" />
|
||||
<path d="M2 13h.01" />
|
||||
<path d="M2 17v4.286a.71.71 0 0 0 1.212.502l2.202-2.202A2 2 0 0 1 6.828 19H8" />
|
||||
<path d="M2 5a2 2 0 0 1 2-2" />
|
||||
<path d="M2 9h.01" />
|
||||
<path d="M20 3a2 2 0 0 1 2 2" />
|
||||
<path d="M22 13h.01" />
|
||||
<path d="M22 17a2 2 0 0 1-2 2" />
|
||||
<path d="M22 9h.01" />
|
||||
<path d="M8 3h.01" />
|
||||
<path d="M14 3h2" />
|
||||
<path d="M16 19h-2" />
|
||||
<path d="M2 12v-2" />
|
||||
<path d="M2 16v5.286a.71.71 0 0 0 1.212.502l1.149-1.149" />
|
||||
<path d="M20 19a2 2 0 0 0 2-2v-1" />
|
||||
<path d="M22 10v2" />
|
||||
<path d="M22 6V5a2 2 0 0 0-2-2" />
|
||||
<path d="M4 3a2 2 0 0 0-2 2v1" />
|
||||
<path d="M8 19h2" />
|
||||
<path d="M8 3h2" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 622 B After Width: | Height: | Size: 523 B |
@@ -9,9 +9,9 @@
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M17 17H4a2 2 0 0 1-2-2V5c0-1.5 1-2 1-2" />
|
||||
<path d="M22 15V5a2 2 0 0 0-2-2H9" />
|
||||
<path d="M8 21h8" />
|
||||
<path d="M12 17v4" />
|
||||
<path d="M17 17H4a2 2 0 0 1-2-2V5a2 2 0 0 1 1.184-1.826" />
|
||||
<path d="m2 2 20 20" />
|
||||
<path d="M8 21h8" />
|
||||
<path d="M8.656 3H20a2 2 0 0 1 2 2v10a2 2 0 0 1-.293 1.042" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 375 B After Width: | Height: | Size: 408 B |
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"$schema": "../icon.schema.json",
|
||||
"contributors": [
|
||||
"colebemis",
|
||||
"csandman",
|
||||
"ericfennis",
|
||||
"jguddas",
|
||||
"lt25106"
|
||||
],
|
||||
"tags": [
|
||||
"fax",
|
||||
"office",
|
||||
"device",
|
||||
"cross",
|
||||
"cancel",
|
||||
"remove",
|
||||
"error"
|
||||
],
|
||||
"categories": [
|
||||
"devices"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<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"
|
||||
>
|
||||
<path d="M12.531 22H7a1 1 0 0 1-1-1v-6a1 1 0 0 1 1-1h6.377" />
|
||||
<path d="m16.5 16.5 5 5" />
|
||||
<path d="m16.5 21.5 5-5" />
|
||||
<path d="M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v1.5" />
|
||||
<path d="M6 9V3a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v6" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 463 B |
@@ -9,7 +9,7 @@
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M8.34 8.34 2 9.27l5 4.87L5.82 21 12 17.77 18.18 21l-.59-3.43" />
|
||||
<path d="M18.42 12.76 22 9.27l-6.91-1L12 2l-1.44 2.91" />
|
||||
<line x1="2" x2="22" y1="2" y2="22" />
|
||||
<path d="m10.344 4.688 1.181-2.393a.53.53 0 0 1 .95 0l2.31 4.679a2.12 2.12 0 0 0 1.595 1.16l5.166.756a.53.53 0 0 1 .294.904l-3.237 3.152" />
|
||||
<path d="m17.945 17.945.43 2.505a.53.53 0 0 1-.771.56l-4.618-2.428a2.12 2.12 0 0 0-1.973 0L6.396 21.01a.53.53 0 0 1-.77-.56l.881-5.139a2.12 2.12 0 0 0-.611-1.879L2.16 9.795a.53.53 0 0 1 .294-.906l5.165-.755a8 8 0 0 0 .4-.099" />
|
||||
<path d="m2 2 20 20" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 385 B After Width: | Height: | Size: 608 B |
@@ -10,7 +10,7 @@
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M10.5 17h1.227a2 2 0 0 0 1.345-.52L18 12" />
|
||||
<path d="m12 13.5 3.75.5" />
|
||||
<path d="m12 13.5 3.794.506" />
|
||||
<path d="m3.173 8.18 11-5a2 2 0 0 1 2.647.993L18.56 8" />
|
||||
<path d="M6 10V8" />
|
||||
<path d="M6 14v1" />
|
||||
|
||||
|
Before Width: | Height: | Size: 477 B After Width: | Height: | Size: 480 B |
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"$schema": "../icon.schema.json",
|
||||
"contributors": [
|
||||
"danielbayley"
|
||||
"danielbayley",
|
||||
"karsa-mistmere"
|
||||
],
|
||||
"tags": [
|
||||
"indirection",
|
||||
|
||||
@@ -9,11 +9,11 @@
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<circle cx="12" cy="4.5" r="2.5" />
|
||||
<path d="m10.2 6.3-3.9 3.9" />
|
||||
<circle cx="4.5" cy="12" r="2.5" />
|
||||
<path d="M7 12h10" />
|
||||
<circle cx="19.5" cy="12" r="2.5" />
|
||||
<path d="m13.8 17.7 3.9-3.9" />
|
||||
<circle cx="12" cy="19.5" r="2.5" />
|
||||
<path d="m10.586 5.414-5.172 5.172" />
|
||||
<path d="m18.586 13.414-5.172 5.172" />
|
||||
<path d="M6 12h12" />
|
||||
<circle cx="12" cy="20" r="2" />
|
||||
<circle cx="12" cy="4" r="2" />
|
||||
<circle cx="20" cy="12" r="2" />
|
||||
<circle cx="4" cy="12" r="2" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 453 B After Width: | Height: | Size: 453 B |
@@ -21,6 +21,7 @@
|
||||
"addjsons": "node ./scripts/addMissingIconJsonFiles.mts",
|
||||
"checkIcons": "node ./scripts/checkIconsAndCategories.mts",
|
||||
"generate:changelog": "node ./scripts/generateChangelog.mts",
|
||||
"generate:backers": "node scripts/updateOpenCollectiveBackers.mts",
|
||||
"generate:contributors": "node ./scripts/updateContributors.mts icons/*.svg",
|
||||
"generate:nextJSAliases": "node ./scripts/generateNextJSAliases.mts",
|
||||
"suggest:tags": "node ./scripts/suggestTags.mts",
|
||||
|
||||
@@ -68,7 +68,16 @@ Lucide is licensed under the ISC license. See [LICENSE](https://lucide.dev/licen
|
||||
|
||||
<a href="https://www.digitalocean.com/?refcode=b0877a2caebd&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge"><img src="https://lucide.dev/digitalocean.svg" width="200" alt="DigitalOcean Referral Badge" /></a>
|
||||
|
||||
[//]: <> (Open Collective backers)
|
||||
### Awesome backers 🍺
|
||||
|
||||
<a href="https://www.scipress.io?utm_source=lucide"><img src="https://lucide.dev/sponsors/scipress.svg" width="180" alt="Scipress sponsor badge" /></a>
|
||||
<a href="https://github.com/pdfme/pdfme"><img src="https://lucide.dev/sponsors/pdfme.svg" width="180" alt="pdfme sponsor badge" /></a>
|
||||
<a href="https://github.com/pdfme/pdfme"><img src="https://lucide.dev/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="https://lucide.dev/sponsors/paxhistoria.svg?" width="180" alt="Pax Historia – An alternate history sandbox game" /></a>
|
||||
|
||||
### Backers ☕
|
||||
|
||||
<a href="https://www.fina.money/"><img src="https://lucide.dev/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).
|
||||
|
||||
@@ -39,18 +39,20 @@
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"build": "pnpm clean && pnpm copy:license && pnpm build:icons",
|
||||
"build": "pnpm clean && pnpm copy:license && pnpm copy:utils && pnpm build:icons",
|
||||
"build:icons": "build-icons --output=./src --templateSrc=./scripts/exportTemplate.mts --renderUniqueKey --withAliases --aliasesFileExtension=.ts --iconFileExtension=.ts --exportFileName=index.ts --pretty=false",
|
||||
"clean": "rm -rf dist && rm -rf stats && rm -rf ./src/icons/*.ts",
|
||||
"copy:license": "cp ../../LICENSE ./LICENSE",
|
||||
"test": "pnpm build:icons && vitest run",
|
||||
"copy:utils": "mkdir -p ./src/utils && for f in hasA11yProp toKebabCase mergeClasses; do cp -f ../../packages/shared/src/utils/$f.ts ./src/utils/; done",
|
||||
"test": "pnpm copy:utils && pnpm build:icons && vitest run",
|
||||
"test:watch": "pnpm build:icons && vitest run --watch",
|
||||
"version": "pnpm version --git-tag-version=false"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@astrojs/ts-plugin": "^1.10.4",
|
||||
"@lucide/build-icons": "workspace:*",
|
||||
"@testing-library/dom": "^10.4.0",
|
||||
"@testing-library/jest-dom": "^6.6.3",
|
||||
"@testing-library/jest-dom": "^6.8.0",
|
||||
"jest-serializer-html": "^7.1.0",
|
||||
"linkedom": "^0.18.5",
|
||||
"prettier": "^3.4.2",
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
---
|
||||
import defaultAttributes from './defaultAttributes';
|
||||
import type { IconProps as Props } from './types';
|
||||
import { hasA11yProp } from './utils/hasA11yProp';
|
||||
|
||||
|
||||
const {
|
||||
color = 'currentColor',
|
||||
@@ -20,6 +22,7 @@ const {
|
||||
height: size,
|
||||
stroke: color,
|
||||
'stroke-width': absoluteStrokeWidth ? (Number(strokeWidth) * 24) / Number(size) : strokeWidth,
|
||||
...(!hasA11yProp(rest) && { 'aria-hidden': 'true' }),
|
||||
...rest,
|
||||
}}
|
||||
class:list={['lucide', className]}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { mergeClasses, toKebabCase } from './utils';
|
||||
import type { AstroComponentFactory } from 'astro/runtime/server/render/astro/factory.js';
|
||||
import type { IconNode } from './types';
|
||||
import { render, renderSlot, createComponent, renderComponent } from 'astro/compiler-runtime';
|
||||
import Icon from './Icon.astro';
|
||||
import { mergeClasses } from './utils/mergeClasses';
|
||||
import { toKebabCase } from './utils/toKebabCase';
|
||||
|
||||
export default (iconName: string, iconNode: IconNode): AstroComponentFactory => {
|
||||
const Component = createComponent(
|
||||
|
||||
@@ -10,6 +10,7 @@ export interface IconProps extends SVGAttributes {
|
||||
absoluteStrokeWidth?: boolean;
|
||||
class?: string;
|
||||
iconNode?: IconNode;
|
||||
title?: string;
|
||||
}
|
||||
|
||||
export type SVGAttributes = HTMLAttributes<'svg'>;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { airVent } from './testIconNodes';
|
||||
import { render } from './utils';
|
||||
import { Icon } from '../src/lucide-astro';
|
||||
import { createAstroHTMLString, render } from './utils';
|
||||
import { Icon, Rocket } from '../src/lucide-astro';
|
||||
|
||||
describe('Using Icon Component', async () => {
|
||||
const { container } = await render(Icon, {
|
||||
@@ -16,3 +16,44 @@ describe('Using Icon Component', async () => {
|
||||
expect(container.innerHTML).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Icon Component Accessibility', () => {
|
||||
it('should have aria-hidden prop when no aria prop is present', async () => {
|
||||
const { container } = await render(Icon, {
|
||||
props: {
|
||||
iconNode: airVent,
|
||||
size: 48,
|
||||
stroke: 'red',
|
||||
absoluteStrokeWidth: true,
|
||||
},
|
||||
});
|
||||
|
||||
expect(container.firstChild).toHaveAttribute('aria-hidden', 'true');
|
||||
});
|
||||
|
||||
it('should not have aria-hidden prop when aria prop is present', async () => {
|
||||
const { container } = await render(Rocket, {
|
||||
props: {
|
||||
size: 48,
|
||||
stroke: 'red',
|
||||
absoluteStrokeWidth: true,
|
||||
'aria-label': 'Release icon',
|
||||
},
|
||||
});
|
||||
|
||||
expect(container.firstChild).not.toHaveAttribute('aria-hidden');
|
||||
});
|
||||
|
||||
it('should not have aria-hidden prop when title prop is present', async () => {
|
||||
const { container } = await render(Rocket, {
|
||||
props: {
|
||||
size: 48,
|
||||
stroke: 'red',
|
||||
absoluteStrokeWidth: true,
|
||||
title: 'Release icon',
|
||||
},
|
||||
});
|
||||
|
||||
expect(container.firstChild).not.toHaveAttribute('aria-hidden');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -10,6 +10,7 @@ exports[`Using Icon Component > should render icon and match snapshot 1`] = `
|
||||
stroke-width="1"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
aria-hidden="true"
|
||||
class="lucide"
|
||||
>
|
||||
<path d="M6 12H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2">
|
||||
|
||||
@@ -10,6 +10,7 @@ exports[`Using createLucideIcon > should create a component from an iconNode 1`]
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
aria-hidden="true"
|
||||
class="lucide lucide-air-vent"
|
||||
>
|
||||
<path d="M6 12H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2">
|
||||
|
||||
@@ -10,6 +10,7 @@ exports[`Using lucide icon components > should add a non-default attribute to th
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
aria-hidden="true"
|
||||
style="position: absolute"
|
||||
class="lucide lucide-smile"
|
||||
>
|
||||
@@ -45,6 +46,7 @@ exports[`Using lucide icon components > should adjust the size, stroke color and
|
||||
stroke-width="4"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
aria-hidden="true"
|
||||
class="lucide lucide-grid-3x3"
|
||||
>
|
||||
<rect width="18"
|
||||
@@ -75,6 +77,7 @@ exports[`Using lucide icon components > should apply all classes to the element
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
aria-hidden="true"
|
||||
class="lucide lucide-droplet my-icon"
|
||||
>
|
||||
<path d="M12 22a7 7 0 0 0 7-7c0-2-1-3.9-3-5.5s-3.5-4-4-6.5c-.5 2.5-2 4.9-4 6.5C6 11.1 5 13 5 15a7 7 0 0 0 7 7z">
|
||||
@@ -92,6 +95,7 @@ exports[`Using lucide icon components > should not scale the strokeWidth when ab
|
||||
stroke-width="1"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
aria-hidden="true"
|
||||
class="lucide lucide-grid-3x3"
|
||||
>
|
||||
<rect width="18"
|
||||
@@ -122,6 +126,7 @@ exports[`Using lucide icon components > should pass children to the icon slot 1`
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
aria-hidden="true"
|
||||
class="lucide lucide-smile"
|
||||
>
|
||||
<circle cx="12"
|
||||
@@ -159,6 +164,7 @@ exports[`Using lucide icon components > should render a component 1`] = `
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
aria-hidden="true"
|
||||
class="lucide lucide-grid-3x3"
|
||||
>
|
||||
<rect width="18"
|
||||
@@ -189,6 +195,7 @@ exports[`Using lucide icon components > should render the icon with default attr
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
aria-hidden="true"
|
||||
class="lucide lucide-grid-3x3"
|
||||
>
|
||||
<rect width="18"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { Pen, Edit2, Grid, Droplet, Smile } from '../src/lucide-astro';
|
||||
import { Pen, Edit2, Grid, Droplet, Smile, Rocket } from '../src/lucide-astro';
|
||||
import defaultAttributes from '../src/defaultAttributes';
|
||||
import { createAstroHTMLString, render } from './utils';
|
||||
|
||||
|
||||
@@ -67,7 +67,16 @@ Lucide is licensed under the ISC license. See [LICENSE](https://lucide.dev/licen
|
||||
|
||||
<a href="https://www.digitalocean.com/?refcode=b0877a2caebd&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge"><img src="https://lucide.dev/digitalocean.svg" width="200" alt="DigitalOcean Referral Badge" /></a>
|
||||
|
||||
[//]: <> (Open Collective backers)
|
||||
### Awesome backers 🍺
|
||||
|
||||
<a href="https://www.scipress.io?utm_source=lucide"><img src="https://lucide.dev/sponsors/scipress.svg" width="180" alt="Scipress sponsor badge" /></a>
|
||||
<a href="https://github.com/pdfme/pdfme"><img src="https://lucide.dev/sponsors/pdfme.svg" width="180" alt="pdfme sponsor badge" /></a>
|
||||
<a href="https://github.com/pdfme/pdfme"><img src="https://lucide.dev/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="https://lucide.dev/sponsors/paxhistoria.svg?" width="180" alt="Pax Historia – An alternate history sandbox game" /></a>
|
||||
|
||||
### Backers ☕
|
||||
|
||||
<a href="https://www.fina.money/"><img src="https://lucide.dev/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).
|
||||
|
||||
@@ -26,12 +26,13 @@
|
||||
"Font Awesome"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "pnpm clean && pnpm copy:license && pnpm build:icons && pnpm build:ng",
|
||||
"build": "pnpm clean && pnpm copy:license && pnpm copy:utils &&pnpm build:icons && pnpm build:ng",
|
||||
"copy:license": "cp ../../LICENSE ./LICENSE",
|
||||
"copy:utils": "mkdir -p ./src/utils && cp -rf ../../packages/shared/src/utils/hasA11yProp.ts ./src/utils/",
|
||||
"clean": "rm -rf dist && rm -rf ./src/icons/*.ts",
|
||||
"build:icons": "build-icons --output=./src --templateSrc=./scripts/exportTemplate.mts --renderUniqueKey --withAliases --aliasesFileExtension=.ts --iconFileExtension=.ts --exportFileName=lucide-icons.ts",
|
||||
"build:ng": "ng build --configuration production",
|
||||
"test": "ng test --no-watch --no-progress --browsers=ChromeHeadlessCI",
|
||||
"test": "pnpm copy:utils && ng test --no-watch --no-progress --browsers=ChromeHeadlessCI",
|
||||
"test:watch": "ng test",
|
||||
"lint": "npx eslint 'src/**/*.{js,jsx,ts,tsx,html,css,scss}' --quiet --fix",
|
||||
"e2e": "ng e2e",
|
||||
|
||||
@@ -8,8 +8,10 @@ import { LucideIcons } from '../icons/types';
|
||||
describe('LucideAngularComponent', () => {
|
||||
let testHostComponent: TestHostComponent;
|
||||
let testHostFixture: ComponentFixture<TestHostComponent>;
|
||||
const getSvgAttribute = (attr: string) =>
|
||||
const getAttribute = (attr: string) =>
|
||||
testHostFixture.nativeElement.querySelector('svg').getAttribute(attr);
|
||||
const getRootAttribute = (attr: string) =>
|
||||
testHostFixture.nativeElement.querySelector('i-lucide').getAttribute(attr);
|
||||
const testIcons: LucideIcons = {
|
||||
Demo: [['polyline', { points: '1 1 22 22' }]],
|
||||
};
|
||||
@@ -31,28 +33,28 @@ describe('LucideAngularComponent', () => {
|
||||
it('should add all classes', () => {
|
||||
testHostFixture.detectChanges();
|
||||
|
||||
expect(getSvgAttribute('class')).toBe('lucide lucide-demo my-icon');
|
||||
expect(getAttribute('class')).toBe('lucide lucide-demo my-icon');
|
||||
});
|
||||
|
||||
it('should set color', () => {
|
||||
const color = 'red';
|
||||
testHostComponent.setColor(color);
|
||||
testHostFixture.detectChanges();
|
||||
expect(getSvgAttribute('stroke')).toBe(color);
|
||||
expect(getAttribute('stroke')).toBe(color);
|
||||
});
|
||||
|
||||
it('should set size', () => {
|
||||
const size = 12;
|
||||
testHostComponent.setSize(size);
|
||||
testHostFixture.detectChanges();
|
||||
expect(getSvgAttribute('width')).toBe(size.toString(10));
|
||||
expect(getAttribute('width')).toBe(size.toString(10));
|
||||
});
|
||||
|
||||
it('should set stroke width', () => {
|
||||
const strokeWidth = 1.41;
|
||||
testHostComponent.setStrokeWidth(strokeWidth);
|
||||
testHostFixture.detectChanges();
|
||||
expect(getSvgAttribute('stroke-width')).toBe(strokeWidth.toString(10));
|
||||
expect(getAttribute('stroke-width')).toBe(strokeWidth.toString(10));
|
||||
});
|
||||
|
||||
it('should adjust stroke width', () => {
|
||||
@@ -62,27 +64,61 @@ describe('LucideAngularComponent', () => {
|
||||
testHostComponent.setSize(12);
|
||||
testHostComponent.setAbsoluteStrokeWidth(true);
|
||||
testHostFixture.detectChanges();
|
||||
expect(getSvgAttribute('stroke-width')).toBe(
|
||||
expect(getAttribute('stroke-width')).toBe(
|
||||
formatFixed(strokeWidth / (size / defaultAttributes.height)),
|
||||
);
|
||||
});
|
||||
|
||||
it('should have aria-hidden prop when no aria prop is present', async () => {
|
||||
testHostFixture.detectChanges();
|
||||
expect(getRootAttribute('aria-hidden')).toBe('true');
|
||||
});
|
||||
|
||||
it('should not have aria-hidden prop when aria prop is present', async () => {
|
||||
const ariaLabel = 'Demo icon';
|
||||
testHostComponent.setAriaLabel(ariaLabel);
|
||||
testHostFixture.detectChanges();
|
||||
expect(getRootAttribute('aria-label')).toBe(ariaLabel);
|
||||
expect(getRootAttribute('aria-hidden')).toBeNull();
|
||||
});
|
||||
|
||||
it('should not have aria-hidden prop when title prop is present', async () => {
|
||||
const ariaLabel = 'Demo icon';
|
||||
testHostComponent.setTitle(ariaLabel);
|
||||
testHostFixture.detectChanges();
|
||||
expect(getRootAttribute('title')).toBe(ariaLabel);
|
||||
expect(getRootAttribute('aria-hidden')).toBeNull();
|
||||
});
|
||||
|
||||
it('should never override aria-hidden prop', async () => {
|
||||
testHostComponent.setAriaHidden(true);
|
||||
testHostFixture.detectChanges();
|
||||
expect(getRootAttribute('aria-hidden')).toBe('true');
|
||||
});
|
||||
|
||||
@Component({
|
||||
selector: 'lucide-spec-host-component',
|
||||
template: ` <i-lucide
|
||||
template: `<i-lucide
|
||||
name="demo"
|
||||
class="my-icon"
|
||||
[color]="color"
|
||||
[size]="size"
|
||||
[strokeWidth]="strokeWidth"
|
||||
[absoluteStrokeWidth]="absoluteStrokeWidth"
|
||||
></i-lucide>`,
|
||||
[attr.aria-label]="ariaLabel"
|
||||
[attr.title]="title"
|
||||
[attr.aria-hidden]="ariaHidden"
|
||||
>
|
||||
</i-lucide>`,
|
||||
})
|
||||
class TestHostComponent {
|
||||
color?: string;
|
||||
size?: number;
|
||||
strokeWidth?: number;
|
||||
absoluteStrokeWidth = true;
|
||||
ariaLabel?: string = undefined;
|
||||
title?: string = undefined;
|
||||
ariaHidden?: boolean = undefined;
|
||||
|
||||
setColor(color: string): void {
|
||||
this.color = color;
|
||||
@@ -99,5 +135,17 @@ describe('LucideAngularComponent', () => {
|
||||
setAbsoluteStrokeWidth(absoluteStrokeWidth: boolean): void {
|
||||
this.absoluteStrokeWidth = absoluteStrokeWidth;
|
||||
}
|
||||
|
||||
setAriaLabel(label: string): void {
|
||||
this.ariaLabel = label;
|
||||
}
|
||||
|
||||
setTitle(title: string): void {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
setAriaHidden(ariaHidden: boolean): void {
|
||||
this.ariaHidden = ariaHidden;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -12,6 +12,7 @@ import { LucideIconData } from '../icons/types';
|
||||
import defaultAttributes from '../icons/constants/default-attributes';
|
||||
import { LUCIDE_ICONS, LucideIconProviderInterface } from './lucide-icon.provider';
|
||||
import { LucideIconConfig } from './lucide-icon.config';
|
||||
import { hasA11yProp } from '../utils/hasA11yProp';
|
||||
|
||||
interface TypedChange<T> extends SimpleChange {
|
||||
previousValue: T;
|
||||
@@ -99,6 +100,12 @@ export class LucideAngularComponent implements OnChanges {
|
||||
this.strokeWidth = this.parseNumber(this.strokeWidth ?? this.iconConfig.strokeWidth);
|
||||
this.absoluteStrokeWidth = this.absoluteStrokeWidth ?? this.iconConfig.absoluteStrokeWidth;
|
||||
const nameOrIcon = this.img ?? this.name;
|
||||
const restAttributes = this.getRestAttributes();
|
||||
|
||||
if (!hasA11yProp(restAttributes)) {
|
||||
this.renderer.setAttribute(this.elem.nativeElement, 'aria-hidden', 'true');
|
||||
}
|
||||
|
||||
if (typeof nameOrIcon === 'string') {
|
||||
const icoOfName = this.getIcon(this.toPascalCase(nameOrIcon));
|
||||
if (icoOfName) {
|
||||
@@ -119,6 +126,8 @@ export class LucideAngularComponent implements OnChanges {
|
||||
}
|
||||
|
||||
replaceElement(img: LucideIconData): void {
|
||||
const childElements = this.elem.nativeElement.childNodes;
|
||||
|
||||
const attributes = {
|
||||
...defaultAttributes,
|
||||
width: this.size,
|
||||
@@ -133,6 +142,7 @@ export class LucideAngularComponent implements OnChanges {
|
||||
if (typeof this.name === 'string') {
|
||||
icoElement.classList.add(`lucide-${this.name.replace('_', '-')}`);
|
||||
}
|
||||
|
||||
if (this.class) {
|
||||
icoElement.classList.add(
|
||||
...this.class
|
||||
@@ -141,13 +151,21 @@ export class LucideAngularComponent implements OnChanges {
|
||||
.filter((a) => a.length > 0),
|
||||
);
|
||||
}
|
||||
const childElements = this.elem.nativeElement.childNodes;
|
||||
|
||||
for (const child of childElements) {
|
||||
this.renderer.removeChild(this.elem.nativeElement, child);
|
||||
}
|
||||
this.renderer.appendChild(this.elem.nativeElement, icoElement);
|
||||
}
|
||||
|
||||
getRestAttributes(): Record<string, string> {
|
||||
const restAttributeMap: NamedNodeMap = this.elem.nativeElement.attributes;
|
||||
const restAttributes = Object.fromEntries(
|
||||
Array.from(restAttributeMap).map((item) => [item.name, item.value]),
|
||||
);
|
||||
return restAttributes;
|
||||
}
|
||||
|
||||
toPascalCase(str: string): string {
|
||||
return str.replace(
|
||||
/(\w)([a-z0-9]*)(_|-|\s*)/g,
|
||||
|
||||
@@ -67,7 +67,16 @@ Lucide is licensed under the ISC license. See [LICENSE](https://lucide.dev/licen
|
||||
|
||||
<a href="https://www.digitalocean.com/?refcode=b0877a2caebd&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge"><img src="https://lucide.dev/digitalocean.svg" width="200" alt="DigitalOcean Referral Badge" /></a>
|
||||
|
||||
[//]: <> (Open Collective backers)
|
||||
### Awesome backers 🍺
|
||||
|
||||
<a href="https://www.scipress.io?utm_source=lucide"><img src="https://lucide.dev/sponsors/scipress.svg" width="180" alt="Scipress sponsor badge" /></a>
|
||||
<a href="https://github.com/pdfme/pdfme"><img src="https://lucide.dev/sponsors/pdfme.svg" width="180" alt="pdfme sponsor badge" /></a>
|
||||
<a href="https://github.com/pdfme/pdfme"><img src="https://lucide.dev/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="https://lucide.dev/sponsors/paxhistoria.svg?" width="180" alt="Pax Historia – An alternate history sandbox game" /></a>
|
||||
|
||||
### Backers ☕
|
||||
|
||||
<a href="https://www.fina.money/"><img src="https://lucide.dev/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).
|
||||
|
||||
@@ -46,10 +46,10 @@
|
||||
"@lucide/rollup-plugins": "workspace:*",
|
||||
"@lucide/shared": "workspace:*",
|
||||
"@preact/preset-vite": "^2.10.2",
|
||||
"@testing-library/jest-dom": "^6.1.4",
|
||||
"@testing-library/jest-dom": "^6.8.0",
|
||||
"@testing-library/preact": "^3.2.3",
|
||||
"jest-serializer-html": "^7.1.0",
|
||||
"preact": "^10.19.2",
|
||||
"preact": "^10.27.3",
|
||||
"rollup": "^4.53.3",
|
||||
"rollup-plugin-dts": "^6.2.3",
|
||||
"typescript": "^5.8.3",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { h, toChildArray } from 'preact';
|
||||
import defaultAttributes from './defaultAttributes';
|
||||
import type { IconNode, LucideProps } from './types';
|
||||
import { hasA11yProp } from '@lucide/shared';
|
||||
|
||||
interface IconComponentProps extends LucideProps {
|
||||
iconNode: IconNode;
|
||||
@@ -42,6 +43,7 @@ const Icon = ({
|
||||
? (Number(strokeWidth) * 24) / Number(size)
|
||||
: strokeWidth,
|
||||
class: ['lucide', classes].join(' '),
|
||||
...(!children && !hasA11yProp(rest) && { 'aria-hidden': 'true' }),
|
||||
...rest,
|
||||
},
|
||||
[...iconNode.map(([tag, attrs]) => h(tag, attrs)), ...toChildArray(children)],
|
||||
|
||||
@@ -31,3 +31,62 @@ describe('Using Icon Component', () => {
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Icon Component Accessibility', () => {
|
||||
it('should not have aria-hidden prop when aria prop is present', async () => {
|
||||
const { container } = render(
|
||||
<Icon
|
||||
iconNode={airVent}
|
||||
size={48}
|
||||
stroke="red"
|
||||
absoluteStrokeWidth
|
||||
aria-label="Air conditioning"
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(container.firstChild).not.toHaveAttribute('aria-hidden');
|
||||
});
|
||||
|
||||
it('should not have aria-hidden prop when title prop is present', async () => {
|
||||
const { container } = render(
|
||||
<Icon
|
||||
iconNode={airVent}
|
||||
size={48}
|
||||
stroke="red"
|
||||
absoluteStrokeWidth
|
||||
title="Air conditioning"
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(container.firstChild).not.toHaveAttribute('aria-hidden');
|
||||
});
|
||||
|
||||
it('should not have aria-hidden prop when there are children that could be a <title> element', async () => {
|
||||
const { container } = render(
|
||||
<Icon
|
||||
iconNode={airVent}
|
||||
size={48}
|
||||
stroke="red"
|
||||
absoluteStrokeWidth
|
||||
>
|
||||
<title>Some title</title>
|
||||
</Icon>,
|
||||
);
|
||||
|
||||
expect(container.firstChild).not.toHaveAttribute('aria-hidden');
|
||||
});
|
||||
|
||||
it('should never override aria-hidden prop', async () => {
|
||||
const { container } = render(
|
||||
<Icon
|
||||
iconNode={airVent}
|
||||
size={48}
|
||||
stroke="red"
|
||||
absoluteStrokeWidth
|
||||
aria-hidden={false}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(container.firstChild).toHaveAttribute('aria-hidden', 'false');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
exports[`Using Icon Component > should render icon and match snapshot 1`] = `
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="lucide "
|
||||
fill="none"
|
||||
height="48"
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
exports[`Using createLucideIcon > should create a component from an iconNode 1`] = `
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="lucide lucide-air-vent"
|
||||
fill="none"
|
||||
height="24"
|
||||
@@ -30,6 +31,7 @@ exports[`Using createLucideIcon > should create a component from an iconNode 1`]
|
||||
|
||||
exports[`Using createLucideIcon > should create a component from an iconNode with iconName 1`] = `
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="lucide lucide-air-vent"
|
||||
fill="none"
|
||||
height="24"
|
||||
@@ -58,6 +60,7 @@ exports[`Using createLucideIcon > should create a component from an iconNode wit
|
||||
|
||||
exports[`Using createLucideIcon > should include backwards compatible className 1`] = `
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="lucide lucide-layout2 lucide-layout-2"
|
||||
fill="none"
|
||||
height="24"
|
||||
|
||||
@@ -11,6 +11,7 @@ exports[`Using lucide icon components > should adjust the size, stroke color and
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="lucide lucide-grid3x3 lucide-grid-3x3"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<rect width="18"
|
||||
height="18"
|
||||
@@ -41,6 +42,7 @@ exports[`Using lucide icon components > should not scale the strokeWidth when ab
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="lucide lucide-grid3x3 lucide-grid-3x3"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<rect width="18"
|
||||
height="18"
|
||||
@@ -71,6 +73,7 @@ exports[`Using lucide icon components > should render an component 1`] = `
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="lucide lucide-grid3x3 lucide-grid-3x3"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<rect width="18"
|
||||
height="18"
|
||||
|
||||
@@ -67,7 +67,16 @@ Lucide is licensed under the ISC license. See [LICENSE](https://lucide.dev/licen
|
||||
|
||||
<a href="https://www.digitalocean.com/?refcode=b0877a2caebd&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge"><img src="https://lucide.dev/digitalocean.svg" width="200" alt="DigitalOcean Referral Badge" /></a>
|
||||
|
||||
[//]: <> (Open Collective backers)
|
||||
### Awesome backers 🍺
|
||||
|
||||
<a href="https://www.scipress.io?utm_source=lucide"><img src="https://lucide.dev/sponsors/scipress.svg" width="180" alt="Scipress sponsor badge" /></a>
|
||||
<a href="https://github.com/pdfme/pdfme"><img src="https://lucide.dev/sponsors/pdfme.svg" width="180" alt="pdfme sponsor badge" /></a>
|
||||
<a href="https://github.com/pdfme/pdfme"><img src="https://lucide.dev/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="https://lucide.dev/sponsors/paxhistoria.svg?" width="180" alt="Pax Historia – An alternate history sandbox game" /></a>
|
||||
|
||||
### Backers ☕
|
||||
|
||||
<a href="https://www.fina.money/"><img src="https://lucide.dev/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).
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
"@lucide/rollup-plugins": "workspace:*",
|
||||
"@lucide/build-icons": "workspace:*",
|
||||
"@lucide/shared": "workspace:*",
|
||||
"@testing-library/jest-dom": "^6.1.6",
|
||||
"@testing-library/jest-dom": "^6.8.0",
|
||||
"@testing-library/react": "^14.1.2",
|
||||
"@types/prop-types": "^15.7.5",
|
||||
"@types/react": "^18.0.21",
|
||||
|
||||
@@ -67,7 +67,16 @@ Lucide is licensed under the ISC license. See [LICENSE](https://lucide.dev/licen
|
||||
|
||||
<a href="https://www.digitalocean.com/?refcode=b0877a2caebd&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge"><img src="https://lucide.dev/digitalocean.svg" width="200" alt="DigitalOcean Referral Badge" /></a>
|
||||
|
||||
[//]: <> (Open Collective backers)
|
||||
### Awesome backers 🍺
|
||||
|
||||
<a href="https://www.scipress.io?utm_source=lucide"><img src="https://lucide.dev/sponsors/scipress.svg" width="180" alt="Scipress sponsor badge" /></a>
|
||||
<a href="https://github.com/pdfme/pdfme"><img src="https://lucide.dev/sponsors/pdfme.svg" width="180" alt="pdfme sponsor badge" /></a>
|
||||
<a href="https://github.com/pdfme/pdfme"><img src="https://lucide.dev/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="https://lucide.dev/sponsors/paxhistoria.svg?" width="180" alt="Pax Historia – An alternate history sandbox game" /></a>
|
||||
|
||||
### Backers ☕
|
||||
|
||||
<a href="https://www.fina.money/"><img src="https://lucide.dev/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).
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
"@lucide/build-icons": "workspace:*",
|
||||
"@lucide/rollup-plugins": "workspace:*",
|
||||
"@lucide/shared": "workspace:*",
|
||||
"@testing-library/jest-dom": "^6.1.6",
|
||||
"@testing-library/jest-dom": "^6.8.0",
|
||||
"@testing-library/react": "^14.1.2",
|
||||
"@types/react": "^18.2.37",
|
||||
"@vitejs/plugin-react": "^4.4.1",
|
||||
|
||||
@@ -33,6 +33,19 @@ describe('Using Icon Component', () => {
|
||||
});
|
||||
|
||||
describe('Icon Component Accessibility', () => {
|
||||
it('should have aria-hidden prop when no aria prop is present', async () => {
|
||||
const { container } = render(
|
||||
<Icon
|
||||
iconNode={airVent}
|
||||
size={48}
|
||||
stroke="red"
|
||||
absoluteStrokeWidth
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(container.firstChild).toHaveAttribute('aria-hidden', 'true');
|
||||
});
|
||||
|
||||
it('should not have aria-hidden prop when aria prop is present', async () => {
|
||||
const { container } = render(
|
||||
<Icon
|
||||
|
||||
@@ -22,5 +22,5 @@
|
||||
"types": ["@testing-library/jest-dom"],
|
||||
},
|
||||
"exclude": ["**/node_modules"],
|
||||
"include": ["src"],
|
||||
"include": ["src", "tests"],
|
||||
}
|
||||
|
||||
@@ -67,7 +67,16 @@ Lucide is licensed under the ISC license. See [LICENSE](https://lucide.dev/licen
|
||||
|
||||
<a href="https://www.digitalocean.com/?refcode=b0877a2caebd&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge"><img src="https://lucide.dev/digitalocean.svg" width="200" alt="DigitalOcean Referral Badge" /></a>
|
||||
|
||||
[//]: <> (Open Collective backers)
|
||||
### Awesome backers 🍺
|
||||
|
||||
<a href="https://www.scipress.io?utm_source=lucide"><img src="https://lucide.dev/sponsors/scipress.svg" width="180" alt="Scipress sponsor badge" /></a>
|
||||
<a href="https://github.com/pdfme/pdfme"><img src="https://lucide.dev/sponsors/pdfme.svg" width="180" alt="pdfme sponsor badge" /></a>
|
||||
<a href="https://github.com/pdfme/pdfme"><img src="https://lucide.dev/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="https://lucide.dev/sponsors/paxhistoria.svg?" width="180" alt="Pax Historia – An alternate history sandbox game" /></a>
|
||||
|
||||
### Backers ☕
|
||||
|
||||
<a href="https://www.fina.money/"><img src="https://lucide.dev/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).
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
"build:bundle": "rollup -c rollup.config.mjs",
|
||||
"build:icons": "build-icons --output=./src --templateSrc=./scripts/exportTemplate.mts --renderUniqueKey --withAliases --separateAliasesFile --separateAliasesFileIgnore=fingerprint --aliasesFileExtension=.ts --iconFileExtension=.tsx --exportFileName=index.ts",
|
||||
"test": "pnpm build:icons && vitest run",
|
||||
"test:watch": "vitest watch",
|
||||
"version": "pnpm version --git-tag-version=false"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -74,7 +75,7 @@
|
||||
"@lucide/shared": "workspace:*",
|
||||
"@rollup/plugin-babel": "^6.0.4",
|
||||
"@solidjs/testing-library": "^0.8.10",
|
||||
"@testing-library/jest-dom": "^6.4.2",
|
||||
"@testing-library/jest-dom": "^6.8.0",
|
||||
"babel-preset-solid": "^1.8.12",
|
||||
"jest-serializer-html": "^7.1.0",
|
||||
"rollup": "^4.53.3",
|
||||
|
||||
@@ -2,7 +2,7 @@ import { For, splitProps } from 'solid-js';
|
||||
import { Dynamic } from 'solid-js/web';
|
||||
import defaultAttributes from './defaultAttributes';
|
||||
import { IconNode, LucideProps } from './types';
|
||||
import { mergeClasses, toKebabCase, toPascalCase } from '@lucide/shared';
|
||||
import { hasA11yProp, mergeClasses, toKebabCase, toPascalCase } from '@lucide/shared';
|
||||
|
||||
interface IconProps {
|
||||
name?: string;
|
||||
@@ -44,6 +44,7 @@ const Icon = (props: LucideProps & IconProps) => {
|
||||
: []),
|
||||
localProps.class != null ? localProps.class : '',
|
||||
)}
|
||||
aria-hidden={!localProps.children && !hasA11yProp(rest) ? 'true' : undefined}
|
||||
{...rest}
|
||||
>
|
||||
<For each={localProps.iconNode}>
|
||||
|
||||
@@ -31,3 +31,76 @@ describe('Using Icon Component', () => {
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Icon Component Accessibility', () => {
|
||||
it('should have aria-hidden prop when no aria prop is present', async () => {
|
||||
const { container } = render(() => (
|
||||
<Icon
|
||||
iconNode={airVent}
|
||||
size={48}
|
||||
stroke="red"
|
||||
absoluteStrokeWidth
|
||||
/>
|
||||
));
|
||||
|
||||
expect(container.firstChild).toHaveAttribute('aria-hidden', 'true');
|
||||
});
|
||||
|
||||
it('should not have aria-hidden prop when aria prop is present', async () => {
|
||||
const { container } = render(() => (
|
||||
<Icon
|
||||
iconNode={airVent}
|
||||
size={48}
|
||||
stroke="red"
|
||||
absoluteStrokeWidth
|
||||
aria-label="Air conditioning"
|
||||
/>
|
||||
));
|
||||
|
||||
expect(container.firstChild).not.toHaveAttribute('aria-hidden');
|
||||
});
|
||||
|
||||
it('should not have aria-hidden prop when title prop is present', async () => {
|
||||
const { container } = render(() => (
|
||||
<Icon
|
||||
iconNode={airVent}
|
||||
size={48}
|
||||
stroke="red"
|
||||
absoluteStrokeWidth
|
||||
// @ts-expect-error
|
||||
title="Air conditioning"
|
||||
/>
|
||||
));
|
||||
|
||||
expect(container.firstChild).not.toHaveAttribute('aria-hidden');
|
||||
});
|
||||
|
||||
it('should not have aria-hidden prop when there are children that could be a <title> element', async () => {
|
||||
const { container } = render(() => (
|
||||
<Icon
|
||||
iconNode={airVent}
|
||||
size={48}
|
||||
stroke="red"
|
||||
absoluteStrokeWidth
|
||||
>
|
||||
<title>Some title</title>
|
||||
</Icon>
|
||||
));
|
||||
|
||||
expect(container.firstChild).not.toHaveAttribute('aria-hidden');
|
||||
});
|
||||
|
||||
it('should never override aria-hidden prop', async () => {
|
||||
const { container } = render(() => (
|
||||
<Icon
|
||||
iconNode={airVent}
|
||||
size={48}
|
||||
stroke="red"
|
||||
absoluteStrokeWidth
|
||||
aria-hidden={false}
|
||||
/>
|
||||
));
|
||||
|
||||
expect(container.firstChild).toHaveAttribute('aria-hidden', 'false');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
exports[`Using Icon Component > should render icon and match snapshot 1`] = `
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="lucide lucide-icon"
|
||||
fill="none"
|
||||
height="48"
|
||||
|
||||
@@ -11,6 +11,7 @@ exports[`Using lucide icon components > should adjust the size, stroke color and
|
||||
stroke="red"
|
||||
stroke-width="4"
|
||||
class="lucide lucide-icon lucide-grid3x3 lucide-grid-3x3"
|
||||
aria-hidden="true"
|
||||
data-testid="grid-icon"
|
||||
>
|
||||
<rect width="18"
|
||||
@@ -51,6 +52,7 @@ exports[`Using lucide icon components > should not scale the strokeWidth when ab
|
||||
stroke="red"
|
||||
stroke-width="1"
|
||||
class="lucide lucide-icon lucide-grid3x3 lucide-grid-3x3"
|
||||
aria-hidden="true"
|
||||
data-testid="grid-icon"
|
||||
>
|
||||
<rect width="18"
|
||||
@@ -91,6 +93,7 @@ exports[`Using lucide icon components > should render a component 1`] = `
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
class="lucide lucide-icon lucide-grid3x3 lucide-grid-3x3"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<rect width="18"
|
||||
height="18"
|
||||
|
||||
@@ -81,7 +81,16 @@ Lucide is licensed under the ISC license. See [LICENSE](https://lucide.dev/licen
|
||||
|
||||
<a href="https://www.digitalocean.com/?refcode=b0877a2caebd&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge"><img src="https://lucide.dev/digitalocean.svg" width="200" alt="DigitalOcean Referral Badge" /></a>
|
||||
|
||||
[//]: <> (Open Collective backers)
|
||||
### Awesome backers 🍺
|
||||
|
||||
<a href="https://www.scipress.io?utm_source=lucide"><img src="https://lucide.dev/sponsors/scipress.svg" width="180" alt="Scipress sponsor badge" /></a>
|
||||
<a href="https://github.com/pdfme/pdfme"><img src="https://lucide.dev/sponsors/pdfme.svg" width="180" alt="pdfme sponsor badge" /></a>
|
||||
<a href="https://github.com/pdfme/pdfme"><img src="https://lucide.dev/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="https://lucide.dev/sponsors/paxhistoria.svg?" width="180" alt="Pax Historia – An alternate history sandbox game" /></a>
|
||||
|
||||
### Backers ☕
|
||||
|
||||
<a href="https://www.fina.money/"><img src="https://lucide.dev/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).
|
||||
|
||||
@@ -67,7 +67,16 @@ Lucide is licensed under the ISC license. See [LICENSE](https://lucide.dev/licen
|
||||
|
||||
<a href="https://www.digitalocean.com/?refcode=b0877a2caebd&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge"><img src="https://lucide.dev/digitalocean.svg" width="200" alt="DigitalOcean Referral Badge" /></a>
|
||||
|
||||
[//]: <> (Open Collective backers)
|
||||
### Awesome backers 🍺
|
||||
|
||||
<a href="https://www.scipress.io?utm_source=lucide"><img src="https://lucide.dev/sponsors/scipress.svg" width="180" alt="Scipress sponsor badge" /></a>
|
||||
<a href="https://github.com/pdfme/pdfme"><img src="https://lucide.dev/sponsors/pdfme.svg" width="180" alt="pdfme sponsor badge" /></a>
|
||||
<a href="https://github.com/pdfme/pdfme"><img src="https://lucide.dev/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="https://lucide.dev/sponsors/paxhistoria.svg?" width="180" alt="Pax Historia – An alternate history sandbox game" /></a>
|
||||
|
||||
### Backers ☕
|
||||
|
||||
<a href="https://www.fina.money/"><img src="https://lucide.dev/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).
|
||||
|
||||
@@ -46,13 +46,14 @@
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "pnpm clean && pnpm copy:license && pnpm build:icons && pnpm build:package && pnpm build:license",
|
||||
"build": "pnpm clean && pnpm copy:license && pnpm copy:utils && pnpm build:icons && pnpm build:package && pnpm build:license",
|
||||
"copy:license": "cp ../../LICENSE ./LICENSE",
|
||||
"copy:utils": "mkdir -p ./src/utils && for f in hasA11yProp mergeClasses; do cp -f ../../packages/shared/src/utils/$f.ts ./src/utils/; done",
|
||||
"clean": "rm -rf dist && rm -rf stats && rm -rf ./src/icons/*.svelte && rm -f index.js",
|
||||
"build:icons": "build-icons --output=./src --templateSrc=./scripts/exportTemplate.mts --exportFileName=index.ts --iconFileExtension=.svelte --importImportFileExtension=.svelte --separateIconFileExport --separateIconFileExportExtension=.ts --withAliases --aliasesFileExtension=.ts --separateAliasesFile --separateAliasesFileExtension=.ts --aliasImportFileExtension=.js --pretty=false",
|
||||
"build:package": "svelte-package --input ./src",
|
||||
"build:license": "node ./scripts/appendBlockComments.mts",
|
||||
"test": "pnpm copy:license && pnpm build:icons && vitest run",
|
||||
"test": "pnpm copy:license && pnpm copy:utils && pnpm build:icons && vitest run",
|
||||
"test:watch": "vitest watch",
|
||||
"version": "pnpm version --git-tag-version=false"
|
||||
},
|
||||
@@ -61,7 +62,7 @@
|
||||
"@lucide/helpers": "workspace:*",
|
||||
"@sveltejs/package": "^2.2.3",
|
||||
"@sveltejs/vite-plugin-svelte": "^2.4.2",
|
||||
"@testing-library/jest-dom": "^6.1.4",
|
||||
"@testing-library/jest-dom": "^6.8.0",
|
||||
"@testing-library/svelte": "^4.0.2",
|
||||
"@tsconfig/svelte": "^5.0.0",
|
||||
"jest-serializer-html": "^7.1.0",
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<script lang="ts">
|
||||
import defaultAttributes from './defaultAttributes'
|
||||
import type { IconNode } from './types';
|
||||
import { hasA11yProp } from './utils/hasA11yProp';
|
||||
import { mergeClasses } from './utils/mergeClasses';
|
||||
|
||||
export let name: string | undefined = undefined
|
||||
export let color = 'currentColor'
|
||||
@@ -8,18 +10,11 @@
|
||||
export let strokeWidth: number | string = 2
|
||||
export let absoluteStrokeWidth: boolean = false
|
||||
export let iconNode: IconNode = []
|
||||
|
||||
const mergeClasses = <ClassType = string | undefined | null>(
|
||||
...classes: ClassType[]
|
||||
) => classes.filter((className, index, array) => {
|
||||
return Boolean(className) && array.indexOf(className) === index;
|
||||
})
|
||||
.join(' ');
|
||||
|
||||
</script>
|
||||
|
||||
<svg
|
||||
{...defaultAttributes}
|
||||
{...(!hasA11yProp($$restProps) ? {'aria-hidden': 'true'} : undefined)}
|
||||
{...$$restProps}
|
||||
width={size}
|
||||
height={size}
|
||||
|
||||
@@ -31,3 +31,60 @@ describe('Using Icon Component', () => {
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Icon Component Accessibility', () => {
|
||||
it('should have aria-hidden prop when no aria prop is present', async () => {
|
||||
const { container } = render(Icon, {
|
||||
props: {
|
||||
iconNode: airVent,
|
||||
size: 48,
|
||||
color: 'red',
|
||||
absoluteStrokeWidth: true,
|
||||
},
|
||||
});
|
||||
|
||||
expect(container.firstChild).toHaveAttribute('aria-hidden', 'true');
|
||||
});
|
||||
|
||||
it('should not have aria-hidden prop when aria prop is present', async () => {
|
||||
const { container } = render(Icon, {
|
||||
props: {
|
||||
iconNode: airVent,
|
||||
size: 48,
|
||||
color: 'red',
|
||||
absoluteStrokeWidth: true,
|
||||
'aria-label': 'Air conditioning',
|
||||
},
|
||||
});
|
||||
|
||||
expect(container.firstChild).not.toHaveAttribute('aria-hidden');
|
||||
});
|
||||
|
||||
it('should not have aria-hidden prop when title prop is present', async () => {
|
||||
const { container } = render(Icon, {
|
||||
props: {
|
||||
iconNode: airVent,
|
||||
size: 48,
|
||||
color: 'red',
|
||||
absoluteStrokeWidth: true,
|
||||
title: 'Air conditioning',
|
||||
},
|
||||
});
|
||||
|
||||
expect(container.firstChild).not.toHaveAttribute('aria-hidden');
|
||||
});
|
||||
|
||||
it('should never override aria-hidden prop', async () => {
|
||||
const { container } = render(Icon, {
|
||||
props: {
|
||||
iconNode: airVent,
|
||||
size: 48,
|
||||
color: 'red',
|
||||
absoluteStrokeWidth: true,
|
||||
'aria-hidden': 'false',
|
||||
},
|
||||
});
|
||||
|
||||
expect(container.firstChild).toHaveAttribute('aria-hidden', 'false');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
exports[`Using Icon Component > should render icon and match snapshot 1`] = `
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="lucide-icon lucide"
|
||||
fill="none"
|
||||
height="48"
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
exports[`Using lucide icon components > should add a class to the element 1`] = `
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="lucide-icon lucide lucide-smile my-icon"
|
||||
fill="none"
|
||||
height="24"
|
||||
@@ -44,6 +45,7 @@ exports[`Using lucide icon components > should add a class to the element 1`] =
|
||||
exports[`Using lucide icon components > should adjust the size, stroke color and stroke width 1`] = `
|
||||
<div>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="lucide-icon lucide lucide-smile"
|
||||
fill="none"
|
||||
height="48"
|
||||
@@ -94,6 +96,7 @@ exports[`Using lucide icon components > should not scale the strokeWidth when ab
|
||||
stroke-width="1"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
aria-hidden="true"
|
||||
data-testid="smile-icon"
|
||||
class="lucide-icon lucide lucide-smile"
|
||||
>
|
||||
@@ -122,6 +125,7 @@ exports[`Using lucide icon components > should not scale the strokeWidth when ab
|
||||
exports[`Using lucide icon components > should render an component 1`] = `
|
||||
<div>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="lucide-icon lucide lucide-smile"
|
||||
fill="none"
|
||||
height="24"
|
||||
@@ -165,6 +169,7 @@ exports[`Using lucide icon components > should render an component 1`] = `
|
||||
exports[`Using lucide icon components > should render an icon slot 1`] = `
|
||||
<div>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="lucide-icon lucide lucide-smile"
|
||||
fill="none"
|
||||
height="24"
|
||||
|
||||
@@ -67,7 +67,16 @@ Lucide is licensed under the ISC license. See [LICENSE](https://lucide.dev/licen
|
||||
|
||||
<a href="https://www.digitalocean.com/?refcode=b0877a2caebd&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge"><img src="https://lucide.dev/digitalocean.svg" width="200" alt="DigitalOcean Referral Badge" /></a>
|
||||
|
||||
[//]: <> (Open Collective backers)
|
||||
### Awesome backers 🍺
|
||||
|
||||
<a href="https://www.scipress.io?utm_source=lucide"><img src="https://lucide.dev/sponsors/scipress.svg" width="180" alt="Scipress sponsor badge" /></a>
|
||||
<a href="https://github.com/pdfme/pdfme"><img src="https://lucide.dev/sponsors/pdfme.svg" width="180" alt="pdfme sponsor badge" /></a>
|
||||
<a href="https://github.com/pdfme/pdfme"><img src="https://lucide.dev/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="https://lucide.dev/sponsors/paxhistoria.svg?" width="180" alt="Pax Historia – An alternate history sandbox game" /></a>
|
||||
|
||||
### Backers ☕
|
||||
|
||||
<a href="https://www.fina.money/"><img src="https://lucide.dev/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).
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
"@lucide/build-icons": "workspace:*",
|
||||
"@lucide/rollup-plugins": "workspace:*",
|
||||
"@lucide/shared": "workspace:*",
|
||||
"@testing-library/jest-dom": "^6.1.6",
|
||||
"@testing-library/jest-dom": "^6.8.0",
|
||||
"@testing-library/vue": "^8.1.0",
|
||||
"@vitejs/plugin-vue": "^6.0.2",
|
||||
"@vue/test-utils": "2.4.6",
|
||||
@@ -56,7 +56,7 @@
|
||||
"rollup-plugin-dts": "^6.2.3",
|
||||
"vite": "^7.2.4",
|
||||
"vitest": "^4.0.12",
|
||||
"vue": "^3.4.21"
|
||||
"vue": "^3.5.20"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": ">=3.0.1"
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import { type FunctionalComponent, h } from 'vue';
|
||||
import { mergeClasses, toKebabCase, toPascalCase, isEmptyString } from '@lucide/shared';
|
||||
import {
|
||||
mergeClasses,
|
||||
toKebabCase,
|
||||
toPascalCase,
|
||||
isEmptyString,
|
||||
hasA11yProp,
|
||||
} from '@lucide/shared';
|
||||
import defaultAttributes from './defaultAttributes';
|
||||
import { IconNode, LucideProps } from './types';
|
||||
|
||||
@@ -46,6 +52,7 @@ const Icon: FunctionalComponent<LucideProps & IconProps> = (
|
||||
? [`lucide-${toKebabCase(toPascalCase(name))}-icon`, `lucide-${toKebabCase(name)}`]
|
||||
: ['lucide-icon']),
|
||||
),
|
||||
...(!slots.default && !hasA11yProp(props) && { 'aria-hidden': 'true' }),
|
||||
},
|
||||
[...iconNode.map((child) => h(...child)), ...(slots.default ? [slots.default()] : [])],
|
||||
);
|
||||
|
||||