added locale switch and adjusted css

This commit is contained in:
ClovertaTheTrilobita 2026-03-25 00:28:23 +02:00
parent 4c623030ba
commit 80e2e1b156
32 changed files with 3641 additions and 171 deletions

95
build/fontmin.js Normal file
View file

@ -0,0 +1,95 @@
import fs from "fs";
import path from "path";
import Fontmin from "fontmin";
function getFiles(dir) {
const results = [];
const list = fs.readdirSync(dir);
for (const file of list) {
const filePath = path.join(dir, file);
const stat = fs.statSync(filePath);
if (stat.isDirectory()) {
results.push(...getFiles(filePath));
} else {
results.push(filePath);
}
}
return results;
}
function scanDirectory(dir) {
let set = new Set();
const files = getFiles(dir);
for (const file of files) {
const ignoredExtensions = [
".ttf",
".otf",
".woff",
".woff2",
".eot",
".png",
".jpg",
".jpeg",
".webp",
".gif",
".ico",
".pdf",
];
if (ignoredExtensions.some((ext) => file.endsWith(ext))) continue;
try {
const content = fs.readFileSync(file, "utf8");
const currentSet = new Set(content);
set = new Set([...set, ...currentSet]);
} catch {
// 跳过二进制等不可读文件
}
}
return set;
}
function subsetFont(src, text) {
return new Promise((resolve, reject) => {
const fontmin = new Fontmin()
.src(src)
.use(
Fontmin.glyph({
text,
hinting: false,
})
)
.use(Fontmin.ttf2woff2())
.dest("public/fonts/subset");
fontmin.run((err) => {
if (err) return reject(err);
resolve();
});
});
}
async function main() {
const baseChars =
"首页文章标签关于作者评论发布于切换主题,。!?:“”‘’()《》【】、—…·-_/\\'\"()[]{}<>:;.!? ";
const scanned = Array.from(scanDirectory("src")).join("");
const chars = Array.from(new Set((scanned + baseChars).split(""))).join("");
await Promise.all([
subsetFont("public/fonts/MapleMono-CN-Regular.ttf", chars),
subsetFont("public/fonts/MapleMono-CN-Bold.ttf", chars),
subsetFont("public/fonts/MapleMono-CN-Italic.ttf", chars),
]);
console.log(`中文子集字体生成完成,共收集 ${chars.length} 个字符`);
}
main().catch((err) => {
console.error(err);
process.exit(1);
});

2988
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -7,9 +7,10 @@
}, },
"scripts": { "scripts": {
"dev": "astro dev", "dev": "astro dev",
"build": "astro build",
"preview": "astro preview", "preview": "astro preview",
"astro": "astro" "astro": "astro",
"subset-font": "node build/fontmin.js",
"build": "npm run subset-font && astro build"
}, },
"dependencies": { "dependencies": {
"@astrojs/rss": "^4.0.17", "@astrojs/rss": "^4.0.17",
@ -17,6 +18,7 @@
"url": "^0.11.4" "url": "^0.11.4"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^25.5.0" "@types/node": "^25.5.0",
"fontmin": "^1.1.1"
} }
} }

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -1,8 +1,57 @@
--- ---
const platform = "github"; import { getLangFromUrl, getTranslations, type Lang } from "@/i18n";
const username = "ClovertaTheTrilobita";
const lang = getLangFromUrl(Astro.url);
const t = getTranslations(lang);
--- ---
<footer> <footer class="footer">
<p>Learn more about my projects on <a href={`https://www.${platform}.com/${username}`}>{platform}</a>!</p> <p set:html={t.footer.githubIntro} />
</footer> <p set:html={t.footer.repoIntro} />
</footer>
<style>
.footer {
margin-top: 1rem;
padding: 1.5rem 0 0;
font-size: 0.95rem;
opacity: 0.9;
position: relative;
}
.footer::before {
content: "";
display: block;
width: 100%;
height: 12px;
margin-bottom: 1rem;
background: repeating-linear-gradient(
-45deg,
#e96b6b 0 14px,
transparent 14px 28px,
#7da2ff 28px 42px,
transparent 42px 56px
);
pointer-events: none;
}
.footer p {
margin: 0.4rem 0;
}
.footer a {
color: #7fb3ff;
font-weight: 700;
text-decoration: underline;
}
:global(.dark) .footer::before {
background: repeating-linear-gradient(
-45deg,
#ff8a8a 0 14px,
transparent 14px 28px,
#9ecbff 28px 42px,
transparent 42px 56px
);
}
</style>

View file

@ -1,21 +1,43 @@
--- ---
import Navigation from "./Navigation.astro"; import Navigation from "./Navigation.astro";
import ThemeIcon from "./ThemeIcon.astro"; import ThemeIcon from "./ThemeIcon.astro";
import { getLangFromUrl, getTranslations } from "@/i18n";
const lang = getLangFromUrl(Astro.url);
const t = getTranslations(lang);
--- ---
<header> <header class="site-header">
<nav> <div class="theme-icon-wrap">
<h1>SanYeCao Blog</h1> <ThemeIcon />
<div> </div>
<ThemeIcon />
</div> <nav class="header-nav">
<h1>{t.banner.title}</h1>
<Navigation /> <Navigation />
</nav> </nav>
</header> </header>
<style> <style>
div { .site-header {
position: relative;
padding-top: 0.5rem;
}
.theme-icon-wrap {
position: absolute;
top: 0;
right: 0;
}
.header-nav {
display: flex; display: flex;
justify-content: space-between; flex-direction: column;
gap: 1rem;
}
.header-nav h1 {
margin: 0;
font-style: italic;
} }
</style> </style>

View file

@ -1,13 +1,69 @@
--- ---
import { getLangFromUrl, useTranslations } from "@/i18n"; import { getLangFromUrl, getTranslations } from "@/i18n";
const langParam = Astro.url.searchParams.get("lang"); const lang = getLangFromUrl(Astro.url);
const lang = langParam === "en" ? "en" : "zh"; const t = getTranslations(lang);
const t = useTranslations(lang);
--- ---
<nav> <nav class="site-nav">
<a href={`/?lang=${lang}`}>{t("nav.home")}</a> <a href={`/${lang}`}>{t.nav.home}</a>
<a href={`/about?lang=${lang}`}>{t("nav.about")}</a> <a href={`/${lang}/about`}>{t.nav.about}</a>
<a href={`/tags?lang=${lang}`}>{t("nav.tags")}</a> <a href={`/${lang}/tags`}>{t.nav.tags}</a>
</nav> </nav>
<style>
.site-nav {
display: flex;
gap: 1.5rem;
align-items: center;
margin: 1rem 0;
padding-bottom: 3rem;
position: relative;
}
.site-nav::after {
content: "";
position: absolute;
left: 0;
right: 0;
bottom: 0;
height: 12px;
background: repeating-linear-gradient(
-45deg,
#e96b6b 0 14px,
transparent 14px 28px,
#7da2ff 28px 42px,
transparent 42px 56px
);
pointer-events: none;
}
:global(.dark) .site-nav::after {
background: repeating-linear-gradient(
-45deg,
#ff8a8a 0 14px,
transparent 14px 28px,
#9ecbff 28px 42px,
transparent 42px 56px
);
}
.site-nav a {
color: black;
text-decoration: underline;
font-weight: 700;
font-size: 1.2rem;
}
.site-nav a:hover {
text-decoration: underline;
}
:global(.dark) .site-nav a {
color: white;
}
:global(.dark) .site-nav a:hover {
color: #ddd;
}
</style>

View file

@ -1,36 +1,90 @@
--- ---
const pathname = Astro.url.pathname;
const segments = pathname.split("/").filter(Boolean);
const currentLang = segments[0] === "en" ? "en" : "zh";
const nextLang = currentLang === "zh" ? "en" : "zh";
const switchLabel = currentLang === "zh" ? "EN" : "中文";
if (segments.length > 0) {
segments[0] = nextLang;
}
const switchHref = "/" + segments.join("/");
---
--- <div class="top-actions">
<button id="themeToggle" aria-label="Toggle theme">
<svg
aria-hidden="true"
width="30px"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
>
<path
class="sun"
fill-rule="evenodd"
d="M12 17.5a5.5 5.5 0 1 0 0-11 5.5 5.5 0 0 0 0 11zm0 1.5a7 7 0 1 0 0-14 7 7 0 0 0 0 14zm12-7a.8.8 0 0 1-.8.8h-2.4a.8.8 0 0 1 0-1.6h2.4a.8.8 0 0 1 .8.8zM4 12a.8.8 0 0 1-.8.8H.8a.8.8 0 0 1 0-1.6h2.5a.8.8 0 0 1 .8.8zm16.5-8.5a.8.8 0 0 1 0 1l-1.8 1.8a.8.8 0 0 1-1-1l1.7-1.8a.8.8 0 0 1 1 0zM6.3 17.7a.8.8 0 0 1 0 1l-1.7 1.8a.8.8 0 1 1-1-1l1.7-1.8a.8.8 0 0 1 1 0zM12 0a.8.8 0 0 1 .8.8v2.5a.8.8 0 0 1-1.6 0V.8A.8.8 0 0 1 12 0zm0 20a.8.8 0 0 1 .8.8v2.4a.8.8 0 0 1-1.6 0v-2.4a.8.8 0 0 1 .8-.8zM3.5 3.5a.8.8 0 0 1 1 0l1.8 1.8a.8.8 0 1 1-1 1L3.5 4.6a.8.8 0 0 1 0-1zm14.2 14.2a.8.8 0 0 1 1 0l1.8 1.7a.8.8 0 0 1-1 1l-1.8-1.7a.8.8 0 0 1 0-1z"
></path>
<path
class="moon"
fill-rule="evenodd"
d="M16.5 6A10.5 10.5 0 0 1 4.7 16.4 8.5 8.5 0 1 0 16.4 4.7l.1 1.3zm-1.7-2a9 9 0 0 1 .2 2 9 9 0 0 1-11 8.8 9.4 9.4 0 0 1-.8-.3c-.4 0-.8.3-.7.7a10 10 0 0 0 .3.8 10 10 0 0 0 9.2 6 10 10 0 0 0 4-19.2 9.7 9.7 0 0 0-.9-.3c-.3-.1-.7.3-.6.7a9 9 0 0 1 .3.8z"
></path>
</svg>
</button>
<button id="themeToggle" aria-label="Toggle theme"> <a class="lang-switch" href={switchHref}>
<svg {switchLabel}
aria-hidden="true" </a>
width="30px" </div>
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
>
<path
class="sun"
fill-rule="evenodd"
d="M12 17.5a5.5 5.5 0 1 0 0-11 5.5 5.5 0 0 0 0 11zm0 1.5a7 7 0 1 0 0-14 7 7 0 0 0 0 14zm12-7a.8.8 0 0 1-.8.8h-2.4a.8.8 0 0 1 0-1.6h2.4a.8.8 0 0 1 .8.8zM4 12a.8.8 0 0 1-.8.8H.8a.8.8 0 0 1 0-1.6h2.5a.8.8 0 0 1 .8.8zm16.5-8.5a.8.8 0 0 1 0 1l-1.8 1.8a.8.8 0 0 1-1-1l1.7-1.8a.8.8 0 0 1 1 0zM6.3 17.7a.8.8 0 0 1 0 1l-1.7 1.8a.8.8 0 1 1-1-1l1.7-1.8a.8.8 0 0 1 1 0zM12 0a.8.8 0 0 1 .8.8v2.5a.8.8 0 0 1-1.6 0V.8A.8.8 0 0 1 12 0zm0 20a.8.8 0 0 1 .8.8v2.4a.8.8 0 0 1-1.6 0v-2.4a.8.8 0 0 1 .8-.8zM3.5 3.5a.8.8 0 0 1 1 0l1.8 1.8a.8.8 0 1 1-1 1L3.5 4.6a.8.8 0 0 1 0-1zm14.2 14.2a.8.8 0 0 1 1 0l1.8 1.7a.8.8 0 0 1-1 1l-1.8-1.7a.8.8 0 0 1 0-1z"
></path>
<path
class="moon"
fill-rule="evenodd"
d="M16.5 6A10.5 10.5 0 0 1 4.7 16.4 8.5 8.5 0 1 0 16.4 4.7l.1 1.3zm-1.7-2a9 9 0 0 1 .2 2 9 9 0 0 1-11 8.8 9.4 9.4 0 0 1-.8-.3c-.4 0-.8.3-.7.7a10 10 0 0 0 .3.8 10 10 0 0 0 9.2 6 10 10 0 0 0 4-19.2 9.7 9.7 0 0 0-.9-.3c-.3-.1-.7.3-.6.7a9 9 0 0 1 .3.8z"
></path>
</svg>
</button>
<style> <style>
.top-actions {
display: flex;
align-items: center;
gap: 0.7rem;
}
#themeToggle { #themeToggle {
border: 0; border: 0;
background: none; background: none;
cursor: pointer; cursor: pointer;
padding: 0.35rem;
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 999px;
transition:
transform 0.2s ease,
opacity 0.2s ease;
transform-origin: center;
} }
#themeToggle:hover {
transform: rotate(12deg);
}
#themeToggle:active {
transform: rotate(18deg) scale(0.96);
}
.lang-switch {
color: black; !important
font-weight: 700; !important
font-size: 0.95rem; !important
text-decoration: none; !important
line-height: 1; !important
}
.lang-switch:hover {
text-decoration: underline; !important
}
.sun { .sun {
fill: black; fill: black;
} }
.moon { .moon {
fill: transparent; fill: transparent;
} }
@ -38,9 +92,14 @@
:global(.dark) .sun { :global(.dark) .sun {
fill: transparent; fill: transparent;
} }
:global(.dark) .moon { :global(.dark) .moon {
fill: white; fill: white;
} }
:global(.dark) .lang-switch {
color: #eaeaea;
}
</style> </style>
<script is:inline> <script is:inline>

View file

@ -1,4 +1,8 @@
export default { export default {
banner: {
title: "Cloverta's Blog",
subtitle: ""
},
nav: { nav: {
home: "Home", home: "Home",
posts: "Posts", posts: "Posts",
@ -13,4 +17,31 @@ export default {
theme: { theme: {
toggle: "Toggle theme", toggle: "Toggle theme",
}, },
langSwitcher: "中文",
home: {
content: ""
},
about: {
title: "About Me, and This Blog",
name: "Cloverta",
slogan: "Sata Andagi!!!",
profilePicture: "https://files.seeusercontent.com/2026/03/24/Ne8b/009BC44B87E00F74351AA6730F8B7353.jpg",
content: [
"Who am I? Who are you? What even am I?",
"Whatever brought you to this page, I'm glad our paths crossed here on the internet.",
'This blog is built with <a href="https://astro.build/">Astro</a> and is a fully static frontend website. Compared with third-party commercial blog platforms or WordPress, it is simpler, more efficient, faster, and, at least to my taste, more beautiful.',
"At first, I self-hosted WordPress on my server, but it was just too heavy — loading an article of only a few hundred words could still take several seconds. For any programmer anxiously searching online for a solution, that can be quite frustrating. So for a long time, I had wanted to build a static blog from scratch myself.",
"As you can see, the functionality of this website may still be incomplete for now. I will continue improving and maintaining it over time.",
"The design of this blog was inspired by:",
'· <a href="https://ex-tasty.com/">極限風味</a>',
'· <a href="https://blog.cloudti.de/">Parsifal\'s Blog</a>',
"· And some other websites whose names I have unfortunately forgotten",
"Thank you for your ideas and passion!",
"In addition, this blog is fully open source. You can find its source code through the link in the footer.",
]
},
footer: {
githubIntro: 'See more on <a href="https://www.github.com/ClovertaTheTrilobita">GitHub</a>!',
repoIntro: 'This blog is fully open source at <a href="https://www.github.com/ClovertaTheTrilobita/SanYeCao-blog">ClovertaTheTrilobita/SanYeCao-blog</a>'
}
}; };

View file

@ -1,5 +1,5 @@
import zh from "./zh.ts"; import zh from "./zh";
import en from "./en.ts"; import en from "./en";
export const languages = { export const languages = {
zh, zh,
@ -10,19 +10,9 @@ export type Lang = keyof typeof languages;
export function getLangFromUrl(url: URL): Lang { export function getLangFromUrl(url: URL): Lang {
const lang = url.pathname.split("/")[1]; const lang = url.pathname.split("/")[1];
if (lang === "en") return "en"; return lang === "en" ? "en" : "zh";
return "zh";
} }
export function useTranslations(lang: Lang) { export function getTranslations(lang: Lang) {
return function t(path: string) { return languages[lang];
const keys = path.split(".");
let current: any = languages[lang];
for (const key of keys) {
current = current?.[key];
}
return current ?? path;
};
} }

View file

@ -1,4 +1,8 @@
export default { export default {
banner: {
title: "Cloverta 的博客",
subtitle: ""
},
nav: { nav: {
home: "首页", home: "首页",
posts: "文章", posts: "文章",
@ -13,4 +17,31 @@ export default {
theme: { theme: {
toggle: "切换主题", toggle: "切换主题",
}, },
langSwitcher: "EN",
home: {
content: ""
},
about: {
title: "关于我,和这个博客",
name: "三叶",
slogan: "Sata Andagi!!!",
profilePicture: "https://files.seeusercontent.com/2026/03/24/Ne8b/009BC44B87E00F74351AA6730F8B7353.jpg",
content: [
"我是?你是?我是??",
"无论你是因为什么点开了这个页面,都很高兴能在互联网中偶遇你。",
'这个博客使用 <a href="https://astro.build/">Astro</a> 构建,是一个完全静态的纯前端网页。相比第三方商业博客平台或 WordPress它更简洁、更高效、更快速并且至少在我的审美里也更美观。',
"最初,我曾在服务器上自建过 WordPress但它实在太重了——加载短短几百字的文章竟然也要花上好几秒。这对于任何一个火急火燎地上网查解决方案的码农来说都是相当折磨的事情。因此我从很久以前就想着自己从头写一个静态博客。",
"如你所见,这个网站的功能现在可能还不算完善。我会在接下来的时间里慢慢把它维护好。",
"这个博客在设计理念上参考了:",
'· <a href="https://ex-tasty.com/">極限風味</a>',
'· <a href="https://blog.cloudti.de/">Parsifal\'s Blog</a>',
"· 还有一些已经忘记名字的网站",
"谢谢你们的想法和热情!",
"此外,这个博客完全开源,你可以从页脚的链接处获取它的源代码。",
]
},
footer: {
githubIntro: '在 <a href="https://www.github.com/ClovertaTheTrilobita">GitHub</a> 查看更多!',
repoIntro: '这个博客完全开源于 <a href="https://www.github.com/ClovertaTheTrilobita/SanYeCao-blog">ClovertaTheTrilobita/SanYeCao-blog</a>'
}
}; };

View file

@ -1,11 +1,8 @@
--- ---
import Footer from "../components/Footer.astro"; import Footer from "@/components/Footer.astro";
import Header from "@/components/Header.astro"; import Header from "@/components/Header.astro";
import { getLangFromUrl, useTranslations } from "@/i18n";
const { pageTitle } = Astro.props; const { pageTitle } = Astro.props;
const lang = getLangFromUrl(Astro.url);
const t = useTranslations(lang);
--- ---
<!doctype html> <!doctype html>

View file

@ -0,0 +1,96 @@
---
import BaseLayout from "@/layouts/BaseLayout.astro";
import { getLangFromUrl, getTranslations } from "@/i18n";
import "@/styles/global.css";
export function getStaticPaths() {
return [{ params: { lang: "zh" } }, { params: { lang: "en" } }];
}
const lang = getLangFromUrl(Astro.url);
const t = getTranslations(lang);
const pageTitle = t.about.title;
---
<BaseLayout pageTitle={pageTitle}>
<main class="about">
<h1>{t.about.title}</h1>
<div class="intro">
<div class="intro-text">
<h2 class="name">{t.about.name}</h2>
<p class="slogan">{t.about.slogan}</p>
</div>
<img
src={t.about.profilePicture}
alt={t.about.name}
width="160"
class="avatar"
/>
</div>
<div class="content">
{t.about.content.map((line: string) => <p set:html={line} />)}
</div>
</main>
</BaseLayout>
<style>
.about {
max-width: 800px;
margin: 0 auto;
padding: 2rem 1rem;
}
.about h1 {
font-size: 1.5rem;
}
.intro {
display: flex;
justify-content: space-between;
align-items: center;
gap: 2rem;
margin-bottom: 2rem;
}
.intro-text {
flex: 1;
min-width: 0;
}
.name {
margin: 0 0 0.4rem 0;
font-size: 2rem;
font-weight: 700;
}
.slogan {
margin: 0;
color: gray;
font-style: italic;
}
.avatar {
display: block;
border-radius: 50%;
flex-shrink: 0;
}
.content p {
line-height: 1.8;
margin: 1rem 0;
}
.content a {
text-decoration: underline;
}
@media (max-width: 640px) {
.intro {
flex-direction: column-reverse;
align-items: flex-start;
}
}
</style>

View file

@ -0,0 +1,17 @@
---
import BaseLayout from "@/layouts/BaseLayout.astro";
import PostList from "@/components/Posts/PostList.astro";
import "@/styles/global.css";
export function getStaticPaths() {
return [{ params: { lang: "zh" } }, { params: { lang: "en" } }];
}
const { lang } = Astro.params;
const pageTitle = "Homepage";
---
<BaseLayout pageTitle={pageTitle}>
<h1>My Astro Site</h1>
<PostList />
</BaseLayout>

View file

@ -0,0 +1,29 @@
---
import { getCollection, render } from "astro:content";
import MarkdownPostLayout from "@/layouts/MarkdownPostLayout.astro";
export async function getStaticPaths() {
const posts = await getCollection("blog");
const langs = ["zh", "en"];
return langs.flatMap((lang) =>
posts.map((post) => ({
params: {
lang,
slug: post.id,
},
props: {
post,
lang,
},
})),
);
}
const { post, lang } = Astro.props;
const { Content } = await render(post);
---
<MarkdownPostLayout frontmatter={post.data} lang={lang}>
<Content />
</MarkdownPostLayout>

View file

@ -0,0 +1,40 @@
---
import { getCollection } from "astro:content";
import BaseLayout from "@/layouts/BaseLayout.astro";
import PostItem from "@/components/Posts/PostItem.astro";
import { getTranslations, type Lang } from "@/i18n";
export async function getStaticPaths() {
const allPosts = await getCollection("blog");
const uniqueTags = [
...new Set(allPosts.map((post) => post.data.tags).flat()),
];
const langs: Lang[] = ["zh", "en"];
return langs.flatMap((lang) =>
uniqueTags.map((tag) => {
const filteredPosts = allPosts.filter((post) =>
post.data.tags.includes(tag),
);
return {
params: { lang, tag },
props: { posts: filteredPosts, lang },
};
}),
);
}
const { tag } = Astro.params;
const { posts, lang } = Astro.props;
const t = getTranslations(lang);
---
<BaseLayout pageTitle={String(tag)}>
<p>
{lang === "zh" ? `带有标签 ${tag} 的文章` : `Posts tagged with ${tag}`}
</p>
<ul>
{posts.map((post) => <PostItem post={post} lang={lang} />)}
</ul>
</BaseLayout>

View file

@ -1,10 +1,18 @@
--- ---
import BaseLayout from "@/layouts/BaseLayout.astro"; import BaseLayout from "@/layouts/BaseLayout.astro";
import { getCollection } from "astro:content"; import { getCollection } from "astro:content";
import { getTranslations } from "@/i18n";
import "@/styles/global.css"; import "@/styles/global.css";
export function getStaticPaths() {
return [{ params: { lang: "zh" } }, { params: { lang: "en" } }];
}
const { lang } = Astro.params;
const t = getTranslations(lang);
const allPosts = await getCollection("blog"); const allPosts = await getCollection("blog");
const tags = [...new Set(allPosts.map((post: any) => post.data.tags).flat())]; const tags = [...new Set(allPosts.map((post: any) => post.data.tags).flat())];
const pageTitle = "Tag Index"; const pageTitle = lang === "zh" ? "标签索引" : "Tag Index";
--- ---
<BaseLayout pageTitle={pageTitle}> <BaseLayout pageTitle={pageTitle}>
@ -12,12 +20,13 @@ const pageTitle = "Tag Index";
{ {
tags.map((tag) => ( tags.map((tag) => (
<p class="tag"> <p class="tag">
<a href={`/tags/${tag}`}>{tag}</a> <a href={`/${lang}/tags/${tag}`}>{tag}</a>
</p> </p>
)) ))
} }
</div> </div>
</BaseLayout> </BaseLayout>
<style> <style>
a { a {
color: #00539f; color: #00539f;

View file

@ -1,21 +0,0 @@
---
import BaseLayout from "@/layouts/BaseLayout.astro";
import "@/styles/global.css";
const pageTitle = "About Me";
---
<BaseLayout pageTitle={pageTitle}>
<h1>{pageTitle}</h1>
<h2>... and my new Astro site!</h2>
<p>
I am working through Astro's introductory tutorial. This is the second page
on my website, and it's the first one I built myself!
</p>
<p>
This site will update as I complete more of the tutorial, so keep checking
back and see how my journey is going!
</p>
</BaseLayout>

View file

@ -1,15 +0,0 @@
---
import BaseLayout from "../layouts/BaseLayout.astro";
import PostList from "@/components/Posts/PostList.astro";
import "../styles/global.css";
const pageTitle = "Homepage";
// Welcome to Astro! Wondering what to do next? Check out the Astro documentation at https://docs.astro.build
// Don't want to use any of this? Delete everything in this file, the `assets`, `components`, and `layouts` directories, and start fresh.
---
<BaseLayout pageTitle={pageTitle}>
<h1>My Astro Site</h1>
<PostList />
</BaseLayout>

View file

@ -1,19 +0,0 @@
---
import { getCollection, render } from "astro:content";
import MarkdownPostLayout from "@/layouts/MarkdownPostLayout.astro";
export async function getStaticPaths() {
const posts = await getCollection("blog");
return posts.map((post) => ({
params: { slug: post.id },
props: { post },
}));
}
const { post } = Astro.props;
const { Content } = await render(post);
---
<MarkdownPostLayout frontmatter={post.data}>
<Content />
</MarkdownPostLayout>

View file

@ -1,36 +0,0 @@
---
import { getCollection } from "astro:content";
import BaseLayout from "../../layouts/BaseLayout.astro";
import PostItem from "@/components/Posts/PostItem.astro";
export async function getStaticPaths() {
const allPosts = await getCollection("blog");
const uniqueTags = [
...new Set(allPosts.map((post) => post.data.tags).flat()),
];
return uniqueTags.map((tag) => {
const filteredPosts = allPosts.filter((post) =>
post.data.tags.includes(tag),
);
return {
params: { tag },
props: { posts: filteredPosts },
};
});
}
const { tag } = Astro.params;
const { posts } = Astro.props;
---
<BaseLayout pageTitle={tag}>
<p>Posts tagged with {tag}</p>
<ul>
{
posts.map((post) => (
<PostItem url={`/posts/${post.id}/`} title={post.data.title} />
))
}
</ul>
</BaseLayout>

0
src/scripts/main.ts Normal file
View file

View file

@ -6,6 +6,14 @@
font-display: swap; font-display: swap;
} }
@font-face {
font-family: "Maple Mono";
src: url("/fonts/MapleMono-Italic.ttf.woff2") format("woff2");
font-weight: 400;
font-style: italic;
font-display: swap;
}
@font-face { @font-face {
font-family: "Maple Mono"; font-family: "Maple Mono";
src: url("/fonts/MapleMono-Bold.ttf.woff2") format("woff2"); src: url("/fonts/MapleMono-Bold.ttf.woff2") format("woff2");
@ -14,9 +22,34 @@
font-display: swap; font-display: swap;
} }
@font-face {
font-family: "Maple Mono CN";
src: url("/fonts/subset/MapleMono-CN-Regular.woff2") format("woff2");
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Maple Mono CN";
src: url("/fonts/subset/MapleMono-CN-Italic.woff2") format("woff2");
font-weight: 400;
font-style: italic;
font-display: swap;
}
@font-face {
font-family: "Maple Mono CN";
src: url("/fonts/subset/MapleMono-CN-Bold.woff2") format("woff2");
font-weight: 700;
font-style: normal;
font-display: swap;
}
html { html {
/* background-color: #f1f5f9; */ font-family: "Maple Mono", "Maple Mono CN", monospace;
font-family: "Maple Mono", monospace; background-color: #ffffff;
color: #1f2328;
} }
body { body {
@ -24,7 +57,7 @@ body {
width: 100%; width: 100%;
max-width: 80ch; max-width: 80ch;
padding: 1rem; padding: 1rem;
line-height: 1.5; line-height: 1.7;
} }
* { * {
@ -34,27 +67,46 @@ body {
h1 { h1 {
margin: 1rem 0; margin: 1rem 0;
font-size: 2.5rem; font-size: 2.5rem;
line-height: 1.2;
}
a {
color: #7fb3ff;
font-weight: 700;
text-decoration: none;
}
a:hover,
a:focus {
text-decoration: underline;
} }
html.dark { html.dark {
background-color: #0d0950; background-color: #1e1e1e;
color: #fff; color: #e6e6e6;
} }
.dark .menu { .dark .menu {
background-color: #fff; background-color: #2a2a2a;
color: #000; color: #e6e6e6;
}
.dark .nav-links a {
color: #9ecbff;
font-weight: 700;
} }
.dark .nav-links a:hover, .dark .nav-links a:hover,
.dark .nav-links a:focus { .dark .nav-links a:focus {
color: #0d0950; color: #c2deff;
}
.dark .nav-links a {
color: #fff;
} }
.dark a { .dark a {
color: #ff9776; color: #9ecbff;
font-weight: 700;
}
.dark a:hover,
.dark a:focus {
color: #c2deff;
} }