Shirone Authoring & Usage Guide
Welcome to Shirone (白音) — an expressive, anime-inspired blog theme crafted around Astro 7, Svelte 5, and the Material 3 Expressive (M3E) design system.
This guide walks you through post creation, frontmatter specifications, directory structure, and the full suite of built-in Markdown and MDX extensions.
1. Creating a New Post
You can quickly scaffold a new post with standard frontmatter using the built-in CLI command:
# Create a single-file postpnpm new-post my-first-post
# Or create a post in a sub-directorypnpm new-post guides/getting-startedThe newly created file will be placed in src/content/posts/.
2. Frontmatter Specification
Every Markdown (.md) or MDX (.mdx) post starts with a YAML frontmatter block defining its metadata.
Example
---title: "Exploring Material 3 Expressive Design"published: 2026-08-26updated: 2026-08-27publishedAt: 2026-08-26T10:00:00+08:00updatedAt: 2026-08-27T09:30:00+08:00pinned: truedescription: "A deep dive into dynamic HCT color science and fluid transitions in Shirone."image: "./cover.webp"tags: [M3E, Design, Frontend]category: Guidesdraft: falsecomment: true---Supported Frontmatter Fields
| Field | Type | Required | Description |
|---|---|---|---|
title | string | Yes | The main title of the post. |
published | Date | Yes | Publication date in YYYY-MM-DD format. |
publishedAt | Date | No | Precise publication instant used to order posts published on the same day. It must fall on published in the configured site time zone. |
updated | Date | No | Last updated date. When provided, an update notice badge is displayed. |
updatedAt | Date | No | Precise update instant used by feeds and machine-readable metadata. It must be paired with updated. |
pinned | boolean | No | Pin the post to the top of article lists (default: false). |
description | string | No | Post summary displayed in article cards, search results, and OpenGraph metadata. |
image | string | No | Cover image path. Supports relative (./cover.webp), public (/images/cover.jpg), or remote URLs. |
tags | string[] | No | Array of tag names for taxonomy filtering and tag clouds. |
category | string | No | Primary category name for taxonomy indexing. |
draft | boolean | No | Mark as draft. Draft posts are hidden during production build (pnpm build). |
comment | boolean | No | Toggle comment section for this specific post (default: true). |
lang | string | No | Language code (e.g. en, zh_CN, ja) if different from site default. |
3. Post Encryption
Shirone provides client-side post encryption. This is a static password gate, not server-side access control: the encrypted payload and asset URLs remain publicly downloadable, so do not use it for genuinely confidential files or regulated secrets. For private journals or restricted articles, specify a password in frontmatter:
---title: "Private Research Notes"published: 2026-08-26encrypted: truepassword: "your-secret-passphrase"passwordHint: "Favorite anime character"hideHomeContent: true---encrypted: Set totrueto enable encryption;password: Passphrase string or number required to unlock the post;passwordHint: Optional hint shown above the password entry form;hideHomeContent: Hide word counts and content previews on the homepage to prevent data leakage.
4. Organizing Post Files
Shirone supports both folder-based co-location and single-file layouts:
Folder Structure (Recommended for Local Assets)
Co-locating your post and its media makes asset management straightforward:
src/content/posts/├── my-great-post/│ ├── index.md <-- Post content│ ├── cover.webp <-- Cover image (image: "./cover.webp")│ └── diagram.png <-- Inline illustration referenced in markdownSingle-File Structure (Lightweight Prose)
src/content/posts/├── hello-world.md└── quick-thoughts.md5. Rich Markdown & MDX Extensions
Shirone includes modern Markdown extensions out of the box:
5.1 Admonitions
Use container directives for notes, tips, warnings, and alerts:
:::tipUse admonition containers to highlight key takeaways or best practices.:::
:::warningUse warning containers to signal potential pitfalls or breaking changes.:::5.2 GitHub Repository Cards
Embed live, beautifully styled GitHub repository cards using the directive syntax:
::github{repo="LyraVoid/Shirone"}5.3 Expressive Code Blocks
Enhanced code blocks feature syntax highlighting, file name badges, line numbers, and selective line highlighting:
// Dynamic HCT color token derivationimport { argbFromHex, themeFromSourceColor } from "@material/material-color-utilities";
const theme = themeFromSourceColor(argbFromHex("#f472b6"));console.log("Primary color token:", theme.schemes.light.primary);5.4 Mathematical Typesetting (KaTeX)
Render elegant LaTeX mathematical notation directly in Markdown:
- Inline math: or Euler’s formula .
- Block math:
5.5 Mermaid Diagrams
Create flowcharts, sequence diagrams, and architecture maps using plain text:
sequenceDiagram
autonumber
actor Visitor as Visitor
participant Page as Shirone Page
participant Swup as Swup Container
participant Audio as Persistent Shell
Visitor->>Page: Click Navigation Link
Page->>Swup: Trigger Seamless Transition
Swup->>Page: Replace #swup-container
Note over Audio: Background music plays continuously
Page-->>Visitor: New Page Rendered5.6 Image Galleries & Fancybox Lightbox
Images automatically integrate with Fancybox for lossless zoom, pan gestures, and full-screen preview:
6. Next Steps & Customization
- Site Configuration: Learn about global settings in
src/config/siteConfig.tsandsrc/config/README.md. - Design Tokens: Explore tokens and color palettes in
DESIGN.mdanddocs/m3e-standard.md. - Feedback & Community: Share your ideas and questions on GitHub Issues.
markdown.md
An h1 header
Paragraphs are separated by a blank line.
2nd paragraph. Italic, bold, and monospace. Itemized lists
look like:
- this one
- that one
- the other one
Note that --- not considering the asterisk --- the actual text content starts at 4-columns in.
Block quotes are written like so.
They can span multiple paragraphs, if you like.
Use 3 dashes for an em-dash. Use 2 dashes for ranges (ex., “it’s all in chapters 12—14”). Three dots … will be converted to an ellipsis. Unicode is supported. ☺
An h2 header
Here’s a numbered list:
- first item
- second item
- third item
Note again how the actual text starts at 4 columns in (4 characters from the left side). Here’s a code sample:
# Let me re-iterate ...for i in 1 .. 10 { do-something(i) }As you probably guessed, indented 4 spaces. By the way, instead of indenting the block, you can use delimited blocks, if you like:
define foobar() { print "Welcome to flavor country!";}(which makes copying & pasting easier). You can optionally mark the delimited block for Pandoc to syntax highlight it:
import time# Quick, count to ten!for i in range(10): # (but not *too* quick) time.sleep(0.5) print iAn h3 header
Now a nested list:
-
First, get these ingredients:
- carrots
- celery
- lentils
-
Boil some water.
-
Dump everything in the pot and follow this algorithm:
find wooden spoonuncover potstircover potbalance wooden spoon precariously on pot handlewait 10 minutesgoto first step (or shut off burner when done)Do not bump wooden spoon or it will fall.
Notice again how text always lines up on 4-space indents (including that last line which continues item 3 above).
Here’s a link to a website, to a local doc, and to a section heading in the current doc. Here’s a footnote 1.
Tables can look like this:
size material color
9 leather brown 10 hemp canvas natural 11 glass transparent
Table: Shoes, their sizes, and what they’re made of
(The above is the caption for the table.) Pandoc also supports multi-line tables:
keyword text
red Sunsets, apples, and other red or reddish things.
green Leaves, grass, frogs and other things it’s not easy being.
A horizontal rule follows.
Here’s a definition list:
apples : Good for making applesauce. oranges : Citrus! tomatoes : There’s no “e” in tomatoe.
Again, text is indented 4 spaces. (Put a blank line between each term/definition pair to spread things out more.)
Here’s a “line block”:
| Line one | Line too | Line tree
and images can be specified like so:
Inline math equations go in like so: . Display math should get its own line and be put in in double-dollarsigns:
And note that you can backslash-escape any punctuation characters which you wish to be displayed literally, ex.: `foo`, *bar*, etc.
markdown-enhancements.md
Shirone provides a collection of theme-exclusive Markdown extensions and custom syntax containers. Built on top of our native unified AST processing pipeline, all extensions render into accessible, semantic HTML during site build time with zero client JavaScript hydration overhead and 100% M3E design token alignment.
File Trees
File Trees turn multi-level project structures, source hierarchies, and terminal directory outputs into compact, interactive tree views with automatic extension icons, diff highlighting, and collapsible branches.
1. Nested List Syntax (:::file-tree)
Use the :::file-tree block directive when writing the file hierarchy directly as a Markdown nested list.
:::file-tree{title="Shirone source tree"}- src - components/ - ++ Navigation.svelte # added component - -- Button.astro # removed component - content - posts/ - markdown-enhancements.md - layouts/ - PostLayout.astro - plugins - markdown/ - rehype-file-tree.mjs - styles - markdown/ - trees.css - **content.config.ts** # important file- public/ - favicon.svg- package.json:::src
components
- +Navigation.svelteadded component
- -Button.astroremoved component
content
posts
- markdown-enhancements.md
layouts
- PostLayout.astro
plugins
markdown
- rehype-file-tree.mjs
styles
markdown
- trees.css
- content.config.tsimportant file
public
- favicon.svg
- package.json
Authoring Rules & Markers
- Diff States: Prefix an item with
++(green background & badge) or--(red background & strikethrough) to highlight changes. - Comments: Any text following a
#is rendered as a muted, right-aligned inline comment. - Emphasis: Wrap names in
**bold**to give key files prominent visual weight. - Collapsible Folders: Directories inferred from nested list items start expanded by default. Add a trailing slash (e.g.
components/) to create a collapsed directory that readers can expand on click or via keyboard navigation.
2. Terminal Output Syntax (```file-tree)
When you already have directory tree text generated from command-line tools like tree, paste it directly into a file-tree fenced code block. Both Unicode branch characters (├──, └──, │) and ASCII branches are automatically parsed.
```file-tree title="Build output" icon="simple"dist├── _astro/│ ├── index.css│ └── page.js└── favicon.ico```dist
_astro
- index.css
- page.js
- favicon.ico
Configuration Options
title="string": Sets a custom header title and accessible label for the tree.icon="colored" | "simple": Choose between multi-color extension icons (colored, default) or minimal monochrome icons (simple).
Code Trees
Interactive Code Trees pair a multi-level file hierarchy navigation pane on the left with instant code panel switching on the right. They provide an IDE-like reading experience for multi-file examples, modules, or whole directory walk-throughs.
1. Container Syntax (:::code-tree)
Combine multiple fenced code blocks within a :::code-tree block directive. Each code block specifies its path via title="path/to/file".
:::code-tree{title="Shirone Component Demo" height="380px" entry="src/Button.svelte"}```svelte title="src/Button.svelte"<script lang="ts"> let { label = "Click me" } = $props();</script>
<button class="m3-btn">{label}</button>```
```stylus title="src/styles/button.styl".m3-btn background: var(--primary) color: var(--on-primary) border-radius: var(--shape-corner-m)```
```json title="package.json"{ "name": "button-demo", "version": "1.0.0"}```:::<script lang="ts"> let { label = "Click me" } = $props();</script>
<button class="m3-btn">{label}</button>Configuration & Markers
title="string": Sets the header title and accessible label for the code tree.height="string": Sets the height for the desktop view (default420px, e.g.380px,26rem).entry="filepath": Specifies which file is active upon first load.icon="colored" | "simple": Switch between colorful or minimal monochrome file icons.:active: Place:activeon any fenced code block to designate it as the default active tab.
2. Local Directory Auto-Import (@[code-tree])
Point directly to any local directory path in the workspace to automatically scan and generate an interactive code tree at build time without manually copying file contents.
@[code-tree title="Anime Utilities" entry="status.ts"](/src/utils/anime)import type { SiteConfig } from "@/types/config";import type { ResolvedTextureOptions, TextureConfig,} from "@/types/textureConfig";import { withUserConfig } from "../utils/config-overlay.ts";
/** * 站点核心配置:标题 / 语言 / 主题色(HCT 动态配色)/ 横幅 / 目录 / 进度条 / favicon。 * 类型见 src/types/config.ts。 */export const siteConfig: SiteConfig = withUserConfig("site", { site: "https://blog-c6q.pages.dev/", base: "/", title: "Huixuanbiao", subtitle: "喜欢你", // 电脑端顶栏标题与导航内容区域:"left" 左对齐,"center" 居中。 topAppBar: { contentAlign: "center", }, // 显示设置面板控制:配置各项前端切换项的可见性(默认全部开启)。 displaySettings: { colorStyle: true, // 是否展示配色风格 9 宫格 colorSpec: true, // 是否展示 Color Spec 调色规范切换 wallpaperMode: true, // 是否展示页面背景(纯色/横幅)切换 layoutMode: true, // 是否展示文章列表布局(列表/网格)切换 reduceMotion: true, // 是否展示减少动效切换 texture: true, // 是否展示背景纹理选择 }, lang: "zh_CN", // Language code, e.g. 'en', 'zh_CN', 'ja', etc. // IANA time zone for precise post and moment timestamps. It is independent of lang. timeZone: "Asia/Shanghai", themeColor: { hue: 315, // Default hue 0-360. 站点设计默认粉紫(偏二次元);262 紫 / 345 粉 也可选 fixed: false, // Hide the theme color picker for visitors // Dynamic Material 3 palette style (TonalSpot/Vibrant/Content/Expressive/Rainbow/FruitSalad/Monochrome/Neutral/Fidelity) style: "tonalSpot", // Design spec version: "2021" (MD3) or "2025" (M3 Expressive)。角色集一致, // 差异仅在调色板派生(库的 colorSpec 静态为 2025 委托) spec: "2025", }, // 默认页面背景模式:"banner" 使用壁纸横幅,"none" 使用主题纯色。 // 访客在“显示设置”中的选择会保存在浏览器中,并覆盖这里的默认值。 wallpaperMode: { defaultMode: "banner", }, // 页面背景纹理系统配置(5 大精美预设 + 零开销 HCT 动态取色) texture: { enable: true, // 是否启用背景纹理系统 defaultPreset: "starlight", // 默认纹理预设:"none" | "starlight" | "cyber-dots" | "topography" | "geometric" | "sakura" defaultOpacity: 0.12, // 默认纹理浓度 (0.05 ~ 0.25) allowMotion: true, // 是否允许背景微动效(开启 reduced-motion 时自动静止) }, banner: { // 推荐将图片放入 src/assets,并填写相对 src 的路径,以启用构建期 AVIF/WebP 响应式优化。 // 以 "/" 开头的 public 路径与远程 URL 仍可用,但会保留原图、不生成候选。 // desktop 用于 >= 1024px;mobile 仅用于 < 1024px 的首页,手机非首页不显示壁纸。 // 数组顺序就是轮播顺序;只需要静态 Banner 时,每组保留一张图片即可。 src: { desktop: ["assets/images/banner/desktop/1.webp"], mobile: ["assets/images/banner/mobile/1.webp"], }, // 图片裁切焦点:"top"、"center" 或 "bottom"。 position: "center", dim: { // 在图片上覆盖黑色遮罩以提高标题和顶部栏的对比度;opacity 范围为 0-1。 enable: true, opacity: 0.24, }, homeText: { // 仅在首页 Banner 中显示,标题与副标题会上下居中排列。 enable: true, title: "Huixuanbiao", subtitle: [ "特別なことはないけど、君がいると十分です", "今でもあなたは私の光", "君ってさ、知らないうちに私の毎日になってたよ", "君と話すと、なんか毎日がちょっと楽しくなるんだ", "今日はなんでもない日。でも、ちょっとだけいい日", ], typewriter: { // 副标题逐字显示;关闭后直接显示完整副标题。 enable: true, // 打字速度(每个字符间隔,毫秒)。 speed: 100, // 回退反向删除速度(每个字符间隔,毫秒)。 deleteSpeed: 50, // 打字完成后停顿时间,单位为毫秒。 pauseTime: 2000, // 完成后是否循环播放;关闭表示只播放一次。 loop: true, }, }, carousel: { // 是否开启多张图片自动轮播;多张图片时生效,单张图片时自动降级为静态展示。 enable: true, // 轮播切换间隔时间(毫秒),运行时最小值限制为 3000ms。 interval: 6000, // 交叉淡入淡出(Crossfade)过渡时长(毫秒,默认 1200ms)。 fadeDuration: 1200, // 运镜呼吸动画模式:"ken-burns"(默认,循环运镜)| "zoom-in"(推进)| "zoom-out"(拉远)| "pan-left"(左移)| "pan-right"(右移)| "none"(无运镜)。 animation: "ken-burns", }, waves: { // 在 Banner 底部渲染页面背景色水波纹;关闭后不输出波浪 DOM。 enable: true, }, }, // Markdown 正文图片处理;仅匹配远程图片,不会产生额外网络请求或客户端代码。 imageOptimization: { // 为需要防盗链兼容的图片 CDN 添加 referrerpolicy="no-referrer",支持通配符。 noReferrerDomains: ["*.hdslb.com"], }, toc: { enable: true, // Display the table of contents on the right side of the post depth: 2, // Maximum heading depth to show in the table, from 1 to 3 }, progressIndicator: { // 进度条预设样式:dual 双向扫描(官方默认双线)/ single 单向扫描(单线) style: "dual", }, favicon: [ // 浏览器标签页图标,路径相对于 public 目录。 { src: "/logo/icon.webp" }, ],});
/** * 解析并返回背景纹理配置选项(包含关闭短路与 0 开销优化判定) */export function resolveTextureOptions( config: boolean | TextureConfig | undefined = siteConfig.texture, displaySettingsTexture: boolean = siteConfig.displaySettings?.texture ?? true,): ResolvedTextureOptions { if (config === false || config === undefined) { return { enable: false, defaultPreset: "none", defaultOpacity: 0.12, allowMotion: false, }; }
if (config === true) { return { enable: true, defaultPreset: "starlight", defaultOpacity: 0.12, allowMotion: true, }; }
const enable = config.enable ?? true; const defaultPreset = config.defaultPreset ?? "starlight"; const defaultOpacity = config.defaultOpacity ?? 0.12; const allowMotion = config.allowMotion ?? true;
// 性能短路优化: // 如果配置 enable: false,或者 defaultPreset: "none" 且显示设置面板未允许切换(访客也无法开启), // 则自动视为完全关闭以达成零 DOM、零 CSS、零运行时代价。 const effectiveEnable = enable && (defaultPreset !== "none" || displaySettingsTexture);
return { enable: effectiveEnable, defaultPreset, defaultOpacity, allowMotion, };}
/** 站点默认配色风格(访客未做选择时的回退值) */export function getDefaultStyle(): string { return siteConfig.themeColor.style;}
/** 站点默认 Color Spec(2021 / 2025) */export function getDefaultSpec(): string { return siteConfig.themeColor.spec;}
/** 解析并返回显示设置面板各项开关(未配置时默认 true) */export function resolveDisplaySettings(): { colorStyle: boolean; colorSpec: boolean; wallpaperMode: boolean; layoutMode: boolean; reduceMotion: boolean; texture: boolean;} { const cfg = siteConfig.displaySettings; const textureOpts = resolveTextureOptions( siteConfig.texture, cfg?.texture ?? true, ); return { colorStyle: cfg?.colorStyle ?? true, colorSpec: cfg?.colorSpec ?? true, wallpaperMode: cfg?.wallpaperMode ?? true, layoutMode: cfg?.layoutMode ?? true, reduceMotion: cfg?.reduceMotion ?? true, texture: textureOpts.enable && (cfg?.texture ?? true), };}markdown-extended.md
GitHub Repository Cards
You can add dynamic cards that link to GitHub repositories, on page load, the repository information is pulled from the GitHub API.
Create a GitHub repository card with the code ::github{repo="<owner>/<repo>"}.
::github{repo="saicaca/fuwari"}Mermaid Diagrams
Fenced mermaid blocks are rendered as diagrams and follow the active color scheme.
flowchart LR
accTitle: Markdown rendering pipeline
accDescr: Markdown source is transformed into semantic HTML and then enhanced as a themed SVG diagram.
A[Markdown source] --> B[Astro content pipeline]
B --> C[Semantic HTML]
C --> D[Themed diagram]Admonitions
Following types of admonitions are supported: note tip important warning caution
Basic Syntax
:::noteHighlights information that users should take into account, even when skimming.:::
:::tipOptional information to help a user be more successful.:::Custom Titles
The title of the admonition can be customized.
:::note[MY CUSTOM TITLE]This is a note with a custom title.:::GitHub Syntax
> [!NOTE]> The GitHub syntax is also supported.
> [!TIP]> The GitHub syntax is also supported.Spoiler
You can add spoilers to your text. The text also supports Markdown syntax.
The content !
The content :spoiler[is hidden **ayyy**]!Image Widths and Captions
A standalone image accepts an optional w-N% width token in its alt text and a Markdown title rendered as a centered caption below the image:

Valid widths range from w-1% to w-100%; invalid tokens stay in the alt text. The width and the caption are independent — a title alone also produces a caption:


markdown-fields.md
Use field-group when several related options belong to the same API or component. Put the field name on the opening line, then add metadata tags before the description.
TeX parser options.
'svg'Output format, SVG or generic HTML.
Basic Fields
Required, optional, and deprecated statuses can be mixed in one group. Default values are kept separate from the type so they remain easy to scan.
The visible title of the component. This value is shown in the page heading and should be short enough to scan quickly.
falseWhether the control starts in a disabled state.
'en'Locale used for formatting dates, numbers, and accessible labels.
Rich Descriptions
Descriptions are ordinary Markdown. Links, emphasis, lists, and inline code remain available after the metadata lines.
Render a value into the final output. The callback should return a safe string and may use the formatValue helper.
- Keep rendering deterministic.
- Avoid network requests inside the callback.
3Maximum number of attempts before the request is reported as failed.
Kept for backwards compatibility. New integrations should use compatibility instead.
Standalone Field
A single field can be used without a group when documenting one option next to an example or code block.
'short'Controls how the result is formatted.
Authoring Notes
@typeand@defaultvalues are rendered as code tokens.@required,@optional, and@deprecatedadd a status badge.- Any normal Markdown after the metadata becomes the field description.
- Unknown
@tagsremain visible as description text instead of being discarded.
markdown-abbreviations.md
Abbreviations keep technical writing compact while preserving the full term for readers who need it. A defined term renders as a native abbr element with its meaning available on hover and to assistive technology.
In context
SSR-first output keeps the initial document visible before JavaScript runs. When measuring its reading experience, LCP and CLS reveal whether the first visible content is fast and stable.
An abbreviation can also appear next to ordinary Markdown such as SSR guidance, but literal code such as SSR and links like LCP documentation remain untouched.
Define terms
Place definitions anywhere in the same Markdown document. They do not render as visible paragraphs, and only matching terms in that article receive the semantic abbreviation treatment.
*[SSR]: Server-Side Rendering*[LCP]: Largest Contentful Paint*[CLS]: Cumulative Layout Shift
SSR makes an HTML response available before client code runs.Authoring boundaries
Terms must begin with a letter or number and may contain letters, numbers, periods, underscores, plus signs, and hyphens. Each definition applies to the current article only; an invalid or duplicate definition remains ordinary Markdown instead of silently replacing another term.
Markdown File Includes
Shirone can include a local Markdown file or a safe slice of one.
export function greet(name: string) { return `Hello, ${name}`;}The full file and line-range forms are also supported:
<!-- @include: src/content/snippets/include-example.md{1-4} --><!-- @include: src/content/snippets/include-example.md{5-} --><!-- @include: src/content/snippets/include-example.md{-4} -->Include comments inside fenced code remain literal.
markdown-mermaid.md
Mermaid turns text descriptions in Markdown into diagrams. The examples below use Shirone’s content workflow to demonstrate diagram types commonly used in technical articles and project notes.
Flowchart
Flowcharts describe a process, including decisions and paths that return to an earlier step.
flowchart TD
accTitle: Article publishing workflow
accDescr: An article moves through writing, validation, preview, and build before publication. Failed validation returns it for revision.
Draft[Write Markdown] --> Check{Validation passed?}
Check -->|No| Revise[Revise article]
Revise --> Check
Check -->|Yes| Preview[Preview locally]
Preview --> Build[Build static page]
Build --> Publish[Publish]Sequence Diagram
Sequence diagrams present collaboration between participants in chronological order. This example follows a Swup navigation from request to Mermaid rendering.
sequenceDiagram
accTitle: Diagram rendering after in-site navigation
accDescr: A reader starts navigation, Swup replaces the article content, and the Mermaid renderer enhances diagrams on the new page.
actor Reader
participant Browser
participant Swup
participant Content as Article region
participant Renderer as Mermaid renderer
Reader->>Browser: Open another article
Browser->>Swup: Start in-site navigation
Swup->>Content: Replace page content
Swup-->>Renderer: Emit content:replace
Renderer->>Content: Find Mermaid containers
Renderer-->>Browser: Insert themed SVGsEntity Relationship Diagram
Entity relationship diagrams model structured data and the connections between authors, posts, tags, and comments.
erDiagram
accTitle: Blog content relationships
accDescr: Authors write posts, posts receive comments, and join records connect posts to multiple tags.
AUTHOR ||--o{ POST : writes
POST ||--o{ COMMENT : receives
POST ||--o{ POST_TAG : classified_by
TAG ||--o{ POST_TAG : groups
AUTHOR {
string id PK
string display_name
}
POST {
string slug PK
string title
datetime published_at
string author_id FK
}
COMMENT {
string id PK
string post_slug FK
string body
}
TAG {
string id PK
string label
}
POST_TAG {
string post_slug FK
string tag_id FK
}Class Diagram
Class diagrams communicate responsibilities, public methods, and dependency directions in a software design.
classDiagram
accTitle: Markdown rendering modules
accDescr: The content pipeline uses a Mermaid plugin to create fallback markup, which the client renderer later enhances into an SVG.
class ContentPipeline {
+render(markdown)
+collectMetadata()
}
class MermaidPlugin {
+transform(codeFence)
+createFallback()
}
class DiagramRenderer {
+initialize()
+renderAll()
+refreshTheme()
}
class ThemeTokens {
+primary
+surface
+outline
}
ContentPipeline --> MermaidPlugin : uses
DiagramRenderer --> MermaidPlugin : enhances output
DiagramRenderer --> ThemeTokens : readsState Diagram
State diagrams show the lifecycle of an object and the events that move it between states.
stateDiagram-v2
accTitle: Article lifecycle
accDescr: An article moves from draft to review and publication. It may return for revision or eventually be archived.
[*] --> Draft
Draft --> InReview : submit
InReview --> Draft : request changes
InReview --> Published : approve
Published --> Draft : retract
Published --> Archived : archive
Archived --> [*]XY Chart
XY charts combine bars and lines to compare values and trends over a shared axis.
xychart-beta
accTitle: Six weeks of content performance
accDescr: Bars show normalized weekly publishing volume, while the line shows normalized reading completion.
title "Six weeks of content performance"
x-axis "Week" [1, 2, 3, 4, 5, 6]
y-axis "Relative score" 0 --> 100
bar [36, 52, 44, 68, 76, 84]
line [48, 55, 62, 61, 73, 81]Pie Chart
Pie charts provide a compact comparison of how categories contribute to a whole.
pie showData
accTitle: Article topics by share
accDescr: Engineering accounts for forty percent, design systems for twenty-five percent, and the remainder is split between guides and essays.
title Article topics by share
"Engineering" : 40
"Design systems" : 25
"Guides" : 20
"Essays" : 15Gantt Chart
Gantt charts arrange tasks, dependencies, and milestones along a calendar timeline.
gantt
accTitle: Theme release plan
accDescr: The release plan moves from requirements and interaction design through component development, testing, and release.
title Theme release plan
dateFormat YYYY-MM-DD
axisFormat %m/%d
section Design
Confirm requirements :done, brief, 2024-05-06, 2d
Refine interactions :done, interaction, after brief, 3d
section Implementation
Develop components :active, components, after interaction, 6d
Write examples :examples, after interaction, 4d
section Validation
Automated tests :tests, after components, 3d
Release :milestone, release, after tests, 0dMind Map
Mind maps expand a central topic into related areas and supporting concepts.
mindmap
root((Shirone))
Content experience
Markdown
Search
Diagrams
Interface system
M3E tokens
Responsive layout
Color schemes
Engineering quality
Astro Check
Playwright
AccessibilityTimeline
Timelines summarize significant events or phases without requiring exact calendar durations.
timeline
title Mermaid support evolution
Pipeline design : Detect Mermaid fences
: Preserve source fallback
Client enhancement : Load the runtime on demand
: Apply theme tokens
Reliability : Support Swup navigation
: Verify responsive and accessible outputUser Journey
User journey diagrams combine actions, participants, and experience scores across the stages of a task.
journey
accTitle: A reader understanding a technical article
accDescr: The reader discovers an article, combines prose with diagrams to understand it, and then explores related topics.
title A reader understanding a technical article
section Discover
Browse the article list: 4: Reader
Choose a topic: 5: Reader
section Understand
Read the article: 4: Reader
Inspect a relationship diagram: 5: Reader
section Continue
Open a related article: 4: Reader
Bookmark the page: 3: ReaderGit Graph
Git graphs show how work progresses on a feature branch before it merges into the main line.
gitGraph
accTitle: Mermaid feature branch history
accDescr: A feature branch adds the renderer and tests before merging into the main branch for release.
commit id: "base"
branch mermaid
checkout mermaid
commit id: "add-renderer"
commit id: "add-tests"
checkout main
merge mermaid id: "merge-mermaid"
commit id: "release"Kanban Board
Kanban boards group tasks by workflow state to make current progress easy to scan.
kanban
backlog[Backlog]
docs[Write author documentation]
examples[Expand example data]
active[In progress]
themes[Verify theme adaptation]
complete[Complete]
fallback[Source fallback]
rendering[Client rendering]Sankey Diagram
Sankey diagrams use link width to show how traffic or another quantity flows between nodes.
sankey-beta Landing,Reading,720 Discovery,Reading,430 Reading,Explore,360 Reading,Topics,210 Reading,Outbound,140
Each example uses a standard mermaid code fence. The server preserves readable source markup, and the browser enhances it into an SVG that follows the active theme. Diagrams render again when the theme changes or when Swup navigates to this article.
admonitions.md
Admonitions keep supporting information visually distinct while preserving the article’s reading flow. Every form is rendered on the server and uses the same compact M3E component.
Semantic variants
Optional details
Inspect the complete command
The disclosure uses native browser semantics and remains keyboard accessible without client JavaScript.
npx.cmd astro checkpnpm.cmd build- It starts closed.
- Long code can scroll inside its own code block.
- The container remains within the article width on narrow screens.
Author syntax
:::note[Existing title syntax]Content:::
::: warning Plume-compatible title syntaxContent:::
> [!TIP]> GitHub Alert syntax
::: details Optional contentHidden until the reader opens it.:::collapse-panels.md
Collapse panels keep related optional details in one compact group. Titles and bodies retain inline and block Markdown, while native disclosure semantics make every panel usable without client JavaScript.
Independent panels
Items open independently by default. Prefix a title with :+ to open that item initially or :- to keep it closed when the group uses expand.
Package requirements
Use Node.js 22 or newer and enable Corepack before installing packages.
Install dependencies
Run the workspace package command from the repository root.
pnpm.cmd installValidation commands
Check the content pipeline before building the production output.
pnpm.cmd check:manifestnpx.cmd astro check
Accordion mode
Add accordion when only one answer should remain open. The browser groups the native disclosures directly, so opening another item closes the previous one without hydration.
What does expand do here?
It opens the first item initially when no item has a :+ marker.
Can a title contain Markdown?
Yes. Titles support inline emphasis and code, while panel bodies support full block Markdown.
What happens on a narrow screen?
Content padding becomes compact, long text wraps, and embedded code keeps its own horizontal scrolling area.
Author syntax
::: collapse accordion- :+ First title
First panel content.
- Second title with `code`
Second panel content.:::The container must contain exactly one top-level unordered list. Every item needs a title paragraph, a blank line, and body content. Invalid or mixed input remains an ordinary readable Markdown list.
spoilers.md
Spoilers conceal a short answer or plot detail without removing it from the document. Hover, focus, or activate the native control to reveal the content.
Inline details
The answer is , and this sentence remains ordinary Markdown around it.
Spoilers can include inline code and .
Author syntax
The answer is :spoiler[42].The generated HTML uses a native button with an aria-expanded state. Without JavaScript, hover and focus still reveal the text; the optional runtime adds click and keyboard toggling.
marker-highlights.md
Marker highlights bring attention to a specific phrase without turning the surrounding paragraph into a separate component. They render as native <mark> elements during the build and inherit the active M3E color system.
Default emphasis
Use ==text== when the article’s primary color should carry the emphasis. This is useful for one decision that readers should retain while they continue through an ordinary paragraph.
The marker may contain nested Markdown emphasis when the phrase needs a stronger hierarchy.
Semantic colors
Use a suffix when the meaning needs a different tonal role. The available variants are primary, secondary, tertiary, error, and tip.
- Primary connects the phrase to the active theme
- Secondary keeps a supporting distinction quiet
- Tertiary adds a separate editorial signal
- Error identifies a condition that needs correction
- Tip highlights practical guidance
Author syntax
==Primary marker==
==Secondary marker=={.secondary}==Tertiary marker=={.tertiary}==Error marker=={.error}==Tip marker=={.tip}Inline code such as ==literal marker syntax== and fenced examples stay literal, so documentation can explain the syntax without triggering it.
steps.md
Use Steps for procedures whose order matters. The component keeps the article reading flow intact: a quiet numbered rail provides orientation while headings, paragraphs, links, lists, and code retain their native Markdown roles.
Ordered list syntax
Wrap one Markdown ordered list in a :::steps container. Each top-level list item becomes one step.
:::steps[Production deployment]1. **Clone and prepare the workspace**
Clone the repository and enter the project directory.
```powershell git clone https://github.com/LyraVoid/Shirone.git Set-Location Shirone ```
2. **Install dependencies**
Use the repository's pinned package manager.
```powershell pnpm.cmd install ```
3. **Run project checks**
Confirm Astro diagnostics and TypeScript checks pass.
```powershell npx.cmd astro check pnpm.cmd type-check ```
4. **Build the production site**
Generate the static site and search index.
```powershell pnpm.cmd build ```:::Production deployment
Clone and prepare the workspace
Clone the repository and enter the project directory.
Terminal window git clone https://github.com/LyraVoid/Shirone.gitSet-Location ShironeInstall dependencies
Use the repository’s pinned package manager.
Terminal window pnpm.cmd installRun project checks
Confirm Astro diagnostics and TypeScript checks pass.
Terminal window npx.cmd astro checkpnpm.cmd type-checkBuild the production site
Generate the static site and search index.
Terminal window pnpm.cmd build
Options
:::steps[Title]ortitle="Title"adds a visible label and accessible name.start=4changes the first displayed step number.- The container must contain exactly one ordered list. Invalid or mixed input remains ordinary readable Markdown instead of being interpreted heuristically.
- Rendering is completed during the site build and adds no client JavaScript or network requests.
option-groups.md
Option groups keep equivalent instructions together without repeating the surrounding explanation. Each option accepts full block Markdown, while the selected value can synchronize with another group on the same page.
Choose a package manager
Use @tab:active to select the initial option. A suffix after # supplies a stable value without changing the visible title.
Install the package with npm:
npm install astroInstall the package with pnpm:
pnpm.cmd add astroInstall the package with Bun:
bun add astroRun the project
This group shares the package-manager id. Selecting an option above updates the matching command below and remembers that choice for the next visit.
npm run devpnpm.cmd devbun run devMany alternatives
Longer option rows remain on one line and scroll within their own navigation area on narrow screens.
Use the local toolchain while developing a feature.
Publish a temporary preview for review.
Run deterministic validation for every change.
Promote a verified artifact to production.
Restore from a local artifact when the network is unavailable.
Author syntax
::: tabs#package-manager
@tab npm
Use npm instructions here.
@tab:active **pnpm**#pnpm
Use pnpm instructions here.
:::Each group needs at least two @tab sections, and every section needs body content separated from its marker by a blank line. Invalid or incomplete groups remain readable as ordinary Markdown.
content-annotations.md
Content annotations keep supporting context close to a sentence without placing it directly in the reading flow. Activate the small note marker to reveal its content.
Basic syntax
Add a [+label] reference in ordinary prose, then define the matching note elsewhere in the same article.
Astro renders most of a page ahead of time and hydrates **interactive islands** [+islands] only when they need to become interactive.
[+islands]: An island is an interactive UI component surrounded by static HTML. This keeps the default page lightweight while preserving focused interactivity.Astro renders most of a page ahead of time and hydrates interactive islands only when they need to become interactive.
Rich content
Definitions may contain paragraphs, emphasis, links, lists, and inline code while the surrounding sentence continues normally.
Multiple definitions
Reuse a label to present a short sequence of related notes behind one marker.
Undefined references such as [+missing] remain ordinary text, so an unfinished definition never creates an empty control.
expressive-code.md
Here, we’ll explore how code blocks look using Expressive Code. The provided examples are based on the official documentation, which you can refer to for further details.
Expressive Code
Syntax Highlighting
Regular syntax highlighting
console.log('This code is syntax highlighted!')Rendering ANSI escape sequences
ANSI colors:- Regular: Red Green Yellow Blue Magenta Cyan- Bold: Red Green Yellow Blue Magenta Cyan- Dimmed: Red Green Yellow Blue Magenta Cyan
256 colors (showing colors 160-177):160 161 162 163 164 165166 167 168 169 170 171172 173 174 175 176 177
Full RGB colors:ForestGreen - RGB(34, 139, 34)
Text formatting: Bold Dimmed Italic UnderlineEditor & Terminal Frames
Code editor frames
console.log('Title attribute example')<div>File name comment example</div>Terminal frames
echo "This terminal frame has no title"Write-Output "This one has a title!"Overriding frame types
echo "Look ma, no frame!"# Without overriding, this would be a terminal framefunction Watch-Tail { Get-Content -Tail 20 -Wait $args }New-Alias tail Watch-TailText & Line Markers
Marking full lines & line ranges
// Line 1 - targeted by line number// Line 2// Line 3// Line 4 - targeted by line number// Line 5// Line 6// Line 7 - targeted by range "7-8"// Line 8 - targeted by range "7-8"Selecting line marker types (mark, ins, del)
function demo() { console.log('this line is marked as deleted') // This line and the next one are marked as inserted console.log('this is the second inserted line')
return 'this line uses the neutral default marker type'}Adding labels to line markers
<button role="button" {...props} value={value} className={buttonClassName} disabled={disabled} active={active}> {children && !active && (typeof children === 'string' ? <span>{children}</span> : children)}</button>Adding long labels on their own lines
<button role="button" {...props}
value={value} className={buttonClassName}
disabled={disabled} active={active}>
{children && !active && (typeof children === 'string' ? <span>{children}</span> : children)}</button>Using diff-like syntax
this line will be marked as insertedthis line will be marked as deletedthis is a regular line--- a/README.md+++ b/README.md@@ -1,3 +1,4 @@+this is an actual diff file-all contents will remain unmodified no whitespace will be removed eitherCombining syntax highlighting with diff-like syntax
function thisIsJavaScript() { // This entire block gets highlighted as JavaScript, // and we can still add diff markers to it! console.log('Old code to be removed') console.log('New and shiny code!')}Marking individual text inside lines
function demo() { // Mark any given text inside lines return 'Multiple matches of the given text are supported';}Regular expressions
console.log('The words yes and yep will be marked.')Escaping forward slashes
echo "Test" > /home/test.txtSelecting inline marker types (mark, ins, del)
function demo() { console.log('These are inserted and deleted marker types'); // The return statement uses the default marker type return true;}Word Wrap
Configuring word wrap per block
// Example with wrapfunction getLongString() { return 'This is a very long string that will most probably not fit into the available space unless the container is extremely wide'}// Example with wrap=falsefunction getLongString() { return 'This is a very long string that will most probably not fit into the available space unless the container is extremely wide'}Configuring indentation of wrapped lines
// Example with preserveIndent (enabled by default)function getLongString() { return 'This is a very long string that will most probably not fit into the available space unless the container is extremely wide'}// Example with preserveIndent=falsefunction getLongString() { return 'This is a very long string that will most probably not fit into the available space unless the container is extremely wide'}Collapsible Sections
5 collapsed lines
// All this boilerplate setup code will be collapsedimport { someBoilerplateEngine } from '@example/some-boilerplate'import { evenMoreBoilerplate } from '@example/even-more-boilerplate'
const engine = someBoilerplateEngine(evenMoreBoilerplate())
// This part of the code will be visible by defaultengine.doSomething(1, 2, 3, calcFn)
function calcFn() { // You can have multiple collapsed sections3 collapsed lines
const a = 1 const b = 2 const c = a + b
// This will remain visible console.log(`Calculation result: ${a} + ${b} = ${c}`) return c}
4 collapsed lines
// All this code until the end of the block will be collapsed againengine.closeConnection()engine.freeMemory()engine.shutdown({ reason: 'End of example boilerplate code' })Line Numbers
Displaying line numbers per block
// This code block will show line numbersconsole.log('Greetings from line 2!')console.log('I am on line 3')// Line numbers are disabled for this blockconsole.log('Hello?')console.log('Sorry, do you know what line I am on?')Changing the starting line number
console.log('Greetings from line 5!')console.log('I am on line 6')Audio Reader: Japanese Anime Mystery Voices
These short Japanese voice fragments feel as though they were picked up from the edge of an anime scene: a teasing call, a bright greeting, a tiny laugh, and a few lines with no clear origin. They are mood samples rather than dialogue transcripts, so let the sound carry the meaning.
Audio Reader keeps them quiet until you choose to listen. Each speaker button loads and plays its clip only after it is pressed.
:audio-reader[Clip title]{src="/assets/audio/filename.wav"}The fragments
- Baka: バカ
- Ciallo: Ciallo!!
- Ehe: A joking sense
- Imoi: イモい
- Zako: 雑魚じゃん、雑魚雑魚
src must use a site-root path or an HTTPS URL, and the directive label cannot be empty. Invalid or incomplete directives remain ordinary Markdown and do not load Audio Reader resources.
video.md
Just copy the embed code from YouTube or other platforms, and paste it in the markdown file.
---title: Include Video in the Postpublished: 2023-10-19// ...---
<iframe width="100%" height="468" src="https://www.youtube.com/embed/5gIf0_xpFPI?si=N1WTorLKL0uwLsU_" title="YouTube video player" frameborder="0" allowfullscreen></iframe>YouTube
Bilibili
AcFun
ArtPlayer
Image Gallery Grid: Syntax and Complete Examples
:::grid is the blog’s image gallery container directive. It arranges ordinary Markdown images in a responsive grid with a consistent aspect ratio and automatically enables lightbox viewing. Use it for article images, screenshots, portfolios, or small albums.
Images in the same gallery use the same card ratio. By default, center cropping fills every card and keeps each row tidy; clicking an image opens the complete original in a lightbox. Every gallery has its own lightbox group and does not mix with other images in the post.
This post is both feature documentation and a visual test page. View the examples at desktop, tablet, and mobile widths, then click any image to verify lightbox grouping.
Minimal Syntax
Write Markdown images directly between :::grid and the closing ::::
:::grid
:::Each image must occupy its own paragraph, with a blank line between images. Keep only images in a gallery; write paragraphs, lists, and code blocks outside the container.
Here is the result of the minimal syntax. Without parameters, the grid uses three columns, a 16/10 ratio, and cover by default.
Parameters at a Glance
Write all parameters in braces after the opening directive: :::grid{parameter="value"}.
| Parameter | Allowed values | Default | Purpose |
|---|---|---|---|
columns | Integers from 1 to 6 | 3 | Number of columns per row on desktop. Invalid values fall back to 3. |
aspect | A positive ratio, such as 16/9, 3/4, or 1/1 | 16/10 | The displayed card ratio, not the original image ratio. |
fit | cover, contain | cover | Image fitting mode. cover crops to fill; contain preserves the complete image and may leave empty space. |
Complete example:
:::grid{columns="3" aspect="16/9" fit="cover"}

:::The following result uses the three-column landscape syntax above. Compare the card ratio, column count, and the way a title takes precedence over alt text as the caption:
Captions and Alt Text
An image’s alt text serves both as accessible alternative text and as its default caption. When an image has an optional title, the title is used as the caption instead:
In the same row, captions align to the bottom of every card. A wrapping caption does not make the others float at a different height. Ratio text such as 3:4 and 16:9 can be written directly in body text, headings, and alt text without escaping.
This example demonstrates the default alt-text caption, an explicit title caption, and bottom alignment for a longer caption:
Layout and Cropping
Desktop layouts use the number of columns specified by columns. Below 768px, grids use at most two columns; below 480px, they switch to one column. The card wrapper fixes the aspect ratio and clips rounded corners, while the image fills the card without the theme’s default image margins.
- Choose
cover: the recommended default. Images are cropped from the center to fill the card, making the gallery look consistent. - Choose
contain: the full original image is shown without cropping. When its ratio differs from the card, the theme background remains visible; use this for images that cannot be cropped. - To preserve the complete image without empty space, set
aspectclose to the original image ratio or place the image in a grid of its own.
The following examples place the same portrait images in 16/9 cards with cover and contain. The first crops them; the second preserves the full image and leaves background space.
:::grid{columns="3" aspect="16/9" fit="cover"}
:::
:::grid{columns="3" aspect="16/9" fit="contain"}
:::Default Configuration
Without attributes, the default is three columns, a 16/10 ratio, and cover cropping. These three portrait images verify default cropping and captions.
:::grid

:::Three-Column Portraits: 3<4>4>
With aspect="3/4", the three portrait images fill consistently proportioned vertical cards. If an original image has a different ratio, cover crops its edges from the center.
:::grid{columns="3" aspect="3/4"}

:::Three-Column Landscapes: 16<9>9>
This set demonstrates a common video-cover ratio in a three-column layout. Cropping is minimal when the landscape images are close to the card ratio.
:::grid{columns="3" aspect="16/9"}

:::Two-Column Squares: 1<1>1>
Two columns work well when larger preview cards are needed. The third image moves to the next row. The final row keeps its grid-track width instead of stretching images to fill the row.
:::grid{columns="2" aspect="1/1"}

:::Four Columns with contain
fit="contain" does not crop the original image. When the image ratio differs from the card ratio, the theme background remains visible. This is intentional, not a layout issue. It also verifies that four-column grids and separate lightbox groups do not interfere with each other.
:::grid{columns="4" aspect="16/9" fit="contain"}

:::Single-Column Detail Image
One column is suitable when an image needs a larger reading size. It remains one column on desktop, tablet, and mobile, and the original is still available in the lightbox.
:::grid{columns="1" aspect="16/9"}:::Sparse Five-Column Row
Five columns verify a higher supported column count. With only three images, the final row remains left-aligned instead of stretching the images.
:::grid{columns="5" aspect="1/1"}

:::Mixed Images in Six Columns
Six columns are the current maximum. Mixing landscape and portrait images verifies cover cropping, captions on narrow cards, and a dense desktop layout. For readable article content, two to four columns are usually preferable.
:::grid{columns="6" aspect="1/1"}




:::Four-Column Squares: 1<1>1>
Four square images with the same ratio are a typical four-column layout. Desktop displays all four in one row; tablet collapses to two columns and mobile to one.
:::grid{columns="4" aspect="1/1"}


:::Six-Column Landscapes: 16<9>9>
Six landscape columns work well for thumbnail previews, portfolios, and screenshot indexes. Even if original ratios differ slightly, cover fills every 16/9 card consistently.
:::grid{columns="6" aspect="16/9"}




:::Three-Column Portraits: 3<4>4>
This group of six portrait images demonstrates a common layout for people, posters, or mobile screenshots. The images form two rows of three, with captions aligned to the bottom.
:::grid{columns="3" aspect="3/4"}




:::Edge-Critical Content: cover and Lightbox
These images contain important text or details near their edges. cover keeps the grid tidy but may crop those edges; click an image to view the uncropped original in the lightbox. Use clear captions for edge-sensitive images, or use contain below.
:::grid{columns="3" aspect="16/9" fit="cover"}

:::Extreme Ratios with contain
For banners, long screenshots, and other extreme image ratios, contain displays the complete original. Unlike cover, it may leave theme-background space, but it never crops content.
:::grid{columns="3" aspect="16/9" fit="contain"}

:::Transparent Images
Transparent images reveal the card’s theme background. This single-column contain example makes the transparent areas, original edges, and lightbox behavior easy to inspect.
:::grid{columns="1" aspect="16/9" fit="contain"}:::Lightbox Navigation
Click any image in a grid to open the Fancybox lightbox. There you can zoom, rotate, enter fullscreen, view thumbnails, and navigate with the arrow keys. Navigation is limited to the current :::grid container: for example, clicking “16<9>9> test image one” only opens the other two landscape images in that section.
Ordinary Markdown images in the same post continue to be handled separately; they are not added to any grid gallery.
Checklist
- Images in each grid have consistent dimensions, with captions below the cards.
- Images scale slightly on hover; after clicking, they can be zoomed, rotated, and navigated with the keyboard.
- Clicking “16<9>9> test image one” lets the lightbox browse only the other two landscape images in that section.
- Below 768px, grids use at most two columns; below 480px, they use one column.
- Portrait images in “Four Columns with
contain” are fully visible with empty space and no cropping. - Five- and six-column grids retain their specified column count on wide screens, then collapse to two or one column according to the responsive rules.
encrypted-demo.md
Password Protected Article
Congratulations! You have successfully unlocked this encrypted post. The browser decrypted this pre-compiled content locally using the Web Crypto API (AES-256-GCM + PBKDF2).
1. Security Architecture and Core Features
Shirone’s post encryption system shares a unified security foundation with protected albums, providing enterprise-grade security for static publishing:
-
Zero Plaintext in Static HTML Build
During the Astro SSG build pipeline, post Markdown is compiled to HTML and immediately encrypted with AES-256-GCM before emitting pages. The published static HTML contains zero plaintext of the protected body or outline. -
Authenticated Encryption with AAD Scope Binding
- Key derivation follows OWASP recommendations with 310,000 PBKDF2 iterations (SHA-256) and a cryptographically random 16-byte salt;
- Every encryption payload generates an independent 12-byte random IV;
- An Additional Authenticated Data (AAD) binding
shirone-protected-content:1:post:${slug}guarantees that ciphertexts cannot be replayed across different posts or albums.
-
Session Persistence and Zero Disk Password Storage
- Decrypted content is cached in ephemeral browser session storage with a 30-minute expiration;
- Plaintext passwords are never written to disk or storage;
- Decrypted states persist seamlessly across Swup client-side navigations and page reloads in the same session.
-
Full-Site Leakage Prevention
- Search Indexing: Static pages contain no plaintext, preventing search engines and Pagefind from indexing private content;
- RSS Feed: Protected articles emit localized placeholders in feeds, preventing RSS aggregators from pulling sensitive text;
- Card Summaries & Word Counts: When
hideHomeContent: trueis configured, descriptions and word counts are masked on index and archive cards; - Table of Contents (TOC): Heading hierarchies remain hidden until unlock, and are dynamically rebuilt and synchronized with M3E styling upon decryption.
💡 Demo Note: The default unlock password for this demo post is
shirone-secret.
2. Interactive Rich Content Demonstration
Post decryption coordinates with runtime helpers to dynamically mount syntax highlighting, code collapse, interactive Mermaid diagrams, LaTeX formulas, and image lightboxes.
2.1 Code Blocks and Syntax Highlighting
The code block below tests Expressive Code syntax highlighting, copy actions, and line decorations:
import { decryptProtectedContent, type ProtectedPayload } from "@/utils/password-protection";
/** * Client-side post decryption example */async function unlockArticle(payload: ProtectedPayload, password: string): Promise<string> { const scope = payload.scope; console.log(`[Shirone] Decrypting scope: ${scope}`);
// Execute AES-256-GCM decryption with AAD verification const decryptedHtml = await decryptProtectedContent(payload, password, scope); console.log("[Shirone] Decryption successful, length:", decryptedHtml.length); return decryptedHtml;}# Verify build and type checkingnpx.cmd astro checkpnpm.cmd type-checkpnpm.cmd test2.2 Mermaid Architecture Diagram
The flowchart below is rendered via Mermaid and dynamically bound upon decryption:
flowchart TD
subgraph build["Build Pipeline"]
A[Markdown Source] --> B[Astro Pipeline]
B --> C[Render HTML]
C --> D[encryptProtectedContent]
D --> E[ProtectedPayload]
E --> F[Static HTML with ProtectedPost]
end
subgraph runtime["Client Runtime"]
G[Reader Visits Page] --> H{Valid Session?}
H -->|Yes| K[Render Decrypted HTML]
H -->|No| I[Display PasswordGate]
I --> J[User Submits Password]
J --> L[Web Crypto AES-GCM Decrypt]
L -->|Incorrect| M[Show Error Feedback]
L -->|Success| N[Store Session Record]
N --> K
K --> O[Trigger post-decryption Coordinator]
O --> P[Initialize Copy, Mermaid, KaTeX, and TOC]
end2.3 LaTeX Mathematical Formulas
Inline formulas: Euler’s identity and Gaussian integral .
Display mathematical formulas with horizontal scroll containers:
2.4 Admonitions
2.5 GitHub Repository Card
3. Configuration Reference
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
encrypted | boolean | No | false | Explicitly marks the post as encrypted. Implicitly true if password is set. |
password | string | Yes | None | Plaintext password used to encrypt at build time and unlock at runtime. |
passwordHint | string | No | "" | Optional hint displayed under the password input field. |
hideHomeContent | boolean | No | true | Hides post description and word count metrics in index cards, archive, and RSS feeds. |
4. Summary
This demo verifies the entire encryption lifecycle in Shirone: zero plaintext in static output, robust cryptographic verification, session persistence across navigation and page reloads, and dynamic runtime rehydration.
Server-Side RenderingServer-Side RenderingLargest Contentful PaintCumulative Layout ShiftServer-Side RenderingServer-Side RenderingFootnotes
-
Footnote text goes here. ↩
分享文章
生成精美分享图或复制链接,与更多人分享本文。
最后更新于 ,距今已过 0 天
部分内容可能已过时































