fix: improve social previews and shared icons (#52)

Add share metadata and a real product social card so OpenSEO links render with a stronger preview. Sync the landing site and app shell favicons with the shared Every App icon set.
This commit is contained in:
Ben Senescu 2026-03-27 18:56:14 -04:00 committed by GitHub
parent cdd729ec0d
commit 191879c87c
19 changed files with 69 additions and 20 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 564 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 832 B

After

Width:  |  Height:  |  Size: 705 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -1,6 +1,6 @@
{
"name": "",
"short_name": "",
"name": "OpenSEO",
"short_name": "OpenSEO",
"icons": [
{
"src": "/android-chrome-192x192.png",
@ -13,7 +13,7 @@
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"theme_color": "#0a0f0d",
"background_color": "#0a0f0d",
"display": "standalone"
}

View File

@ -56,7 +56,8 @@ export const Route = createRootRoute({
sizes: "16x16",
href: "/favicon-16x16.png",
},
{ rel: "icon", href: "/favicon.ico" },
{ rel: "icon", type: "image/x-icon", href: "/favicon.ico" },
{ rel: "manifest", href: "/site.webmanifest" },
],
scripts: [],
}),

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 564 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 705 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
web/public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

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

BIN
web/public/social-card.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

View File

@ -1,4 +1,6 @@
const DEFAULT_SITE_URL = "https://openseo.so";
const DEFAULT_SOCIAL_IMAGE_PATH = "/social-card.png";
const DEFAULT_SOCIAL_IMAGE_ALT = "OpenSEO product preview";
export const SITE_URL = (
process.env.SITE_URL ??
@ -23,6 +25,8 @@ type BuildSeoParams = {
description?: string;
titleSuffix?: string;
ogType?: "website" | "article";
imagePath?: string;
imageAlt?: string;
};
export function buildPageSeo({
@ -31,20 +35,34 @@ export function buildPageSeo({
description,
titleSuffix,
ogType = "website",
imagePath = DEFAULT_SOCIAL_IMAGE_PATH,
imageAlt = DEFAULT_SOCIAL_IMAGE_ALT,
}: BuildSeoParams) {
const fullTitle = titleSuffix ? `${title} - ${titleSuffix}` : title;
const canonicalUrl = toCanonicalUrl(path);
const socialImageUrl = toCanonicalUrl(imagePath);
return {
meta: [
{ title: fullTitle },
...(description ? [{ name: "description", content: description }] : []),
{ property: "og:site_name", content: "OpenSEO" },
{ property: "og:type", content: ogType },
{ property: "og:title", content: fullTitle },
...(description
? [{ property: "og:description", content: description }]
: []),
{ property: "og:url", content: canonicalUrl },
{ property: "og:image", content: socialImageUrl },
{ property: "og:image:alt", content: imageAlt },
{ property: "og:image:type", content: "image/png" },
{ property: "og:image:width", content: "1200" },
{ property: "og:image:height", content: "630" },
{ name: "twitter:card", content: "summary_large_image" },
{ name: "twitter:title", content: fullTitle },
...(description ? [{ name: "twitter:description", content: description }] : []),
{ name: "twitter:image", content: socialImageUrl },
{ name: "twitter:image:alt", content: imageAlt },
],
links: [{ rel: "canonical", href: canonicalUrl }],
};

View File

@ -22,6 +22,24 @@ export const Route = createRootRoute({
links: [
{ rel: "stylesheet", href: appCss },
{ rel: "icon", type: "image/x-icon", href: "/favicon.ico" },
{
rel: "apple-touch-icon",
sizes: "180x180",
href: "/apple-touch-icon.png",
},
{
rel: "icon",
type: "image/png",
sizes: "32x32",
href: "/favicon-32x32.png",
},
{
rel: "icon",
type: "image/png",
sizes: "16x16",
href: "/favicon-16x16.png",
},
{ rel: "manifest", href: "/site.webmanifest" },
],
}),
component: RootComponent,

View File

@ -1,25 +1,18 @@
import { createFileRoute, Link } from "@tanstack/react-router";
import { useState } from "react";
import { toCanonicalUrl } from "@/lib/seo";
import { buildPageSeo } from "@/lib/seo";
const homeTitle = "OpenSEO - Open Source SEO Platform";
const homeDescription =
"Own your SEO. OpenSEO helps teams manage keyword research, backlink analysis, competitor monitoring, and site audits without expensive monthly SEO software subscriptions.";
export const Route = createFileRoute("/")({
head: () => ({
meta: [
{ title: homeTitle },
{ name: "description", content: homeDescription },
{ property: "og:type", content: "website" },
{ property: "og:title", content: homeTitle },
{ property: "og:description", content: homeDescription },
{ property: "og:url", content: toCanonicalUrl("/") },
{ name: "twitter:card", content: "summary_large_image" },
{ name: "twitter:title", content: homeTitle },
{ name: "twitter:description", content: homeDescription },
],
links: [{ rel: "canonical", href: toCanonicalUrl("/") }],
head: () =>
buildPageSeo({
title: homeTitle,
description: homeDescription,
path: "/",
imageAlt: "OpenSEO keyword research dashboard preview",
}),
component: Home,
});