From a28631e2a42e174e88fe3663e425eb2a69e4eeb1 Mon Sep 17 00:00:00 2001 From: Eric Fennis Date: Thu, 29 Jan 2026 16:19:32 +0100 Subject: [PATCH] chore(docs): Improve SEO icon detail pages (#4040) * improve-seo-website * Add some description texts --- docs/.vitepress/api/icons/[iconName].get.ts | 2 + docs/.vitepress/config.ts | 31 +++++++++++++- docs/.vitepress/getStructuredData.ts | 46 +++++++++++++++++++++ docs/contributing.md | 2 + docs/icons/categories.md | 1 + docs/icons/index.md | 1 + docs/license.md | 1 + docs/packages.md | 1 + 8 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 docs/.vitepress/getStructuredData.ts diff --git a/docs/.vitepress/api/icons/[iconName].get.ts b/docs/.vitepress/api/icons/[iconName].get.ts index e2d2091bb..733eeb5ea 100644 --- a/docs/.vitepress/api/icons/[iconName].get.ts +++ b/docs/.vitepress/api/icons/[iconName].get.ts @@ -22,6 +22,7 @@ export default eventHandler((event) => { const height = getQuery(event).height || undefined; const color = getQuery(event).color || undefined; const strokeWidth = getQuery(event).strokeWidth || undefined; + const background = getQuery(event).background || undefined; const LucideIcon = createLucideIcon(params.iconName, iconNode); @@ -32,6 +33,7 @@ export default eventHandler((event) => { height, color: color ? `#${color}` : undefined, strokeWidth, + style: background ? { background } : undefined, }), ), ).toString('utf8'); diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 45325d8c4..c0e788f0e 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -1,6 +1,7 @@ import { fileURLToPath, URL } from 'node:url'; import { defineConfig } from 'vitepress'; import sidebar from './sidebar'; +import getStructuredData from './getStructuredData'; const title = 'Lucide'; const socialTitle = 'Lucide Icons'; @@ -151,8 +152,36 @@ export default defineConfig({ }, ], ], + async transformPageData(pageData) { + if ( + pageData.relativePath.startsWith('icons/') && + !pageData.relativePath.startsWith('icons/lab/') && + pageData.params?.name + ) { + const iconName = pageData.params.name; + pageData.title = `${iconName} icon details`; + + const taggedAs = pageData.params?.tags?.length + ? `Tagged as: ${pageData.params.tags.join(', ')}.` + : ''; + const categorizedIn = pageData.params?.category?.length + ? `Categorized in: ${pageData.params.category.join(', ')}.` + : ''; + + pageData.description = + `Details and related icons for ${iconName} icon. ${taggedAs} ${categorizedIn}`.trim(); + + const structuredData = await getStructuredData(iconName, pageData); + + pageData.frontmatter.head ??= []; + pageData.frontmatter.head.push([ + 'script', + { type: 'application/ld+json' }, + JSON.stringify(structuredData), + ]); + } + }, themeConfig: { - // https://vitepress.dev/reference/default-theme-config logo: { light: '/logo.light.svg', dark: '/logo.dark.svg', diff --git a/docs/.vitepress/getStructuredData.ts b/docs/.vitepress/getStructuredData.ts new file mode 100644 index 000000000..2ac47df1e --- /dev/null +++ b/docs/.vitepress/getStructuredData.ts @@ -0,0 +1,46 @@ +import { PageData } from 'vitepress'; + +export default async function getStructuredData(iconName: string, pageData: PageData) { + const url = `https://lucide.dev/icons/${iconName}`; + + return { + '@context': 'https://schema.org', + '@type': 'WebPage', + '@id': url, + name: pageData.title, + description: pageData.description, + url, + inLanguage: 'en', + isPartOf: { + '@type': 'WebSite', + name: 'Lucide Icons', + url: 'https://lucide.dev', + }, + breadcrumb: { + '@type': 'BreadcrumbList', + itemListElement: [ + { '@type': 'ListItem', position: 1, name: 'Icons', item: 'https://lucide.dev/icons' }, + { '@type': 'ListItem', position: 2, name: iconName, item: url }, + ], + }, + mainEntity: { + '@type': 'ImageObject', + '@id': `${url}#icon`, + name: iconName, + // TODO: Add support for description extraction from icon metadata + // description: 'An ...SVG icon from the Lucide icon set.', + contentUrl: `https://lucide.dev/api/icons/${iconName}}?width=24&height=24&background=white`, + thumbnailUrl: `https://lucide.dev/api/icons/${iconName}}?width=256&height=256&background=white`, + encodingFormat: 'image/svg+xml', + keywords: pageData.params?.tags, + width: 24, + height: 24, + creator: { + '@type': 'Organization', + name: 'Lucide Icons', + }, + datePublished: pageData?.params?.createdRelease?.date, + dateModified: pageData?.params?.changedRelease?.date, + }, + }; +} diff --git a/docs/contributing.md b/docs/contributing.md index 8176e1b59..3834bd059 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -1,5 +1,7 @@ --- aside: false +title: Contributing to Lucide +description: Guidelines and instructions for contributing to the Lucide project. --- diff --git a/docs/icons/categories.md b/docs/icons/categories.md index 04c507358..2dc1a9d44 100644 --- a/docs/icons/categories.md +++ b/docs/icons/categories.md @@ -1,5 +1,6 @@ --- title: Categories +description: Explore Lucide icons organized into various categories for easier browsing. layout: page outline: 2 outlineTitle: Categories diff --git a/docs/icons/index.md b/docs/icons/index.md index c5df9e1b9..9da20410b 100644 --- a/docs/icons/index.md +++ b/docs/icons/index.md @@ -1,5 +1,6 @@ --- title: Icons +description: Browse all Lucide icons. layout: page outline: 2 outlineTitle: Categories diff --git a/docs/license.md b/docs/license.md index c7b1d66c7..ee82f244f 100644 --- a/docs/license.md +++ b/docs/license.md @@ -1,5 +1,6 @@ --- title: License +description: Review the licensing terms for using Lucide icons and resources. aside: false editLink: false --- diff --git a/docs/packages.md b/docs/packages.md index 4e466cae6..6dd9e22ed 100644 --- a/docs/packages.md +++ b/docs/packages.md @@ -1,5 +1,6 @@ --- title: Packages +description: Discover the various packages available in the Lucide ecosystem. layout: page outline: 2 outlineTitle: Packages