* fix(web): SEO fixes from Search Console and Lighthouse audit Technical: - Emit BreadcrumbList JSON-LD from the same trail as the visual breadcrumbs on every SeoSubpageShell page. - absoluteUrl() no longer appends a trailing slash to file paths. The Organization and publisher logo URLs pointed at /vocalinux.png/, a 404. - Fetch the GitHub star count once at build time in next.config.js and drop the client-side api.github.com fetch and preconnect (Lighthouse "third-party code" on every page). Falls back to the "GitHub" label. - Fix h1 -> h3 heading skips on 11 pages by promoting the leading card headings to h2; new .subpage-card-title keeps the old h3 sizing. - Header and footer logo get alt="" next to the visible "Vocalinux" text. - Footer links every guide, distro and feature page, so the pages added in #1038 and /vs-nerd-dictation/ (Discovered, not indexed) and /writers/ (no inbound links) are linked sitewide. Titles and descriptions: - Rewrite titles/descriptions for pages with high impressions and low CTR in Search Console (home, FAQ, compare, languages, open-source, GPU, alternatives, offline, install) to lead with the queries they rank for. - Distro pages use "<Distro> Speech to Text: Offline Voice Typing Setup" as the search title; the on-page h1 is unchanged. - Remove brand repetition and shorten 75+ character titles. - Bump sitemap lastmod for the rewritten pages. * fix(web): keep home install card titles styled The home page's install cards still use h3; only /install/ moved to h2. Style both so the home cards keep their size and spacing.
79 lines
1.8 KiB
TypeScript
79 lines
1.8 KiB
TypeScript
"use client";
|
|
|
|
import React from "react";
|
|
import Image from "next/image";
|
|
|
|
interface OptimizedImageProps {
|
|
src: string;
|
|
alt: string;
|
|
width: number;
|
|
height: number;
|
|
className?: string;
|
|
priority?: boolean;
|
|
}
|
|
|
|
/**
|
|
* Optimized image component that uses WebP format with PNG fallback.
|
|
* Browsers that support WebP will load the smaller WebP file, while older
|
|
* browsers will fallback to PNG format.
|
|
*/
|
|
export const OptimizedImage: React.FC<OptimizedImageProps> = ({
|
|
src,
|
|
alt,
|
|
width,
|
|
height,
|
|
className = "",
|
|
priority = false,
|
|
}) => {
|
|
// Get WebP version of the image
|
|
const webpSrc = src.replace(/\.(png|jpg|jpeg)$/i, '.webp');
|
|
|
|
// For Next.js Image component with priority and proper optimization
|
|
// we use the Next.js Image with WebP source
|
|
return (
|
|
<picture className={className}>
|
|
<source srcSet={webpSrc} type="image/webp" />
|
|
<Image
|
|
src={src}
|
|
alt={alt}
|
|
width={width}
|
|
height={height}
|
|
className={className}
|
|
priority={priority}
|
|
/>
|
|
</picture>
|
|
);
|
|
};
|
|
|
|
/**
|
|
* Specialized Vocalinux logo component with WebP support
|
|
*/
|
|
export const VocalinuxLogo: React.FC<{
|
|
className?: string;
|
|
width?: number;
|
|
height?: number;
|
|
priority?: boolean;
|
|
/** Set when the visible "Vocalinux" text sits next to the mark. */
|
|
decorative?: boolean;
|
|
}> = ({
|
|
className = "",
|
|
width = 32,
|
|
height = 32,
|
|
priority = false,
|
|
decorative = false,
|
|
}) => {
|
|
return (
|
|
// SVG lockup; next/image is for raster.
|
|
// eslint-disable-next-line @next/next/no-img-element
|
|
<img
|
|
src="/brand/vocalinux-mark-circle.svg"
|
|
alt={decorative ? "" : "Vocalinux"}
|
|
width={width}
|
|
height={height}
|
|
className={className}
|
|
decoding="async"
|
|
loading={priority ? "eager" : "lazy"}
|
|
/>
|
|
);
|
|
};
|