From 78c9508195f6f25aac36f5d5c88b97b2e32dc4b4 Mon Sep 17 00:00:00 2001 From: Rui Saraiva Date: Sun, 26 Jul 2026 14:31:02 +0100 Subject: [PATCH] feat(seo): declare the author and add llms.txt The about modal is lazily mounted, so the credits and contact address are not in the server-rendered HTML and no crawler ever sees them. Authorship therefore goes in the head, where it is always present: a meta author, a rel=author link to ruisaraiva.dev, and SoftwareApplication JSON-LD. The structured data names Rui as author and Augusto and Miguel as contributors, because the modal credits three people and the markup should not tell a machine a tidier story than the page tells a reader. Also adds public/llms.txt: accepted input formats, the 310px and 512px minimums, and exactly what the zip contains, including that favicon.svg only appears for SVG input and manifest.json only with the PWA option. It is written from utils/favicon.ts and the route handler rather than from memory. Note it is a static file, so it is the one thing here that can drift from the sizes it documents. Two hardcoded favycon.vercel.app strings become the SITE_URL constant the rest of the file now uses. --- components/seo/index.tsx | 103 ++++++++++++++++++++++++++------------- public/llms.txt | 48 ++++++++++++++++++ 2 files changed, 117 insertions(+), 34 deletions(-) create mode 100644 public/llms.txt diff --git a/components/seo/index.tsx b/components/seo/index.tsx index 8b33f8b..11b76f0 100644 --- a/components/seo/index.tsx +++ b/components/seo/index.tsx @@ -1,43 +1,78 @@ import Head from 'next/head' +const SITE_URL = 'https://favycon.vercel.app' + +const AUTHOR = { name: 'Rui Saraiva', url: 'https://ruisaraiva.dev' } + +// The tool credits three people in its About modal, so the structured data says +// the same thing rather than a tidier untruth: Rui as author, the other two as +// contributors, linked where the modal links them. +const CONTRIBUTORS = [ + { name: 'Augusto Lopes', url: 'https://twitter.com/aboutaugusto' }, + { name: 'Miguel Teixeira', url: 'https://github.com/miguellteixeira' }, +] + type SEOProps = { title: string description: string } -const SEO = ({ title, description }: SEOProps) => ( - - {title} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -) +const SEO = ({ title, description }: SEOProps) => { + const structuredData = { + '@context': 'https://schema.org', + '@type': 'SoftwareApplication', + name: 'Favycon', + description, + url: SITE_URL, + applicationCategory: 'DeveloperApplication', + operatingSystem: 'Any', + browserRequirements: 'Requires JavaScript', + image: `${SITE_URL}/share.png`, + license: 'https://opensource.org/licenses/MIT', + // It is genuinely free, and Google wants the price stated to believe it. + offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' }, + author: { '@type': 'Person', ...AUTHOR }, + contributor: CONTRIBUTORS.map((person) => ({ '@type': 'Person', ...person })), + codeRepository: 'https://github.com/ruisaraiva19/favycon', + } + + return ( + + {title} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +