Merge pull request #24 from radishzzz/dev

 feat: optimize performance, enhance animations and improve accessibility

- perf: replace overlayscrollbars with native scrollbar to fix page loading lag
- feat: implement GSAP animations throughout the site with optimized curves and durations
- style: refine scrollbar styling and heading margins for better visual consistency
- fix: resolve accessibility issues with footer links and improve mobile responsiveness
- refactor: reorganize CSS files and optimize DOM selectors for better performance
- chore: update dependencies and implement astro-compress
- docs: update README and theme guides
This commit is contained in:
radishzz 2025-05-19 03:28:55 +01:00 committed by GitHub
commit 3d3e422ef2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
63 changed files with 1697 additions and 2027 deletions

81
.vscode/settings.json vendored
View file

@ -57,84 +57,5 @@
"typescript.tsdk": "node_modules/typescript/lib", "typescript.tsdk": "node_modules/typescript/lib",
"files.associations": { "files.associations": {
"*.mdx": "markdown" "*.mdx": "markdown"
}, }
"cSpell.words": [
"abbrlink",
"antfu",
"apiflash",
"apos",
"Artículos",
"astrodotbuild",
"astrojs",
"attributify",
"backref",
"belleza",
"Beze",
"Blockquotes",
"blurhash",
"bmoji",
"canvaskit",
"Cpath",
"Csvg",
"Disqus",
"Etiquetas",
"figcaption",
"Frontmatter",
"Fuwriu",
"Giscus",
"GSAP",
"gtag",
"hètí",
"Heti",
"katex",
"Lightbox",
"Macbook",
"mdast",
"Moeyua",
"msrc",
"msvalidate",
"noopener",
"noreferrer",
"Noto",
"oklch",
"opengraph",
"overlayscrollbars",
"pagefind",
"partytown",
"photoswipe",
"pswp",
"radishzz",
"rashomon",
"rehype",
"reimagines",
"Retipografía",
"Retypeset",
"Reviviendo",
"Roundhand",
"Segoe",
"Sentimentalisme",
"Servetus",
"shiki",
"Sobre",
"srcset",
"STIX",
"stylesheet",
"tipográfica",
"titlebar",
"Twikoo",
"Umami",
"unocss",
"unpic",
"vite",
"waline",
"walinejs",
"weibo",
"Возрождая",
"красоту",
"Переверстка",
"Посты",
"себе",
"Теги",
"типографики"
]
} }

View file

@ -42,7 +42,6 @@ Retypeset is a static blog theme based on the [Astro](https://astro.build/) fram
## Performance ## Performance
<br> <br>
<p align="center"> <p align="center">
<a href="https://pagespeed.web.dev/analysis?url=https%3A%2F%2Fretypeset.radishzz.cc%2Fen%2F"> <a href="https://pagespeed.web.dev/analysis?url=https%3A%2F%2Fretypeset.radishzz.cc%2Fen%2F">
<img width="710" alt="Retypeset Lighthouse Score" src="images/retypeset-lighthouse-score.svg"> <img width="710" alt="Retypeset Lighthouse Score" src="images/retypeset-lighthouse-score.svg">
@ -52,15 +51,13 @@ Retypeset is a static blog theme based on the [Astro](https://astro.build/) fram
## Getting Started ## Getting Started
1. [Fork](https://github.com/radishzzz/astro-theme-retypeset/fork) this repository, or use this template to create a new repository. 1. [Fork](https://github.com/radishzzz/astro-theme-retypeset/fork) this repository, or use this template to create a new repository.
2. Run the following commands in your terminal:
2. Click the `Code` button, copy the `HTTPS` URL, and run the following commands in your terminal:
```bash ```bash
# Clone the repository # Clone the repository
git clone repository-url git clone <repository-url>
# Navigate to the project directory # Navigate to the project directory
cd repository-name cd <repository-name>
# Install pnpm globally (if not already installed) # Install pnpm globally (if not already installed)
npm install -g pnpm npm install -g pnpm
@ -71,23 +68,14 @@ Retypeset is a static blog theme based on the [Astro](https://astro.build/) fram
# Start the development server # Start the development server
pnpm dev pnpm dev
``` ```
3. Refer to the [Theme Guide](https://retypeset.radishzz.cc/en/posts/theme-guide/) to customize your blog and create new articles.
4. Refer to the [Astro Deployment Guides](https://docs.astro.build/en/guides/deploy/) to deploy your blog to Netlify, Vercel, or other platforms.
3. Open [localhost:4321](http://localhost:4321/) in your browser to see a live preview of your website. &emsp;[![Deploy to Netlify](images/deploy-netlify.svg)](https://app.netlify.com/start) [![Deploy to Vercel](images/deploy-vercel.svg)](https://vercel.com/new)
4. Refer to the [Theme Guide](https://retypeset.radishzz.cc/en/posts/theme-guide/) to customize your blog and create new articles.
5. Refer to the [Astro Deployment Guides](https://docs.astro.build/en/guides/deploy/) to deploy your blog to Netlify, Vercel, or other platforms.
&emsp;[![Deploy to Netlify](images/deploy-netlify.svg)](https://app.netlify.com/start)
[![Deploy to Vercel](images/deploy-vercel.svg)](https://vercel.com/new)
## Updates ## Updates
- Retypeset releases [new features](https://github.com/radishzzz/astro-theme-retypeset/issues/18) from time to time, which can be updated as follows. Retypeset releases [new features](https://github.com/radishzzz/astro-theme-retypeset/issues/18) from time to time. You can refer to the [GitHub Docs](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork) and run `Sync fork` in your repository to sync with the latest branch. Do not click `Discard Changes`, or you will lose your modifications.
- Refer to the [GitHub Docs](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork) to run `Sync fork` in your repository to sync with the latest branch.
- Do not click `Discard Changes`, or you will lose your modifications.
## Credits ## Credits

View file

@ -2,7 +2,7 @@
<img alt="Cover Image" src="images/retypeset-zh-mobile.webp"/> <img alt="Cover Image" src="images/retypeset-zh-mobile.webp"/>
<div align="center"> <div align="center">
<a title="en" href="README.md"> <a title="en" href="https://github.com/radishzzz/astro-theme-retypeset?tab=readme-ov-file#retypeset">
<img src="https://img.shields.io/badge/-English-545759?style=for-the-badge" alt="English"> <img src="https://img.shields.io/badge/-English-545759?style=for-the-badge" alt="English">
</a> </a>
<picture> <picture>
@ -42,7 +42,6 @@ Retypeset 是一款基于 [Astro](https://astro.build/) 框架的静态博客主
## 性能 ## 性能
<br> <br>
<p align="center"> <p align="center">
<a href="https://pagespeed.web.dev/analysis?url=https%3A%2F%2Fretypeset.radishzz.cc%2F"> <a href="https://pagespeed.web.dev/analysis?url=https%3A%2F%2Fretypeset.radishzz.cc%2F">
<img width="710" alt="Retypeset Lighthouse Score" src="images/retypeset-lighthouse-score.svg"> <img width="710" alt="Retypeset Lighthouse Score" src="images/retypeset-lighthouse-score.svg">
@ -52,15 +51,13 @@ Retypeset 是一款基于 [Astro](https://astro.build/) 框架的静态博客主
## 开始 ## 开始
1. [Fork](https://github.com/radishzzz/astro-theme-retypeset/fork) 此仓库,或使用此模版创建新仓库。 1. [Fork](https://github.com/radishzzz/astro-theme-retypeset/fork) 此仓库,或使用此模版创建新仓库。
2. 在终端执行以下指令:
2. 点击 `Code` 按钮,复制 `HTTPS` 地址,在终端执行:
```bash ```bash
# 克隆仓库 # 克隆仓库
git clone 仓库地址 git clone <仓库地址>
# 进入项目目录 # 进入项目目录
cd 仓库名称 cd <仓库名称>
# 全局安装 pnpm如果未安装 # 全局安装 pnpm如果未安装
npm install -g pnpm npm install -g pnpm
@ -71,23 +68,14 @@ Retypeset 是一款基于 [Astro](https://astro.build/) 框架的静态博客主
# 启动开发服务器 # 启动开发服务器
pnpm dev pnpm dev
``` ```
3. 参考 [主题上手指南](https://retypeset.radishzz.cc/posts/theme-guide/),自定义你的博客并创建新文章。
4. 参考 [Astro 部署指南](https://docs.astro.build/zh-cn/guides/deploy/),将博客部署至 Netlify、Vercel 等平台。
3. 在浏览器中打开 [localhost:4321](http://localhost:4321/),查看网站的实时预览。 &emsp;[![Deploy to Netlify](images/deploy-netlify.svg)](https://app.netlify.com/start) [![Deploy to Vercel](images/deploy-vercel.svg)](https://vercel.com/new)
4. 参考 [主题上手指南](https://retypeset.radishzz.cc/posts/theme-guide/),自定义你的博客并创建新文章。
5. 参考 [Astro 部署指南](https://docs.astro.build/zh-cn/guides/deploy/),将博客部署至 Netlify、Vercel 等平台。
&emsp;[![Deploy to Netlify](images/deploy-netlify.svg)](https://app.netlify.com/start)
[![Deploy to Vercel](images/deploy-vercel.svg)](https://vercel.com/new)
## 更新 ## 更新
- Retypeset 会不定期发布 [新功能](https://github.com/radishzzz/astro-theme-retypeset/issues/18),更新方法如下。 Retypeset 会不定期发布 [新功能](https://github.com/radishzzz/astro-theme-retypeset/issues/18)。你可以参考 [GitHub 文档](https://docs.github.com/zh/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork),在仓库中执行 `Sync fork` 以同步最新分支。不要点击 `Discard Changes`,否则会丢失你的更改。
- 参考 [GitHub 文档](https://docs.github.com/zh/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork),在仓库中执行 `Sync fork` 同步最新分支。
- 不要点击 `Discard Changes`,否则会丢失你的更改。
## 鸣谢 ## 鸣谢

View file

@ -2,6 +2,7 @@ import type { Element } from 'hast'
import mdx from '@astrojs/mdx' import mdx from '@astrojs/mdx'
import partytown from '@astrojs/partytown' import partytown from '@astrojs/partytown'
import sitemap from '@astrojs/sitemap' import sitemap from '@astrojs/sitemap'
import Compress from 'astro-compress'
import robotsTxt from 'astro-robots-txt' import robotsTxt from 'astro-robots-txt'
import { defineConfig } from 'astro/config' import { defineConfig } from 'astro/config'
import rehypeAutolinkHeadings from 'rehype-autolink-headings' import rehypeAutolinkHeadings from 'rehype-autolink-headings'
@ -15,6 +16,7 @@ import UnoCSS from 'unocss/astro'
import { themeConfig } from './src/config' import { themeConfig } from './src/config'
import { langMap } from './src/i18n/config' import { langMap } from './src/i18n/config'
import { rehypeImgToFigure } from './src/plugins/rehype-img-to-figure.mjs' import { rehypeImgToFigure } from './src/plugins/rehype-img-to-figure.mjs'
import { rehypeUnwrapImg } from './src/plugins/rehype-unwrap-img.mjs'
import { remarkAdmonitions } from './src/plugins/remark-admonitions.mjs' import { remarkAdmonitions } from './src/plugins/remark-admonitions.mjs'
import { remarkGithubCard } from './src/plugins/remark-github-card.mjs' import { remarkGithubCard } from './src/plugins/remark-github-card.mjs'
import { remarkReadingTime } from './src/plugins/remark-reading-time.mjs' import { remarkReadingTime } from './src/plugins/remark-reading-time.mjs'
@ -57,6 +59,13 @@ export default defineConfig({
}), }),
sitemap(), sitemap(),
robotsTxt(), robotsTxt(),
Compress({
CSS: false,
HTML: true, // Enable HTML compression only to remove comments
Image: false,
JavaScript: false,
SVG: false,
}),
], ],
markdown: { markdown: {
remarkPlugins: [ remarkPlugins: [
@ -70,6 +79,7 @@ export default defineConfig({
rehypeKatex, rehypeKatex,
rehypeSlug, rehypeSlug,
rehypeImgToFigure, rehypeImgToFigure,
rehypeUnwrapImg, // Must be after rehypeImgToFigure
[ [
rehypeAutolinkHeadings, rehypeAutolinkHeadings,
{ {

View file

@ -2,7 +2,7 @@
"name": "astro-theme-retypeset", "name": "astro-theme-retypeset",
"type": "module", "type": "module",
"version": "0.0.1", "version": "0.0.1",
"packageManager": "pnpm@10.10.0", "packageManager": "pnpm@10.11.0",
"repository": "https://github.com/radishzzz/astro-theme-retypeset", "repository": "https://github.com/radishzzz/astro-theme-retypeset",
"scripts": { "scripts": {
"dev": "astro check && astro dev", "dev": "astro check && astro dev",
@ -10,23 +10,24 @@
"preview": "astro preview", "preview": "astro preview",
"astro": "astro", "astro": "astro",
"lint": "eslint .", "lint": "eslint .",
"lint:fix": "eslint . --fix" "lint:fix": "eslint . --fix",
"comments": "curl -L https://unpkg.com/@waline/client@latest/dist/waline.css -o public/assets/waline/waline.css && curl -L https://unpkg.com/@waline/client@latest/dist/waline.js -o public/assets/waline/waline.js"
}, },
"dependencies": { "dependencies": {
"@astrojs/mdx": "^4.2.6", "@astrojs/mdx": "^4.2.6",
"@astrojs/partytown": "^2.1.4", "@astrojs/partytown": "^2.1.4",
"@astrojs/sitemap": "^3.4.0", "@astrojs/sitemap": "^3.4.0",
"@waline/client": "^3.5.7",
"astro": "^5.7.13", "astro": "^5.7.13",
"astro-compress": "^2.3.8",
"astro-og-canvas": "^0.7.0", "astro-og-canvas": "^0.7.0",
"astro-robots-txt": "^1.0.0", "astro-robots-txt": "^1.0.0",
"canvaskit-wasm": "^0.40.0", "canvaskit-wasm": "^0.40.0",
"feed": "^5.0.1", "feed": "^5.0.1",
"gsap": "^3.13.0",
"katex": "^0.16.22", "katex": "^0.16.22",
"markdown-it": "^14.1.0", "markdown-it": "^14.1.0",
"mdast-util-to-string": "^4.0.0", "mdast-util-to-string": "^4.0.0",
"node-html-parser": "^7.0.1", "node-html-parser": "^7.0.1",
"overlayscrollbars": "^2.11.2",
"photoswipe": "^5.4.4", "photoswipe": "^5.4.4",
"reading-time": "^1.5.0", "reading-time": "^1.5.0",
"rehype-autolink-headings": "^7.1.0", "rehype-autolink-headings": "^7.1.0",
@ -35,26 +36,26 @@
"rehype-slug": "^6.0.0", "rehype-slug": "^6.0.0",
"remark-directive": "^4.0.0", "remark-directive": "^4.0.0",
"remark-math": "^6.0.0", "remark-math": "^6.0.0",
"sanitize-html": "^2.16.0", "sanitize-html": "^2.17.0",
"sharp": "^0.34.1", "sharp": "^0.34.1",
"unist-util-visit": "^5.0.0" "unist-util-visit": "^5.0.0"
}, },
"devDependencies": { "devDependencies": {
"@antfu/eslint-config": "^4.13.0", "@antfu/eslint-config": "^4.13.1",
"@astrojs/check": "^0.9.4", "@astrojs/check": "^0.9.4",
"@types/hast": "^3.0.4", "@types/hast": "^3.0.4",
"@types/markdown-it": "^14.1.2", "@types/markdown-it": "^14.1.2",
"@types/node": "^22.15.17", "@types/node": "^22.15.18",
"@types/sanitize-html": "^2.16.0", "@types/sanitize-html": "^2.16.0",
"@unocss/eslint-plugin": "66.1.1", "@unocss/eslint-plugin": "66.1.2",
"@unocss/preset-attributify": "66.1.1", "@unocss/preset-attributify": "66.1.2",
"@unocss/reset": "66.1.1", "@unocss/reset": "66.1.2",
"astro-eslint-parser": "^1.2.2", "astro-eslint-parser": "^1.2.2",
"eslint": "^9.26.0", "eslint": "^9.27.0",
"eslint-plugin-astro": "^1.3.1", "eslint-plugin-astro": "^1.3.1",
"lint-staged": "^16.0.0", "lint-staged": "^16.0.0",
"typescript": "~5.8.3", "typescript": "~5.8.3",
"unocss": "66.1.1", "unocss": "66.1.2",
"unocss-preset-theme": "^0.14.1" "unocss-preset-theme": "^0.14.1"
}, },
"lint-staged": { "lint-staged": {

2169
pnpm-lock.yaml generated

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,91 @@
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

View file

@ -28,9 +28,9 @@ const nextUrl = useSupportedLangs
<div <div
class:list={[ class:list={[
'absolute flex gap-6 top-14.6 right-7.25vw min-[823px]:max-[1024px]:right-[calc(50vw-22rem)]', 'absolute right-7.25vw top-14.6 flex gap-6 min-[823px]:max-[1024px]:right-[calc(50vw-22rem)]',
'[@supports(-webkit-touch-callout:none)]:top-13.6', // fix position issue on ios '[@supports(-webkit-touch-callout:none)]:top-13.6', // fix position issue on ios
'lg:(fixed w-14rem top-auto bottom-47 right-[max(5.625rem,calc(50vw-34.375rem))])', 'lg:(fixed bottom-47 right-[max(5rem,calc(50vw-35rem))] top-auto w-14rem)',
]} ]}
> >
<!-- Language Switcher --> <!-- Language Switcher -->
@ -49,8 +49,9 @@ const nextUrl = useSupportedLangs
<!-- Theme Toggle --> <!-- Theme Toggle -->
<button <button
id="theme-toggle-button"
aria-label="Switch light/dark theme" aria-label="Switch light/dark theme"
class="button-theme-toggle aspect-square w-4 c-secondary active:scale-90 hover:c-primary" class="aspect-square w-4 c-secondary active:scale-90 hover:c-primary"
> >
<ThemeToggleIcon <ThemeToggleIcon
aria-hidden="true" aria-hidden="true"
@ -69,7 +70,7 @@ function updateTheme() {
// Get current theme // Get current theme
const isDark = document.documentElement.classList.contains('dark') const isDark = document.documentElement.classList.contains('dark')
// Update meta theme color // Update meta theme color
const metaThemeColor = document.querySelector('meta[name="theme-color"]') const metaThemeColor = document.head.querySelector('meta[name="theme-color"]')
if (metaThemeColor) { if (metaThemeColor) {
metaThemeColor.setAttribute('content', isDark ? darkMode : lightMode) metaThemeColor.setAttribute('content', isDark ? darkMode : lightMode)
} }
@ -82,10 +83,10 @@ function updateTheme() {
// Bind click event to the button // Bind click event to the button
function setupThemeToggle() { function setupThemeToggle() {
// Locate theme toggle button // Locate theme toggle button
const themeToggleButtons = document.querySelectorAll('.button-theme-toggle') const themeToggleButton = document.getElementById('theme-toggle-button')
// Add click listener to each button // Add click listener to the button
themeToggleButtons.forEach((button) => { if (themeToggleButton) {
button.addEventListener('click', () => { themeToggleButton.addEventListener('click', () => {
// If browser doesn't support View Transitions API, update theme directly // If browser doesn't support View Transitions API, update theme directly
if (!document.startViewTransition) { if (!document.startViewTransition) {
updateTheme() updateTheme()
@ -93,18 +94,18 @@ function setupThemeToggle() {
} }
// Temporarily add markers during animation to implement view transition and disable CSS transitions // Temporarily add markers during animation to implement view transition and disable CSS transitions
document.documentElement.style.setProperty('view-transition-name', 'theme-transition') document.documentElement.style.setProperty('view-transition-name', 'animation-theme-toggle')
document.documentElement.setAttribute('data-theme-transition', '') document.documentElement.setAttribute('data-theme-changing', '')
// If browser supports View Transitions API, use it to update theme // If browser supports View Transitions API, use it to update theme
const themeTransition = document.startViewTransition(updateTheme) const themeTransition = document.startViewTransition(updateTheme)
// Remove markers after animation // Remove markers after animation
themeTransition.finished.then(() => { themeTransition.finished.then(() => {
document.documentElement.style.removeProperty('view-transition-name') document.documentElement.style.removeProperty('view-transition-name')
document.documentElement.removeAttribute('data-theme-transition') document.documentElement.removeAttribute('data-theme-changing')
})
}) })
}) })
}
} }
// Initialize click event (on first load or page transition) // Initialize click event (on first load or page transition)

View file

@ -2,13 +2,6 @@
import { defaultLocale, themeConfig } from '@/config' import { defaultLocale, themeConfig } from '@/config'
import { walineLocaleMap } from '@/i18n/config' import { walineLocaleMap } from '@/i18n/config'
const {
serverURL = '',
emoji = [],
search = false,
imageUploader = false,
} = themeConfig.comment?.waline ?? {}
// Get the language code of Waline // Get the language code of Waline
function getWalineLang(currentPath: string, defaultLocale: string): string { function getWalineLang(currentPath: string, defaultLocale: string): string {
// Extract language code from path // Extract language code from path
@ -20,43 +13,46 @@ function getWalineLang(currentPath: string, defaultLocale: string): string {
return walineLocaleMap[lang as keyof typeof walineLocaleMap] return walineLocaleMap[lang as keyof typeof walineLocaleMap]
} }
// Get Waline language and generate configuration json
const walineLang = getWalineLang(Astro.url.pathname, defaultLocale) const walineLang = getWalineLang(Astro.url.pathname, defaultLocale)
const walineConfigJson = JSON.stringify({ const { waline: { serverURL = '', emoji = [], search = false, imageUploader = false } = {} } = themeConfig.comment ?? {}
serverURL,
lang: walineLang,
emoji,
search,
imageUploader,
})
--- ---
<div <div
id="waline" id="waline"
class="mt-16" class="mt-16"
data-config={walineConfigJson} ></div>
>
</div>
<!-- Not use is:inline or define:vars >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> --> <!-- >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> -->
<script>
import { init } from '@waline/client' <script
import '@waline/client/style' is:inline
define:vars={{
walineLang,
serverURL,
emoji,
search,
imageUploader,
}}
type="module"
>
import { init } from '/assets/waline/waline.js'
function initWaline() { function initWaline() {
const walineEl = document.getElementById('waline')
const walineConfig = JSON.parse(walineEl?.dataset.config || '{}')
init({ init({
el: '#waline', el: '#waline',
serverURL,
// Share comments on posts in different languages
path: window.location.pathname.replace(/^\/([a-z]{2}(-[a-z]{2})?)\//, '/'), path: window.location.pathname.replace(/^\/([a-z]{2}(-[a-z]{2})?)\//, '/'),
lang: walineLang,
emoji,
dark: 'html.dark', dark: 'html.dark',
requiredMeta: ['nick', 'mail'], requiredMeta: ['nick', 'mail'],
highlighter: false, highlighter: false,
texRenderer: false, texRenderer: false,
imageUploader,
search,
noCopyright: true, noCopyright: true,
reaction: [], reaction: [],
...walineConfig,
}) })
} }
@ -79,13 +75,13 @@ document.addEventListener('astro:page-load', initWaline)
--at-apply: 'leading-3.6 mt-1.4'; --at-apply: 'leading-3.6 mt-1.4';
} }
#waline .wl-panel { #waline .wl-panel {
--at-apply: 'm-0 rounded border-secondary/25' --at-apply: 'm-0 rounded-lg border-secondary/25'
} }
#waline .wl-header { #waline .wl-header {
--at-apply: 'p-0'; --at-apply: 'p-0';
} }
#waline .wl-header-item { #waline .wl-header-item {
border-bottom: 1px solid var(--waline-border-color); --at-apply: 'border-b border-solid border-primary/25';
} }
#waline .wl-header label { #waline .wl-header label {
--at-apply: 'text-3'; --at-apply: 'text-3';
@ -104,14 +100,11 @@ document.addEventListener('astro:page-load', initWaline)
--at-apply: 'min-h-24'; --at-apply: 'min-h-24';
} }
#waline .wl-editor::placeholder { #waline .wl-editor::placeholder {
color: var(--waline-light-grey); --at-apply: 'c-primary/25';
} }
#waline .wl-footer { #waline .wl-footer {
--at-apply: 'm-2'; --at-apply: 'm-2';
} }
#waline .wl-info .wl-btn {
--at-apply: 'rounded';
}
#waline .wl-text-number, #waline .wl-text-number,
#waline .wl-action[title="Markdown Guide"], #waline .wl-action[title="Markdown Guide"],
#waline .wl-sort, #waline .wl-sort,
@ -119,16 +112,10 @@ document.addEventListener('astro:page-load', initWaline)
--at-apply: 'hidden'; --at-apply: 'hidden';
} }
#waline .wl-emoji-popup { #waline .wl-emoji-popup {
--at-apply: 'start-0 rounded border-secondary/25'; --at-apply: 'start-0 border-secondary/25 max-w-532px';
} }
#waline .wl-emoji-popup .wl-tab-wrapper::-webkit-scrollbar { #waline .wl-emoji-popup .wl-tab-wrapper {
--at-apply: 'w-1.2'; scrollbar-width: thin;
}
#waline .wl-emoji-popup .wl-tab-wrapper::-webkit-scrollbar-thumb {
background: oklch(var(--un-preset-theme-colors-secondary) / 0.25);
}
#waline .wl-emoji-popup .wl-tab-wrapper::-webkit-scrollbar-track-piece {
--at-apply: 'bg-transparent';
} }
#waline .wl-gif-popup { #waline .wl-gif-popup {
--at-apply: 'border-secondary/25'; --at-apply: 'border-secondary/25';
@ -155,7 +142,7 @@ document.addEventListener('astro:page-load', initWaline)
--at-apply: 'leading-6 text-3.5'; --at-apply: 'leading-6 text-3.5';
} }
#waline .wl-time { #waline .wl-time {
color: oklch(var(--un-preset-theme-colors-primary) / 0.75); --at-apply: 'c-primary/75';
} }
#waline .wl-edit, #waline .wl-edit,
#waline .wl-delete { #waline .wl-delete {
@ -167,7 +154,6 @@ document.addEventListener('astro:page-load', initWaline)
</style> </style>
<!-- Official CSS Variables >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> --> <!-- Official CSS Variables >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> -->
<!-- https://waline.js.org/reference/client/style.html -->
<style> <style>
#waline { #waline {
/* Regular Colors */ /* Regular Colors */

View file

@ -10,39 +10,46 @@ const year = Number(startYear) === currentYear
? startYear ? startYear
: `${startYear}-${currentYear}` : `${startYear}-${currentYear}`
// i18n RSS Path // i18n RSS Feed Path
const currentLang = getLangFromPath(Astro.url.pathname) const currentLang = getLangFromPath(Astro.url.pathname)
const links = socialLinks.map((link) => { const links = socialLinks.map((link) => {
if (link.name === 'RSS') { if (link.name === 'RSS') {
return { return {
...link, ...link,
url: currentLang === defaultLocale ? link.url : `/${currentLang}${link.url}`, url: currentLang === defaultLocale
? link.url
: `/${currentLang}${link.url}`,
}
}
if (link.name === 'Email') {
return {
...link,
url: `mailto:${link.url}`,
} }
} }
return link return link
}) })
const footerLinkClass = 'highlight-hover footer-highlight-position-fix py-0.8 transition-colors after:bottom-0.15em hover:c-primary'
--- ---
<footer <footer
class="text-3 leading-4.75 font-navbar lg:text-3.5" class="mb-4 text-3 leading-1.25em font-navbar lg:(mb-0 text-3.5)"
lg="uno-desktop-column bottom-20" lg="uno-desktop-column bottom-20"
> >
<p> <p>
{links.map((link, index) => ( {links.map((link, index) => (
<> <>
<a class="highlight-hover transition-colors hover:c-primary after:bottom-0!" href={link.url} target="_blank" rel="noopener noreferrer"> <a class={footerLinkClass} href={link.url} target="_blank" rel="noopener noreferrer">{link.name}</a>&nbsp;{index < links.length - 1 && '/'}
{link.name}
</a>
{index < links.length - 1 && ' / '}
</> </>
))} ))}
</p> </p>
<p> <p>
Powered by <a class="highlight-hover transition-colors hover:c-primary after:bottom-0!" href="https://astro.build/" target="_blank" rel="noopener noreferrer">Astro</a> and <a class="highlight-hover transition-colors hover:c-primary after:bottom-0!" href="https://github.com/radishzzz/astro-theme-retypeset" target="_blank" rel="noopener noreferrer">Retypeset</a> © {year} {author}
</p> </p>
<p> <p>
© {year} {author} Powered by <a class={footerLinkClass} href="https://astro.build/" target="_blank" rel="noopener noreferrer">Astro</a> and <a class={footerLinkClass} href="https://github.com/radishzzz/astro-theme-retypeset" target="_blank" rel="noopener noreferrer">Retypeset</a>
</p> </p>
</footer> </footer>

View file

@ -32,7 +32,7 @@ const SubtitleTag = isPost ? 'div' : 'h2'
<div <div
class="box-content inline-block pr-1" class="box-content inline-block pr-1"
transition:name={`site-title-${currentLang}`} transition:name={`site-title-${currentLang}`}
data-disable-transition-on-theme data-disable-theme-transition
> >
<a <a
id="site-title-link" id="site-title-link"

View file

@ -10,7 +10,9 @@ const isTagActive = isTag
const isAboutActive = isAbout const isAboutActive = isAbout
function getNavItemClass(isActive: boolean) { function getNavItemClass(isActive: boolean) {
return isActive ? 'font-bold c-primary highlight-static' : 'hover:(c-primary font-bold) transition-all highlight-hover' return isActive
? 'highlight-static c-primary font-bold after:bottom-0.5em'
: 'highlight-hover transition-colors transition-font-weight after:bottom-0.5em hover:(c-primary font-bold)'
} }
const navItems = [ const navItems = [
@ -36,8 +38,8 @@ const navItems = [
aria-label="Site Navigation" aria-label="Site Navigation"
class:list={[ class:list={[
isPost ? 'hidden lg:block' : '', isPost ? 'hidden lg:block' : '',
'mb-10.5 text-3.6 font-semibold leading-8.75 font-navbar', 'mb-10.5 text-3.6 font-semibold leading-2.45em font-navbar',
'lg:(uno-desktop-column text-4 leading-9.72 bottom-50) cjk:tracking-0.02em', 'lg:(uno-desktop-column text-4 bottom-50) cjk:tracking-0.02em',
]} ]}
> >
<ul> <ul>
@ -45,7 +47,7 @@ const navItems = [
<li> <li>
<a <a
href={getLocalizedPath(item.href)} href={getLocalizedPath(item.href)}
class={item.className} class={`${item.className} navbar-highlight-position-fix`}
> >
{item.label} {item.label}
</a> </a>

View file

@ -37,13 +37,13 @@ function getPostPath(post: Post) {
> >
{/* post title */} {/* post title */}
<h3 class="inline"> <h3 class="inline transition-colors hover:c-primary">
<a <a
class="hover:c-primary cjk:tracking-0.02em" class="cjk:tracking-0.02em"
lg={isHome ? 'font-medium text-4.5' : ''} lg={isHome ? 'font-medium text-4.5' : ''}
href={getPostPath(post)} href={getPostPath(post)}
transition:name={`post-${post.data.abbrlink || post.id}-${lang}`} transition:name={`post-${post.data.abbrlink || post.id}-${lang}`}
data-disable-transition-on-theme data-disable-theme-transition
> >
{post.data.title} {post.data.title}
</a> </a>
@ -60,9 +60,9 @@ function getPostPath(post: Post) {
{/* mobile post time */} {/* mobile post time */}
<div <div
class="text-3.5 leading-6.875 font-time lg:hidden" class="py-0.8 text-3.5 font-time lg:hidden"
transition:name={`time-${post.data.abbrlink || post.id}-${lang}`} transition:name={`time-${post.data.abbrlink || post.id}-${lang}`}
data-disable-transition-on-theme data-disable-theme-transition
> >
<PostDate <PostDate
date={post.data.published} date={post.data.published}
@ -71,7 +71,7 @@ function getPostPath(post: Post) {
</div> </div>
{/* desktop post time */} {/* desktop post time */}
<div class="hidden text-3.65 leading-6.875 font-time lg:(ml-2.5 inline)"> <div class="hidden text-3.65 font-time lg:(ml-2.5 inline)">
<PostDate <PostDate
date={post.data.published} date={post.data.published}
minutes={post.remarkPluginFrontmatter.minutes} minutes={post.remarkPluginFrontmatter.minutes}
@ -82,7 +82,7 @@ function getPostPath(post: Post) {
{isHome && ( {isHome && (
<div <div
class="heti hidden" class="heti hidden"
lg="mt-2 block" lg="mt-2.25 block"
> >
<p>{generateDescription(post, 'list')}</p> <p>{generateDescription(post, 'list')}</p>
</div> </div>

View file

@ -5,7 +5,7 @@ import GoBackIcon from '@/assets/icons/go-back.svg';
<button <button
id="back-button" id="back-button"
class="hidden" class="hidden"
lg="block absolute c-secondary/40 left--10 top-1/2 aspect-square w-4.5 translate-y--1/2 transition-colors ease-out c-secondary active:scale-90 hover:c-primary/80" lg="block absolute c-secondary/40 left--10 top-1/2 aspect-square w-4.5 translate-y--1/2 transition-colors ease-out c-secondary active:scale-90! hover:c-secondary/80"
aria-label="Go back" aria-label="Go back"
> >
<GoBackIcon <GoBackIcon

View file

@ -8,7 +8,7 @@
<button <button
id="back-to-top-button" id="back-to-top-button"
aria-label="Back to top" aria-label="Back to top"
class="fixed bottom-8 right-8 h-10 w-10 rounded-full bg-background transition-all duration-300 ease-out" class="fixed bottom-8 right-8 h-10 w-10 rounded-full bg-background transition-all ease-out"
> >
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"

View file

@ -1,9 +1,10 @@
<script> <script>
function setupGithubCards() { function setupGithubCards() {
const githubCards = document.querySelectorAll('.gc-container') const githubCards = document.getElementsByClassName('gc-container')
if (githubCards.length === 0) if (githubCards.length === 0)
return return
// Create an intersection observer to lazy load GitHub repo data when cards enter viewport
const observer = new IntersectionObserver((entries) => { const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => { entries.forEach((entry) => {
if (entry.isIntersecting) { if (entry.isIntersecting) {
@ -13,7 +14,7 @@ function setupGithubCards() {
}) })
}, { rootMargin: '400px' }) }, { rootMargin: '400px' })
githubCards.forEach(card => observer.observe(card)) Array.from(githubCards).forEach(card => observer.observe(card))
} }
async function loadCardData(card: HTMLElement) { async function loadCardData(card: HTMLElement) {
@ -21,11 +22,11 @@ async function loadCardData(card: HTMLElement) {
if (!repo) if (!repo)
return return
const avatarEl = card.querySelector('.gc-owner-avatar') as HTMLElement const avatarEl = card.getElementsByClassName('gc-owner-avatar')[0] as HTMLElement
const descEl = card.querySelector('.gc-repo-description') as HTMLElement const descEl = card.getElementsByClassName('gc-repo-description')[0] as HTMLElement
const starsEl = card.querySelector('.gc-stars-count') as HTMLElement const starsEl = card.getElementsByClassName('gc-stars-count')[0] as HTMLElement
const forksEl = card.querySelector('.gc-forks-count') as HTMLElement const forksEl = card.getElementsByClassName('gc-forks-count')[0] as HTMLElement
const licenseEl = card.querySelector('.gc-license-info') as HTMLElement const licenseEl = card.getElementsByClassName('gc-license-info')[0] as HTMLElement
try { try {
const response = await fetch(`https://api.github.com/repos/${repo}`) const response = await fetch(`https://api.github.com/repos/${repo}`)

View file

@ -0,0 +1,99 @@
<script>
import { gsap } from 'gsap'
function setupPostPageAnimation() {
const allElements = Array.from(document.querySelectorAll('#gsap-post-page-content > *, #gsap-post-page-tags, #waline'))
const tocList = document.getElementById('toc-list')
const tocListChildren = tocList ? Array.from(tocList.children) : []
const dateElement = document.getElementById('gsap-post-page-date')
const backButton = document.getElementById('back-button')
const tocIcon = document.getElementById('toc-icon')
const tocContainer = document.getElementById('toc-container')
const isLargeScreen = window.matchMedia('(min-width: 1024px)').matches
const isSmallScreen = window.matchMedia('(max-width: 1535px)').matches
// Post Content + Tags + Comments
// First 15 elements
gsap.from(allElements.slice(0, 15), {
opacity: 0,
y: '2rem',
duration: 0.3,
delay: 0.1,
ease: 'power2.out',
stagger: 0.05,
})
// Rest elements as the 16 element
if (allElements.length > 15) {
gsap.from(allElements.slice(15), {
opacity: 0,
y: '2rem',
duration: 0.3,
delay: 0.1 + 0.05 * 15,
ease: 'power2.out',
})
}
// Desktop Animations
if (isLargeScreen) {
// Post Date
if (dateElement) {
gsap.from(dateElement, {
opacity: 0,
y: '1rem',
duration: 0.3,
delay: 0.1,
ease: 'power2.out',
})
}
// TOC Icon
if (tocIcon) {
gsap.from(tocIcon, {
opacity: 0,
y: '0.5rem',
duration: 0.3,
delay: 0.125,
ease: 'power2.out',
})
}
// Toc List
if (tocListChildren.length > 0) {
gsap.from(tocListChildren, {
opacity: 0,
y: '1rem',
duration: 0.3,
delay: 0.15,
ease: 'power2.out',
stagger: 0.025,
})
}
// Back Button
if (backButton) {
gsap.from(backButton, {
opacity: 0,
x: '0.5rem',
duration: 0.3,
delay: 0.15,
ease: 'power2.out',
})
}
}
// Mobile Animation (for screens smaller than 1536px)
if (isSmallScreen && tocContainer) {
gsap.from(tocContainer, {
opacity: 0,
y: '2rem',
duration: 0.3,
delay: 0.1,
ease: 'power2.out',
})
}
}
setupPostPageAnimation()
document.addEventListener('astro:after-swap', setupPostPageAnimation)
</script>

View file

@ -10,7 +10,8 @@ function setupPhotoSwipe() {
if (bodyElement.hasAttribute('data-photoswipe-initialized')) if (bodyElement.hasAttribute('data-photoswipe-initialized'))
return return
const images = document.querySelectorAll('article.heti img') const article = document.querySelector('article.heti')
const images = article ? article.getElementsByTagName('img') : []
if (images.length === 0) if (images.length === 0)
return return

View file

@ -1,110 +0,0 @@
<script>
import { OverlayScrollbars } from 'overlayscrollbars'
function setupScrollbar() {
// Add scrollbar to body
const bodyElement = document.body
if (!bodyElement.hasAttribute('data-scrollbar-initialized')) {
OverlayScrollbars({
target: bodyElement,
cancel: {
nativeScrollbarsOverlaid: true,
},
}, {
scrollbars: {
theme: 'scrollbar-body',
autoHide: 'scroll',
autoHideDelay: 800,
},
overflow: {
x: 'hidden',
},
})
bodyElement.setAttribute('data-scrollbar-initialized', 'true')
}
// Add scrollbar to TOC content
const tocElement = document.getElementById('toc-content')
if (tocElement && !tocElement.hasAttribute('data-scrollbar-initialized')) {
OverlayScrollbars({
target: tocElement,
}, {
scrollbars: {
theme: 'scrollbar-widget',
autoHide: 'never',
},
overflow: {
x: 'hidden',
},
})
tocElement.setAttribute('data-scrollbar-initialized', 'true')
}
// Add scrollbar to code blocks
const preElements = document.querySelectorAll('pre')
preElements.forEach((pre) => {
if (!pre.hasAttribute('data-scrollbar-initialized')) {
OverlayScrollbars({
target: pre,
}, {
scrollbars: {
theme: 'scrollbar-widget',
autoHide: 'leave',
autoHideDelay: 500,
},
overflow: {
y: 'hidden',
},
})
pre.setAttribute('data-scrollbar-initialized', 'true')
}
})
}
setupScrollbar()
document.addEventListener('astro:after-swap', setupScrollbar)
</script>
<style is:global>
@import 'overlayscrollbars/overlayscrollbars.css';
.scrollbar-body {
--os-size: 0.8rem;
--os-padding-perpendicular: 0.15rem;
--os-padding-axis: 0.2rem;
--os-handle-border-radius: 99rem;
--os-handle-perpendicular-size: 75%;
--os-handle-perpendicular-size-hover: 100%;
--os-handle-perpendicular-size-active: 100%;
--os-handle-interactive-area-offset: 0.2rem;
--os-handle-bg: oklch(var(--un-preset-theme-colors-secondary) / 0.25);
--os-handle-bg-hover: oklch(var(--un-preset-theme-colors-secondary) / 0.40);
--os-handle-bg-active: oklch(var(--un-preset-theme-colors-secondary) / 0.40);
--os-handle-max-size: 80%;
--os-handle-min-size: 12%;
}
.scrollbar-widget {
--os-size: 0.4rem;
--os-padding-perpendicular: 0;
--os-padding-axis: 0.05rem;
--os-handle-border-radius: 99rem;
--os-handle-perpendicular-size: 75%;
--os-handle-perpendicular-size-hover: 100%;
--os-handle-perpendicular-size-active: 100%;
--os-handle-interactive-area-offset: 0.2rem;
--os-handle-bg: oklch(var(--un-preset-theme-colors-secondary) / 0.15);
--os-handle-bg-hover: oklch(var(--un-preset-theme-colors-secondary) / 0.30);
--os-handle-bg-active: oklch(var(--un-preset-theme-colors-secondary) / 0.30);
--os-handle-min-size: 12%;
}
@media (min-width: 1536px) {
#toc-content .os-scrollbar {
--at-apply: 'hidden';
}
}
</style>

View file

@ -19,38 +19,51 @@ const filteredHeadings = headings.filter(heading =>
--- ---
{filteredHeadings.length > 0 && ( {filteredHeadings.length > 0 && (
<div class="mb-4 uno-round-border bg-secondary/5 2xl:(fixed left-0 top-43.5 max-w-[min(calc(50vw-38rem),13rem)] border-none bg-transparent)"> // TOC Container
<div
id="toc-container"
class="mb-4 uno-round-border bg-secondary/5 2xl:(fixed left-0 top-43.5 max-w-[min(calc(50vw-38rem),13rem)] border-none bg-secondary/0)"
>
{/* Hidden Checkbox */}
<input <input
type="checkbox" type="checkbox"
id="toc-toggle" id="toc-toggle"
class="accordion-toggle" class="accordion-toggle"
hidden hidden
/> />
<div class="relative h-12 w-full bg-transparent"> {/* TOC Toggle */}
<div class="relative h-12 w-full">
<label <label
for="toc-toggle" for="toc-toggle"
class="absolute inset-0 flex cursor-pointer items-center 2xl:(static flex c-secondary/40 transition-colors ease-out hover:c-secondary/80)" class="absolute inset-0 flex cursor-pointer items-center 2xl:(static flex c-secondary/40 transition-colors ease-out hover:c-secondary/80)"
> >
{/* Title on Mobile */}
<span class="toc-title"> <span class="toc-title">
{currentUI.toc} {currentUI.toc}
</span> </span>
{/* Icon on Desktop */}
<TocIcon <TocIcon
id="toc-icon"
aria-hidden="true" aria-hidden="true"
class="ml-4 hidden aspect-square w-4.2 2xl:(mt-4 block origin-center active:scale-90)" class="ml-1 hidden aspect-square w-4.2 2xl:(mt-4 block origin-center active:scale-90!)"
fill="currentColor" fill="currentColor"
/> />
</label> </label>
</div> </div>
{/* Expandable content wrapper */} {/* Expandable Content Wrapper with Accordion Animation */}
<div class="accordion-wrapper"> <div class="accordion-wrapper">
<nav <nav
id="toc-content" id="toc-content"
class="accordion-content" class="accordion-content"
aria-label="Table of Contents" aria-label="Table of Contents"
> >
<ul class="toc-list"> {/* TOC List */}
<ul
id="toc-list"
class="toc-list"
>
{filteredHeadings.map(heading => ( {filteredHeadings.map(heading => (
<li <li
class:list={{ class:list={{
@ -80,21 +93,21 @@ const filteredHeadings = headings.filter(heading =>
<!-- Override heti default styles >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> --> <!-- Override heti default styles >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> -->
<style> <style>
.toc-title { .toc-title {
--at-apply: 'font-semibold ml-4 select-none 2xl:hidden'; --at-apply: 'font-semibold ml-4 2xl:hidden';
} }
.toc-list { .toc-list {
--at-apply: 'list-none pl-0 space-y-2 mt-1 mb-4 2xl:space-y-1.2'; --at-apply: 'list-none pb-3.3 pl-0 space-y-2 mt-1 mb-4 2xl:space-y-1.2';
} }
.toc-link-h2, .toc-link-h3, .toc-link-h4 { .toc-link-h2, .toc-link-h3, .toc-link-h4 {
--at-apply: 'text-sm no-underline font-normal text-balance select-none 2xl:(text-3.2 c-secondary/60 transition-colors ease-in hover:(c-secondary font-medium))'; --at-apply: 'text-sm no-underline font-normal text-balance 2xl:(text-3.2 c-secondary/60 transition-colors transition-font-weight duration-300 ease-out hover:(c-secondary font-medium))';
} }
/* Initial collapsed state with zero height grid row */ /* Initial collapsed state with zero height grid row */
.accordion-wrapper { .accordion-wrapper {
--at-apply: 'grid rows-[0fr] duration-300 ease-in-out'; --at-apply: 'grid rows-[0fr] transition-all duration-350 ease-in-out';
} }
.accordion-content { .accordion-content {
--at-apply: 'overflow-hidden max-h-66 2xl:(max-h-[calc(100vh-21.5rem)]) pl-4 pr-6'; --at-apply: 'max-h-66 overflow-hidden pl-4 pr-6 2xl:(max-h-[calc(100vh-26rem)] pl-1)';
} }
/* When toggle is checked, expand the wrapper to show content */ /* When toggle is checked, expand the wrapper to show content */
@ -104,6 +117,10 @@ const filteredHeadings = headings.filter(heading =>
.accordion-toggle:checked ~ .accordion-wrapper .accordion-content { .accordion-toggle:checked ~ .accordion-wrapper .accordion-content {
--at-apply: 'overflow-y-auto'; --at-apply: 'overflow-y-auto';
} }
#toc-content {
scrollbar-width: thin;
scrollbar-color: oklch(var(--un-preset-theme-colors-secondary) / 0.15) transparent;
}
@media (min-width: 1536px) { @media (min-width: 1536px) {
.accordion-wrapper { .accordion-wrapper {
@ -121,6 +138,12 @@ const filteredHeadings = headings.filter(heading =>
.toc-link-active { .toc-link-active {
--at-apply: 'c-secondary font-medium'; --at-apply: 'c-secondary font-medium';
} }
#toc-content {
--at-apply: 'scrollbar-hidden';
}
#toc-content::-webkit-scrollbar {
display: none;
}
} }
</style> </style>
@ -131,7 +154,7 @@ function setupTOCHighlight() {
if (!tocContent) if (!tocContent)
return return
const tocLinks = tocContent.querySelectorAll('a') const tocLinks = tocContent.getElementsByTagName('a')
if (tocLinks.length === 0) if (tocLinks.length === 0)
return return
@ -140,7 +163,7 @@ function setupTOCHighlight() {
// Build mapping from heading IDs to TOC links // Build mapping from heading IDs to TOC links
const headingMap = new Map<string, HTMLAnchorElement>() const headingMap = new Map<string, HTMLAnchorElement>()
tocLinks.forEach((link) => { Array.from(tocLinks).forEach((link) => {
const id = link.getAttribute('href')?.substring(1) const id = link.getAttribute('href')?.substring(1)
if (id) if (id)
headingMap.set(id, link as HTMLAnchorElement) headingMap.set(id, link as HTMLAnchorElement)

View file

@ -60,6 +60,8 @@ export const themeConfig: ThemeConfig = {
dateFormat: 'YYYY-MM-DD', // YYYY-MM-DD, MM-DD-YYYY, DD-MM-YYYY, MONTH DAY YYYY, DAY MONTH YYYY dateFormat: 'YYYY-MM-DD', // YYYY-MM-DD, MM-DD-YYYY, DD-MM-YYYY, MONTH DAY YYYY, DAY MONTH YYYY
// enable KaTeX for mathematical formulas rendering // enable KaTeX for mathematical formulas rendering
katex: true, // true, false katex: true, // true, false
// enable table of contents for all posts by default
toc: true, // true, false
}, },
// GLOBAL SETTINGS >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> END // GLOBAL SETTINGS >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> END
@ -139,16 +141,16 @@ export const themeConfig: ThemeConfig = {
url: 'https://github.com/radishzzz/astro-theme-retypeset', url: 'https://github.com/radishzzz/astro-theme-retypeset',
}, },
{ {
name: 'X', name: 'Email',
url: 'https://x.com/radishzz_', url: 'email@radishzz.cc',
}, }
// { // {
// name: 'Email', // name: 'X',
// url: 'https://example@gmail.com', // url: 'https://x.com/radishzz_',
// } // },
], ],
// year of website start // year of website start
startYear: 2024, startYear: 2025,
}, },
// FOOTER SETTINGS >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> END // FOOTER SETTINGS >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> END
@ -157,8 +159,6 @@ export const themeConfig: ThemeConfig = {
// link prefetch // link prefetch
// docs: https://docs.astro.build/en/guides/prefetch/#prefetch-strategies // docs: https://docs.astro.build/en/guides/prefetch/#prefetch-strategies
linkPrefetch: 'viewport', // hover, tap, viewport, load linkPrefetch: 'viewport', // hover, tap, viewport, load
// comment server url
commentURL: 'https://retypeset-comment.radishzz.cc',
// image hosting url // image hosting url
imageHostURL: 'https://image.radishzz.cc', imageHostURL: 'https://image.radishzz.cc',
// custom google analytics js // custom google analytics js

View file

@ -1,6 +1,6 @@
import { allLocales } from '@/config'
import { glob } from 'astro/loaders' import { glob } from 'astro/loaders'
import { defineCollection, z } from 'astro:content' import { defineCollection, z } from 'astro:content'
import { allLocales, themeConfig } from '@/config'
const posts = defineCollection({ const posts = defineCollection({
loader: glob({ pattern: '**/*.{md,mdx}', base: './src/content/posts' }), loader: glob({ pattern: '**/*.{md,mdx}', base: './src/content/posts' }),
@ -15,7 +15,7 @@ const posts = defineCollection({
// Advanced // Advanced
draft: z.boolean().optional().default(false), draft: z.boolean().optional().default(false),
pin: z.number().int().min(0).max(99).optional().default(0), pin: z.number().int().min(0).max(99).optional().default(0),
toc: z.boolean().optional().default(true), toc: z.boolean().optional().default(themeConfig.global.toc),
lang: z.enum(['', ...allLocales]).optional().default(''), lang: z.enum(['', ...allLocales]).optional().default(''),
abbrlink: z.string().optional().default('').refine( abbrlink: z.string().optional().default('').refine(
abbrlink => !abbrlink || /^[a-z0-9\-]*$/.test(abbrlink), abbrlink => !abbrlink || /^[a-z0-9\-]*$/.test(abbrlink),

View file

@ -17,7 +17,7 @@ $$
t=\frac{1}{|G|}\sum_{g\in G}|\text{Fix}(g)| t=\frac{1}{|G|}\sum_{g\in G}|\text{Fix}(g)|
$$ $$
For each integer $n\ge2$, the quotient group $\mathbb{Z}/n\mathbb{Z}$ is a cyclic group generated by $1+n\mathbb{Z}$ and so $\color{red}{\mathbb{Z}/n\mathbb{Z}\cong\mathbb{Z}_n}$. For each integer $n\ge2$, the quotient group $\mathbb{Z}/n\mathbb{Z}$ is a cyclic group generated by $1+n\mathbb{Z}$ and so $\mathbb{Z}/n\mathbb{Z}\cong\mathbb{Z}_n$.
The quotient group $\mathbb{R}/\mathbb{Z}$ is isomorphic to $([0,1),+_1)$, the group of real numbers in the interval $[0,1)$, under addition modulo 1. The quotient group $\mathbb{R}/\mathbb{Z}$ is isomorphic to $([0,1),+_1)$, the group of real numbers in the interval $[0,1)$, under addition modulo 1.

View file

@ -17,7 +17,7 @@ $$
t=\frac{1}{|G|}\sum_{g\in G}|\text{Fix}(g)| t=\frac{1}{|G|}\sum_{g\in G}|\text{Fix}(g)|
$$ $$
Para cada entero $n\ge2$, el grupo cociente $\mathbb{Z}/n\mathbb{Z}$ es un grupo cíclico generado por $1+n\mathbb{Z}$ y por tanto $\color{red}{\mathbb{Z}/n\mathbb{Z}\cong\mathbb{Z}_n}$. Para cada entero $n\ge2$, el grupo cociente $\mathbb{Z}/n\mathbb{Z}$ es un grupo cíclico generado por $1+n\mathbb{Z}$ y por tanto $\mathbb{Z}/n\mathbb{Z}\cong\mathbb{Z}_n$.
El grupo cociente $\mathbb{R}/\mathbb{Z}$ es isomorfo a $([0,1),+_1)$, el grupo de números reales en el intervalo $[0,1)$, bajo la adición módulo 1. El grupo cociente $\mathbb{R}/\mathbb{Z}$ es isomorfo a $([0,1),+_1)$, el grupo de números reales en el intervalo $[0,1)$, bajo la adición módulo 1.

View file

@ -17,7 +17,7 @@ $$
t=\frac{1}{|G|}\sum_{g\in G}|\text{Fix}(g)| t=\frac{1}{|G|}\sum_{g\in G}|\text{Fix}(g)|
$$ $$
各整数 $n\ge2$ に対して、商群 $\mathbb{Z}/n\mathbb{Z}$ は $1+n\mathbb{Z}$ によって生成される巡回群であり、したがって $\color{red}{\mathbb{Z}/n\mathbb{Z}\cong\mathbb{Z}_n}$ となります。 各整数 $n\ge2$ に対して、商群 $\mathbb{Z}/n\mathbb{Z}$ は $1+n\mathbb{Z}$ によって生成される巡回群であり、したがって $\mathbb{Z}/n\mathbb{Z}\cong\mathbb{Z}_n$ となります。
商群 $\mathbb{R}/\mathbb{Z}$ は $([0,1),+_1)$ と同型です。これは区間 $[0,1)$ 上の実数のモジュロ1の加法群です。 商群 $\mathbb{R}/\mathbb{Z}$ は $([0,1),+_1)$ と同型です。これは区間 $[0,1)$ 上の実数のモジュロ1の加法群です。

View file

@ -17,7 +17,7 @@ $$
t=\frac{1}{|G|}\sum_{g\in G}|\text{Fix}(g)| t=\frac{1}{|G|}\sum_{g\in G}|\text{Fix}(g)|
$$ $$
Для каждого целого числа $n\ge2$ фактор-группа $\mathbb{Z}/n\mathbb{Z}$ является циклической группой, порождённой элементом $1+n\mathbb{Z}$, и поэтому $\color{red}{\mathbb{Z}/n\mathbb{Z}\cong\mathbb{Z}_n}$. Для каждого целого числа $n\ge2$ фактор-группа $\mathbb{Z}/n\mathbb{Z}$ является циклической группой, порождённой элементом $1+n\mathbb{Z}$, и поэтому $\mathbb{Z}/n\mathbb{Z}\cong\mathbb{Z}_n$.
Фактор-группа $\mathbb{R}/\mathbb{Z}$ изоморфна $([0,1),+_1)$, группе вещественных чисел в интервале $[0,1)$ с операцией сложения по модулю 1. Фактор-группа $\mathbb{R}/\mathbb{Z}$ изоморфна $([0,1),+_1)$, группе вещественных чисел в интервале $[0,1)$ с операцией сложения по модулю 1.

View file

@ -17,7 +17,7 @@ $$
t=\frac{1}{|G|}\sum_{g\in G}|\text{Fix}(g)| t=\frac{1}{|G|}\sum_{g\in G}|\text{Fix}(g)|
$$ $$
對於每個整數 $n\ge2$,商群 $\mathbb{Z}/n\mathbb{Z}$ 是由 $1+n\mathbb{Z}$ 生成的循環群,因此 $\color{red}{\mathbb{Z}/n\mathbb{Z}\cong\mathbb{Z}_n}$。 對於每個整數 $n\ge2$,商群 $\mathbb{Z}/n\mathbb{Z}$ 是由 $1+n\mathbb{Z}$ 生成的循環群,因此 $\mathbb{Z}/n\mathbb{Z}\cong\mathbb{Z}_n$。
商群 $\mathbb{R}/\mathbb{Z}$ 同構於 $([0,1),+_1)$,即區間 $[0,1)$ 上以 1 為模的實數加法群。 商群 $\mathbb{R}/\mathbb{Z}$ 同構於 $([0,1),+_1)$,即區間 $[0,1)$ 上以 1 為模的實數加法群。

View file

@ -17,7 +17,7 @@ $$
t=\frac{1}{|G|}\sum_{g\in G}|\text{Fix}(g)| t=\frac{1}{|G|}\sum_{g\in G}|\text{Fix}(g)|
$$ $$
对于每个整数 $n\ge2$,商群 $\mathbb{Z}/n\mathbb{Z}$ 是由 $1+n\mathbb{Z}$ 生成的循环群,因此 $\color{red}{\mathbb{Z}/n\mathbb{Z}\cong\mathbb{Z}_n}$。 对于每个整数 $n\ge2$,商群 $\mathbb{Z}/n\mathbb{Z}$ 是由 $1+n\mathbb{Z}$ 生成的循环群,因此 $\mathbb{Z}/n\mathbb{Z}\cong\mathbb{Z}_n$。
商群 $\mathbb{R}/\mathbb{Z}$ 同构于 $([0,1),+_1)$,即区间 $[0,1)$ 上以 1 为模的实数加法群。 商群 $\mathbb{R}/\mathbb{Z}$ 同构于 $([0,1),+_1)$,即区间 $[0,1)$ 上以 1 为模的实数加法群。

View file

@ -17,9 +17,9 @@ To create automatic figure captions, use the standard Markdown image syntax `![a
### Syntax ### Syntax
``` ```
![Image description](./full/or/relative/path/of/image) ![Image description](https://image.example.com/image-01.webp)
![_Image description](./full/or/relative/path/of/image) ![_Image description](https://image.example.com/image-01.webp)
``` ```
### Output ### Output

View file

@ -17,9 +17,9 @@ Para crear leyendas automáticas para figuras, utilice la sintaxis estándar de
### Sintaxis ### Sintaxis
``` ```
![Descripción de la imagen](./full/or/relative/path/of/image) ![Descripción de la imagen](https://image.example.com/image-01.webp)
![_Descripción de la imagen](./full/or/relative/path/of/image) ![_Descripción de la imagen](https://image.example.com/image-01.webp)
``` ```
### Resultado ### Resultado

View file

@ -17,9 +17,9 @@ abbrlink: markdown-extended-features
### 構文 ### 構文
``` ```
![画像の説明](./full/or/relative/path/of/image) ![画像の説明](https://image.example.com/image-01.webp)
![_画像の説明](./full/or/relative/path/of/image) ![_画像の説明](https://image.example.com/image-01.webp)
``` ```
### 効果 ### 効果

View file

@ -17,9 +17,9 @@ abbrlink: markdown-extended-features
### Синтаксис ### Синтаксис
``` ```
![Описание изображения](./full/or/relative/path/of/image) ![Описание изображения](https://image.example.com/image-01.webp)
![_Описание изображения](./full/or/relative/path/of/image) ![_Описание изображения](https://image.example.com/image-01.webp)
``` ```
### Результат ### Результат

View file

@ -17,9 +17,9 @@ abbrlink: markdown-extended-features
### 語法 ### 語法
``` ```
![圖片描述](./full/or/relative/path/of/image) ![圖片描述](https://image.example.com/image-01.webp)
![_圖片描述](./full/or/relative/path/of/image) ![_圖片描述](https://image.example.com/image-01.webp)
``` ```
### 效果 ### 效果

View file

@ -17,9 +17,9 @@ abbrlink: markdown-extended-features
### 语法 ### 语法
``` ```
![图片描述](./full/or/relative/path/of/image) ![图片描述](https://image.example.com/image-01.webp)
![_图片描述](./full/or/relative/path/of/image) ![_图片描述](https://image.example.com/image-01.webp)
``` ```
### 效果 ### 效果

View file

@ -61,7 +61,9 @@ To add an image, add an exclamation mark `!`, followed by alt text in brackets `
### Syntax ### Syntax
``` ```
![Image Description](./full/or/relative/path/of/image) ![Image Description](../_images/image-01.jpeg)
![Image Description](https://image.example.com/image-01.webp)
``` ```
### Output ### Output
@ -211,51 +213,3 @@ To create a code block, add three backticks ```` ``` ```` at the start and end o
- Milk - Milk
- Cheese - Cheese
``` ```
#### Output
- Fruit
- Apple
- Orange
- Banana
- Dairy
- Milk
- Cheese
## Other Elements
Including `<sup>` superscript, `<sub>` subscript, `<abbr>` abbreviation, `<del>` strikethrough, `<u>` wavy underline, `<kbd>` keyboard input, and `<mark>` highlight.
### Syntax
```html
H<sub>2</sub>O
X<sup>n</sup> + Y<sup>n</sup> = Z<sup>n</sup>
<abbr title="Graphics Interchange Format">GIF</abbr> is a bitmap image format.
Good writers always check for <u title="spelling">speling</u> mistakes.
Press <kbd>CTRL</kbd> + <kbd>ALT</kbd> + <kbd>Delete</kbd> to end the session.
There is <del>nothing</del> no code either good or bad, but running it makes it so.
Most <mark>salamanders</mark> are nocturnal, and hunt for insects, worms, and other small creatures.
```
### Output
H<sub>2</sub>O
X<sup>n</sup> + Y<sup>n</sup> = Z<sup>n</sup>
<abbr title="Graphics Interchange Format">GIF</abbr> is a bitmap image format.
Good writers always check for <u title="spelling">speling</u> mistakes.
Press <kbd>CTRL</kbd> + <kbd>ALT</kbd> + <kbd>Delete</kbd> to end the session.
There is <del>nothing</del> no code either good or bad, but running it makes it so.
Most <mark>salamanders</mark> are nocturnal, and hunt for insects, worms, and other small creatures.

View file

@ -61,7 +61,9 @@ Para agregar una imagen, añada un signo de exclamación `!`, seguido de texto a
### Sintaxis ### Sintaxis
``` ```
![Descripción de la Imagen](./full/or/relative/path/of/image) ![Descripción de la Imagen](../_images/image-01.jpeg)
![Descripción de la Imagen](https://image.example.com/image-01.webp)
``` ```
### Resultado ### Resultado

View file

@ -61,7 +61,9 @@ abbrlink: markdown-style-guide
### 構文 ### 構文
``` ```
![画像の説明](./full/or/relative/path/of/image) ![画像の説明](../_images/image-01.jpeg)
![画像の説明](https://image.example.com/image-01.webp)
``` ```
### 効果 ### 効果

View file

@ -61,7 +61,9 @@ Itatur? Quiatae cullecum rem ent aut odis in re eossequodi nonsequ idebis ne sap
### Синтаксис ### Синтаксис
``` ```
![Описание изображения](./full/or/relative/path/of/image) ![Описание изображения](../_images/image-01.jpeg)
![Описание изображения](https://image.example.com/image-01.webp)
``` ```
### Результат ### Результат

View file

@ -61,7 +61,9 @@ abbrlink: markdown-style-guide
### 語法 ### 語法
``` ```
![圖片描述](./full/or/relative/path/of/image) ![圖片描述](../_images/image-01.jpeg)
![圖片描述](https://image.example.com/image-01.webp)
``` ```
### 效果 ### 效果

View file

@ -61,7 +61,9 @@ abbrlink: markdown-style-guide
### 语法 ### 语法
``` ```
![图片描述](./full/or/relative/path/of/image) ![图片描述](../_images/image-01.jpeg)
![图片描述](https://image.example.com/image-01.webp)
``` ```
### 效果 ### 效果

View file

@ -86,6 +86,8 @@ global: {
dateFormat: 'YYYY-MM-DD' dateFormat: 'YYYY-MM-DD'
// enable KaTeX for mathematical formulas rendering // enable KaTeX for mathematical formulas rendering
katex: true // true, false katex: true // true, false
// enable table of contents for all posts by default
toc: true // true, false
} }
``` ```
@ -163,16 +165,16 @@ footer: {
url: 'https://github.com/radishzzz/astro-theme-retypeset', url: 'https://github.com/radishzzz/astro-theme-retypeset',
}, },
{ {
name: 'X', name: 'Email',
url: 'https://x.com/radishzz_', url: 'email@radishzz.cc',
}, }
// { // {
// name: 'Email', // name: 'X',
// url: 'https://example@gmail.com', // url: 'https://x.com/radishzz_',
// } // },
] ]
// year of website start // year of website start
startYear: 2024 startYear: 2025
} }
``` ```
@ -182,8 +184,6 @@ footer: {
preload: { preload: {
// link prefetch strategies // link prefetch strategies
linkPrefetch: 'viewport' // hover, tap, viewport, load linkPrefetch: 'viewport' // hover, tap, viewport, load
// comment server url
commentURL: 'https://retypeset-comment.radishzz.cc'
// image hosting url // image hosting url
imageHostURL: 'https://image.radishzz.cc' imageHostURL: 'https://image.radishzz.cc'
// custom google analytics js // custom google analytics js
@ -236,7 +236,7 @@ const EXCERPT_LENGTHS: Record<ExcerptScene, {
### Open Graph ### Open Graph
Open Graph social image styles. [Open Graph social card](https://orcascan.com/tools/open-graph-validator?url=https%3A%2F%2Fretypeset.radishzz.cc%2Fen%2Fposts%2Ftheme-guide%2F) styles.
```ts ```ts
// src/pages/og/[...image].ts // src/pages/og/[...image].ts
@ -265,7 +265,7 @@ getImageOptions: (_path, page) => ({
### RSS Feed ### RSS Feed
RSS feed page styles. [RSS feed page](https://retypeset.radishzz.cc/en/rss.xml) styles.
```html ```html
<!-- public/rss/rss-style.xsl --> <!-- public/rss/rss-style.xsl -->
@ -317,7 +317,7 @@ Pins the article to the top. The higher the number, the higher the priority of t
#### toc #### toc
Generate table of contents. Shows h2 to h4 headings. Default is true. Generate table of contents. Shows h2 to h4 headings. Uses the global `global.toc` configuration by default, but can be overridden individually in each article.
#### lang #### lang

View file

@ -86,6 +86,8 @@ global: {
dateFormat: 'YYYY-MM-DD' dateFormat: 'YYYY-MM-DD'
// habilitar KaTeX para renderizar fórmulas matemáticas // habilitar KaTeX para renderizar fórmulas matemáticas
katex: true // true, false katex: true // true, false
// habilitar tabla de contenidos para todos los artículos por defecto
toc: true // true, false
} }
``` ```
@ -163,16 +165,16 @@ footer: {
url: 'https://github.com/radishzzz/astro-theme-retypeset', url: 'https://github.com/radishzzz/astro-theme-retypeset',
}, },
{ {
name: 'X', name: 'Email',
url: 'https://x.com/radishzz_', url: 'email@radishzz.cc',
}, }
// { // {
// name: 'Email', // name: 'X',
// url: 'https://example@gmail.com', // url: 'https://x.com/radishzz_',
// } // },
] ]
// año de inicio del sitio web // año de inicio del sitio web
startYear: 2024 startYear: 2025
} }
``` ```
@ -182,8 +184,6 @@ footer: {
preload: { preload: {
// estrategias de precarga de enlaces // estrategias de precarga de enlaces
linkPrefetch: 'viewport' // hover, tap, viewport, load linkPrefetch: 'viewport' // hover, tap, viewport, load
// URL del servidor de comentarios
commentURL: 'https://retypeset-comment.radishzz.cc'
// URL de alojamiento de imágenes // URL de alojamiento de imágenes
imageHostURL: 'https://image.radishzz.cc' imageHostURL: 'https://image.radishzz.cc'
// js personalizado de google analytics // js personalizado de google analytics
@ -236,7 +236,7 @@ const EXCERPT_LENGTHS: Record<ExcerptScene, {
### Open Graph ### Open Graph
Estilos de imágenes sociales Open Graph. Estilos de [tarjetas sociales Open Graph](https://orcascan.com/tools/open-graph-validator?url=https%3A%2F%2Fretypeset.radishzz.cc%2Fes%2Fposts%2Ftheme-guide%2F).
```ts ```ts
// src/pages/og/[...image].ts // src/pages/og/[...image].ts
@ -265,7 +265,7 @@ getImageOptions: (_path, page) => ({
### Canal RSS ### Canal RSS
Estilos de página del feed RSS. Estilos de [página del feed RSS](https://retypeset.radishzz.cc/es/rss.xml).
```html ```html
<!-- public/rss/rss-style.xsl --> <!-- public/rss/rss-style.xsl -->
@ -317,7 +317,7 @@ Fija el artículo en la parte superior. Cuanto mayor sea el número, mayor será
#### toc #### toc
Genera tabla de contenidos. Muestra encabezados h2 a h4. El valor predeterminado es true. Genera tabla de contenidos. Muestra encabezados de h2 a h4. Utiliza la configuración global `global.toc` por defecto, pero puede ser modificada individualmente en cada artículo.
#### lang #### lang

View file

@ -86,6 +86,8 @@ global: {
dateFormat: 'YYYY-MM-DD' dateFormat: 'YYYY-MM-DD'
// 数式表示のためのKaTeXを有効化 // 数式表示のためのKaTeXを有効化
katex: true // true, false katex: true // true, false
// デフォルトですべての記事に目次を表示
toc: true // true, false
} }
``` ```
@ -163,16 +165,16 @@ footer: {
url: 'https://github.com/radishzzz/astro-theme-retypeset', url: 'https://github.com/radishzzz/astro-theme-retypeset',
}, },
{ {
name: 'X', name: 'Email',
url: 'https://x.com/radishzz_', url: 'email@radishzz.cc',
}, }
// { // {
// name: 'Email', // name: 'X',
// url: 'https://example@gmail.com', // url: 'https://x.com/radishzz_',
// } // },
] ]
// サイト開始年 // サイト開始年
startYear: 2024 startYear: 2025
} }
``` ```
@ -182,8 +184,6 @@ footer: {
preload: { preload: {
// リンクプリフェッチ戦略 // リンクプリフェッチ戦略
linkPrefetch: 'viewport' // hover, tap, viewport, load linkPrefetch: 'viewport' // hover, tap, viewport, load
// コメントサーバー URL
commentURL: 'https://retypeset-comment.radishzz.cc'
// 画像ホスティング URL // 画像ホスティング URL
imageHostURL: 'https://image.radishzz.cc' imageHostURL: 'https://image.radishzz.cc'
// カスタム Google Analytics JS // カスタム Google Analytics JS
@ -236,7 +236,7 @@ const EXCERPT_LENGTHS: Record<ExcerptScene, {
### Open Graph ### Open Graph
Open Graphソーシャル画像スタイル。 [Open Graphソーシャルカード](https://orcascan.com/tools/open-graph-validator?url=https%3A%2F%2Fretypeset.radishzz.cc%2Fja%2Fposts%2Ftheme-guide%2F)スタイル。
```ts ```ts
// src/pages/og/[...image].ts // src/pages/og/[...image].ts
@ -265,7 +265,7 @@ getImageOptions: (_path, page) => ({
### RSSフィード ### RSSフィード
RSSフィードページスタイル。 [RSSフィードページ](https://retypeset.radishzz.cc/ja/rss.xml)スタイル。
```html ```html
<!-- public/rss/rss-style.xsl --> <!-- public/rss/rss-style.xsl -->
@ -317,7 +317,7 @@ abbrlink: theme-guide
#### toc #### toc
目次を自動生成するかどうか。h2からh4までの見出しを表示します。デフォルトは true 目次を生成するかどうか。h2からh4までの見出しを表示します。デフォルトではグローバル設定 `global.toc` を使用しますが、記事ごとに個別に設定することも可能です
#### lang #### lang

View file

@ -86,6 +86,8 @@ global: {
dateFormat: 'YYYY-MM-DD' dateFormat: 'YYYY-MM-DD'
// включить KaTeX для отображения математических формул // включить KaTeX для отображения математических формул
katex: true // true, false katex: true // true, false
// включить оглавление для всех статей по умолчанию
toc: true // true, false
} }
``` ```
@ -163,16 +165,16 @@ footer: {
url: 'https://github.com/radishzzz/astro-theme-retypeset', url: 'https://github.com/radishzzz/astro-theme-retypeset',
}, },
{ {
name: 'X', name: 'Email',
url: 'https://x.com/radishzz_', url: 'email@radishzz.cc',
}, }
// { // {
// name: 'Email', // name: 'X',
// url: 'https://example@gmail.com', // url: 'https://x.com/radishzz_',
// } // },
] ]
// год начала работы веб-сайта // год начала работы веб-сайта
startYear: 2024 startYear: 2025
} }
``` ```
@ -182,8 +184,6 @@ footer: {
preload: { preload: {
// стратегии предзагрузки ссылок // стратегии предзагрузки ссылок
linkPrefetch: 'viewport' // hover, tap, viewport, load linkPrefetch: 'viewport' // hover, tap, viewport, load
// URL сервера комментариев
commentURL: 'https://retypeset-comment.radishzz.cc'
// URL хостинга изображений // URL хостинга изображений
imageHostURL: 'https://image.radishzz.cc' imageHostURL: 'https://image.radishzz.cc'
// пользовательский скрипт Google Analytics // пользовательский скрипт Google Analytics
@ -236,7 +236,7 @@ const EXCERPT_LENGTHS: Record<ExcerptScene, {
### Open Graph ### Open Graph
Стили изображений Open Graph для социальных сетей. Стили [карточек Open Graph для социальных сетей](https://orcascan.com/tools/open-graph-validator?url=https%3A%2F%2Fretypeset.radishzz.cc%2Fru%2Fposts%2Ftheme-guide%2F).
```ts ```ts
// src/pages/og/[...image].ts // src/pages/og/[...image].ts
@ -265,7 +265,7 @@ getImageOptions: (_path, page) => ({
### RSS-лента ### RSS-лента
Стили страницы RSS-ленты. Стили [страницы RSS-ленты](https://retypeset.radishzz.cc/ru/rss.xml).
```html ```html
<!-- public/rss/rss-style.xsl --> <!-- public/rss/rss-style.xsl -->
@ -317,7 +317,7 @@ abbrlink: theme-guide
#### toc #### toc
Генерировать оглавление. Показывает заголовки от h2 до h4. По умолчанию — true. Генерировать оглавление. Показывает заголовки от h2 до h4. По умолчанию использует глобальный параметр `global.toc`, но может быть изменен индивидуально в каждой статье.
#### lang #### lang

View file

@ -86,6 +86,8 @@ global: {
dateFormat: 'YYYY-MM-DD' dateFormat: 'YYYY-MM-DD'
// 啟用 KaTeX 數學公式渲染 // 啟用 KaTeX 數學公式渲染
katex: true // true, false katex: true // true, false
// 預設為所有文章開啟目錄
toc: true // true, false
} }
``` ```
@ -163,16 +165,16 @@ footer: {
url: 'https://github.com/radishzzz/astro-theme-retypeset', url: 'https://github.com/radishzzz/astro-theme-retypeset',
}, },
{ {
name: 'X', name: 'Email',
url: 'https://x.com/radishzz_', url: 'email@radishzz.cc',
}, }
// { // {
// name: 'Email', // name: 'X',
// url: 'https://example@gmail.com', // url: 'https://x.com/radishzz_',
// } // },
] ]
// 建站年份 // 建站年份
startYear: 2024 startYear: 2025
} }
``` ```
@ -182,8 +184,6 @@ footer: {
preload: { preload: {
// 鏈接預加載策略 // 鏈接預加載策略
linkPrefetch: 'viewport' // hover, tap, viewport, load linkPrefetch: 'viewport' // hover, tap, viewport, load
// 評論服務器地址
commentURL: 'https://retypeset-comment.radishzz.cc'
// 圖床地址 // 圖床地址
imageHostURL: 'https://image.radishzz.cc' imageHostURL: 'https://image.radishzz.cc'
// 定制 google analytics js // 定制 google analytics js
@ -236,7 +236,7 @@ const EXCERPT_LENGTHS: Record<ExcerptScene, {
### Open Graph ### Open Graph
Open Graph 社交圖片樣式。 [Open Graph 社交卡片](https://orcascan.com/tools/open-graph-validator?url=https%3A%2F%2Fretypeset.radishzz.cc%2Fzh-tw%2Fposts%2Ftheme-guide%2F) 樣式。
```ts ```ts
// src/pages/og/[...image].ts // src/pages/og/[...image].ts
@ -265,7 +265,7 @@ getImageOptions: (_path, page) => ({
### RSS 訂閱 ### RSS 訂閱
RSS 訂閱頁配色。 [RSS 訂閱頁](https://retypeset.radishzz.cc/zh-tw/rss.xml) 配色。
```html ```html
<!-- public/rss/rss-style.xsl --> <!-- public/rss/rss-style.xsl -->
@ -317,7 +317,7 @@ abbrlink: theme-guide
#### toc #### toc
是否生成目錄。顯示 h2 至 h4 標題。預設為 true 是否生成目錄。顯示 h2 至 h4 標題。預設為全域配置 `global.toc` 的選項,可在文章中單獨設定以覆蓋全域配置
#### lang #### lang

View file

@ -86,6 +86,8 @@ global: {
dateFormat: 'YYYY-MM-DD' dateFormat: 'YYYY-MM-DD'
// 启用 KaTeX 数学公式渲染 // 启用 KaTeX 数学公式渲染
katex: true // true, false katex: true // true, false
// 默认为所有文章开启目录
toc: true // true, false
} }
``` ```
@ -163,16 +165,16 @@ footer: {
url: 'https://github.com/radishzzz/astro-theme-retypeset', url: 'https://github.com/radishzzz/astro-theme-retypeset',
}, },
{ {
name: 'X', name: 'Email',
url: 'https://x.com/radishzz_', url: 'email@radishzz.cc',
}, }
// { // {
// name: 'Email', // name: 'X',
// url: 'https://example@gmail.com', // url: 'https://x.com/radishzz_',
// } // },
] ]
// 建站年份 // 建站年份
startYear: 2024 startYear: 2025
} }
``` ```
@ -182,8 +184,6 @@ footer: {
preload: { preload: {
// 链接预加载策略 // 链接预加载策略
linkPrefetch: 'viewport' // hover, tap, viewport, load linkPrefetch: 'viewport' // hover, tap, viewport, load
// 评论服务器地址
commentURL: 'https://retypeset-comment.radishzz.cc'
// 图床地址 // 图床地址
imageHostURL: 'https://image.radishzz.cc' imageHostURL: 'https://image.radishzz.cc'
// 定制 google analytics js // 定制 google analytics js
@ -236,7 +236,7 @@ const EXCERPT_LENGTHS: Record<ExcerptScene, {
### Open Graph ### Open Graph
Open Graph 社交图片样式。 [Open Graph 社交卡片](https://orcascan.com/tools/open-graph-validator?url=https%3A%2F%2Fretypeset.radishzz.cc%2Fposts%2Ftheme-guide%2F) 样式。
```ts ```ts
// src/pages/og/[...image].ts // src/pages/og/[...image].ts
@ -265,7 +265,7 @@ getImageOptions: (_path, page) => ({
### RSS 订阅 ### RSS 订阅
RSS 订阅页配色。 [RSS 订阅页](https://retypeset.radishzz.cc/rss.xml) 配色。
```html ```html
<!-- public/rss/rss-style.xsl --> <!-- public/rss/rss-style.xsl -->
@ -317,7 +317,7 @@ abbrlink: theme-guide
#### toc #### toc
是否生成目录。显示 h2 至 h4 标题。默认为 true 是否生成目录。显示 h2 至 h4 标题。默认为全局配置 `global.toc` 的选项,可在文章中单独设置以覆盖全局配置
#### lang #### lang

View file

@ -18,10 +18,11 @@ const langCode = currentLang === defaultLocale ? '' : `${currentLang}/`
const { title, subtitle, description, author, url, favicon, i18nTitle } = themeConfig.site const { title, subtitle, description, author, url, favicon, i18nTitle } = themeConfig.site
const { mode, light: { background: lightMode }, dark: { background: darkMode } } = themeConfig.color const { mode, light: { background: lightMode }, dark: { background: darkMode } } = themeConfig.color
const { katex } = themeConfig.global const { katex: katexEnabled } = themeConfig.global
const { enabled: commentEnabled = false, waline: { serverURL: walineServerURL = '' } = {} } = themeConfig.comment ?? {}
const { verification = {}, twitterID = '', googleAnalyticsID = '', umamiAnalyticsID = '', apiflashKey = '' } = themeConfig.seo ?? {} const { verification = {}, twitterID = '', googleAnalyticsID = '', umamiAnalyticsID = '', apiflashKey = '' } = themeConfig.seo ?? {}
const { google = '', bing = '', yandex = '', baidu = '' } = verification const { google = '', bing = '', yandex = '', baidu = '' } = verification
const { commentURL = '', customGoogleAnalyticsJS = '', customUmamiAnalyticsJS = '' } = themeConfig.preload const { customGoogleAnalyticsJS = '', customUmamiAnalyticsJS = '' } = themeConfig.preload
const initMetaTheme = mode === 'dark' ? darkMode : lightMode const initMetaTheme = mode === 'dark' ? darkMode : lightMode
const siteTitle = i18nTitle ? currentUI.title : title const siteTitle = i18nTitle ? currentUI.title : title
@ -54,9 +55,9 @@ const pageImage = postSlug
<link rel="preload" href="/fonts/EarlySummer-Subset.woff2" as="font" type="font/woff2" crossorigin /> <link rel="preload" href="/fonts/EarlySummer-Subset.woff2" as="font" type="font/woff2" crossorigin />
<link rel="preload" href="/fonts/Snell-Black.woff2" as="font" type="font/woff2" crossorigin /> <link rel="preload" href="/fonts/Snell-Black.woff2" as="font" type="font/woff2" crossorigin />
<link rel="preload" href="/fonts/Snell-Bold.woff2" as="font" type="font/woff2" crossorigin /> <link rel="preload" href="/fonts/Snell-Bold.woff2" as="font" type="font/woff2" crossorigin />
{katex && <link rel="stylesheet" href={katexCSS} />} <link rel="preload" href="/fonts/STIX-Italic.woff2" as="font" type="font/woff2" crossorigin />
{commentURL && <link rel="preconnect" href={commentURL} crossorigin />} {commentEnabled && walineServerURL && <link rel="stylesheet" href="/assets/waline/waline.css" />}
{commentURL && <link rel="dns-prefetch" href={commentURL} />} {katexEnabled && <link rel="stylesheet" href={katexCSS} />}
<link rel="alternate" href="/rss.xml" type="application/rss+xml" title="RSS Feed" /> <link rel="alternate" href="/rss.xml" type="application/rss+xml" title="RSS Feed" />
<link rel="alternate" href="/atom.xml" type="application/atom+xml" title="Atom Feed" /> <link rel="alternate" href="/atom.xml" type="application/atom+xml" title="Atom Feed" />
<link rel="sitemap" href="/sitemap-index.xml" /> <link rel="sitemap" href="/sitemap-index.xml" />
@ -111,7 +112,7 @@ function isCurrentDark() {
function initTheme(doc = document) { function initTheme(doc = document) {
const isDark = isCurrentDark() const isDark = isCurrentDark()
doc.documentElement.classList.toggle('dark', isDark) doc.documentElement.classList.toggle('dark', isDark)
const metaTheme = doc.querySelector('meta[name="theme-color"]') const metaTheme = doc.head.querySelector('meta[name="theme-color"]')
if (metaTheme) { if (metaTheme) {
metaTheme.setAttribute('content', isDark ? darkMode : lightMode) metaTheme.setAttribute('content', isDark ? darkMode : lightMode)
} }

View file

@ -4,15 +4,16 @@ import Footer from '@/components/Footer.astro'
import Header from '@/components/Header.astro' import Header from '@/components/Header.astro'
import Navbar from '@/components/Navbar.astro' import Navbar from '@/components/Navbar.astro'
import GithubCard from '@/components/Widgets/GithubCard.astro' import GithubCard from '@/components/Widgets/GithubCard.astro'
import GsapAnimation from '@/components/Widgets/GsapAnimation.astro'
import PhotoSwipe from '@/components/Widgets/PhotoSwipe.astro' import PhotoSwipe from '@/components/Widgets/PhotoSwipe.astro'
import Scrollbar from '@/components/Widgets/Scrollbar.astro'
import themeConfig from '@/config' import themeConfig from '@/config'
import Head from '@/layouts/Head.astro' import Head from '@/layouts/Head.astro'
import { getPageInfo } from '@/utils/page' import { getPageInfo } from '@/utils/page'
import '@/styles/global.css'
import '@/styles/font.css'
import '@/styles/heti.css'
import '@/styles/extend.css' import '@/styles/extend.css'
import '@/styles/font.css'
import '@/styles/global.css'
import '@/styles/markdown.css'
import '@/styles/transition.css'
interface Props { interface Props {
postTitle?: string postTitle?: string
@ -25,22 +26,20 @@ const { postTitle, postDescription, postSlug, supportedLangs = [] } = Astro.prop
const { isPost } = getPageInfo(Astro.url.pathname) const { isPost } = getPageInfo(Astro.url.pathname)
const fontStyle = themeConfig.global.fontStyle === 'serif' ? 'font-serif' : 'font-sans' const fontStyle = themeConfig.global.fontStyle === 'serif' ? 'font-serif' : 'font-sans'
const MarginBottom = isPost && themeConfig.comment?.enabled const MarginBottom = isPost && themeConfig.comment?.enabled
? 'mb-10' // Post page with comment system ? 'mb-10' // Post page with comments
: 'mb-12' // Other pages without comment system : 'mb-12' // Other pages without comments
--- ---
<html <html
lang={Astro.currentLocale} lang={Astro.currentLocale}
class:list={[fontStyle, { 'scroll-smooth': isPost }]} class:list={[fontStyle, { 'scroll-smooth': isPost }]}
data-overlayscrollbars-initialize
> >
<Head {postTitle} {postDescription} {postSlug} /> <Head {postTitle} {postDescription} {postSlug} />
<body data-overlayscrollbars-initialize> <body>
<div <div
class="mx-auto max-w-205.848 min-h-vh w-full min-h-dvh" class="mx-auto max-w-205.848 min-h-vh w-full min-h-dvh"
p="x-[min(7.25vw,3.731rem)] y-9" p="x-[min(7.25vw,3.731rem)] y-9"
lg="p-0 min-h-full max-w-[min(calc(75vw-16rem),44rem)] mx-[max(5.625rem,calc(50vw-34.375rem))] my-20" lg="mx-[max(5rem,calc(50vw-35rem))] my-20 max-w-[min(calc(75vw-16rem),44rem)] min-h-full p-0"
> >
<Header /> <Header />
<Navbar /> <Navbar />
@ -49,8 +48,8 @@ const MarginBottom = isPost && themeConfig.comment?.enabled
</main> </main>
<Footer /> <Footer />
</div> </div>
<GsapAnimation />
<Button supportedLangs={supportedLangs} /> <Button supportedLangs={supportedLangs} />
<Scrollbar />
<GithubCard /> <GithubCard />
<PhotoSwipe /> <PhotoSwipe />
</body> </body>

View file

@ -6,8 +6,15 @@ import Layout from '@/layouts/Layout.astro'
<!-- Decorative Line --> <!-- Decorative Line -->
<div class="uno-decorative-line"></div> <div class="uno-decorative-line"></div>
<!-- Page Not Found --> <!-- Page Not Found -->
<h3 class="mt--1.3 text-8 text-primary font-bold leading-1.2em font-navbar lg:text-9">PAGE<br>NOT<br>FOUND</h3> <h3 class="mt--1.3 flex flex-col text-8 text-primary font-bold leading-1.2em font-navbar lg:text-9">
<p class="mt-3.6 text-3.6 leading-1.4em font-navbar lg:(mt-4 text-4)">It looks like the page you're looking for<br>does not exist or has been moved.</p> <span>PAGE</span>
<span>NOT</span>
<span>FOUND</span>
</h3>
<p class="mt-3.6 flex flex-col text-3.6 leading-1.4em font-navbar lg:(mt-4 text-4)">
<span>It looks like the page you're looking for</span>
<span>does not exist or has been moved.</span>
</p>
<!-- Unused Div --> <!-- Unused Div -->
<div class=""></div> <div class=""></div>
</Layout> </Layout>

View file

@ -3,7 +3,7 @@ import type { CollectionEntry } from 'astro:content'
import { getCollection, render } from 'astro:content' import { getCollection, render } from 'astro:content'
import Comments from '@/components/Comments/index.astro' import Comments from '@/components/Comments/index.astro'
import PostDate from '@/components/PostDate.astro' import PostDate from '@/components/PostDate.astro'
import GoBack from '@/components/Widgets/GoBack.astro' import BackButton from '@/components/Widgets/BackButton.astro'
import TOC from '@/components/Widgets/TOC.astro' import TOC from '@/components/Widgets/TOC.astro'
import { allLocales, defaultLocale, moreLocales } from '@/config' import { allLocales, defaultLocale, moreLocales } from '@/config'
import { getTagPath } from '@/i18n/path' import { getTagPath } from '@/i18n/path'
@ -109,12 +109,12 @@ const { Content, headings, remarkPluginFrontmatter } = await render(post)
<article class="heti mb-12.6"> <article class="heti mb-12.6">
<div class="relative"> <div class="relative">
<!-- Go Back Button On Desktop --> <!-- Go Back Button On Desktop -->
<GoBack /> <BackButton />
<!-- Title --> <!-- Title -->
<h1 class="post-title"> <h1 class="post-title">
<span <span
transition:name={`post-${post.data.abbrlink || post.id}-${lang}`} transition:name={`post-${post.data.abbrlink || post.id}-${lang}`}
data-disable-transition-on-theme data-disable-theme-transition
> >
{post.data.title} {post.data.title}
</span> </span>
@ -123,9 +123,10 @@ const { Content, headings, remarkPluginFrontmatter } = await render(post)
<!-- Date --> <!-- Date -->
<div <div
id="gsap-post-page-date"
class="mb-16.3 block c-primary font-time" class="mb-16.3 block c-primary font-time"
transition:name={`time-${post.data.abbrlink || post.id}-${lang}`} transition:name={`time-${post.data.abbrlink || post.id}-${lang}`}
data-disable-transition-on-theme data-disable-theme-transition
> >
<PostDate <PostDate
date={post.data.published} date={post.data.published}
@ -136,11 +137,14 @@ const { Content, headings, remarkPluginFrontmatter } = await render(post)
<!-- TOC --> <!-- TOC -->
{post.data.toc && <TOC headings={headings} />} {post.data.toc && <TOC headings={headings} />}
<!-- Content --> <!-- Content -->
<div id="gsap-post-page-content">
<Content /> <Content />
</div>
</article> </article>
<!-- Tags --> <!-- Tags -->
{post.data.tags && post.data.tags.length > 0 && ( {post.data.tags && post.data.tags.length > 0 && (
<div id="gsap-post-page-tags">
<div class="uno-decorative-line"></div> <div class="uno-decorative-line"></div>
<div class="uno-tags-wrapper"> <div class="uno-tags-wrapper">
{post.data.tags.map((tag: string) => ( {post.data.tags.map((tag: string) => (
@ -152,6 +156,7 @@ const { Content, headings, remarkPluginFrontmatter } = await render(post)
</a> </a>
))} ))}
</div> </div>
</div>
)} )}
<!-- Comments --> <!-- Comments -->
<Comments /> <Comments />

View file

@ -52,7 +52,7 @@ const supportedLangs = await getTagSupportedLangs(tag)
href={getTagPath(tagName, lang)} href={getTagPath(tagName, lang)}
class={`uno-tags-style ${ class={`uno-tags-style ${
tag === tagName tag === tagName
? 'border-secondary/75 text-primary' ? 'border-secondary/80 text-primary'
: '' : ''
}`} }`}
> >

View file

@ -0,0 +1,23 @@
import { visit } from 'unist-util-visit'
export function rehypeUnwrapImg() {
return (tree) => {
visit(tree, 'element', (node, index, parent) => {
if (
node.tagName === 'p'
&& node.children
&& parent
&& node.children.every(child =>
child.tagName === 'img'
|| (child.type === 'text' && child.value.trim() === ''),
)
) {
const imgNodes = node.children.filter(child => child.tagName === 'img')
if (imgNodes.length > 0) {
parent.children.splice(index, 1, ...imgNodes)
}
}
})
}
}

View file

@ -1,28 +1,47 @@
/* KaTeX Overflow Fix */ /* GitHub Card */
.katex-display { .gc-container {
--at-apply: 'overflow-x-auto overflow-y-hidden scrollbar-hidden'; --at-apply: 'block mb-4 px-5 py-4 overflow-x-auto uno-round-border bg-secondary/5';
} --at-apply: 'transition-colors ease-out lg:(px-6 py-5) hover:(bg-secondary/10 c-primary)';
.katex-display::-webkit-scrollbar { scrollbar-color: oklch(var(--un-preset-theme-colors-secondary) / 0.15) transparent;
display: none;
} }
/* Heading Anchor Link */ /* Title Bar */
.heading-anchor-link { .gc-title-bar {
--at-apply: 'inline-block translate-y-0.1em c-secondary/0'; --at-apply: 'flex items-center gap-2.5 lg:gap-3';
} }
h1:hover .heading-anchor-link, .gc-owner-avatar {
h2:hover .heading-anchor-link, --at-apply: 'flex-shrink-0 w-5.5 aspect-square rounded-full bg-secondary/20';
h3:hover .heading-anchor-link,
h4:hover .heading-anchor-link {
--at-apply: 'c-secondary/40';
} }
.heading-anchor-link svg { .gc-repo-title {
--at-apply: 'ml-0.4em aspect-square w-0.9em transition-colors active:scale-90 hover:c-secondary/80'; --at-apply: 'flex items-center leading-normal lg:text-lg';
}
.gc-slash {
--at-apply: 'mx-1 op-40 lg:mx-1.2';
}
.gc-github-icon {
--at-apply: 'flex-shrink-0 ml-auto w-5.5 lg:w-6';
} }
/* Video */ /* Repo Description */
iframe { .gc-repo-description.gc-repo-description {
--at-apply: 'mb-4 w-full aspect-video'; --at-apply: 'mt-2.45 mb-3.5 text-sm text-start lg:(mt-2.8 mb-4 text-base)';
}
/* Info Bar */
.gc-info-bar {
--at-apply: 'flex items-center gap-1.75 text-xs lg:(gap-2 text-sm)';
}
.gc-info-icon {
--at-apply: 'flex-shrink-0';
}
.gc-stars-count {
--at-apply: 'mr-3 lg:mr-4';
}
.gc-forks-count {
--at-apply: 'mr-3.75 lg:mr-5';
}
.gc-license-info {
--at-apply: 'ml-0.5 mr-4';
} }
/* Admonition >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> */ /* Admonition >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> */
@ -93,55 +112,12 @@ iframe {
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4.47.22A.749.749 0 0 1 5 0h6c.199 0 .389.079.53.22l4.25 4.25c.141.14.22.331.22.53v6a.749.749 0 0 1-.22.53l-4.25 4.25A.749.749 0 0 1 11 16H5a.749.749 0 0 1-.53-.22L.22 11.53A.749.749 0 0 1 0 11V5c0-.199.079-.389.22-.53Zm.84 1.28L1.5 5.31v5.38l3.81 3.81h5.38l3.81-3.81V5.31L10.69 1.5ZM8 4a.75.75 0 0 1 .75.75v3.5a.75.75 0 0 1-1.5 0v-3.5A.75.75 0 0 1 8 4Zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z'%3E%3C/path%3E%3C/svg%3E"); mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4.47.22A.749.749 0 0 1 5 0h6c.199 0 .389.079.53.22l4.25 4.25c.141.14.22.331.22.53v6a.749.749 0 0 1-.22.53l-4.25 4.25A.749.749 0 0 1 11 16H5a.749.749 0 0 1-.53-.22L.22 11.53A.749.749 0 0 1 0 11V5c0-.199.079-.389.22-.53Zm.84 1.28L1.5 5.31v5.38l3.81 3.81h5.38l3.81-3.81V5.31L10.69 1.5ZM8 4a.75.75 0 0 1 .75.75v3.5a.75.75 0 0 1-1.5 0v-3.5A.75.75 0 0 1 8 4Zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z'%3E%3C/path%3E%3C/svg%3E");
} }
/* GitHub Card >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> */
.gc-container {
--at-apply: 'block mb-4 px-5 py-4 overflow-x-auto uno-round-border bg-secondary/5 scrollbar-hidden';
--at-apply: 'transition-colors lg:(px-6 py-5) hover:(bg-secondary/10 c-primary)';
}
.gc-container::-webkit-scrollbar {
display: none;
}
/* Title Bar */
.gc-title-bar {
--at-apply: 'flex items-center gap-2.5 lg:gap-3';
}
.gc-owner-avatar {
--at-apply: 'flex-shrink-0 w-5.5 aspect-square rounded-full bg-secondary/20';
}
.gc-repo-title {
--at-apply: 'flex items-center leading-normal lg:text-lg';
}
.gc-slash {
--at-apply: 'mx-1 op-40 lg:mx-1.2';
}
.gc-github-icon {
--at-apply: 'flex-shrink-0 ml-auto w-5.5 lg:w-6';
}
/* Repo Description */
.gc-repo-description.gc-repo-description {
--at-apply: 'mt-2.45 mb-3.5 text-sm text-start lg:(mt-2.8 mb-4 text-base)';
}
/* Info Bar */
.gc-info-bar {
--at-apply: 'flex items-center gap-1.75 text-xs lg:(gap-2 text-sm)';
}
.gc-info-icon {
--at-apply: 'flex-shrink-0';
}
.gc-stars-count {
--at-apply: 'mr-3 lg:mr-4';
}
.gc-forks-count {
--at-apply: 'mr-3.75 lg:mr-5';
}
.gc-license-info {
--at-apply: 'ml-0.5 mr-4';
}
/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> */ /* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> */
/* Video */
/* iframe {
--at-apply: 'mb-4 w-full aspect-video';
} */
/* :where(details) { /* :where(details) {
--at-apply: 'my-4 px-4 py-3 border border-solid border-secondary/25 rounded cursor-pointer'; --at-apply: 'my-4 px-4 py-3 border border-solid border-secondary/25 rounded cursor-pointer';
} }

View file

@ -1,3 +1,41 @@
/* Snell Roundhand Static Font */
@font-face {
font-family: "Snell-Bold";
src: url("/fonts/Snell-Bold.woff2") format("woff2");
font-display: swap;
unicode-range: U+0030-0039,U+0041-005A,U+0061-007A,U+00C1,U+00C9,U+00CD,U+00D3,U+00DA,U+00DC,U+00D1,U+00E1,U+00E9,U+00ED,U+00F3,U+00FA,U+00FC,U+00F1,U+0410-044F,U+0401,U+0451,U+0021-002F,U+003A-0040,U+00A9;
}
@font-face {
font-family: "Snell-Black";
src: url("/fonts/Snell-Black.woff2") format("woff2");
font-display: swap;
unicode-range: U+0030-0039,U+0041-005A,U+0061-007A,U+00C1,U+00C9,U+00CD,U+00D3,U+00DA,U+00DC,U+00D1,U+00E1,U+00E9,U+00ED,U+00F3,U+00FA,U+00FC,U+00F1,U+0410-044F,U+0401,U+0451,U+0021-002F,U+003A-0040,U+00A9;
}
/* STIXTwoText Variable Font */
@font-face {
font-family: "STIX";
src: url("/fonts/STIX.woff2") format("woff2-variations");
font-display: swap;
font-weight: 400 700;
unicode-range: U+0030-0039,U+0041-005A,U+0061-007A,U+00C1,U+00C9,U+00CD,U+00D3,U+00DA,U+00DC,U+00D1,U+00E1,U+00E9,U+00ED,U+00F3,U+00FA,U+00FC,U+00F1,U+0410-044F,U+0401,U+0451,U+0021-002F,U+003A-0040,U+00A9;
}
@font-face {
font-family: "STIX-Italic";
src: url("/fonts/STIX-Italic.woff2") format("woff2-variations");
font-display: swap;
font-weight: 400 700;
unicode-range: U+0030-0039,U+0041-005A,U+0061-007A,U+00C1,U+00C9,U+00CD,U+00D3,U+00DA,U+00DC,U+00D1,U+00E1,U+00E9,U+00ED,U+00F3,U+00FA,U+00FC,U+00F1,U+0410-044F,U+0401,U+0451,U+0021-002F,U+003A-0040,U+00A9;
}
/* Minimal Subset of EarlySummerSerif Variable Font */
@font-face {
font-family: "EarlySummer-Subset";
src: url("/fonts/EarlySummer-Subset.woff2") format("woff2-variations");
font-display: swap;
font-weight: 400 700;
}
/* EarlySummerSerif Variable Font */ /* EarlySummerSerif Variable Font */
@font-face {font-family:"EarlySummer";src:url("/fonts/EarlySummer-Split/264097d8d9da16491b480a1c46afe035.woff2") format("woff2-variations");font-display: swap;font-weight: 400 700;unicode-range:U+B7,U+9F2C,U+9F3B,U+9F3E,U+9F84,U+9F99,U+9F9F;} @font-face {font-family:"EarlySummer";src:url("/fonts/EarlySummer-Split/264097d8d9da16491b480a1c46afe035.woff2") format("woff2-variations");font-display: swap;font-weight: 400 700;unicode-range:U+B7,U+9F2C,U+9F3B,U+9F3E,U+9F84,U+9F99,U+9F9F;}
@font-face {font-family:"EarlySummer";src:url("/fonts/EarlySummer-Split/c0df1afa44c13da520351e6aa9be3d9a.woff2") format("woff2-variations");font-display: swap;font-weight: 400 700;unicode-range:U+9711,U+9784,U+97A6,U+97C1,U+97C6,U+982C,U+983C,U+9854-9855,U+98B1,U+98B3,U+98BA,U+9935,U+993D,U+9951,U+99C4-99C6,U+9A12-9A13,U+9AEA,U+9AEE,U+9B06,U+9B0D,U+9B1A,U+9B28,U+9D12,U+9E78,U+9EB5,U+9EBA,U+9ED2,U+9ED9,U+9F07,U+9F15,U+9F62-9F63,U+FF01,U+FF08-FF09,U+FF0C,U+FF1A-FF1B,U+FF1F;} @font-face {font-family:"EarlySummer";src:url("/fonts/EarlySummer-Split/c0df1afa44c13da520351e6aa9be3d9a.woff2") format("woff2-variations");font-display: swap;font-weight: 400 700;unicode-range:U+9711,U+9784,U+97A6,U+97C1,U+97C6,U+982C,U+983C,U+9854-9855,U+98B1,U+98B3,U+98BA,U+9935,U+993D,U+9951,U+99C4-99C6,U+9A12-9A13,U+9AEA,U+9AEE,U+9B06,U+9B0D,U+9B1A,U+9B28,U+9D12,U+9E78,U+9EB5,U+9EBA,U+9ED2,U+9ED9,U+9F07,U+9F15,U+9F62-9F63,U+FF01,U+FF08-FF09,U+FF0C,U+FF1A-FF1B,U+FF1F;}

View file

@ -5,9 +5,9 @@
} }
html { html {
--at-apply: 'bg-background c-secondary antialiased'; --at-apply: 'bg-background c-secondary antialiased';
} scrollbar-width: thin;
::selection { scrollbar-color: oklch(var(--un-preset-theme-colors-secondary) / 0.25) transparent;
--at-apply: 'bg-mark'; scrollbar-gutter: stable both-edges;
} }
/* Fix Flash Issue On iOS */ /* Fix Flash Issue On iOS */
@ -15,6 +15,37 @@ body {
backface-visibility: hidden; backface-visibility: hidden;
-webkit-backface-visibility: hidden; -webkit-backface-visibility: hidden;
} }
body::selection {
--at-apply: 'bg-highlight';
}
/* Fix KaTeX Overflow Issue */
.katex-display {
--at-apply: 'overflow-x-auto overflow-y-hidden scrollbar-hidden';
}
.katex-display::-webkit-scrollbar {
display: none;
}
/* Heading Anchor Link */
.heading-anchor-link {
--at-apply: 'inline-block translate-y-0.1em';
}
h1:hover .heading-anchor-link svg,
h2:hover .heading-anchor-link svg,
h3:hover .heading-anchor-link svg,
h4:hover .heading-anchor-link svg {
--at-apply: 'op-80';
}
.heading-anchor-link svg {
--at-apply: 'ml-0.4em aspect-square w-0.9em op-0 transition-opacity ease-out active:scale-90';
}
h1:hover .heading-anchor-link svg:hover,
h2:hover .heading-anchor-link svg:hover,
h3:hover .heading-anchor-link svg:hover,
h4:hover .heading-anchor-link svg:hover {
--at-apply: 'op-80';
}
/* Highlight Hover Animation */ /* Highlight Hover Animation */
.highlight-static, .highlight-static,
@ -23,85 +54,22 @@ body {
} }
.highlight-static::after, .highlight-static::after,
.highlight-hover::after { .highlight-hover::after {
--at-apply: 'content-[""] absolute bottom-0.5em left-0 z--1 h-0.5em w-full bg-mark'; --at-apply: 'content-[""] absolute left-0 z--1 h-0.5em w-full bg-highlight';
} }
.highlight-static::after, .highlight-static::after,
.highlight-hover:hover::after { .highlight-hover:hover::after {
--at-apply: 'origin-bottom-left scale-x-100'; --at-apply: 'origin-bottom-left scale-x-100';
} }
.highlight-hover::after { .highlight-hover::after {
--at-apply: 'origin-bottom-right scale-x-0 transition-transform duration-300 ease-out'; --at-apply: 'origin-bottom-right scale-x-0 transition-transform ease-out lg:duration-225';
} }
/* View Transition with Theme Toggle >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> */ /* Fix Highlight Position Issue on iOS */
::view-transition-new(theme-transition) { @supports (-webkit-touch-callout: none) {
animation: reveal 1s cubic-bezier(0.4, 0, 0.2, 1); .navbar-highlight-position-fix {
clip-path: inset(0 0 0 0); --at-apply: 'after:bottom-0.68em';
z-index: 99; }
} .footer-highlight-position-fix {
::view-transition-old(theme-transition) { --at-apply: 'after:bottom-0.39em';
animation: none;
z-index: -1;
}
@keyframes reveal {
from {
clip-path: inset(var(--from));
} }
} }
html.dark {
--from: 0 0 100% 0;
}
html:not(.dark) {
--from: 100% 0 0 0;
}
/* Disable animations for other elements during theme switching */
html[data-theme-transition] [data-disable-transition-on-theme] {
view-transition-name: none !important;
}
/* Fallback animation when view-transition-name is not supported */
@supports not (view-transition-name: none) {
html:not([data-restore-theme]) {
--at-apply: 'transition-colors duration-300 ease-out';
}
}
/* Snell Roundhand Static Font >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> */
@font-face {
font-family: "Snell-Bold";
src: url("/fonts/Snell-Bold.woff2") format("woff2");
font-display: swap;
unicode-range: U+0030-0039,U+0041-005A,U+0061-007A,U+00C1,U+00C9,U+00CD,U+00D3,U+00DA,U+00DC,U+00D1,U+00E1,U+00E9,U+00ED,U+00F3,U+00FA,U+00FC,U+00F1,U+0410-044F,U+0401,U+0451,U+0021-002F,U+003A-0040,U+00A9;
}
@font-face {
font-family: "Snell-Black";
src: url("/fonts/Snell-Black.woff2") format("woff2");
font-display: swap;
unicode-range: U+0030-0039,U+0041-005A,U+0061-007A,U+00C1,U+00C9,U+00CD,U+00D3,U+00DA,U+00DC,U+00D1,U+00E1,U+00E9,U+00ED,U+00F3,U+00FA,U+00FC,U+00F1,U+0410-044F,U+0401,U+0451,U+0021-002F,U+003A-0040,U+00A9;
}
/* STIXTwoText Variable Font */
@font-face {
font-family: "STIX";
src: url("/fonts/STIX.woff2") format("woff2-variations");
font-display: swap;
font-weight: 400 700;
unicode-range: U+0030-0039,U+0041-005A,U+0061-007A,U+00C1,U+00C9,U+00CD,U+00D3,U+00DA,U+00DC,U+00D1,U+00E1,U+00E9,U+00ED,U+00F3,U+00FA,U+00FC,U+00F1,U+0410-044F,U+0401,U+0451,U+0021-002F,U+003A-0040,U+00A9;
}
@font-face {
font-family: "STIX-Italic";
src: url("/fonts/STIX-Italic.woff2") format("woff2-variations");
font-display: swap;
font-weight: 400 700;
unicode-range: U+0030-0039,U+0041-005A,U+0061-007A,U+00C1,U+00C9,U+00CD,U+00D3,U+00DA,U+00DC,U+00D1,U+00E1,U+00E9,U+00ED,U+00F3,U+00FA,U+00FC,U+00F1,U+0410-044F,U+0401,U+0451,U+0021-002F,U+003A-0040,U+00A9;
}
/* Minimal Subset of EarlySummerSerif Variable Font */
@font-face {
font-family: "EarlySummer-Subset";
src: url("/fonts/EarlySummer-Subset.woff2") format("woff2-variations");
font-display: swap;
font-weight: 400 700;
}

View file

@ -7,52 +7,41 @@
/* Global Styles */ /* Global Styles */
.heti { .heti {
--at-apply: 'break-words leading-1.5em hyphens-auto cjk:tracking-0.02em'; --at-apply: 'break-words leading-normal hyphens-auto cjk:tracking-0.02em';
} }
/* Customized Post Title */ /* Customized Post Title */
.heti .post-title { .heti .post-title {
--at-apply: 'mb-2 text-8.6 c-primary font-bold leading-12 lg:text-9'; --at-apply: 'mb-2 text-8.6 font-bold lg:text-9';
} }
/* Headings */ /* Headings */
.heti :where(h1), .heti :where(h1, h2, h3, h4, h5, h6) {
.heti :where(h2), --at-apply: 'mb-4 font-semibold';
.heti :where(h3), }
.heti :where(h4), .heti :where(h1, h2, h3) {
.heti :where(h5), --at-apply: 'text-balance leading-1.33em cjk:text-pretty cjk:tracking-0.05em';
.heti :where(h6) {
--at-apply: 'mb-3 mt-6 font-semibold';
} }
.heti :where(h1) { .heti :where(h1) {
--at-apply: 'text-7 leading-12'; --at-apply: 'mt-9.6 text-7 text-primary';
} }
.heti :where(h2) { .heti :where(h2) {
--at-apply: 'text-6 leading-9'; --at-apply: 'mt-9.6 text-6 text-primary';
} }
.heti :where(h3) { .heti :where(h3) {
--at-apply: 'text-5 leading-9'; --at-apply: 'mt-6.5 text-5';
} }
.heti :where(h4) { .heti :where(h4) {
--at-apply: 'text-4.5 leading-6'; --at-apply: 'mt-6 text-4.5';
} }
.heti :where(h5) { .heti :where(h5) {
--at-apply: 'text-4 leading-6'; --at-apply: 'mt-6 text-4';
} }
.heti :where(h6) { .heti :where(h6) {
--at-apply: 'text-3.5 leading-6'; --at-apply: 'mt-6 font-normal';
} }
.heti :where(h1), .heti :where(h1 + h2, h2 + h3, h3 + h4, h4 + h5, h5 + h6) {
.heti :where(h2), --at-apply: 'mt-4';
.heti :where(h3) {
--at-apply: 'text-balance cjk:text-pretty cjk:tracking-0.05em';
}
.heti :where(h1 + h2),
.heti :where(h2 + h3),
.heti :where(h3 + h4),
.heti :where(h4 + h5),
.heti :where(h5 + h6) {
--at-apply: 'mt-3';
} }
/* Paragraphs */ /* Paragraphs */
@ -62,26 +51,33 @@
/* Links */ /* Links */
.heti :where(a:not(.gc-container)) { .heti :where(a:not(.gc-container)) {
--at-apply: 'break-all font-medium tracking-0 underline underline-0.075em decoration-secondary/40 underline-offset-0.2em'; --at-apply: 'break-all font-semibold tracking-0 underline underline-0.075em decoration-secondary/80 underline-offset-0.1em';
--at-apply: 'transition-colors hover:(c-primary decoration-secondary/80) lg:underline-0.1em'; --at-apply: 'transition-colors ease-out hover:(c-primary decoration-primary/80) lg:underline-0.1em';
} }
/* Images */ /* Images */
.heti :where(img) { .heti :where(img) {
--at-apply: 'mx-auto cursor-zoom-in'; --at-apply: 'mb-4 mx-auto cursor-zoom-in';
transform: translateZ(0);
-webkit-transform: translateZ(0);
} }
.heti :where(figure) { .heti :where(figure) {
--at-apply: 'mx-auto mb-4'; --at-apply: 'mx-auto mb-4';
} }
.heti figure img {
--at-apply: 'mb-0';
}
.heti :where(figcaption) { .heti :where(figcaption) {
--at-apply: 'mt-2 text-center text-sm text-secondary/80'; --at-apply: 'mt-3 text-center text-sm text-secondary/80';
} }
/* Code Blocks */ /* Code Blocks */
.heti :where(pre) { .heti :where(pre) {
--at-apply: 'mb-4 overflow-auto uno-round-border px-4 py-3 bg-secondary/5!'; --at-apply: 'mb-4 overflow-auto uno-round-border px-4 py-3 bg-secondary/5!';
scrollbar-width: thin;
scrollbar-color: oklch(var(--un-preset-theme-colors-secondary) / 0) transparent;
transition: scrollbar-color 0.3s ease-out;
}
.heti :where(pre:hover) {
scrollbar-color: oklch(var(--un-preset-theme-colors-secondary) / 0.15) transparent;
} }
.heti pre :where(code) { .heti pre :where(code) {
--at-apply: 'border-none bg-transparent p-0'; --at-apply: 'border-none bg-transparent p-0';
@ -124,10 +120,13 @@ html.dark .heti pre :where(span) {
/* Tables */ /* Tables */
.heti :where(table) { .heti :where(table) {
--at-apply: 'mb-4 box-border block max-w-full table-fixed overflow-x-auto scrollbar-hidden'; --at-apply: 'mb-4 box-border block max-w-full table-fixed overflow-x-auto';
scrollbar-width: thin;
scrollbar-color: oklch(var(--un-preset-theme-colors-secondary) / 0) transparent;
transition: scrollbar-color 0.3s ease-out;
} }
.heti :where(table)::-webkit-scrollbar { .heti :where(table:hover) {
--at-apply: 'hidden'; scrollbar-color: oklch(var(--un-preset-theme-colors-secondary) / 0.15) transparent;
} }
.heti :where(th), .heti :where(th),
.heti :where(td) { .heti :where(td) {
@ -182,7 +181,7 @@ html.dark .heti pre :where(span) {
/* Superscript and Subscript >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> */ /* Superscript and Subscript >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> */
.heti :where(sub), .heti :where(sub),
.heti :where(sup) { .heti :where(sup) {
--at-apply: 'relative mx-0.1em align-baseline text-0.75em leading-1'; --at-apply: 'relative mx-0.1em align-baseline text-0.75em';
} }
.heti :where(sub) { .heti :where(sub) {
--at-apply: 'bottom--0.25em'; --at-apply: 'bottom--0.25em';
@ -212,7 +211,7 @@ html.dark .heti :where(u) {
/* Highlighted Text */ /* Highlighted Text */
.heti :where(mark) { .heti :where(mark) {
--at-apply: 'bg-mark py-0.65 text-inherit'; --at-apply: 'bg-highlight py-0.65 text-inherit';
} }
/* Footnotes */ /* Footnotes */
@ -222,7 +221,7 @@ html.dark .heti :where(u) {
} }
.heti sup:target, .heti sup:target,
.heti sup a:target { .heti sup a:target {
--at-apply: 'bg-mark'; --at-apply: 'bg-highlight';
} }
.heti .data-footnote-backref { .heti .data-footnote-backref {
--at-apply: 'font-serif no-underline'; --at-apply: 'font-serif no-underline';
@ -253,4 +252,3 @@ html.dark .heti :where(u) {
.heti :where(q:is(:lang(zh), :lang(ja), :lang(ko))) { .heti :where(q:is(:lang(zh), :lang(ja), :lang(ko))) {
quotes: "「" "」" "『" "』"; quotes: "「" "」" "『" "』";
} }

34
src/styles/transition.css Normal file
View file

@ -0,0 +1,34 @@
/* View Transition */
::view-transition-new(animation-theme-toggle) {
animation: reveal 0.8s cubic-bezier(0.4, 0, 0.2, 1);
clip-path: inset(0 0 0 0);
z-index: 99;
}
::view-transition-old(animation-theme-toggle) {
animation: none;
z-index: -1;
}
@keyframes reveal {
from {
clip-path: inset(var(--from));
}
}
html.dark {
--from: 0 0 100% 0;
}
html:not(.dark) {
--from: 100% 0 0 0;
}
/* Disable animations for special elements during theme switching */
html[data-theme-changing] [data-disable-theme-transition] {
view-transition-name: none !important;
}
/* Fallback animation when view-transition-name is not supported */
@supports not (view-transition-name: none) {
html {
--at-apply: 'transition-colors duration-300 ease-out';
}
}

View file

@ -34,6 +34,7 @@ export interface ThemeConfig {
fontStyle: 'sans' | 'serif' fontStyle: 'sans' | 'serif'
dateFormat: 'YYYY-MM-DD' | 'MM-DD-YYYY' | 'DD-MM-YYYY' | 'MONTH DAY YYYY' | 'DAY MONTH YYYY' dateFormat: 'YYYY-MM-DD' | 'MM-DD-YYYY' | 'DD-MM-YYYY' | 'MONTH DAY YYYY' | 'DAY MONTH YYYY'
katex: boolean katex: boolean
toc: boolean
} }
comment: { comment: {
@ -73,7 +74,6 @@ export interface ThemeConfig {
preload: { preload: {
linkPrefetch: 'hover' | 'tap' | 'viewport' | 'load' linkPrefetch: 'hover' | 'tap' | 'viewport' | 'load'
commentURL?: string
imageHostURL?: string imageHostURL?: string
customGoogleAnalyticsJS?: string customGoogleAnalyticsJS?: string
customUmamiAnalyticsJS?: string customUmamiAnalyticsJS?: string

View file

@ -54,7 +54,7 @@ const getOptimizedImageUrl = memoize(async (srcPath: string, baseUrl: string) =>
*/ */
async function fixRelativeImagePaths(htmlContent: string, baseUrl: string): Promise<string> { async function fixRelativeImagePaths(htmlContent: string, baseUrl: string): Promise<string> {
const htmlDoc = htmlParser(htmlContent) const htmlDoc = htmlParser(htmlContent)
const images = htmlDoc.querySelectorAll('img') const images = htmlDoc.getElementsByTagName('img')
const imagePromises = [] const imagePromises = []
for (const img of images) { for (const img of images) {

View file

@ -20,7 +20,7 @@ export default defineConfig({
dark: { dark: {
colors: { colors: {
...dark, ...dark,
mark: 'oklch(0.93 0.195089 103.2532 / 0.2)', // rgba(255,235,0,0.5) highlight: 'oklch(0.93 0.195089 103.2532 / 0.2)', // rgba(255,235,0,0.5)
note: 'oklch(70.7% 0.165 254.624 / 0.8)', // blue-400 note: 'oklch(70.7% 0.165 254.624 / 0.8)', // blue-400
tip: 'oklch(76.5% 0.177 163.223 / 0.8)', // emerald-400 tip: 'oklch(76.5% 0.177 163.223 / 0.8)', // emerald-400
important: 'oklch(71.4% 0.203 305.504 / 0.8)', // purple-400 important: 'oklch(71.4% 0.203 305.504 / 0.8)', // purple-400
@ -34,7 +34,7 @@ export default defineConfig({
theme: { theme: {
colors: { colors: {
...light, ...light,
mark: 'oklch(0.93 0.195089 103.2532 / 0.5)', // rgba(255,235,0,0.5) highlight: 'oklch(0.93 0.195089 103.2532 / 0.5)', // rgba(255,235,0,0.5)
note: 'oklch(48.8% 0.243 264.376 / 0.8)', // blue-700 note: 'oklch(48.8% 0.243 264.376 / 0.8)', // blue-700
tip: 'oklch(50.8% 0.118 165.612 / 0.8)', // emerald-700 tip: 'oklch(50.8% 0.118 165.612 / 0.8)', // emerald-700
important: 'oklch(49.6% 0.265 301.924 / 0.8)', // purple-700 important: 'oklch(49.6% 0.265 301.924 / 0.8)', // purple-700
@ -55,11 +55,11 @@ export default defineConfig({
}], }],
], ],
shortcuts: { shortcuts: {
'uno-desktop-column': 'fixed w-14rem right-[max(5.625rem,calc(50vw-34.375rem))]', 'uno-desktop-column': 'fixed right-[max(5rem,calc(50vw-35rem))] w-14rem',
'uno-tags-style': 'inline-block whitespace-nowrap border border-secondary/25 rounded-full px-3.2 py-0.7 c-secondary transition-colors hover:(border-secondary/75 text-primary)', 'uno-tags-style': 'inline-block whitespace-nowrap border border-secondary/25 rounded-full px-3.2 py-0.7 c-secondary transition-colors ease-out hover:(border-secondary/80 text-primary)',
'uno-decorative-line': 'h-0.25 w-10 bg-secondary/25 mb-4.5 lg:(w-11 mb-6)', 'uno-decorative-line': 'mb-4.5 h-0.25 w-10 bg-secondary/25 lg:(mb-6 w-11)',
'uno-tags-wrapper': 'flex flex-wrap gap-x-3 gap-y-3.2', 'uno-tags-wrapper': 'flex flex-wrap gap-x-3 gap-y-3.2',
'uno-round-border': 'rounded border border-solid border-secondary/5', 'uno-round-border': 'border border-secondary/5 rounded border-solid',
}, },
variants: [ variants: [
(matcher) => { (matcher) => {