Compare commits

..
228 changed files with 6922 additions and 6764 deletions
@@ -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**.
-16
View File
@@ -41,19 +41,3 @@ 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
-2
View File
@@ -20,8 +20,6 @@ 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
+2 -2
View File
@@ -1,6 +1,6 @@
ISC License
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.
Copyright (c) for portions of Lucide are held by Cole Bemis 2013-2023 as part of Feather (MIT). All other copyright (c) for Lucide are held by Lucide Contributors 2025.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ -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-2026 Cole Bemis
Copyright (c) 2013-2023 Cole Bemis
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+2 -11
View File
@@ -88,16 +88,7 @@ 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 – 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).
<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>
-1
View File
@@ -2,7 +2,6 @@
"adobe": "Adobe",
"airplay": "AirPlay",
"amazon": "Amazon",
"android": "Android",
"angular": "Angular",
"aws": "AWS",
"azure": "Azure",
@@ -22,7 +22,6 @@ 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);
@@ -33,7 +32,6 @@ export default eventHandler((event) => {
height,
color: color ? `#${color}` : undefined,
strokeWidth,
style: background ? { background } : undefined,
}),
),
).toString('utf8');
@@ -0,0 +1,13 @@
import { eventHandler, setResponseHeader } from 'h3';
import releaseMeta from '../../data/releaseMetaData.json';
export default eventHandler((event) => {
setResponseHeader(event, 'Cache-Control', 'public, max-age=86400');
setResponseHeader(event, 'Access-Control-Allow-Origin', '*');
return Object.fromEntries(
Object.entries(releaseMeta).map(
([name, { createdRelease }]) => [name, createdRelease.version]
)
);
});
-3
View File
@@ -1,3 +0,0 @@
export default eventHandler(() => {
return { nitro: 'Is Awesome! asda' };
});
+1 -31
View File
@@ -1,7 +1,6 @@
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';
@@ -10,7 +9,6 @@ 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',
@@ -152,36 +150,8 @@ 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',
@@ -171,24 +171,5 @@
],
"source": "https://github.com/heyvito/lucide-rails",
"documentation": "https://github.com/heyvito/lucide-rails/blob/master/README.md"
},
{
"name": "lucide-web-components",
"description": "Implementation of Lucide icons in Web Components custom elements.",
"icon": "/framework-logos/web-components.svg",
"shields": [
{
"alt": "Latest Stable Version",
"src": "https://img.shields.io/npm/v/@dile/iconlib",
"href": "https://dile-components.com/icons/lucide-icons/"
},
{
"alt": "Total Downloads",
"src": "https://img.shields.io/npm/dt/@dile/iconlib",
"href": "https://dile-components.com/icons/lucide-icons/"
}
],
"source": "https://github.com/Polydile/dile-components",
"documentation": "https://github.com/Polydile/dile-components/blob/master/packages/iconlib/README.md"
}
]
-46
View File
@@ -1,46 +0,0 @@
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,5 +1,5 @@
import { createLucideIcon } from 'lucide-react/src/lucide-react';
import { type LucideProps, type IconNode } from 'lucide-react/src/types';
import { type LucideProps, type IconNode } from 'lucide-react/src/createLucideIcon';
import { IconEntity } from '../theme/types';
import { renderToStaticMarkup } from 'react-dom/server';
import { IconContent } from './generateZip';
@@ -1,31 +1,23 @@
<script setup lang="ts">
import { useData } from 'vitepress';
import { computed } from 'vue';
const props = defineProps<{
modelValue: string;
id: string;
}>();
modelValue: string
id: string
}>()
const { isDark } = useData();
const emit = defineEmits(['update:modelValue']);
const emit = defineEmits(['update:modelValue'])
const value = computed({
get: () => {
if (props.modelValue == null || props.modelValue === 'currentColor') {
return isDark.value ? '#ffffff' : '#000000';
}
return props.modelValue;
},
set: (val) => emit('update:modelValue', val),
});
get: () => 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"
@@ -41,7 +33,6 @@ const value = computed({
class="color-input-text"
aria-label="Color picker input"
v-model="value"
placeholder="[default]"
/>
</div>
</template>
@@ -54,33 +45,27 @@ const value = computed({
top: -5px;
left: -5px;
}
.color-input-wrapper {
height: 24px;
width: 24px;
overflow: hidden;
position: relative;
border-radius: 4px;
border-radius: 12px;
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: 3px 8px 3px 3px;
padding: 4px 8px;
height: auto;
font-size: 13px;
font-size: 14px;
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 {
@@ -90,24 +75,19 @@ const value = computed({
border: none;
background: transparent;
color: var(--vp-c-text-1);
font-size: 13px;
font-size: 14px;
text-align: left;
border-radius: 8px;
cursor: text;
transition:
border-color 0.25s,
background 0.4s ease;
letter-spacing: 1px;
transition: border-color 0.25s, background 0.4s ease;
}
.color-picker:hover,
.color-picker:focus-within {
.color-picker:hover, .color-picker:focus {
border-color: var(--vp-c-brand);
}
.color-picker:focus-within {
background-color: var(--vp-c-bg);
background: var(--vp-c-bg-alt);
}
.color-input[value='currentColor'] {
.color-input[value="currentColor"] {
}
</style>
@@ -1,27 +1,22 @@
<script setup>
import Icon from 'lucide-vue-next/src/Icon';
import { search } from '../../../data/iconNodes';
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon'
import { search } from '../../../data/iconNodes'
const SearchIcon = createLucideIcon('search', search)
defineProps({
shortcut: {
type: String,
required: false,
},
});
required: false
}
})
</script>
<template>
<button class="fake-input">
<Icon
:iconNode="search"
class="search-icon"
/>
<slot />
<kbd
v-if="shortcut"
class="shortcut"
>{{ shortcut }}</kbd
>
<component :is="SearchIcon" class="search-icon"/>
<slot/>
<kbd v-if="shortcut" class="shortcut">{{ shortcut }}</kbd>
</button>
</template>
@@ -39,15 +34,12 @@ defineProps({
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 {
@@ -5,6 +5,7 @@
</template>
<style scoped>
.icon-button {
display: inline-flex;
border: 1px solid transparent;
@@ -29,9 +30,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 {
@@ -12,9 +12,6 @@ export interface InputProps {
<script setup lang="ts">
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',
@@ -24,7 +21,7 @@ const input = ref();
const wrapperEl = ref();
const shortcutEl = ref();
const emit = defineEmits(['change', 'input', 'update:modelValue']);
defineEmits(['change', 'input', 'update:modelValue']);
const updateShortcutSpacing = () => {
nextTick(() => {
@@ -35,16 +32,9 @@ const updateShortcutSpacing = () => {
});
};
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();
@@ -70,18 +60,6 @@ defineExpose({
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"
@@ -95,7 +73,6 @@ defineExpose({
.input-wrapper {
position: relative;
}
.input {
justify-content: flex-start;
border: 1px solid transparent;
@@ -105,10 +82,7 @@ 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;
transition: border-color 0.2s ease-in-out;
}
.input.has-shortcut {
@@ -118,23 +92,13 @@ defineExpose({
.input:hover,
.input:focus {
border-color: var(--vp-c-brand);
}
.input:focus {
background-color: var(--vp-c-bg);
background: var(--vp-c-bg-alt);
}
.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;
@@ -152,32 +116,10 @@ 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>
@@ -190,7 +132,4 @@ 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>
@@ -7,8 +7,11 @@ export default {
<script setup lang="ts">
import { computed, ref } from 'vue';
import Input from './Input.vue';
import Icon from 'lucide-vue-next/src/Icon';
import { search } from '../../../data/iconNodes';
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon';
import { search, x } from '../../../data/iconNodes';
const SearchIcon = createLucideIcon('search', search);
const XIcon = createLucideIcon('close', x);
interface Props {
modelValue: string;
@@ -31,6 +34,10 @@ const value = computed({
get: () => props.modelValue,
set: (val) => emit('update:modelValue', val),
});
const clear = () => {
value.value = '';
};
</script>
<template>
@@ -43,12 +50,26 @@ const value = computed({
v-model="value"
class="input-wrapper"
>
<template #icon>
<Icon
:iconNode="search"
<template #startIcon>
<component
:is="SearchIcon"
class="search-icon"
/>
</template>
<template #endIcon>
<button
class="clear-button"
type="button"
v-if="value"
>
<component
:is="XIcon"
class="x-icon"
@click="clear"
/>
</button>
</template>
</Input>
</template>
@@ -60,7 +81,9 @@ const value = computed({
padding: 0 10px 0 12px;
width: 100%;
height: 40px;
color: var(--vp-c-text-1);
background-color: var(--vp-c-bg-alt);
transition: border-color 0.15s ease-in-out;
}
.input:hover,
@@ -70,9 +93,31 @@ const value = computed({
}
.input-wrapper:deep(.input) {
/* padding: 12px 24px; */
padding-block: 12px;
font-size: 14px;
height: 48px;
}
.clear-button {
position: absolute;
padding: 4px;
right: 8px;
top: 8px;
width: 32px;
height: 32px;
background: transparent;
border-radius: 6px;
transition: background 0.2s ease;
}
.clear-button:hover {
background: var(--vp-button-alt-bg);
}
.input-wrapper:deep(.input)::-webkit-search-decoration,
.input-wrapper:deep(.input)::-webkit-search-cancel-button,
.input-wrapper:deep(.input)::-webkit-search-results-button,
.input-wrapper:deep(.input)::-webkit-search-results-decoration {
display: none;
}
</style>
@@ -1,15 +1,14 @@
<script setup lang="ts">
import { rotateCw } from '../../../data/iconNodes';
import Icon from 'lucide-vue-next/src/Icon';
import IconButton from './IconButton.vue';
import { rotateCw } from '../../../data/iconNodes'
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon'
import IconButton from "./IconButton.vue";
const RotateIcon = createLucideIcon('RotateIcon', rotateCw)
</script>
<template>
<IconButton class="reset-button">
<Icon
:size="20"
:iconNode="rotateCw"
/>
<RotateIcon :size="20"/>
</IconButton>
</template>
@@ -33,7 +32,6 @@ import IconButton from './IconButton.vue';
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(359deg);
}
@@ -52,7 +52,6 @@
width: 20px;
height: 20px;
border-radius: 50%;
/* background-color: var(--vp-c-neutral); */
box-shadow: var(--vp-shadow-1);
}
@@ -22,16 +22,16 @@ export default {
logo: '/framework-logos/svelte.svg',
label: 'Lucide documentation for Svelte',
},
{
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-solid',
logo: '/framework-logos/solid.svg',
label: 'Lucide documentation for Solid',
},
{
name: 'lucide-angular',
logo: '/framework-logos/angular.svg',
@@ -48,6 +48,11 @@ 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,48 +2,45 @@
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 Icon from 'lucide-vue-next/src/Icon';
// 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 { 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;
}>();
drawerOpen: boolean
}>()
const CloseIcon = createLucideIcon('Close', x)
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"
>
<Icon
:iconNode="x"
:size="20"
absoluteStrokeWidth
/>
<IconButton @click="showAd = false" class="hide-button">
<component :is="CloseIcon" :size="20" absoluteStrokeWidth />
</IconButton>
<VPDocAsideCarbonAds :carbon-ads="theme.carbonAds" />
<VPDocAsideCarbonAds
:carbon-ads="theme.carbonAds"
/>
</div>
</template>
@@ -54,9 +51,7 @@ 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 {
@@ -72,11 +67,8 @@ 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,68 +1,70 @@
<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>
@@ -73,10 +75,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,44 +1,41 @@
<script setup lang="ts">
import { computed, useSlots } from 'vue';
import { copy } from '../../../data/iconNodes';
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon'
import { copy } from '../../../data/iconNodes'
import useConfetti from '../../composables/useConfetti';
import Icon from 'lucide-vue-next/src/Icon';
const { animate, confetti } = useConfetti();
const slots = useSlots();
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 />
<Icon
:iconNode="copy"
:size="20"
class="copy-icon"
/>
<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 0.15s;
transition: background ease-in .15s;;
padding: 2px 8px;
border-radius: 8px;
width: auto;
@@ -51,7 +48,7 @@ function copyText() {
}
.icon-name:hover .copy-icon {
opacity: 0.9;
opacity: .9;
}
.icon-name:before,
@@ -68,10 +65,10 @@ function copyText() {
opacity: 0;
margin-left: 12px;
margin-top: 6px;
transition: ease 0.3s opacity;
transition:ease .3s opacity;
}
.icon-name:hover .copy-icon:hover {
opacity: 0.6;
opacity: .6;
}
</style>
@@ -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"
@@ -0,0 +1,21 @@
import { getAllData } from '../../../lib/icons';
import { getAllCategoryFiles, mapCategoryIconCount } from '../../../lib/categories';
import iconsMetaData from '../../../data/iconMetaData';
import { fetchAllReleases } from '../../../../scripts/writeReleaseMetadata.mts';
import { satisfies } from 'semver';
export default {
async load() {
const versions = await fetchAllReleases();
const mappedVersions = versions
.map((tag) => tag.replace('v', ''))
.reverse()
mappedVersions.length = 100
return {
versions: mappedVersions,
};
},
};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { ref, computed, defineAsyncComponent, onMounted, watch } from 'vue';
import { ref, computed, defineAsyncComponent, onMounted, watch, watchEffect } from 'vue';
import type { IconEntity } from '../../types';
import { useElementSize, useEventListener, useVirtualList } from '@vueuse/core';
import { useRoute } from 'vitepress';
@@ -11,9 +11,13 @@ import useSearchShortcut from '../../utils/useSearchShortcut';
import StickyBar from './StickyBar.vue';
import useFetchTags from '../../composables/useFetchTags';
import useFetchCategories from '../../composables/useFetchCategories';
import useFetchVersionIcons from '../../composables/useFetchVersionIcons';
import chunkArray from '../../utils/chunkArray';
import CarbonAdOverlay from './CarbonAdOverlay.vue';
import VersionSelect from './VersionSelect.vue';
import { sort, satisfies } from 'semver';
import useSearchPlaceholder from '../../utils/useSearchPlaceholder.ts';
import { data as versionData } from './IconsOverview.data';
const ICON_SIZE = 56;
const ICON_GRID_GAP = 8;
@@ -32,9 +36,15 @@ const props = defineProps<{
}>();
const activeIconName = ref(null);
const selectedVersion = ref();
const { execute: fetchTags, data: tags } = useFetchTags();
const { execute: fetchCategories, data: categories } = useFetchCategories();
const { execute: fetchVersionIcons, data: versionIcons } = useFetchVersionIcons(selectedVersion);
watch(selectedVersion, () => {
fetchVersionIcons();
});
const overviewEl = ref<HTMLElement | null>(null);
const { width: containerWidth } = useElementSize(overviewEl);
@@ -44,20 +54,31 @@ const columnSize = computed(() => {
});
const mappedIcons = computed(() => {
if (tags.value == null) {
return props.icons;
let icons = props.icons;
if (tags.value != null && categories.value != null) {
icons = props.icons.map((icon) => {
const iconTags = tags.value[icon.name];
const iconCategories = categories.value?.[icon.name] ?? [];
return {
...icon,
tags: iconTags,
categories: iconCategories,
};
});
}
return props.icons.map((icon) => {
const iconTags = tags.value[icon.name];
const iconCategories = categories.value?.[icon.name] ?? [];
if (selectedVersion.value == null || versionIcons.value == null) {
console.log('no release info');
return {
...icon,
tags: iconTags,
categories: iconCategories,
};
});
return icons;
}
return Object.values(versionIcons.value).filter(([name, iconNode]) => ({
name,
iconNode,
}));
});
const { searchInput, searchQuery, searchQueryDebounced } = useSearchInput();
@@ -107,6 +128,12 @@ function onFocusSearchInput() {
}
}
// function onFocusVersionSelect() {
// if (releaseInfo.value == null) {
// fetchReleaseInfo();
// }
// }
const NoResults = defineAsyncComponent(() => import('./NoResults.vue'));
const IconDetailOverlay = defineAsyncComponent(() => import('./IconDetailOverlay.vue'));
@@ -129,13 +156,17 @@ function handleCloseDrawer() {
>
<StickyBar>
<InputSearch
:placeholder="`Search ${icons.length} icons…`"
:placeholder="`Search ${mappedIcons.length} icons ...`"
v-model="searchQuery"
ref="searchInput"
:shortcut="kbdSearchShortcut"
class="input-wrapper"
@focus="onFocusSearchInput"
/>
<VersionSelect
v-model="selectedVersion"
:versions="versionData.versions"
/>
</StickyBar>
<NoResults
v-if="searchPlaceholder.isNoResults"
@@ -1,72 +1,75 @@
<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
@@ -74,11 +77,7 @@ 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>
@@ -118,7 +117,7 @@ const customizingActive = computed(() => {
<InputField
id="absolute-stroke-width"
label="Absolute stroke width"
label="Absolute Stroke width"
>
<Switch
id="absolute-stroke-width"
@@ -144,7 +143,6 @@ const customizingActive = computed(() => {
font-size: 16px;
/* margin-bottom: 12px; */
}
.customizer-card {
background: var(--vp-c-bg);
padding: 12px 24px 24px;
@@ -153,7 +151,7 @@ const customizingActive = computed(() => {
position: relative;
z-index: 0;
border: 1px solid transparent;
transition: border-color 0.4s ease-in-out;
transition: border-color .4s ease-in-out;
}
.customizer-card.customized {
@@ -14,6 +14,7 @@
margin-top: -32px;
width: 100%;
display: flex;
gap: 16px;
margin-bottom: 32px;
background: var(--vp-c-bg);
box-shadow: 0 16px 24px var(--vp-c-bg);
@@ -0,0 +1,221 @@
<script setup lang="ts">
import { ref, computed } from 'vue';
import {
Combobox,
ComboboxInput,
ComboboxButton,
ComboboxOptions,
ComboboxOption,
} from '@headlessui/vue';
import { chevronsUpDown, check } from '../../../data/iconNodes';
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon';
const model = defineModel<string | undefined>();
const props = defineProps<{
versions: string[];
}>();
let query = ref('');
let filteredVersions = computed(() =>
query.value === ''
? [...props.versions]
: props.versions.filter((version) =>
version
.toLowerCase()
.replace(/\s+/g, '')
.includes(query.value.toLowerCase().replace(/\s+/g, '')),
),
);
const emit = defineEmits(['focus']);
const ChevronUpDown = createLucideIcon('ChevronUpDown', chevronsUpDown);
const Check = createLucideIcon('Check', check);
</script>
<template>
<Combobox v-model="model">
<div class="combobox-container">
<div class="combobox-input-container">
<ComboboxInput
class="combobox-input"
placeholder="Latest version"
@change="query = $event.target.value"
@focus="emit('focus')"
/>
<ComboboxButton class="combobox-button">
<ChevronUpDown
class="icon-chevron"
aria-hidden="true"
/>
</ComboboxButton>
</div>
<ComboboxOptions class="combobox-options">
<div
v-if="versions.length === 0 && query !== ''"
class="combobox-no-results"
>
No match!
</div>
<ComboboxOption
v-for="version in filteredVersions"
as="template"
:key="version"
:value="version"
v-slot="{ selected, active }"
>
<li
class="combobox-option"
:class="{ active: active }"
>
<span
class="combobox-option-text"
:class="{ selected: selected }"
>
{{ version }}
</span>
<span
v-if="selected"
class="combobox-option-icon"
:class="{ active: active }"
>
<Check
class="icon-check"
aria-hidden="true"
/>
</span>
</li>
</ComboboxOption>
</ComboboxOptions>
</div>
</Combobox>
</template>
<style scoped>
.combobox-input-container {
position: relative;
width: 100%;
height: 48px;
min-width: 160px;
cursor: default;
overflow: hidden;
background-color: var(--vp-c-bg-alt);
text-align: left;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
outline: none;
}
.combobox-input {
width: 100%;
border: none;
padding: 12px;
border-radius: 8px;
font-size: 0.875rem;
line-height: 1.25rem;
color: var(--vp-c-text-1);
border: 1px solid transparent;
transition: border-color 0.15s ease-in-out;
}
.combobox-input:hover,
.combobox-input:focus {
border-color: var(--vp-c-brand);
background: var(--vp-c-bg-alt);
}
.combobox-button {
position: absolute;
top: 4px;
padding: 9px 8px;
right: 0;
display: flex;
align-items: center;
padding-right: 0.5rem;
z-index: 10;
}
.icon-chevron {
height: 20px;
width: 20px;
color: #9ca3af;
}
.combobox-options {
position: absolute;
margin-top: 0.25rem;
width: 100%;
max-width: 160px;
padding: 12px;
min-width: 128px;
overflow-y: auto;
border-radius: 12px;
border: 1px solid var(--vp-c-divider);
background-color: var(--vp-c-bg-elv);
box-shadow: var(--vp-shadow-3);
transition: background-color 0.5s;
max-height: 240px;
outline: none;
right: 0;
}
.combobox-no-results {
position: relative;
cursor: default;
padding: 0.5rem 1rem;
color: #4b5563;
}
.combobox-option {
position: relative;
cursor: default;
padding: 2px 8px;
border-radius: 6px;
color: var(--vp-c-text-1);
line-height: 32px;
font-size: 14px;
font-weight: 500;
white-space: nowrap;
transition:
background-color 0.25s,
color 0.25s;
}
.combobox-option.active {
color: var(--vp-c-brand);
background-color: var(--vp-c-default-soft);
}
.combobox-option-text {
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.combobox-option-text.selected {
font-weight: 500;
}
.combobox-option-icon {
position: absolute;
right: 8px;
top: 8px;
display: flex;
align-items: center;
padding-left: 0.75rem;
color: var(--vp-c-brand) !important;
}
.combobox-option-icon.active {
color: var(--vp-c-brand) !important;
}
.icon-check {
height: 20px;
width: 20px;
}
</style>
@@ -0,0 +1,17 @@
import { useFetch } from '@vueuse/core';
import { computed, Ref } from 'vue';
const useFetchVersionIcons = (version: Ref<string | undefined>) =>{
const fetchUrl = computed(() => {
if(version.value == null ) return ''
return `https://unpkg.com/lucide-static@${version.value}/icon-nodes.json`
})
return useFetch<Record<string, string>>(
fetchUrl,
{
refetch: true
}
).json();
}
export default useFetchVersionIcons;
@@ -1,4 +1,4 @@
import { type IconNode } from 'lucide-vue-next/src/types';
import { IconNode } from 'lucide-vue-next/src/createLucideIcon';
import Vue from 'vue';
declare module '*.vue' {
@@ -20,6 +20,5 @@ declare module 'node:module' {
}
declare module '*.node.json' {
const value: IconNode;
export default value;
export default IconNode;
}
-2
View File
@@ -1,7 +1,5 @@
---
aside: false
title: Contributing to Lucide
description: Guidelines and instructions for contributing to the Lucide project.
---
<!--@include: ../CONTRIBUTING.md -->
-5
View File
@@ -10,11 +10,6 @@ because they can quickly give information.
However, not everyone can understand them easily. When using icons it is very important to consider
the following aspects of accessibility.
::: info
By default, we hide icons from screen readers using `aria-hidden="true"`.
You can make them accessible yourself by following the guidelines below.
:::
## Provide visible labels
Icons are a helpful tool to improve perception, but they aren't a replacement for text.
-12
View File
@@ -167,15 +167,3 @@ 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,19 +192,3 @@ import LucideIcon from './LucideIcon.astro';
<LucideIcon name="Menu" />
```
## Accessibility
By default, we hide icons from screen readers using `aria-hidden="true"`.
You can add accessibility attributes using aria-labels.
```jsx
---
import { Check } from '@lucide/astro';
---
<Check aria-label="Task completed" />
```
For best practices on accessibility, please see our [accessibility guide](../advanced/accessibility.md).
-16
View File
@@ -131,19 +131,3 @@ 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).
+5 -11
View File
@@ -102,16 +102,10 @@ The example below imports all ES Modules, so exercise caution when using it. Imp
### Icon Component Example
```tsx
import * as icons from 'lucide-react-native/icons';
```jsx
import { icons } from 'lucide-react-native';
interface IconProps {
name: keyof typeof icons;
color?: string;
size?: number;
}
const Icon = ({ name, color, size }: IconProps) => {
const Icon = ({ name, color, size }) => {
const LucideIcon = icons[name];
return <LucideIcon color={color} size={size} />;
@@ -122,11 +116,11 @@ export default Icon;
#### Using the Icon Component
```tsx
```jsx
import Icon from './Icon';
const App = () => {
return <Icon name="House" />;
return <Icon name="house" />;
};
export default App;
-16
View File
@@ -109,19 +109,3 @@ const App = () => (
<DynamicIcon name="camera" color="red" size={48} />
);
```
## Accessibility
By default, we hide icons from screen readers using `aria-hidden="true"`.
You can add accessibility attributes using aria-labels.
```jsx
import { Check } from 'lucide-react';
const App = () => {
return <Check aria-label="Task completed" />;
};
```
For best practices on accessibility, please see our [accessibility guide](../advanced/accessibility.md).
-16
View File
@@ -144,19 +144,3 @@ const App = () => {
export default App;
```
## Accessibility
By default, we hide icons from screen readers using `aria-hidden="true"`.
You can add accessibility attributes using aria-labels.
```jsx
import { Check } from 'lucide-solid';
const App = () => {
return <Check aria-label="Task completed" />;
};
```
For best practices on accessibility, please see our [accessibility guide](../advanced/accessibility.md).
-16
View File
@@ -331,19 +331,3 @@ The example below imports all ES Modules, so exercise caution when using it. Imp
<LucideIcon name="Menu" />
```
## Accessibility
By default, we hide icons from screen readers using `aria-hidden="true"`.
You can add accessibility attributes using aria-labels.
```svelte
<script>
import { Check } from '@lucide/svelte';
</script>
<Check aria-label="Task completed" />
```
For best practices on accessibility, please see our [accessibility guide](../advanced/accessibility.md).
-22
View File
@@ -146,25 +146,3 @@ All other props listed above also work on the `Icon` Component.
</div>
</template>
```
## Accessibility
By default, we hide icons from screen readers using `aria-hidden="true"`.
You can add accessibility attributes using aria-labels.
```vue
<script setup>
import { Check } from 'lucide-vue-next';
</script>
<template>
<Check
color="red"
:size="32"
aria-label="Task completed"
/>
</template>
```
For best practices on accessibility, please see our [accessibility guide](../advanced/accessibility.md).
-21
View File
@@ -62,7 +62,6 @@ 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
@@ -204,23 +203,3 @@ 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,6 +1,4 @@
---
title: Categories
description: Explore Lucide icons organized into various categories for easier browsing.
layout: page
outline: 2
outlineTitle: Categories
-2
View File
@@ -1,6 +1,4 @@
---
title: Icons
description: Browse all Lucide icons.
layout: page
outline: 2
outlineTitle: Categories
-1
View File
@@ -2,7 +2,6 @@
# https://vitepress.dev/reference/default-theme-home-page
layout: home
markdownStyles: false
titleTemplate: false
head:
- - link
-2
View File
@@ -1,6 +1,4 @@
---
title: License
description: Review the licensing terms for using Lucide icons and resources.
aside: false
editLink: false
---
+10 -10
View File
@@ -9,14 +9,14 @@
"docs:build": "pnpm run /^prebuild:.*/ && vitepress build",
"docs:preview": "vitepress preview",
"build:docs": "vitepress build",
"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",
"prebuild:iconNodes": "node ./scripts/writeIconNodes.mts",
"prebuild:metaJson": "node ./scripts/writeIconMetaIndex.mts",
"prebuild:releaseJson": "node ./scripts/writeReleaseMetadata.mts",
"prebuild:categoriesJson": "node ./scripts/writeCategoriesMetadata.mts",
"prebuild:relatedIcons": "node ./scripts/writeIconRelatedIcons.mts",
"prebuild:iconDetails": "node ./scripts/writeIconDetails.mts",
"prebuild:brandStopwords": "node ./scripts/writeBrandStopwords.mts",
"postbuild:vercelJson": "node ./scripts/writeVercelOutput.mts",
"dev": "npx nitropack dev",
"prebuild:api": "npx nitropack prepare",
"build:api": "npx nitropack build",
@@ -45,8 +45,8 @@
"element-to-path": "^1.2.1",
"fuse.js": "^7.1.0",
"jszip": "^3.10.1",
"lodash": "^4.17.23",
"lodash-es": "^4.17.23",
"lodash": "^4.17.21",
"lodash-es": "^4.17.21",
"lucide-react": "workspace:*",
"lucide-vue-next": "workspace:*",
"react": "^18.3.1",
-2
View File
@@ -1,6 +1,4 @@
---
title: Packages
description: Discover the various packages available in the Lucide ecosystem.
layout: page
outline: 2
outlineTitle: Packages
@@ -1 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 936 B

-7
View File
@@ -1,7 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 3.3 KiB

@@ -42,7 +42,7 @@ const getRelatedIcons = (currentIcon, icons) => {
};
const iconsMetaDataPromises = svgFiles.map(async (iconName) => {
const metaDataFileContent = await fs.promises.readFile(`../icons/${iconName}`);
const metaDataFileContent = await fs.promises.readFile(`../icons/${iconName}`, 'utf-8');
const metaData = JSON.parse(metaDataFileContent);
const name = iconName.replace('.json', '');
@@ -29,7 +29,7 @@ if (!fs.existsSync(releaseMetaDataDirectory)) {
fs.mkdirSync(releaseMetaDataDirectory);
}
const fetchAllReleases = async () => {
export const fetchAllReleases = async () => {
await git.fetch('https://github.com/lucide-icons/lucide.git', '--tags');
return Promise.all(
@@ -73,7 +73,7 @@ const comparisonsPromises = tags.map(async (tag, index) => {
const comparisons = await Promise.all(comparisonsPromises);
const newReleaseMetaData = {};
comparisons.forEach(({ tag, iconFiles, date } = {}) => {
comparisons.forEach(({ tag, iconFiles, date } = {} as typeof comparisons[number]) => {
if (tag == null) return;
iconFiles.forEach(({ status, file, renamedFile }) => {
@@ -11,7 +11,7 @@ const iconMetaData = await getIconMetaData(path.resolve(scriptDir, '../../icons'
const iconAliasesRedirectRoutes = Object.entries(iconMetaData)
.filter(([, { aliases }]) => aliases?.length)
.map(([iconName, { aliases }]) => {
const aliasRouteMatches = aliases.map((alias) => alias.name).join('|');
const aliasRouteMatches = aliases.map((alias) => typeof alias === 'string' ? alias : alias.name).join('|');
return {
src: `/icons/${aliasRouteMatches}`,
-1
View File
@@ -1,5 +1,4 @@
---
title: Showcase
layout: page
outline: 2
outlineTitle: Showcase
+1 -2
View File
@@ -2,8 +2,7 @@
"$schema": "../icon.schema.json",
"contributors": [
"karsa-mistmere",
"jamiemlaw",
"jguddas"
"jamiemlaw"
],
"tags": [
"plaster",
+1 -1
View File
@@ -13,7 +13,7 @@
<path d="M10 14.01h.01" />
<path d="M14 10.01h.01" />
<path d="M14 14.01h.01" />
<path d="M18 6v12" />
<path d="M18 6v11.5" />
<path d="M6 6v12" />
<rect x="2" y="6" width="20" height="12" rx="2" />
</svg>

Before

Width:  |  Height:  |  Size: 424 B

After

Width:  |  Height:  |  Size: 426 B

+1 -2
View File
@@ -1,8 +1,7 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"danielbayley",
"jguddas"
"danielbayley"
],
"tags": [
"alarm",
+2 -2
View File
@@ -10,6 +10,6 @@
stroke-linejoin="round"
>
<path d="M10.268 21a2 2 0 0 0 3.464 0" />
<path d="M11.68 2.009A6 6 0 0 0 6 8c0 4.499-1.411 5.956-2.738 7.326A1 1 0 0 0 4 17h16a1 1 0 0 0 .74-1.673c-.824-.85-1.678-1.731-2.21-3.348" />
<circle cx="18" cy="5" r="3" />
<path d="M13.916 2.314A6 6 0 0 0 6 8c0 4.499-1.411 5.956-2.74 7.327A1 1 0 0 0 4 17h16a1 1 0 0 0 .74-1.673 9 9 0 0 1-.585-.665" />
<circle cx="18" cy="8" r="3" />
</svg>

Before

Width:  |  Height:  |  Size: 431 B

After

Width:  |  Height:  |  Size: 418 B

+5 -5
View File
@@ -9,9 +9,9 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M13 2H6.5A2.5 2.5 0 0 0 4 4.5v15" />
<path d="M17 2v6" />
<path d="M17 4h2" />
<path d="M20 15.2V21a1 1 0 0 1-1 1H6.5a1 1 0 0 1 0-5H20" />
<circle cx="17" cy="10" r="2" />
<path d="m19 3 1 1" />
<path d="m20 2-4.5 4.5" />
<path d="M20 7.898V21a1 1 0 0 1-1 1H6.5a1 1 0 0 1 0-5H20" />
<path d="M4 19.5v-15A2.5 2.5 0 0 1 6.5 2h7.844" />
<circle cx="14" cy="8" r="2" />
</svg>

Before

Width:  |  Height:  |  Size: 399 B

After

Width:  |  Height:  |  Size: 412 B

+1 -2
View File
@@ -1,8 +1,7 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"danielbayley",
"jguddas"
"danielbayley"
],
"tags": [
"read",
+1 -1
View File
@@ -9,6 +9,6 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<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="m19 21-7-4-7 4V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2Z" />
<path d="m9 10 2 2 4-4" />
</svg>

Before

Width:  |  Height:  |  Size: 371 B

After

Width:  |  Height:  |  Size: 301 B

+1 -2
View File
@@ -2,8 +2,7 @@
"$schema": "../icon.schema.json",
"contributors": [
"siarie",
"ericfennis",
"jguddas"
"ericfennis"
],
"tags": [
"delete",
+2 -2
View File
@@ -9,6 +9,6 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<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" />
<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" />
</svg>

Before

Width:  |  Height:  |  Size: 366 B

After

Width:  |  Height:  |  Size: 317 B

+1 -2
View File
@@ -2,8 +2,7 @@
"$schema": "../icon.schema.json",
"contributors": [
"siarie",
"ericfennis",
"jguddas"
"ericfennis"
],
"tags": [
"add"
+3 -3
View File
@@ -9,7 +9,7 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<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" />
<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" />
</svg>

Before

Width:  |  Height:  |  Size: 389 B

After

Width:  |  Height:  |  Size: 359 B

+1 -2
View File
@@ -1,8 +1,7 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"danielbayley",
"jguddas"
"danielbayley"
],
"tags": [
"read",
+1 -1
View File
@@ -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: 399 B

After

Width:  |  Height:  |  Size: 329 B

+1 -10
View File
@@ -4,18 +4,9 @@
"colebemis",
"csandman",
"siarie",
"ericfennis",
"jguddas"
"ericfennis"
],
"tags": [
"save",
"favorite",
"mark",
"label",
"attachment",
"file",
"stick",
"pin",
"read",
"clip",
"marker",
+1 -1
View File
@@ -9,5 +9,5 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<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="m19 21-7-4-7 4V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v16z" />
</svg>

Before

Width:  |  Height:  |  Size: 342 B

After

Width:  |  Height:  |  Size: 275 B

+1 -2
View File
@@ -2,8 +2,7 @@
"$schema": "../icon.schema.json",
"contributors": [
"danielbayley",
"jamiemlaw",
"jguddas"
"jamiemlaw"
],
"tags": [
"issue",
+4 -6
View File
@@ -10,15 +10,13 @@
stroke-linejoin="round"
>
<path d="M12 20v-8" />
<path d="M12.656 7H14a4 4 0 0 1 4 4v1.344" />
<path d="M14.12 3.88 16 2" />
<path d="M17.123 17.123A6 6 0 0 1 6 14v-3a4 4 0 0 1 1.72-3.287" />
<path d="M15 7.13V6a3 3 0 0 0-5.14-2.1L8 2" />
<path d="M18 12.34V11a4 4 0 0 0-4-4h-1.3" />
<path d="m2 2 20 20" />
<path d="M21 5a4 4 0 0 1-3.55 3.97" />
<path d="M22 13h-3.344" />
<path d="M22 13h-3.34" />
<path d="M3 21a4 4 0 0 1 3.81-4" />
<path d="M3 5a4 4 0 0 0 3.55 3.97" />
<path d="M6 13H2" />
<path d="m8 2 1.88 1.88" />
<path d="M9.712 4.06A3 3 0 0 1 15 6v1.13" />
<path d="M7.7 7.7A4 4 0 0 0 6 11v3a6 6 0 0 0 11.13 3.13" />
</svg>

Before

Width:  |  Height:  |  Size: 656 B

After

Width:  |  Height:  |  Size: 579 B

-14
View File
@@ -1,14 +0,0 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"nickveles"
],
"tags": [
"cannabis",
"weed",
"leaf"
],
"categories": [
"nature"
]
}
-18
View File
@@ -1,18 +0,0 @@
<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 22v-4c1.5 1.5 3.5 3 6 3 0-1.5-.5-3.5-2-5" />
<path d="M13.988 8.327C13.902 6.054 13.365 3.82 12 2a9.3 9.3 0 0 0-1.445 2.9" />
<path d="M17.375 11.725C18.882 10.53 21 7.841 21 6c-2.324 0-5.08 1.296-6.662 2.684" />
<path d="m2 2 20 20" />
<path d="M21.024 15.378A15 15 0 0 0 22 15c-.426-1.279-2.67-2.557-4.25-2.907" />
<path d="M6.995 6.992C5.714 6.4 4.29 6 3 6c0 2 2.5 5 4 6-1.5 0-4.5 1.5-5 3 3.5 1.5 6 1 6 1-1.5 1.5-2 3.5-2 5 2.5 0 4.5-1.5 6-3" />
</svg>

Before

Width:  |  Height:  |  Size: 681 B

+1 -2
View File
@@ -3,8 +3,7 @@
"contributors": [
"it-is-not",
"ericfennis",
"danielbayley",
"torfmuer"
"danielbayley"
],
"tags": [
"movie",
+4 -4
View File
@@ -9,8 +9,8 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="m12.296 3.464 3.02 3.956" />
<path d="M20.2 6 3 11l-.9-2.4c-.3-1.1.3-2.2 1.3-2.5l13.5-4c1.1-.3 2.2.3 2.5 1.3z" />
<path d="M3 11h18v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
<path d="m6.18 5.276 3.1 3.899" />
<path d="M20.2 6 3 11l-.9-2.4c-.3-1.1.3-2.2 1.3-2.5l13.5-4c1.1-.3 2.2.3 2.5 1.3Z" />
<path d="m6.2 5.3 3.1 3.9" />
<path d="m12.4 3.4 3.1 4" />
<path d="M3 11h18v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2Z" />
</svg>

Before

Width:  |  Height:  |  Size: 429 B

After

Width:  |  Height:  |  Size: 415 B

+1 -2
View File
@@ -5,8 +5,7 @@
"csandman",
"ericfennis",
"karsa-mistmere",
"lscheibel",
"jguddas"
"lscheibel"
],
"tags": [
"weather",
+1 -1
View File
@@ -11,5 +11,5 @@
>
<path d="M12 12v4" />
<path d="M12 20h.01" />
<path d="M8.128 16.949A7 7 0 1 1 15.71 8h1.79a1 1 0 0 1 0 9h-1.642" />
<path d="M17 18h.5a1 1 0 0 0 0-9h-1.79A7 7 0 1 0 7 17.708" />
</svg>

Before

Width:  |  Height:  |  Size: 331 B

After

Width:  |  Height:  |  Size: 322 B

+1 -1
View File
@@ -10,5 +10,5 @@
stroke-linejoin="round"
>
<path d="m17 15-5.5 5.5L9 18" />
<path d="M5.516 16.07A7 7 0 1 1 15.71 8h1.79a4.5 4.5 0 0 1 3.501 7.327" />
<path d="M5 17.743A7 7 0 1 1 15.71 10h1.79a4.5 4.5 0 0 1 1.5 8.742" />
</svg>

Before

Width:  |  Height:  |  Size: 320 B

After

Width:  |  Height:  |  Size: 316 B

+2 -2
View File
@@ -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: 385 B

After

Width:  |  Height:  |  Size: 388 B

+1 -2
View File
@@ -2,8 +2,7 @@
"$schema": "../icon.schema.json",
"contributors": [
"ericfennis",
"karsa-mistmere",
"jguddas"
"karsa-mistmere"
],
"tags": [
"weather",
+2 -2
View File
@@ -9,6 +9,6 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<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" />
<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" />
</svg>

Before

Width:  |  Height:  |  Size: 341 B

After

Width:  |  Height:  |  Size: 345 B

-21
View File
@@ -1,21 +0,0 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"colebemis",
"jguddas",
"Spleefies"
],
"tags": [
"storage",
"memory",
"container",
"tin",
"pot",
"bytes",
"servers"
],
"categories": [
"devices",
"development"
]
}
-18
View File
@@ -1,18 +0,0 @@
<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="M21 11.693V5" />
<path d="m22 22-1.875-1.875" />
<path d="M3 12a9 3 0 0 0 8.697 2.998" />
<path d="M3 5v14a9 3 0 0 0 9.28 2.999" />
<circle cx="18" cy="18" r="3" />
<ellipse cx="12" cy="5" rx="9" ry="3" />
</svg>

Before

Width:  |  Height:  |  Size: 435 B

+2 -3
View File
@@ -9,10 +9,9 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M15 8a1 1 0 0 1-1-1V2a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8z" />
<path d="M20 8v12a2 2 0 0 1-2 2h-4.182" />
<path d="M13.85 22H18a2 2 0 0 0 2-2V8a2 2 0 0 0-.586-1.414l-4-4A2 2 0 0 0 14 2H6a2 2 0 0 0-2 2v6.6" />
<path d="M14 2v5a1 1 0 0 0 1 1h5" />
<path d="m3.305 19.53.923-.382" />
<path d="M4 10.592V4a2 2 0 0 1 2-2h8" />
<path d="m4.228 16.852-.924-.383" />
<path d="m5.852 15.228-.383-.923" />
<path d="m5.852 20.772-.383.924" />

Before

Width:  |  Height:  |  Size: 727 B

After

Width:  |  Height:  |  Size: 690 B

+4 -4
View File
@@ -9,9 +9,9 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M10.65 22H18a2 2 0 0 0 2-2V8a2.4 2.4 0 0 0-.706-1.706l-3.588-3.588A2.4 2.4 0 0 0 14 2H6a2 2 0 0 0-2 2v10.1" />
<path d="M14 2v5a1 1 0 0 0 1 1h5" />
<path d="M4 12v6" />
<path d="M4 14h2" />
<path d="M9.65 22H18a2 2 0 0 0 2-2V8a2.4 2.4 0 0 0-.706-1.706l-3.588-3.588A2.4 2.4 0 0 0 14 2H6a2 2 0 0 0-2 2v4" />
<circle cx="4" cy="20" r="2" />
<path d="m10 15 1 1" />
<path d="m11 14-4.586 4.586" />
<circle cx="5" cy="20" r="2" />
</svg>

Before

Width:  |  Height:  |  Size: 445 B

After

Width:  |  Height:  |  Size: 463 B

+4 -4
View File
@@ -9,8 +9,8 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M13 20H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H20a2 2 0 0 1 2 2v1.36" />
<path d="M19 12v6" />
<path d="M19 14h2" />
<circle cx="19" cy="20" r="2" />
<circle cx="16" cy="20" r="2" />
<path d="M10 20H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H20a2 2 0 0 1 2 2v2" />
<path d="m22 14-4.5 4.5" />
<path d="m21 15 1 1" />
</svg>

Before

Width:  |  Height:  |  Size: 413 B

After

Width:  |  Height:  |  Size: 418 B

+1 -10
View File
@@ -1,18 +1,9 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"ericfennis",
"jguddas"
"ericfennis"
],
"tags": [
"machinery",
"industrial",
"warehouse",
"lifting",
"storage",
"equipment",
"heavy-duty",
"moving",
"vehicle",
"transport",
"logistics"
+1 -4
View File
@@ -10,10 +10,7 @@
stroke-linejoin="round"
>
<path d="M12 12H5a2 2 0 0 0-2 2v5" />
<path d="M15 19h7" />
<path d="M16 19V2" />
<path d="M6 12V7a2 2 0 0 1 2-2h2.172a2 2 0 0 1 1.414.586l3.828 3.828A2 2 0 0 1 16 10.828" />
<path d="M7 19h4" />
<circle cx="13" cy="19" r="2" />
<circle cx="5" cy="19" r="2" />
<path d="M8 19h3m5-17v17h6M6 12V7c0-1.1.9-2 2-2h3l5 5" />
</svg>

Before

Width:  |  Height:  |  Size: 483 B

After

Width:  |  Height:  |  Size: 377 B

+1 -2
View File
@@ -4,8 +4,7 @@
"colebemis",
"csandman",
"ericfennis",
"jguddas",
"karsa-mistmere"
"jguddas"
],
"tags": [
"present",
+4 -4
View File
@@ -9,8 +9,8 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M12 7v14" />
<path d="M20 11v8a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-8" />
<path d="M7.5 7a1 1 0 0 1 0-5A4.8 8 0 0 1 12 7a4.8 8 0 0 1 4.5-5 1 1 0 0 1 0 5" />
<rect x="3" y="7" width="18" height="4" rx="1" />
<rect x="3" y="8" width="18" height="4" rx="1" />
<path d="M12 8v13" />
<path d="M19 12v7a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2v-7" />
<path d="M7.5 8a2.5 2.5 0 0 1 0-5A4.8 8 0 0 1 12 8a4.8 8 0 0 1 4.5-5 2.5 2.5 0 0 1 0 5" />
</svg>

Before

Width:  |  Height:  |  Size: 426 B

After

Width:  |  Height:  |  Size: 434 B

+1 -2
View File
@@ -3,8 +3,7 @@
"contributors": [
"colebemis",
"csandman",
"ericfennis",
"jguddas"
"ericfennis"
],
"tags": [
"code",
+2 -1
View File
@@ -9,7 +9,8 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M15 6a9 9 0 0 0-9 9V3" />
<line x1="6" x2="6" y1="3" y2="15" />
<circle cx="18" cy="6" r="3" />
<circle cx="6" cy="18" r="3" />
<path d="M18 9a9 9 0 0 1-9 9" />
</svg>

Before

Width:  |  Height:  |  Size: 313 B

After

Width:  |  Height:  |  Size: 351 B

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