Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/website/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export default defineConfig({
defaultStrategy: 'hover',
},
site: 'https://devicons.io',
trailingSlash: 'always',
build: {
format: 'directory',
inlineStylesheets: 'always',
Expand Down
4 changes: 2 additions & 2 deletions apps/website/src/components/docs/docs-pagination.astro
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const { prev, next } = Astro.props;
<nav aria-label="Docs navigation" class="docs-pagination">
{prev ? (
<a
href={`/docs/${prev.id}`}
href={`/docs/${prev.id}/`}
data-astro-prefetch
class="docs-pagination__card group">
<span class="type-meta group-hover:text-accent transition-colors">
Expand All @@ -27,7 +27,7 @@ const { prev, next } = Astro.props;
)}
{next ? (
<a
href={`/docs/${next.id}`}
href={`/docs/${next.id}/`}
data-astro-prefetch
class="docs-pagination__card docs-pagination__card--next group">
<span class="type-meta group-hover:text-accent transition-colors">
Expand Down
2 changes: 1 addition & 1 deletion apps/website/src/components/home/hero-section.astro
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ const initialDataUrl = `data:image/svg+xml;utf8,${encodeURIComponent(initialSvgD
class="hero-actions flex flex-wrap items-center gap-3 mt-5 sm:mt-10">
<a href="#icons" class="btn btn--primary btn--mono"> Browse Icons </a>
<a
href="/icons/popular"
href="/icons/popular/"
class="btn btn--mono"
data-astro-prefetch="viewport">
Popular
Expand Down
2 changes: 1 addition & 1 deletion apps/website/src/components/icons/icon-grid.astro
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const { items, emptyMessage = 'No icons found.' } = Astro.props;
{items.map(icon => (
<li>
<a
href={`/icons/${icon.slug}`}
href={`/icons/${icon.slug}/`}
data-astro-prefetch="viewport"
class:list={[
'icon-grid-card',
Expand Down
16 changes: 8 additions & 8 deletions apps/website/src/components/layout/footer.astro
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ const { brand } = Astro.props;
<div class="footer-col">
<p class="footer-col__label">[ Browse ]</p>
<ul class="footer-links">
<li><a href="/icons">All icons</a></li>
<li><a href="/icons/popular">Popular</a></li>
<li><a href="/packs">Packs</a></li>
<li><a href="/compare">Compare</a></li>
<li><a href="/search">Search</a></li>
<li><a href="/icons/">All icons</a></li>
<li><a href="/icons/popular/">Popular</a></li>
<li><a href="/packs/">Packs</a></li>
<li><a href="/compare/">Compare</a></li>
<li><a href="/search/">Search</a></li>
</ul>
</div>

Expand All @@ -37,7 +37,7 @@ const { brand } = Astro.props;
{
topTags.map(({ tag }) => (
<li>
<a href={`/icons/tag/${slugifyTag(tag)}`}>
<a href={`/icons/tag/${slugifyTag(tag)}/`}>
{formatTagLabel(tag)}
</a>
</li>
Expand All @@ -63,8 +63,8 @@ const { brand } = Astro.props;
rel="noopener noreferrer">npm ↗</a
>
</li>
<li><a href="/docs">Documentation</a></li>
<li><a href="/legal">Legal</a></li>
<li><a href="/docs/">Documentation</a></li>
<li><a href="/legal/">Legal</a></li>
</ul>
</div>

Expand Down
2 changes: 1 addition & 1 deletion apps/website/src/components/search/search-drawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ function DrawerBody({ icon }: { icon: IconEntry }) {
<div className="flex items-center justify-between gap-3">
<h2 className="type-h2 truncate">{icon.name}</h2>
<a
href={`/icons/${icon.id}`}
href={`/icons/${icon.id}/`}
data-astro-prefetch="viewport"
className="font-mono text-xs font-semibold uppercase tracking-[0.18em] leading-none text-accent hover:text-accent-hover transition-colors shrink-0">
Full page
Expand Down
2 changes: 1 addition & 1 deletion apps/website/src/components/seo/related-icons.astro
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ if (icons.length === 0) return;
{icons.map((icon) => (
<li>
<a
href={`/icons/${icon.slug}`}
href={`/icons/${icon.slug}/`}
data-astro-prefetch
class="flex items-center gap-3 p-3 border border-border hover:border-accent/60 transition-colors group"
>
Expand Down
2 changes: 1 addition & 1 deletion apps/website/src/layouts/Docs.astro
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ const jsonLd = [
isIndex && (
<div class="flex flex-wrap gap-2 pt-1">
<a
href="/docs/installation"
href="/docs/installation/"
data-astro-prefetch
class="btn btn--primary btn--mono btn--sm group">
<span>Get started</span>
Expand Down
15 changes: 8 additions & 7 deletions apps/website/src/layouts/Icon.astro
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const {
version,
website,
license,
brandGuidelines,
mainColor,
otherColors,
badInDark,
Expand Down Expand Up @@ -62,7 +63,7 @@ const jsonLd = [
{ name, url: `/icons/${slug}` },
]),
iconBrandSchema({ name, description, slug, website, mainColor }),
iconImageSchema({ name, slug, license }),
iconImageSchema({ name, slug, license, brandGuidelines }),
];

const colorChips = [
Expand Down Expand Up @@ -118,7 +119,7 @@ const related = await getRelatedIcons(slug, tags, 8);
{version && <Badge variant="neutral">v{version}</Badge>}
{icons.length > 1 ? (
<a
href={`/icons/${slug}/variants`}
href={`/icons/${slug}/variants/`}
data-astro-prefetch
class="inline-flex items-center px-1.5 py-0.5 border font-mono text-[10px] font-semibold uppercase tracking-[0.18em] leading-none bg-success/15 text-success border-success/20 hover:border-success transition-colors">
{icons.length} variants →
Expand Down Expand Up @@ -152,7 +153,7 @@ const related = await getRelatedIcons(slug, tags, 8);
const hexText = hex.toUpperCase();
return bucket ? (
<a
href={`/icons/color/${bucket}`}
href={`/icons/color/${bucket}/`}
aria-label={`${label} brand color ${hexText}`}
class={chipClass}>
{swatch}
Expand All @@ -174,7 +175,7 @@ const related = await getRelatedIcons(slug, tags, 8);
tags.length > 0 && (
<div class="flex gap-2 mt-3 sm:mt-4 flex-wrap">
{tags.map((tag: string) => (
<Tag href={`/icons/tag/${slugifyTag(tag)}`}>{tag}</Tag>
<Tag href={`/icons/tag/${slugifyTag(tag)}/`}>{tag}</Tag>
))}
</div>
)
Expand Down Expand Up @@ -227,7 +228,7 @@ const related = await getRelatedIcons(slug, tags, 8);
{comparisonPartners.map(c => (
<li>
<a
href={`/compare/${c.pairSlug}`}
href={`/compare/${c.pairSlug}/`}
data-astro-prefetch
class="inline-flex items-center gap-2 px-3 py-2 border border-border text-sm hover:border-accent hover:text-accent transition-colors">
<span class="font-mono text-xs uppercase tracking-caps text-text-muted">
Expand All @@ -253,7 +254,7 @@ const related = await getRelatedIcons(slug, tags, 8);
class="mt-12 sm:mt-16 pt-6 sm:pt-8 border-t border-border grid grid-cols-2 gap-3 sm:gap-4">
{prev ? (
<a
href={`/icons/${prev.slug}`}
href={`/icons/${prev.slug}/`}
data-astro-prefetch
class="group flex flex-col gap-2 p-4 border border-border hover:border-accent/60 transition-colors">
<span class="type-meta group-hover:text-accent transition-colors">
Expand All @@ -266,7 +267,7 @@ const related = await getRelatedIcons(slug, tags, 8);
)}
{next ? (
<a
href={`/icons/${next.slug}`}
href={`/icons/${next.slug}/`}
data-astro-prefetch
class="group flex flex-col gap-2 p-4 border border-border hover:border-accent/60 transition-colors text-right sm:col-start-2">
<span class="type-meta group-hover:text-accent transition-colors">
Expand Down
43 changes: 30 additions & 13 deletions apps/website/src/lib/seo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,18 @@ import {

type JsonLd = Record<string, unknown>;

const abs = (path: string, base: string = SITE_URL) =>
new URL(path, base).toString();
// Match the site's `trailingSlash: 'always'` so JSON-LD page URLs align with
// canonicals and Astro's emitted routes. Skips fragments, query strings, and
// asset paths (anything with a file extension) — those should stay as-is.
const abs = (path: string, base: string = SITE_URL) => {
const u = new URL(path, base);
const lastSegment = u.pathname.split('/').pop() ?? '';
const isAsset = lastSegment.includes('.');
if (!isAsset && !u.pathname.endsWith('/')) {
u.pathname = `${u.pathname}/`;
}
return u.toString();
};

const twitterUrl = () =>
TWITTER_HANDLE
Expand Down Expand Up @@ -70,6 +80,7 @@ interface IconSchemaInput {
website?: string;
mainColor?: string;
license?: string;
brandGuidelines?: string;
site?: string;
}

Expand All @@ -94,18 +105,24 @@ export const iconImageSchema = ({
name,
slug,
license,
brandGuidelines,
site = SITE_URL,
}: IconSchemaInput): JsonLd => ({
'@context': 'https://schema.org',
'@type': 'ImageObject',
contentUrl: abs(`/devicons/icons/${slug}.svg`, site),
url: abs(`/icons/${slug}`, site),
name: `${name} logo`,
encodingFormat: 'image/svg+xml',
...(license ? { license } : {}),
creditText: name,
creator: { '@id': abs('/#organization', site) },
});
}: IconSchemaInput): JsonLd => {
const pageUrl = abs(`/icons/${slug}`, site);
return {
'@context': 'https://schema.org',
'@type': 'ImageObject',
contentUrl: abs(`/devicons/icons/${slug}.svg`, site),
url: pageUrl,
name: `${name} logo`,
encodingFormat: 'image/svg+xml',
license: license ?? `${GITHUB_URL}/blob/main/LICENSE`,
acquireLicensePage: brandGuidelines ?? pageUrl,
copyrightNotice: `${name} logo © ${name}. SVG distributed under MIT by ${SITE_TITLE}.`,
creditText: name,
creator: { '@id': abs('/#organization', site) },
};
};

interface TechArticleInput {
title: string;
Expand Down
4 changes: 2 additions & 2 deletions apps/website/src/pages/404.astro
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ const TRACE = [

<div class="mt-8 md:mt-10 flex flex-wrap items-center gap-3">
<a href="/" class="btn btn--primary btn--mono px-8"> &larr; Home </a>
<a href="/docs" class="btn btn--ghost btn--mono"> Read the docs </a>
<a href="/icons" class="btn btn--subtle btn--mono">
<a href="/docs/" class="btn btn--ghost btn--mono"> Read the docs </a>
<a href="/icons/" class="btn btn--subtle btn--mono">
Browse icons
</a>
</div>
Expand Down
2 changes: 1 addition & 1 deletion apps/website/src/pages/compare/[slug].astro
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ const cards = [
)}
</dl>
<a
href={`/icons/${card.id}`}
href={`/icons/${card.id}/`}
data-astro-prefetch
class="btn btn--primary btn--mono btn--sm">
Open {card.data.name} page
Expand Down
2 changes: 1 addition & 1 deletion apps/website/src/pages/compare/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ const jsonLd = [
{items.map(pair => (
<li>
<a
href={`/compare/${pair.slug}`}
href={`/compare/${pair.slug}/`}
data-astro-prefetch="viewport"
class="compare-card">
<div class="compare-card__icons">
Expand Down
10 changes: 5 additions & 5 deletions apps/website/src/pages/icons/[slug]/[framework].astro
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ const { entry, framework } = Astro.props as {
entry: CollectionEntry<'icons'>;
framework: Framework;
};
const { name, description, icons: iconFiles, website, mainColor, license } =
const { name, description, icons: iconFiles, website, mainColor, license, brandGuidelines } =
entry.data;
const meta = FRAMEWORK_META[framework];
const cmp =
Expand Down Expand Up @@ -125,7 +125,7 @@ const jsonLd = [
website,
mainColor,
}),
iconImageSchema({ name, slug: entry.id, license }),
iconImageSchema({ name, slug: entry.id, license, brandGuidelines }),
];

const importSnippet = meta.buildImport(cmp);
Expand Down Expand Up @@ -203,7 +203,7 @@ const renderSnippet = meta.buildRender(cmp);
FRAMEWORKS.filter(f => f !== framework).map(f => (
<li>
<a
href={`/icons/${entry.id}/${f}`}
href={`/icons/${entry.id}/${f}/`}
data-astro-prefetch="viewport"
class="inline-flex items-center gap-2 px-3 py-1.5 font-mono text-[10px] font-semibold uppercase tracking-wider border border-border hover:border-accent hover:text-accent transition-colors">
{FRAMEWORK_META[f].label}
Expand All @@ -213,7 +213,7 @@ const renderSnippet = meta.buildRender(cmp);
}
<li>
<a
href={`/icons/${entry.id}`}
href={`/icons/${entry.id}/`}
data-astro-prefetch
class="inline-flex items-center gap-2 px-3 py-1.5 font-mono text-[10px] font-semibold uppercase tracking-wider border border-border hover:border-accent hover:text-accent transition-colors">
Full {name} page →
Expand All @@ -224,7 +224,7 @@ const renderSnippet = meta.buildRender(cmp);

<nav class="mt-12 pt-6 border-t border-border">
<a
href={`/docs/${framework === 'font' ? 'icon-font' : framework}`}
href={`/docs/${framework === 'font' ? 'icon-font' : framework}/`}
data-astro-prefetch
class="font-mono text-xs font-semibold uppercase tracking-caps text-text-muted hover:text-accent transition-colors">
{meta.label} framework guide →
Expand Down
2 changes: 1 addition & 1 deletion apps/website/src/pages/icons/[slug]/variants.astro
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ const jsonLd = [

<nav class="mt-12 pt-6 border-t border-border">
<a
href={`/icons/${entry.id}`}
href={`/icons/${entry.id}/`}
data-astro-prefetch
class="font-mono text-xs font-semibold uppercase tracking-caps text-text-muted hover:text-accent transition-colors">
← Back to {name}
Expand Down
2 changes: 1 addition & 1 deletion apps/website/src/pages/icons/color/[color].astro
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ const otherBuckets = COLOR_BUCKETS.filter((b) => b.slug !== bucket.slug);
{otherBuckets.map((b) => (
<li>
<a
href={`/icons/color/${b.slug}`}
href={`/icons/color/${b.slug}/`}
class="inline-flex items-center gap-2 px-3 py-1.5 font-mono text-[10px] font-semibold uppercase tracking-wider border border-border hover:border-accent hover:text-accent transition-colors"
>
<span
Expand Down
2 changes: 1 addition & 1 deletion apps/website/src/pages/icons/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ const jsonLd = [
tags.map(({ tag, count }) => (
<li>
<a
href={`/icons/tag/${slugifyTag(tag)}`}
href={`/icons/tag/${slugifyTag(tag)}/`}
data-astro-prefetch="viewport"
class="inline-flex items-center gap-2 px-3 py-1.5 font-mono text-[10px] font-semibold uppercase tracking-wider border border-border hover:border-accent hover:text-accent transition-colors">
{formatTagLabel(tag)}{' '}
Expand Down
2 changes: 1 addition & 1 deletion apps/website/src/pages/icons/tag/[tag].astro
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ const jsonLd = [
relatedTags.map(t => (
<li>
<a
href={`/icons/tag/${t.slug}`}
href={`/icons/tag/${t.slug}/`}
class="inline-flex items-center gap-2 px-3 py-1.5 font-mono text-[10px] font-semibold uppercase tracking-wider border border-border hover:border-accent hover:text-accent transition-colors">
{formatTagLabel(t.tag)}{' '}
<span class="text-text-muted">{t.count}</span>
Expand Down
2 changes: 1 addition & 1 deletion apps/website/src/pages/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ const jsonLd = [
</p>
<h2 class="type-display-sm mb-8">Ship with better icons.</h2>
<a
href="/docs"
href="/docs/"
class="btn btn--primary btn--mono"
data-astro-prefetch="viewport">
Read the docs
Expand Down
2 changes: 1 addition & 1 deletion apps/website/src/pages/packs/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ const jsonLd = [
packs.map(pack => (
<li>
<a
href={`/packs/${pack.id}`}
href={`/packs/${pack.id}/`}
data-astro-prefetch="viewport"
class="block p-6 border border-border hover:border-accent/60 transition-colors group">
<h2 class="type-h3 group-hover:text-accent transition-colors">
Expand Down
2 changes: 1 addition & 1 deletion apps/website/src/pages/use-cases/[slug].astro
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ const jsonLd = [
{related.map(r => (
<li>
<a
href={`/use-cases/${r.slug}`}
href={`/use-cases/${r.slug}/`}
data-astro-prefetch="viewport"
class="inline-flex items-center gap-2 px-3 py-1.5 font-mono text-[10px] font-semibold uppercase tracking-wider border border-border hover:border-accent hover:text-accent transition-colors">
{r.title}
Expand Down
2 changes: 1 addition & 1 deletion apps/website/src/pages/use-cases/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ const jsonLd = [
USE_CASES.map(uc => (
<li>
<a
href={`/use-cases/${uc.slug}`}
href={`/use-cases/${uc.slug}/`}
data-astro-prefetch="viewport"
class="block p-6 border border-border hover:border-accent transition-colors">
<p class="type-label mb-2 text-text-muted">{uc.eyebrow}</p>
Expand Down
Loading