Skip to content

Commit

Permalink
feat: SEO improvements - dynamic sitemap, robots.txt, and meta handli…
Browse files Browse the repository at this point in the history
…ng (#32)

- Dynamic sitemap and robots.txt generation for better SEO. #25
- Move metadata generation from layout to pages for flexibility.
- Abstract markdown parsing for consistent content handling.
- Add dynamic post URLs with Chinese-to-Pinyin slug conversion. #25
- Update `.gitignore` to exclude generated JSON files for posts.
- Improve accessibility, fix aria-label, and enforce only one H1 per page.
- Refactor: remove custom script support and enhance OpenGraph metadata.
- Optimize image loading with priority flag and reduce redundant divs.
- Minor fixes: font size, tag keywords, and improved SEO metadata.
  • Loading branch information
ZL-Asica authored Oct 23, 2024
1 parent e43742c commit 9dd2c15
Show file tree
Hide file tree
Showing 29 changed files with 444 additions and 251 deletions.
26 changes: 7 additions & 19 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
{
"extends": ["next/core-web-vitals", "next/typescript", "prettier"],
"plugins": ["import"],
"extends": [
"next/core-web-vitals",
"next/typescript",
"prettier",
"plugin:jsx-a11y/recommended"
],
"rules": {
// Possible Errors
"no-console": "warn", // Warn on console usage (can change to 'error' to disallow)
Expand Down Expand Up @@ -33,22 +37,6 @@
"arrow-spacing": ["error", { "before": true, "after": true }], // Enforce spacing around arrows
"prefer-const": "error", // Prefer const for variables that are never reassigned
"no-var": "error", // Disallow var (use let or const instead)
"template-curly-spacing": ["error", "never"], // Disallow spaces inside template literals

// Import
"import/order": [
"error",
{
"groups": [
"builtin",
"external",
"internal",
"parent",
"sibling",
"index"
],
"newlines-between": "always"
}
]
"template-curly-spacing": ["error", "never"] // Disallow spaces inside template literals
}
}
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,6 @@ yarn-error.log*
# typescript
*.tsbuildinfo
next-env.d.ts

# User generated files
public/postsData.json
10 changes: 2 additions & 8 deletions config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ author:
link: 'https://www.zla.app' # Link to your personal website or blog.
# Language setting: Use ISO 639-1 code (e.g., 'en' for English, 'zh' for Chinese)
lang: 'zh'
# Your public root url, used for SEO and meta tags. (Do not include a ending slash)
siteUrl: 'https://suzu.zla.app'

# Path to your avatar image. Can be a relative path from /public or a full URL (e.g., https://).
avatar: '/images/avatar.jpg'
Expand Down Expand Up @@ -71,14 +73,6 @@ disqusShortname: 'zla-pub'
# CUSTOM CODE BLOCKS
# ! WARNING: Only modify these if you understand the purpose of custom scripts.
#######################
# Add JavaScript URLs or code snippets to be included inside <head> on your site.
scriptSlotHeader:
-

# Add JavaScript URLs or code snippets to be included before the closing </body> tag.
scriptSlotFooter:
- 'https://cdn.jsdelivr.net/gh/zl-asica/web-cdn/js/zlasica.js'

# Add custom HTML code to be included inside the <footer> section of your site.
slotFooter: |
<!-- Add your custom footer HTML here -->
73 changes: 0 additions & 73 deletions eslint.config.mjs

This file was deleted.

48 changes: 48 additions & 0 deletions public/custom.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
'use client';

// Get the current date
function getCurrentDate() {
const date = new Date();
const formatNumber = (num) => String(num).padStart(2, '0');

const year = date.getFullYear();
const month = formatNumber(date.getMonth() + 1);
const day = formatNumber(date.getDate());
const hours = formatNumber(date.getHours());
const minutes = formatNumber(date.getMinutes());
const seconds = formatNumber(date.getSeconds());

return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}

// Gray scale filter (only for /) on specific dates
function grayScale() {
// Set the date you want to gray scale
const grayScaleDates = ['04-04', '05-12', '09-18', '11-20', '12-13'];

// Get the current date
const currentDate = getCurrentDate();
const currentMonthDay = currentDate
.split(' ')[0]
.split('-')
.slice(1)
.join('-');

// Check if the current date is in the gray scale date list
if (grayScaleDates.includes(currentMonthDay)) {
// If is, set the gray scale filter
document.body.style.filter = 'grayscale(100%)';
}
}

// Custom console log
// eslint-disable-next-line no-console
console.info(
'%c由ZL Asica制作搭建与运行\nBuilt and Operated by ZL Asica\nhttps://www.zla.app',
'background:#fff;color:#000',
);

// Check if the current URL path is "/"
if (window.location.pathname === '/') {
grayScale();
}
24 changes: 0 additions & 24 deletions src/app/about/layout.tsx

This file was deleted.

27 changes: 26 additions & 1 deletion src/app/about/page.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,36 @@
import Loading from '@/app/loading';
import PostLayout from '@/components/layout/PostLayout';
import { getConfig } from '@/services/config/getConfig';
import { getPostData } from '@/services/content/posts';
import { PostData } from '@/types';
import type { Metadata } from 'next';
import { Suspense } from 'react';

export async function generateMetadata(): Promise<Metadata> {
const config = getConfig();
return {
title: `About - ${config.title}`,
description: `About page of ${config.title} - ${config.description}`,
openGraph: {
siteName: config.title,
type: 'profile',
username: config.author.name,
title: `About - ${config.title}`,
description: `About page of ${config.title} - ${config.description}`,
images: config.avatar,
url: '/about',
locale: config.lang,
},
};
}

export default async function AboutPage() {
const post: PostData = await getPostData('About', 'About');
const config = getConfig();

return <PostLayout post={post} showThumbnail={config.thumbnailAbout} />;
return (
<Suspense fallback={<Loading />}>
<PostLayout post={post} showThumbnail={config.thumbnailAbout} />
</Suspense>
);
}
36 changes: 34 additions & 2 deletions src/app/categories/[categorySlug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import Loading from '@/app/loading';
import PostListLayout from '@/components/layout/PostListLayout';
import { getConfig } from '@/services/config/getConfig';
import { getAllPosts } from '@/services/content/posts';
import { Metadata } from 'next';
import { notFound } from 'next/navigation';
import { Suspense } from 'react';

export async function generateStaticParams() {
const config = getConfig();
Expand All @@ -10,6 +13,34 @@ export async function generateStaticParams() {
}));
}

type Props = {
params: Promise<{ categorySlug: string }>;
};

export async function generateMetadata({ params }: Props): Promise<Metadata> {
// read post slug
const category = (await params).categorySlug;

const config = getConfig();

// Find the category based on the slug from params
const categoryData = config.postCategories.find(
(cat) => cat.slug === category,
) || { name: 'Not Found' };
return {
title: `分类:${categoryData.name} - ${config.title}`,
openGraph: {
siteName: config.title,
title: `分类:${categoryData.name} - ${config.title}`,
description: `分类:${categoryData.name} - ${config.description}`,
url: `/categories/${category}`,
images: config.avatar,
type: 'website',
locale: config.lang,
},
};
}

export default async function CategoryPage(props: {
params: Promise<{ categorySlug: string }>;
}) {
Expand All @@ -35,8 +66,9 @@ export default async function CategoryPage(props: {
return (
<div className='container mx-auto p-4'>
<h1 className='mb-6 text-center text-4xl font-bold'>{category.name}</h1>

<PostListLayout posts={filteredPosts} />
<Suspense fallback={<Loading />}>
<PostListLayout posts={filteredPosts} />
</Suspense>
</div>
);
}
24 changes: 0 additions & 24 deletions src/app/friends/layout.tsx

This file was deleted.

26 changes: 25 additions & 1 deletion src/app/friends/page.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,36 @@
import Loading from '@/app/loading';
import PostLayout from '@/components/layout/PostLayout';
import { getConfig } from '@/services/config/getConfig';
import { getPostData } from '@/services/content/posts';
import '@/styles/friendsLinks.css';
import { PostData } from '@/types';
import type { Metadata } from 'next';
import { Suspense } from 'react';

export async function generateMetadata(): Promise<Metadata> {
const config = getConfig();
return {
title: `Friends - ${config.title}`,
description: `Friends page of ${config.title} - ${config.description}`,
openGraph: {
siteName: config.title,
title: `Friends - ${config.title}`,
description: `Friends page of ${config.title} - ${config.description}`,
url: '/friends',
images: config.avatar,
type: 'website',
locale: config.lang,
},
};
}

export default async function FriendsPage() {
const post: PostData = await getPostData('Friends', 'Friends');
const config = getConfig();

return <PostLayout post={post} showThumbnail={config.thumbnailFriends} />;
return (
<Suspense fallback={<Loading />}>
<PostLayout post={post} showThumbnail={config.thumbnailFriends} />
</Suspense>
);
}
11 changes: 0 additions & 11 deletions src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -82,17 +82,6 @@ h4 {
margin-block-end: 1.33em;
}

/* code {
background: #1d1f21;
color: #fff;
word-break: break-word;
font-family: 'Source Code Pro', monospace, Helvetica, Tahoma, Arial, STXihei,
'STHeiti Light', 'Microsoft YaHei', sans-serif;
padding: 2px;
text-shadow: none;
border-radius: 0 0 5px 5px;
} */

/****************************************************
* IMAGES & MEDIA
****************************************************/
Expand Down
Loading

0 comments on commit 9dd2c15

Please sign in to comment.