feat: improve SEO with favicons, optimized OG image, and enhanced schema (#99)

* feat: improve SEO with favicons, optimized OG image, and enhanced schema

- Add favicon PNG assets (16x16, 32x32, 96x96) for better browser/Search display
- Add apple-touch-icon (180x180) for iOS devices
- Add PWA icons (192x192, 512x512) with maskable support
- Optimize OG image from 6.8MB to 279KB (1200x630px)
- Update web manifest with correct icons and theme color (#3CB371)
- Enhance JSON-LD with Organization and WebSite schemas
- Remove hardcoded version from SoftwareApplication schema
- Update sitemap lastmod to 2026-01-29 and changefreq to monthly

* fix: add back softwareVersion for CI sync

* feat: regenerate favicons with high-res transparent image

* feat: replace vocalinux.svg with vocalinux.png in web
This commit is contained in:
Jatin K Malik
2026-01-29 01:16:53 -08:00
committed by GitHub
parent 1244acdb71
commit 97aec35287
13 changed files with 74 additions and 12 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 MiB

After

Width:  |  Height:  |  Size: 273 KiB

+18 -1
View File
@@ -5,7 +5,7 @@
"start_url": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#7c3aed",
"theme_color": "#3CB371",
"icons": [
{
"src": "/favicon-16x16.png",
@@ -17,10 +17,27 @@
"sizes": "32x32",
"type": "image/png"
},
{
"src": "/favicon-96x96.png",
"sizes": "96x96",
"type": "image/png"
},
{
"src": "/apple-touch-icon.png",
"sizes": "180x180",
"type": "image/png"
},
{
"src": "/icon-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "/icon-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any maskable"
}
]
}
+2 -2
View File
@@ -2,8 +2,8 @@
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://vocalinux.com/</loc>
<lastmod>2026-01-19</lastmod>
<changefreq>weekly</changefreq>
<lastmod>2026-01-29</lastmod>
<changefreq>monthly</changefreq>
<priority>1.0</priority>
</url>
</urlset>
Binary file not shown.

After

Width:  |  Height:  |  Size: 207 KiB

+50 -5
View File
@@ -48,10 +48,13 @@ export const metadata: Metadata = {
publisher: "Vocalinux",
icons: {
icon: [
{ url: "/vocalinux.svg", type: "image/svg+xml" },
{ url: "/vocalinux.png", type: "image/png" },
{ url: "/favicon-16x16.png", sizes: "16x16", type: "image/png" },
{ url: "/favicon-32x32.png", sizes: "32x32", type: "image/png" },
{ url: "/favicon-96x96.png", sizes: "96x96", type: "image/png" },
],
apple: [{ url: "/vocalinux.svg", type: "image/svg+xml" }],
shortcut: "/vocalinux.svg"
apple: [{ url: "/apple-touch-icon.png", sizes: "180x180", type: "image/png" }],
shortcut: "/vocalinux.png"
},
manifest: "/site.webmanifest",
appleWebApp: {
@@ -104,7 +107,8 @@ export default function RootLayout({
}: Readonly<{
children: React.ReactNode;
}>) {
const jsonLd = {
// SoftwareApplication schema
const softwareAppJsonLd = {
"@context": "https://schema.org",
"@type": "SoftwareApplication",
"name": "Vocalinux",
@@ -116,7 +120,7 @@ export default function RootLayout({
"priceCurrency": "USD"
},
"description": "Free, open-source voice dictation for Linux. 100% offline with Whisper AI and VOSK. Works with X11 and Wayland.",
"softwareVersion": "0.4.1-alpha",
"softwareVersion": "0.0.0",
"author": {
"@type": "Person",
"name": "Jatin K Malik",
@@ -137,6 +141,47 @@ export default function RootLayout({
]
};
// Organization schema
const organizationJsonLd = {
"@context": "https://schema.org",
"@type": "Organization",
"name": "Vocalinux",
"url": "https://vocalinux.com",
"logo": "https://vocalinux.com/vocalinux.png",
"description": "Free, open-source voice dictation for Linux",
"sameAs": [
"https://github.com/jatinkrmalik/vocalinux"
],
"author": {
"@type": "Person",
"name": "Jatin K Malik",
"url": "https://github.com/jatinkrmalik"
}
};
// WebSite schema
const webSiteJsonLd = {
"@context": "https://schema.org",
"@type": "WebSite",
"name": "Vocalinux",
"url": "https://vocalinux.com",
"description": "Voice dictation for Linux, finally done right. 100% offline and privacy-focused.",
"potentialAction": {
"@type": "SearchAction",
"target": {
"@type": "EntryPoint",
"urlTemplate": "https://github.com/jatinkrmalik/vocalinux/issues?q={search_term_string}"
},
"query-input": {
"@type": "PropertyValueSpecification",
"valueRequired": true,
"valueName": "search_term_string"
}
}
};
const jsonLd = [softwareAppJsonLd, organizationJsonLd, webSiteJsonLd];
return (
<html lang="en" className={`${GeistSans.variable} scroll-smooth`} suppressHydrationWarning>
<head>
+3 -3
View File
@@ -195,7 +195,7 @@ export default function HomePage() {
<nav className="max-w-7xl mx-auto px-4 sm:px-6 py-3 flex items-center justify-between">
<Link href="/" className="flex items-center gap-2 group">
<Image
src="/vocalinux.svg"
src="/vocalinux.png"
alt="Vocalinux Logo"
width={32}
height={32}
@@ -557,7 +557,7 @@ export default function HomePage() {
<div className="relative">
<div className="bg-white dark:bg-zinc-800 rounded-2xl p-8 shadow-xl">
<div className="flex items-center gap-4 mb-6">
<Image src="/vocalinux.svg" alt="Vocalinux" width={64} height={64} className="h-16 w-16" />
<Image src="/vocalinux.png" alt="Vocalinux" width={64} height={64} className="h-16 w-16" />
<div>
<div className="text-2xl font-bold">Vocalinux</div>
<div className="text-sm text-muted-foreground">
@@ -1026,7 +1026,7 @@ export default function HomePage() {
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-8 mb-12">
<div className="md:col-span-2">
<Link href="/" className="flex items-center gap-2 mb-4">
<Image src="/vocalinux.svg" alt="Vocalinux" width={32} height={32} className="h-8 w-8" />
<Image src="/vocalinux.png" alt="Vocalinux" width={32} height={32} className="h-8 w-8" />
<span className="text-xl font-bold">Vocalinux</span>
</Link>
<p className="text-zinc-400 mb-4 max-w-md">
+1 -1
View File
@@ -7,7 +7,7 @@ import Image from "next/image";
export const VocalinuxLogo: React.FC<{ className?: string }> = ({ className = "" }) => {
return (
<Image
src="/vocalinux.svg"
src="/vocalinux.png"
alt="Vocalinux Logo"
width={36}
height={36}