chore(site): Updates to site and updated carbon ads (#4359)

* Update readme

* Add carbon ads component

* Adjust config

* Adjust ads

* minor fixes

* Remove script

* Remove commented code

* Revert icon changes

* Format code

* Apply some feedback from Copilot
This commit is contained in:
Eric Fennis
2026-05-12 08:09:01 +02:00
committed by GitHub
parent fb885f333a
commit 437c370821
9 changed files with 366 additions and 200 deletions

View File

@@ -17,6 +17,7 @@ packages/lucide-static/sprite.svg
packages/lucide-static/tags.json
packages/lucide-static/icon-nodes.json
packages/lucide-static/font
packages/svelte/.svelte-kit
# lucide-svelte
packages/lucide-svelte/src/icons/*.svelte

View File

@@ -1,14 +1,14 @@
import { fileURLToPath, URL } from 'node:url';
import { defineConfig } from 'vitepress';
import { defineConfig, UserConfig } from 'vitepress';
import { groupIconMdPlugin, groupIconVitePlugin } from 'vitepress-plugin-group-icons';
import sidebar from './sidebar';
import snackPlayer from './markdown/snackPlayer';
import sandpackPlugin from './markdown/sandpack';
import { readFile } from 'node:fs/promises';
import { resourcesSidebar } from './sidebar/resources';
import getStructuredData from './getStructuredData';
import { createGeneralOGImage, createIconOGImage } from './createOGImage';
import llmstxt from 'vitepress-plugin-llms';
import { transformPageData } from './transformPageData';
import getHeadConfig from './getHeadConfig';
const defaultSandpackCSS = await readFile(
fileURLToPath(new URL('./theme/sandpack-default.css', import.meta.url)),
@@ -56,6 +56,12 @@ export default defineConfig({
new URL('./theme/components/overrides/VPFooter.vue', import.meta.url),
),
},
{
find: /^.*\/VPCarbonAds\.vue$/,
replacement: fileURLToPath(
new URL('./theme/components/overrides/VPCarbonAds.vue', import.meta.url),
),
},
{
find: '~/.vitepress',
replacement: fileURLToPath(new URL('./', import.meta.url)),
@@ -73,199 +79,11 @@ export default defineConfig({
'brand-logo-statement.md',
'icons/**', // Not working, need investigation
],
}),
}) as unknown as UserConfig['vite']['plugins'][0],
],
},
head: [
[
'link',
{
rel: 'preconnect',
href: 'https://analytics.lucide.dev',
},
],
[
'script',
{
src: 'https://analytics.lucide.dev/js/script.js',
'data-domain': 'lucide.dev',
defer: '',
},
],
[
'meta',
{
property: 'og:locale',
content: 'en_US',
},
],
[
'meta',
{
property: 'og:type',
content: 'website',
},
],
[
'meta',
{
property: 'og:site_name',
content: title,
},
],
[
'meta',
{
property: 'og:title',
content: socialTitle,
},
],
[
'meta',
{
property: 'og:description',
content: description,
},
],
[
'meta',
{
property: 'og:url',
content: 'https://lucide.dev',
},
],
[
'meta',
{
property: 'og:image',
content: 'https://lucide.dev/og.png',
},
],
[
'meta',
{
property: 'og:image:width',
content: '1200',
},
],
[
'meta',
{
property: 'og:image:height',
content: '630',
},
],
[
'meta',
{
property: 'og:image:type',
content: 'image/png',
},
],
[
'meta',
{
property: 'twitter:card',
content: 'summary_large_image',
},
],
[
'meta',
{
property: 'twitter:title',
content: socialTitle,
},
],
[
'meta',
{
property: 'twitter:description',
content: description,
},
],
[
'meta',
{
property: 'twitter:image',
content: 'https://lucide.dev/og.png',
},
],
],
async transformPageData(pageData) {
pageData.frontmatter.head ??= [];
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);
const ogPath = await createIconOGImage(iconName, pageData.params?.tags || []);
if (ogPath) {
const content = `https://lucide.dev${ogPath}`;
pageData.frontmatter.head.push(
[
'meta',
{
property: 'og:image',
content,
},
],
[
'meta',
{
property: 'twitter:image',
content,
},
],
);
}
pageData.frontmatter.head.push([
'script',
{ type: 'application/ld+json' },
JSON.stringify(structuredData),
]);
}
if (pageData.relativePath.startsWith('guide/')) {
const ogPath = await createGeneralOGImage(pageData);
if (ogPath) {
const content = `https://lucide.dev${ogPath}`;
pageData.frontmatter.head.push(
[
'meta',
{
property: 'og:image',
content,
},
],
[
'meta',
{
property: 'twitter:image',
content,
},
],
);
}
}
},
head: getHeadConfig({ title, description, socialTitle }),
transformPageData,
themeConfig: {
logo: {
light: '/logo.light.svg',

View File

@@ -0,0 +1,127 @@
import { HeadConfig } from 'vitepress';
const getHeadConfig = ({
title,
description,
socialTitle,
}: {
title: string;
description: string;
socialTitle?: string;
}): HeadConfig[] => [
[
'link',
{
rel: 'preconnect',
href: 'https://analytics.lucide.dev',
},
],
[
'script',
{
src: 'https://analytics.lucide.dev/js/script.js',
'data-domain': 'lucide.dev',
defer: '',
},
],
[
'meta',
{
property: 'og:locale',
content: 'en_US',
},
],
[
'meta',
{
property: 'og:type',
content: 'website',
},
],
[
'meta',
{
property: 'og:site_name',
content: title,
},
],
[
'meta',
{
property: 'og:title',
content: socialTitle,
},
],
[
'meta',
{
property: 'og:description',
content: description,
},
],
[
'meta',
{
property: 'og:url',
content: 'https://lucide.dev',
},
],
[
'meta',
{
property: 'og:image',
content: 'https://lucide.dev/og.png',
},
],
[
'meta',
{
property: 'og:image:width',
content: '1200',
},
],
[
'meta',
{
property: 'og:image:height',
content: '630',
},
],
[
'meta',
{
property: 'og:image:type',
content: 'image/png',
},
],
[
'meta',
{
property: 'twitter:card',
content: 'summary_large_image',
},
],
[
'meta',
{
property: 'twitter:title',
content: socialTitle,
},
],
[
'meta',
{
property: 'twitter:description',
content: description,
},
],
[
'meta',
{
property: 'twitter:image',
content: 'https://lucide.dev/og.png',
},
],
];
export default getHeadConfig;

View File

@@ -50,7 +50,7 @@ onMounted(() => {
absoluteStrokeWidth
/>
</IconButton>
<VPDocAsideCarbonAds :carbon-ads="theme.carbonAds" />
<VPDocAsideCarbonAds :carbon-ads="theme.carbonAds" overlay />
</div>
</template>

View File

@@ -9,9 +9,7 @@
margin-bottom: 16px;
position: sticky;
z-index: 10;
top: 48px;
padding-top: 24px;
margin-top: -32px;
top: var(--vp-layout-top-height);
width: 100%;
display: flex;
margin-bottom: 32px;
@@ -22,7 +20,8 @@
@media (min-width: 960px) {
.search-bar {
padding-top: 32px;
top: 64px;
margin-top: calc((var(--vp-layout-top-height, 0px) + 32px) * -1);
top: calc(var(--vp-layout-top-height, 0px) + 64px);
}
}
</style>

View File

@@ -0,0 +1,133 @@
<script setup lang="ts">
import type { DefaultTheme } from 'vitepress/theme'
import { ref, watch, onMounted } from 'vue'
import { useAside } from 'vitepress/dist/client/theme-default/composables/aside.js'
import { useData } from 'vitepress/dist/client/theme-default/composables/data.js'
const { page } = useData()
const props = defineProps<{
carbonAds: DefaultTheme.CarbonAdsOptions
overlay?: boolean
}>()
const carbonOptions = props.carbonAds
const { isAsideEnabled } = useAside()
const container = ref()
let isInitialized = false
function init() {
if (!isInitialized) {
isInitialized = true
const params = new URLSearchParams({
serve: carbonOptions.code,
placement: carbonOptions.placement,
format: 'responsive',
})
const s = document.createElement('script')
s.id = '_carbonads_js'
s.src = `//cdn.carbonads.com/carbon.js?${params.toString()}`
s.async = true
container.value.appendChild(s)
}
}
watch(() => page.value.relativePath, () => {
if (isInitialized && isAsideEnabled.value) {
;(window as any)._carbonads?.refresh()
}
})
// no need to account for option changes during dev, we can just
// refresh the page
if (carbonOptions) {
onMounted(() => {
// if the page is loaded when aside is active, load carbon directly.
// otherwise, only load it if the page resizes to wide enough. this avoids
// loading carbon at all on mobile where it's never shown
if (isAsideEnabled.value || props.overlay) {
init()
} else {
watch(isAsideEnabled, (wide) => wide && init())
}
})
}
</script>
<template>
<div class="VPCarbonAds" ref="container" />
</template>
<style scoped>
.VPCarbonAds {
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
border-radius: 12px;
min-height: 256px;
text-align: center;
line-height: 18px;
font-size: 12px;
font-weight: 500;
background-color: var(--vp-carbon-ads-bg-color);
}
.VPCarbonAds :deep(img) {
margin: 0 auto;
border-radius: 6px;
}
.VPCarbonAds :deep(.carbon-text) {
display: block;
margin: 0 auto;
padding-top: 12px;
color: var(--vp-carbon-ads-text-color);
transition: color 0.25s;
}
.VPCarbonAds :deep(.carbon-text:hover) {
color: var(--vp-carbon-ads-hover-text-color);
}
.VPCarbonAds :deep(#carbon-responsive .carbon-poweredby),
.VPCarbonAds :deep(.carbon-poweredby) {
display: block;
padding-bottom: 2px;
font-size: 11px;
font-weight: 500;
color: var(--vp-carbon-ads-poweredby-color);
text-transform: uppercase;
transition: color 0.25s;
}
.VPCarbonAds :deep(#carbon-responsive) {
font-size: 12px;
line-height: 18px;
font-weight: 500;
}
.VPCarbonAds :deep(#carbon-responsive .carbon-poweredby) {
text-align: left;
}
.VPCarbonAds :deep(#carbon-responsive .carbon-poweredby:hover),
.VPCarbonAds :deep(.carbon-poweredby:hover) {
color: var(--vp-carbon-ads-hover-poweredby-color);
}
.VPCarbonAds :deep(> div) {
display: none;
}
.VPCarbonAds :deep(> div:first-of-type) {
display: block;
}
.VPCarbonAds :deep(#carbon-responsive) {
flex-direction: column-reverse;
}
.VPCarbonAds :deep(#carbon-responsive .carbon-responsive-wrap) {
border-radius: 10px;
}
</style>

View File

@@ -23,7 +23,8 @@ const theme: Partial<Theme> = {
'sidebar-nav-after': () => h(IconsSidebarNavAfter),
'home-hero-image': () => h(HomeHeroIconsCard),
'home-hero-actions-after': () => h(HomeHeroAfter),
'layout-top': () => h(LayoutTop),
// Keep this here for now, we might want to add more things to the top bar in the future
// 'layout-top': () => h(LayoutTop),
});
},
enhanceApp({ app }) {

View File

@@ -144,6 +144,14 @@
.VPHomeHero .container {
flex-direction: row;
}
html.has-bb-banner {
--vp-layout-top-height: 72px;
}
html.has-bb-banner {
--Dark-Background: #161618;
}
}
.VPNavBarHamburger .container > span {

View File

@@ -0,0 +1,79 @@
import { type PageData } from 'vitepress';
import getStructuredData from './getStructuredData';
import { createGeneralOGImage, createIconOGImage } from './createOGImage';
export async function transformPageData(pageData: PageData) {
pageData.frontmatter.head ??= [];
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);
const ogPath = await createIconOGImage(iconName, pageData.params?.tags || []);
if (ogPath) {
const content = `https://lucide.dev${ogPath}`;
pageData.frontmatter.head.push(
[
'meta',
{
property: 'og:image',
content,
},
],
[
'meta',
{
property: 'twitter:image',
content,
},
],
);
}
pageData.frontmatter.head.push([
'script',
{ type: 'application/ld+json' },
JSON.stringify(structuredData),
]);
}
if (pageData.relativePath.startsWith('guide/')) {
const ogPath = await createGeneralOGImage(pageData);
if (ogPath) {
const content = `https://lucide.dev${ogPath}`;
pageData.frontmatter.head.push(
[
'meta',
{
property: 'og:image',
content,
},
],
[
'meta',
{
property: 'twitter:image',
content,
},
],
);
}
}
}