feat(website): enhance web app support and icon metadata

- Add PWA-compatible icons in multiple sizes:
  - android-chrome-192x192.png
  - android-chrome-512x512.png
  - apple-touch-icon.png
  - favicon-16x16.png
  - favicon-32x32.png
- Modernize favicon handling:
  - Update favicon.ico
  - Remove deprecated favicon.png and favicon.svg
- Add site.webmanifest for better PWA integration
- Update metadata to reflect total icon count across pages
This commit is contained in:
Bjorn Lammers 2025-04-16 22:51:23 +02:00 committed by Thomas Camlong
parent 9dec2c6daa
commit 6041e37119
No known key found for this signature in database
GPG Key ID: A678F374F428457B
13 changed files with 175 additions and 130 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 753 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" viewBox="0 82.9 512 346.2"><path d="M169.3 102.7c28.8 0 52.2 23.4 52.2 52.2v66.8c1.8-.6 3.6-1 5.5-1 5.9 0 11.1 2.9 14.2 7.4v-73.2c0-39.7-32.3-72-72-72-24.8 0-46.8 12.6-59.7 31.8 5.6 3.6 10.9 7.5 16 11.8 9.4-14.3 25.5-23.8 43.8-23.8m115.6 118.1c1.9 0 3.8.4 5.5 1V155c0-28.8 23.4-52.2 52.2-52.2 18.3 0 34.4 9.5 43.8 23.8 5.1-4.2 10.4-8.2 16-11.8C389.5 95.6 367.5 83 342.7 83c-39.7 0-72 32.3-72 72v73.2c3.1-4.5 8.3-7.4 14.2-7.4m-69.3 104.8c-35.7 8.6-66 28.1-88.1 54-12.2 14.3-21.9 30.6-28.6 48l46.6.3 265.5 1.2v-.1c-29.2-77.8-112.5-123.4-195.4-103.4m11.5-61.9c-9.7 0-17.5 7.8-17.5 17.5s7.8 17.5 17.5 17.5 17.5-7.8 17.5-17.5-7.8-17.5-17.5-17.5m57.8 35.1c9.7 0 17.5-7.8 17.5-17.5s-7.8-17.5-17.5-17.5-17.5 7.8-17.5 17.5 7.8 17.5 17.5 17.5m162.5-75.6 26.7-108.8c-22.6 2.8-43.5 11.1-61.5 23.4-6.3 4.3-12.3 9.2-17.8 14.4-26.4 25.4-42.9 61.1-42.9 100.6 0 30.7 9.9 59.1 26.7 82.1 9.2 12.7 20.6 23.7 33.4 32.6l9.3-37.8c47.9-14.3 84.1-55.7 90.7-106.5zM133.5 334.9c16.8-23 26.7-51.4 26.7-82.1 0-39.5-16.5-75.2-42.9-100.6-5.5-5.3-11.5-10.1-17.8-14.4-17.9-12.3-38.8-20.6-61.5-23.4l26.7 108.8H0c6.6 50.8 42.8 92.2 90.7 106.5l9.3 37.8c12.9-8.9 24.2-19.9 33.5-32.6" style="fill:#fa5252"/></svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1 @@
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#FA5252","background_color":"#1B1B1D","display":"standalone"}

View File

@ -30,16 +30,26 @@ export async function generateMetadata({ params, searchParams }: Props, parent:
const authorData = await getAuthorData(iconsData[icon].update.author.id) const authorData = await getAuthorData(iconsData[icon].update.author.id)
const authorName = authorData.name || authorData.login const authorName = authorData.name || authorData.login
const updateDate = new Date(iconsData[icon].update.timestamp) const updateDate = new Date(iconsData[icon].update.timestamp)
const totalIcons = Object.keys(iconsData).length
console.debug(`Generated metadata for ${icon} by ${authorName} (${authorData.html_url}) updated at ${updateDate.toLocaleString()}`) console.debug(`Generated metadata for ${icon} by ${authorName} (${authorData.html_url}) updated at ${updateDate.toLocaleString()}`)
const iconImageUrl = `${BASE_URL}/png/${icon}.png` const iconImageUrl = `${BASE_URL}/png/${icon}.png`
const pageUrl = `${BASE_URL}/icons/${icon}` const pageUrl = `${BASE_URL}/icons/${icon}`
const formattedIconName = icon.split('-').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(' ')
return { return {
title: `${icon} icon · Dashboard Icons`, title: `${formattedIconName} Icon | Dashboard Icons`,
description: `Download and use the ${icon} icon from Dashboard Icons for your applications`, description: `Download the ${formattedIconName} icon in SVG, PNG, and WEBP formats. Part of a collection of ${totalIcons} curated icons for services, applications and tools, designed specifically for dashboards and app directories.`,
keywords: [`${icon} icon`, "dashboard icon", "free icon", "open source icon", "application icon"], keywords: [
`${formattedIconName} icon`,
"dashboard icon",
"service icon",
"application icon",
"tool icon",
"web dashboard",
"app directory",
],
authors: [ authors: [
{ {
name: "homarr", name: "homarr",
@ -51,8 +61,8 @@ export async function generateMetadata({ params, searchParams }: Props, parent:
}, },
], ],
openGraph: { openGraph: {
title: `${icon} icon · Dashboard Icons`, title: `${formattedIconName} Icon | Dashboard Icons`,
description: `Download and use the ${icon} icon from Dashboard Icons for your applications`, description: `Download the ${formattedIconName} icon in SVG, PNG, and WEBP formats. Part of a collection of ${totalIcons} curated icons for services, applications and tools, designed specifically for dashboards and app directories.`,
type: "article", type: "article",
url: pageUrl, url: pageUrl,
images: [ images: [
@ -60,7 +70,7 @@ export async function generateMetadata({ params, searchParams }: Props, parent:
url: iconImageUrl, url: iconImageUrl,
width: 512, width: 512,
height: 512, height: 512,
alt: `${icon} icon`, alt: `${formattedIconName} Icon`,
type: "image/png", type: "image/png",
}, },
...previousImages, ...previousImages,
@ -71,10 +81,10 @@ export async function generateMetadata({ params, searchParams }: Props, parent:
}, },
twitter: { twitter: {
card: "summary_large_image", card: "summary_large_image",
title: `${icon} icon · Dashboard Icons`, title: `${formattedIconName} Icon | Dashboard Icons`,
description: `Download and use the ${icon} icon from Dashboard Icons for your applications`, description: `Download the ${formattedIconName} icon in SVG, PNG, and WEBP formats. Part of a collection of ${totalIcons} curated icons for services, applications and tools, designed specifically for dashboards and app directories.`,
images: [iconImageUrl], images: [iconImageUrl],
creator: "@ajnavocado", creator: "@homarr_app",
}, },
alternates: { alternates: {
canonical: pageUrl, canonical: pageUrl,

View File

@ -3,13 +3,26 @@ import { getIconsArray } from "@/lib/api"
import type { Metadata } from "next" import type { Metadata } from "next"
import { IconSearch } from "./components/icon-search" import { IconSearch } from "./components/icon-search"
export const metadata: Metadata = { export async function generateMetadata(): Promise<Metadata> {
title: "Browse icons | Dashboard Icons", const icons = await getIconsArray()
description: "Search and browse through our collection of beautiful dashboard icons for your applications", const totalIcons = icons.length
keywords: ["dashboard icons", "browse icons", "icon search", "free icons", "open source icons"],
return {
title: "Browse Icons | Dashboard Icons",
description: `Search and browse through our collection of ${totalIcons} curated icons for services, applications and tools, designed specifically for dashboards and app directories.`,
keywords: [
"browse icons",
"dashboard icons",
"icon search",
"service icons",
"application icons",
"tool icons",
"web dashboard",
"app directory",
],
openGraph: { openGraph: {
title: "Browse Dashboard Icons Collection", title: "Browse Dashboard Icons Collection",
description: "Search and browse through our collection of beautiful dashboard icons for your applications", description: `Search and browse through our collection of ${totalIcons} curated icons for services, applications and tools, designed specifically for dashboards and app directories.`,
type: "website", type: "website",
url: `${BASE_URL}/icons`, url: `${BASE_URL}/icons`,
images: [ images: [
@ -17,7 +30,7 @@ export const metadata: Metadata = {
url: "/og-image-browse.png", url: "/og-image-browse.png",
width: 1200, width: 1200,
height: 630, height: 630,
alt: "Browse Dashboard Icons", alt: "Browse Dashboard Icons Collection",
type: "image/png", type: "image/png",
}, },
], ],
@ -25,13 +38,14 @@ export const metadata: Metadata = {
twitter: { twitter: {
card: "summary_large_image", card: "summary_large_image",
title: "Browse Dashboard Icons Collection", title: "Browse Dashboard Icons Collection",
description: "Search and browse through our collection of beautiful dashboard icons for your applications", description: `Search and browse through our collection of ${totalIcons} curated icons for services, applications and tools, designed specifically for dashboards and app directories.`,
images: ["/og-image-browse.png"], images: ["/og-image-browse.png"],
}, },
alternates: { alternates: {
canonical: `${BASE_URL}/icons`, canonical: `${BASE_URL}/icons`,
}, },
} }
}
export const dynamic = "force-static" export const dynamic = "force-static"

View File

@ -6,6 +6,7 @@ import { Inter } from "next/font/google";
import { Toaster } from "sonner"; import { Toaster } from "sonner";
import "./globals.css"; import "./globals.css";
import { ThemeProvider } from "./theme-provider"; import { ThemeProvider } from "./theme-provider";
import { getTotalIcons } from "@/lib/api"
const inter = Inter({ const inter = Inter({
variable: "--font-inter", variable: "--font-inter",
@ -18,16 +19,20 @@ export const viewport: Viewport = {
themeColor: "#ffffff", themeColor: "#ffffff",
}; };
export const metadata: Metadata = { export async function generateMetadata(): Promise<Metadata> {
metadataBase: new URL("https://icons.homarr.dev"), const { totalIcons } = await getTotalIcons()
title: "Dashboard Icons",
description: "Curated icons for your dashboard", return {
metadataBase: new URL("https://dashboardicons.com"),
title: "Dashboard Icons - Your definitive source for dashboard icons",
description: `A collection of ${totalIcons} curated icons for services, applications and tools, designed specifically for dashboards and app directories.`,
keywords: [ keywords: [
"dashboard", "dashboard icons",
"icons", "service icons",
"open source", "application icons",
"free icons", "tool icons",
"dashboard design", "web dashboard",
"app directory",
], ],
robots: { robots: {
index: true, index: true,
@ -41,9 +46,9 @@ export const metadata: Metadata = {
siteName: "Dashboard Icons", siteName: "Dashboard Icons",
type: "website", type: "website",
locale: "en_US", locale: "en_US",
title: "Dashboard Icons", title: "Dashboard Icons - Your definitive source for dashboard icons",
description: "Curated icons for your dashboard", description: `A collection of ${totalIcons} curated icons for services, applications and tools, designed specifically for dashboards and app directories.`,
url: "https://icons.homarr.dev", url: "https://dashboardicons.com",
images: [ images: [
{ {
url: "/og-image.png", url: "/og-image.png",
@ -58,8 +63,8 @@ export const metadata: Metadata = {
card: "summary_large_image", card: "summary_large_image",
site: "@homarr_app", site: "@homarr_app",
creator: "@homarr_app", creator: "@homarr_app",
title: "Dashboard Icons", title: "Dashboard Icons - Your definitive source for dashboard icons",
description: "Curated icons for your dashboard", description: `A collection of ${totalIcons} curated icons for services, applications and tools, designed specifically for dashboards and app directories.`,
images: ["/og-image.png"], images: ["/og-image.png"],
}, },
applicationName: "Dashboard Icons", applicationName: "Dashboard Icons",
@ -70,24 +75,29 @@ export const metadata: Metadata = {
}, },
alternates: { alternates: {
types: { types: {
"application/rss+xml": "https://icons.homarr.dev/rss.xml", "application/rss+xml": "https://dashboardicons.com/rss.xml",
}, },
}, },
icons: { icons: {
icon: [ icon: [
{ { url: "/favicon.ico", sizes: "any" },
url: "/favicon.ico", { url: "/favicon-16x16.png", sizes: "16x16", type: "image/png" },
type: "image/x-icon", { url: "/favicon-32x32.png", sizes: "32x32", type: "image/png" },
},
], ],
shortcut: [ apple: [
{ url: "/apple-touch-icon.png", sizes: "180x180", type: "image/png" },
],
other: [
{ {
url: "/favicon.ico", rel: "mask-icon",
type: "image/x-icon", url: "/safari-pinned-tab.svg",
color: "#000000",
}, },
], ],
}, },
}; manifest: "/site.webmanifest",
}
}
export default function RootLayout({ export default function RootLayout({
children, children,

View File

@ -3,13 +3,23 @@ import { BASE_URL } from "@/constants"
import { getTotalIcons } from "@/lib/api" import { getTotalIcons } from "@/lib/api"
import type { Metadata } from "next" import type { Metadata } from "next"
export const metadata: Metadata = { export async function generateMetadata(): Promise<Metadata> {
const { totalIcons } = await getTotalIcons()
return {
title: "Dashboard Icons - Beautiful icons for your dashboard", title: "Dashboard Icons - Beautiful icons for your dashboard",
description: "Free, open-source icons for your dashboard. Choose from hundreds of high-quality icons for your web applications.", description: `A collection of ${totalIcons} curated icons for services, applications and tools, designed specifically for dashboards and app directories.`,
keywords: ["self hosted", "dashboard icons", "free icons", "open source icons", "web dashboard", "application icons"], keywords: [
"dashboard icons",
"service icons",
"application icons",
"tool icons",
"web dashboard",
"app directory",
],
openGraph: { openGraph: {
title: "Dashboard Icons - Your definitive source for dashboard icons", title: "Dashboard Icons - Your definitive source for dashboard icons",
description: "Free, open-source icons for your dashboard. Choose from thousands of high-quality icons.", description: `A collection of ${totalIcons} curated icons for services, applications and tools, designed specifically for dashboards and app directories.`,
type: "website", type: "website",
url: BASE_URL, url: BASE_URL,
images: [ images: [
@ -23,7 +33,7 @@ export const metadata: Metadata = {
}, },
twitter: { twitter: {
title: "Dashboard Icons - Your definitive source for dashboard icons", title: "Dashboard Icons - Your definitive source for dashboard icons",
description: "Free, open-source icons for your dashboard. Choose from thousands of high-quality icons.", description: `A collection of ${totalIcons} curated icons for services, applications and tools, designed specifically for dashboards and app directories.`,
card: "summary_large_image", card: "summary_large_image",
images: ["/og-image.png"], images: ["/og-image.png"],
}, },
@ -31,6 +41,7 @@ export const metadata: Metadata = {
canonical: BASE_URL, canonical: BASE_URL,
}, },
} }
}
export default async function Home() { export default async function Home() {
const { totalIcons } = await getTotalIcons() const { totalIcons } = await getTotalIcons()