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.
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 68 KiB |
|
Before Width: | Height: | Size: 107 KiB After Width: | Height: | Size: 564 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 832 B After Width: | Height: | Size: 705 B |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
@ -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"
|
||||
}
|
||||
|
||||
@ -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: [],
|
||||
}),
|
||||
|
||||
BIN
web/public/android-chrome-192x192.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
BIN
web/public/android-chrome-512x512.png
Normal file
|
After Width: | Height: | Size: 564 KiB |
BIN
web/public/apple-touch-icon.png
Normal file
|
After Width: | Height: | Size: 61 KiB |
BIN
web/public/favicon-16x16.png
Normal file
|
After Width: | Height: | Size: 705 B |
BIN
web/public/favicon-32x32.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
web/public/favicon.ico
Normal file
|
After Width: | Height: | Size: 15 KiB |
19
web/public/site.webmanifest
Normal 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
|
After Width: | Height: | Size: 184 KiB |
@ -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 }],
|
||||
};
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
});
|
||||
|
||||