Merge pull request #27 from rotary-dev-fellowship/feat-faviconGen
Dynamic manifest and favicon generation
|
@ -191,14 +191,6 @@ Static files served directly to the browser are within the `public` directory at
|
|||
```md
|
||||
|
||||
public/
|
||||
├── apple-touch-icon.png
|
||||
├── favicon.ico
|
||||
├── icon-192.png
|
||||
├── icon-512.png
|
||||
├── icon.svg
|
||||
├── manifest.webmanifest
|
||||
├── maskable_icon.png
|
||||
├── maskable_icon_x512.png
|
||||
├── scripts/
|
||||
│ └── vendor/
|
||||
│ ├── gsap/ # Animations powered by GSAP (GreenSock Animation Platform)
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
"gsap": "^3.12.5",
|
||||
"html-minifier": "^4.0.0",
|
||||
"preline": "^2.0.3",
|
||||
"sharp": "^0.33.3",
|
||||
"sharp-ico": "^0.1.5",
|
||||
"tailwindcss": "^3.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
Before Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 4.5 KiB |
|
@ -1,35 +0,0 @@
|
|||
{
|
||||
"short_name": "ScrewFast",
|
||||
"name": "ScrewFast",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/icon-192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "any"
|
||||
},
|
||||
{
|
||||
"src": "/icon-512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "any"
|
||||
},
|
||||
{
|
||||
"src": "/maskable_icon_x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
},
|
||||
{
|
||||
"src": "/maskable_icon.png",
|
||||
"sizes": "1000x1000",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
}
|
||||
],
|
||||
"display": "minimal-ui",
|
||||
"id": "/",
|
||||
"start_url": "/",
|
||||
"theme_color": "#FFEDD5",
|
||||
"background_color": "#262626"
|
||||
}
|
Before Width: | Height: | Size: 14 KiB |
|
@ -1,4 +1,8 @@
|
|||
---
|
||||
import { getImage } from "astro:assets";
|
||||
import faviconSvgSrc from "@/images/icon.svg";
|
||||
import faviconSrc from "@/images/icon.png";
|
||||
|
||||
// Default properties for the Meta component. These values are used if props are not provided.
|
||||
// 'title' sets the default page title for the website.
|
||||
// 'meta' sets a default description meta tag to describe the page content.
|
||||
|
@ -35,6 +39,20 @@ const ogDescription: string =
|
|||
"Equip your projects with ScrewFast's top-quality hardware tools and expert construction services. Trusted by industry leaders, ScrewFast offers simplicity, affordability, and reliability. Experience the difference with user-centric design and cutting-edge tools. Start exploring now!"; // Set the Open Graph description
|
||||
const URL: string = `${Astro.site}`; // Set the website URL in astro.config.mjs
|
||||
const socialImage: string = `${Astro.site}/social.png`; // Set the path for the social media image
|
||||
|
||||
// Generate and optimize the favicon images
|
||||
const faviconSvg = await getImage({
|
||||
src: faviconSvgSrc,
|
||||
format: 'svg',
|
||||
});
|
||||
|
||||
const appleTouchIcon = await getImage({
|
||||
src: faviconSrc,
|
||||
width: 180,
|
||||
height: 180,
|
||||
format: 'png',
|
||||
});
|
||||
|
||||
---
|
||||
|
||||
<!-- Inject structured data into the page if provided. This data is formatted as JSON-LD, a method recommended by Google for structured data pass:
|
||||
|
@ -73,15 +91,15 @@ const socialImage: string = `${Astro.site}/social.png`; // Set the path for the
|
|||
<meta name="twitter:image" content={socialImage} />
|
||||
|
||||
<!-- Links to the webmanifest and sitemap -->
|
||||
<link rel="manifest" href="/manifest.webmanifest" />
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
<!-- https://docs.astro.build/en/guides/integrations-guide/sitemap/ -->
|
||||
<link rel="sitemap" href="/sitemap-index.xml" />
|
||||
|
||||
<!-- Links for favicons -->
|
||||
<link href="/favicon.ico" rel="icon" sizes="any" type="image/x-icon" />
|
||||
<link href="/icon.svg" rel="icon" type="image/svg+xml" sizes="any" />
|
||||
<link href={faviconSvg.src} rel="icon" type="image/svg+xml" sizes="any" />
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
<link href="/apple-touch-icon.png" rel="apple-touch-icon" />
|
||||
<link href="/apple-touch-icon.png" rel="shortcut icon" />
|
||||
<link href={appleTouchIcon.src} rel="apple-touch-icon" />
|
||||
<link href={appleTouchIcon.src} rel="shortcut icon" />
|
||||
<!-- Set theme color -->
|
||||
<meta name="theme-color" content="#facc15" />
|
||||
|
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 987 B After Width: | Height: | Size: 987 B |
28
src/pages/favicon.ico.ts
Normal file
|
@ -0,0 +1,28 @@
|
|||
import type { APIRoute } from "astro";
|
||||
import sharp from "sharp";
|
||||
import ico from "sharp-ico";
|
||||
import path from "node:path";
|
||||
|
||||
const faviconSrc = path.resolve("src/images/icon.png");
|
||||
|
||||
export const GET: APIRoute = async () => {
|
||||
|
||||
// Resize the image to multiple sizes
|
||||
const sizes = [16, 32, 48, 64, 128, 256];
|
||||
|
||||
const buffers = await Promise.all(
|
||||
sizes.map(async (size) => {
|
||||
return await sharp(faviconSrc)
|
||||
.resize(size)
|
||||
.toFormat("png")
|
||||
.toBuffer();
|
||||
})
|
||||
);
|
||||
|
||||
// Convert the image to an ICO file
|
||||
const icoBuffer = ico.encode(buffers);
|
||||
|
||||
return new Response(icoBuffer, {
|
||||
headers: { "Content-Type": "image/x-icon" },
|
||||
});
|
||||
};
|
58
src/pages/manifest.json.ts
Normal file
|
@ -0,0 +1,58 @@
|
|||
import type { APIRoute, ImageMetadata } from "astro";
|
||||
import { getImage } from "astro:assets";
|
||||
import icon from "@/images/icon.png";
|
||||
import maskableIcon from "@/images/icon-maskable.png";
|
||||
|
||||
interface Favicon {
|
||||
purpose: 'any' | 'maskable' | 'monochrome';
|
||||
src: ImageMetadata;
|
||||
sizes: number[];
|
||||
}
|
||||
|
||||
const sizes = [192, 512];
|
||||
const favicons: Favicon[] = [
|
||||
{
|
||||
purpose: 'any',
|
||||
src: icon,
|
||||
sizes,
|
||||
},
|
||||
{
|
||||
purpose: 'maskable',
|
||||
src: maskableIcon,
|
||||
sizes,
|
||||
},
|
||||
];
|
||||
|
||||
export const GET: APIRoute = async () => {
|
||||
const icons = await Promise.all(
|
||||
favicons.flatMap((favicon) =>
|
||||
favicon.sizes.map(async (size) => {
|
||||
const image = await getImage({
|
||||
src: favicon.src,
|
||||
width: size,
|
||||
height: size,
|
||||
format: "png",
|
||||
});
|
||||
return {
|
||||
src: image.src,
|
||||
sizes: `${image.options.width}x${image.options.height}`,
|
||||
type: `image/${image.options.format}`,
|
||||
purpose: favicon.purpose,
|
||||
};
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
const manifest = {
|
||||
short_name: "ScrewFast",
|
||||
name: "ScrewFast",
|
||||
icons,
|
||||
display: "minimal-ui",
|
||||
id: "some-unique-id",
|
||||
start_url: "/",
|
||||
theme_color: "#FFEDD5",
|
||||
background_color: "#262626",
|
||||
};
|
||||
|
||||
return new Response(JSON.stringify(manifest));
|
||||
};
|