23902 字
120 分钟
功能演示合集

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:

Terminal window
# Create a single-file post
pnpm new-post my-first-post
# Or create a post in a sub-directory
pnpm new-post guides/getting-started

The 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-26
updated: 2026-08-27
publishedAt: 2026-08-26T10:00:00+08:00
updatedAt: 2026-08-27T09:30:00+08:00
pinned: true
description: "A deep dive into dynamic HCT color science and fluid transitions in Shirone."
image: "./cover.webp"
tags: [M3E, Design, Frontend]
category: Guides
draft: false
comment: true
---

Supported Frontmatter Fields#

FieldTypeRequiredDescription
titlestringYesThe main title of the post.
publishedDateYesPublication date in YYYY-MM-DD format.
publishedAtDateNoPrecise publication instant used to order posts published on the same day. It must fall on published in the configured site time zone.
updatedDateNoLast updated date. When provided, an update notice badge is displayed.
updatedAtDateNoPrecise update instant used by feeds and machine-readable metadata. It must be paired with updated.
pinnedbooleanNoPin the post to the top of article lists (default: false).
descriptionstringNoPost summary displayed in article cards, search results, and OpenGraph metadata.
imagestringNoCover image path. Supports relative (./cover.webp), public (/images/cover.jpg), or remote URLs.
tagsstring[]NoArray of tag names for taxonomy filtering and tag clouds.
categorystringNoPrimary category name for taxonomy indexing.
draftbooleanNoMark as draft. Draft posts are hidden during production build (pnpm build).
commentbooleanNoToggle comment section for this specific post (default: true).
langstringNoLanguage 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-26
encrypted: true
password: "your-secret-passphrase"
passwordHint: "Favorite anime character"
hideHomeContent: true
---
  • encrypted: Set to true to 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:

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 markdown

Single-File Structure (Lightweight Prose)#

src/content/posts/
├── hello-world.md
└── quick-thoughts.md

5. 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:

:::tip
Use admonition containers to highlight key takeaways or best practices.
:::
:::warning
Use 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"}
LyraVoidShirone

5.3 Expressive Code Blocks#

Enhanced code blocks feature syntax highlighting, file name badges, line numbers, and selective line highlighting:

src/utils/theme.ts
// Dynamic HCT color token derivation
import { 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: E=mc2E = mc^2 or Euler’s formula eiπ+1=0e^{i\pi} + 1 = 0.
  • Block math:
∫−∞∞e−x2 dx=π\int_{-\infty}^{\infty} e^{-x^2} \, dx = \sqrt{\pi}

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 Rendered

5.6 Image Galleries & Fancybox Lightbox#

Images automatically integrate with Fancybox for lossless zoom, pan gestures, and full-screen preview:

![Cover preview](./guide/cover.jpeg)

6. Next Steps & Customization#

  • Site Configuration: Learn about global settings in src/config/siteConfig.ts and src/config/README.md.
  • Design Tokens: Explore tokens and color palettes in DESIGN.md and docs/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:

  1. first item
  2. second item
  3. 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 i

An h3 header#

Now a nested list:

  1. First, get these ingredients:

    • carrots
    • celery
    • lentils
  2. Boil some water.

  3. Dump everything in the pot and follow this algorithm:

    find wooden spoon
    uncover pot
    stir
    cover pot
    balance wooden spoon precariously on pot handle
    wait 10 minutes
    goto 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: ω=dϕ/dt\omega = d\phi / dt. Display math should get its own line and be put in in double-dollarsigns:

I=∫ρR2dVI = \int \rho R^{2} dV

π=3.1415926535  8979323846  2643383279  5028841971  6939937510  5820974944  5923078164  0628620899  8628034825  3421170679  …\begin{equation*} \pi =3.1415926535 \;8979323846\;2643383279\;5028841971\;6939937510\;5820974944 \;5923078164\;0628620899\;8628034825\;3421170679\;\ldots \end{equation*}

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
:::
Shirone source tree
  • 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
```
Build output
  • 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"
}
```
:::
Shirone Component Demo
src/Button.svelte
<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 (default 420px, 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 :active on 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)
Site Configuration
siteConfig.ts
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.

FabrizzMMM-OnSpotify

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#

:::note
Highlights information that users should take into account, even when skimming.
:::
:::tip
Optional 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:

Album example image
Half-width image with a caption
![Image description w-50%](./image.webp "Visible caption")

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:

Album example image
Album example image
Caption without a width token

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可选object

TeX parser options.

output可选'svg' | 'chtml'
'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.

title必填string

The visible title of the component. This value is shown in the page heading and should be short enough to scan quickly.

disabled可选boolean
false

Whether the control starts in a disabled state.

locale可选'en' | 'zh-CN' | 'ja-JP'
'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必填(value: unknown) => string

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.
retries可选number
3

Maximum number of attempts before the request is reported as failed.

legacyMode已弃用boolean

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.

format可选'short' | 'long'
'short'

Controls how the result is formatted.

Authoring Notes#

  • @type and @default values are rendered as code tokens.
  • @required, @optional, and @deprecated add a status badge.
  • Any normal Markdown after the metadata becomes the field description.
  • Unknown @tags remain 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:

src/content/snippets/include-example.md
<!-- @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 SVGs

Entity 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 : reads

State 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" : 15

Gantt 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, 0d

Mind 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
      Accessibility

Timeline#

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 output

User 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: Reader

Git 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.

Terminal window
npx.cmd astro check
pnpm.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 syntax
Content
:::
> [!TIP]
> GitHub Alert syntax
::: details Optional content
Hidden 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.

Terminal window
pnpm.cmd install
Validation commands

Check the content pipeline before building the production output.

  • pnpm.cmd check:manifest
  • npx.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

  1. Clone and prepare the workspace

    Clone the repository and enter the project directory.

    Terminal window
    git clone https://github.com/LyraVoid/Shirone.git
    Set-Location Shirone
  2. Install dependencies

    Use the repository’s pinned package manager.

    Terminal window
    pnpm.cmd install
  3. Run project checks

    Confirm Astro diagnostics and TypeScript checks pass.

    Terminal window
    npx.cmd astro check
    pnpm.cmd type-check
  4. Build the production site

    Generate the static site and search index.

    Terminal window
    pnpm.cmd build

Options#

  • :::steps[Title] or title="Title" adds a visible label and accessible name.
  • start=4 changes 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.

npm

Install the package with npm:

Terminal window
npm install astro
pnpm

Install the package with pnpm:

Terminal window
pnpm.cmd add astro
Bun

Install the package with Bun:

Terminal window
bun add astro

Run 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
Terminal window
npm run dev
pnpm
Terminal window
pnpm.cmd dev
Bun
Terminal window
bun run dev

Many alternatives#

Longer option rows remain on one line and scroll within their own navigation area on narrow screens.

Local workstation

Use the local toolchain while developing a feature.

Hosted preview environment

Publish a temporary preview for review.

Continuous integration

Run deterministic validation for every change.

Production deployment

Promote a verified artifact to production.

Offline recovery workflow

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#

Syntax Highlighting

Regular syntax highlighting#

console.log('This code is syntax highlighted!')

Rendering ANSI escape sequences#

Terminal window
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 165
166 167 168 169 170 171
172 173 174 175 176 177
Full RGB colors:
ForestGreen - RGB(34, 139, 34)
Text formatting: Bold Dimmed Italic Underline

Editor & Terminal Frames#

Editor & Terminal Frames

Code editor frames#

my-test-file.js
console.log('Title attribute example')

src/content/index.html
<div>File name comment example</div>

Terminal frames#

Terminal window
echo "This terminal frame has no title"

PowerShell terminal example
Write-Output "This one has a title!"

Overriding frame types#

echo "Look ma, no frame!"

PowerShell Profile.ps1
# Without overriding, this would be a terminal frame
function Watch-Tail { Get-Content -Tail 20 -Wait $args }
New-Alias tail Watch-Tail

Text & Line Markers#

Text & 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)#

line-markers.js
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#

labeled-line-markers.jsx
<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#

labeled-line-markers.jsx
<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 inserted
this line will be marked as deleted
this 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 either

Combining 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#

Terminal window
echo "Test" > /home/test.txt

Selecting 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#

Word Wrap

Configuring word wrap per block#

// Example with wrap
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 wrap=false
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'
}

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=false
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'
}

Collapsible Sections#

Collapsible Sections

5 collapsed lines
// All this boilerplate setup code will be collapsed
import { 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 default
engine.doSomething(1, 2, 3, calcFn)
function calcFn() {
// You can have multiple collapsed sections
3 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 again
engine.closeConnection()
engine.freeMemory()
engine.shutdown({ reason: 'End of example boilerplate code' })

Line Numbers#

Line Numbers

Displaying line numbers per block#

// This code block will show line numbers
console.log('Greetings from line 2!')
console.log('I am on line 3')

// Line numbers are disabled for this block
console.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 Post
published: 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#

YouTube videoYouTube

Bilibili#

Bilibili videoBilibili

AcFun#

AcFun videoAcFun

ArtPlayer#

Sintel trailerhttps://www.pexels.com/download/video/38538991/

:::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
![Image description](./image-grid-demo/./image-1.webp)
![Image description](./image-grid-demo/./image-2.webp)
:::

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.

Minimal syntax result: first image
Minimal syntax result: first image
Minimal syntax result: second image
Minimal syntax result: second image

Parameters at a Glance#

Write all parameters in braces after the opening directive: :::grid{parameter="value"}.

ParameterAllowed valuesDefaultPurpose
columnsIntegers from 1 to 63Number of columns per row on desktop. Invalid values fall back to 3.
aspectA positive ratio, such as 16/9, 3/4, or 1/116/10The displayed card ratio, not the original image ratio.
fitcover, containcoverImage 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"}
![First image](./image-grid-demo/./image-1.webp "Optional caption")
![Second image](./image-grid-demo/./image-2.webp "Optional caption")
![Third image](./image-grid-demo/./image-3.webp "Optional caption")
:::

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:

Parameter example: first landscape image
Landscape caption 1
Parameter example: second landscape image
Landscape caption 2
Parameter example: third landscape image
Landscape caption 3

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:

![Text used for accessibility](./image-grid-demo/./image.webp "Caption shown below the image")

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:

This image has no title, so its alt text is the caption
This image has no title, so its alt text is the caption
Second square image with accessible alt text
This title is displayed as the caption
Accessible description of a 3 poster
This is a longer caption for checking that every caption remains aligned to the bottom of its card when it wraps

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 aspect close 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"}
![Image description](./image-grid-demo/./image-1.webp "Optional caption")
![Image description](./image-grid-demo/./image-2.webp "Optional caption")
:::
:::grid{columns="3" aspect="16/9" fit="contain"}
![Image description](./image-grid-demo/./image-1.webp "Optional caption")
![Image description](./image-grid-demo/./image-2.webp "Optional caption")
:::
First cover result
Cover: center crop
Second cover result
Cover: fill the card
Third cover result
Cover: a more consistent layout
First contain result
Contain: preserve the complete original
Second contain result
Contain: empty space may appear
Third contain result
Contain: suitable for edge details

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
![Image description](./image-grid-demo/./image-1.webp)
![Image description](./image-grid-demo/./image-2.webp)
![Image description](./image-grid-demo/./image-3.webp)
:::
Default configuration: portrait image one
Default configuration: portrait image one
Default configuration: portrait image two
Default configuration: portrait image two
Default configuration: portrait image three
Default configuration: portrait image three

Three-Column Portraits: 3<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"}
![Portrait image description](./image-grid-demo/./portrait-1.webp)
![Portrait image description](./image-grid-demo/./portrait-2.webp)
![Portrait image description](./image-grid-demo/./portrait-3.webp)
:::
3 test image one
Portrait 1
3 test image two
Portrait 2
3 test image three
Portrait 3

Three-Column Landscapes: 16<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"}
![Landscape image description](./image-grid-demo/./landscape-1.webp)
![Landscape image description](./image-grid-demo/./landscape-2.webp)
![Landscape image description](./image-grid-demo/./landscape-3.webp)
:::
16 test image one
16 test image one
16 test image two
16 test image two
16 test image three
16 test image three

Two-Column Squares: 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"}
![Square image description](./image-grid-demo/./square-1.webp)
![Square image description](./image-grid-demo/./square-2.webp)
![Square image description](./image-grid-demo/./square-3.webp)
:::
1 test image one
1 test image one
1 test image two
1 test image two
1 test image three
1 test image three

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"}
![Image description](./image-grid-demo/./image-1.webp)
![Image description](./image-grid-demo/./image-2.webp)
![Image description](./image-grid-demo/./image-3.webp)
:::
Contain: portrait image one
Contain: portrait image one
Contain: portrait image two
Contain: portrait image two
Contain: portrait image three
Contain: portrait image three

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"}
![Image description](./image-grid-demo/./detail.webp)
:::
Single-column test image
Single-column test image

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"}
![Thumbnail description](./image-grid-demo/./thumb-1.webp)
![Thumbnail description](./image-grid-demo/./thumb-2.webp)
![Thumbnail description](./image-grid-demo/./thumb-3.webp)
:::
Five-column test image one
Five-column test image one
Five-column test image two
Five-column test image two
Five-column test image three
Five-column test image three

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"}
![Image description](./image-grid-demo/./image-1.webp)
![Image description](./image-grid-demo/./image-2.webp)
![Image description](./image-grid-demo/./image-3.webp)
![Image description](./image-grid-demo/./image-4.webp)
![Image description](./image-grid-demo/./image-5.webp)
![Image description](./image-grid-demo/./image-6.webp)
:::
Six-column test image one
Six-column test image one
Six-column test image two
Six-column test image two
Six-column test image three
Six-column test image three
Six-column test image four
Six-column test image four
Six-column test image five
Six-column test image five
Six-column test image six
Six-column test image six

Four-Column Squares: 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"}
![Square image description](./image-grid-demo/./square-1.webp)
![Square image description](./image-grid-demo/./square-2.webp)
![Square image description](./image-grid-demo/./square-3.webp)
![Square image description](./image-grid-demo/./square-4.webp)
:::
Square image one
Square image one
Square image two
Square image two
Square image three
Square image three
Square image four
Square image four

Six-Column Landscapes: 16<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"}
![Landscape image description](./image-grid-demo/./landscape-1.webp)
![Landscape image description](./image-grid-demo/./landscape-2.webp)
![Landscape image description](./image-grid-demo/./landscape-3.webp)
![Landscape image description](./image-grid-demo/./landscape-4.webp)
![Landscape image description](./image-grid-demo/./landscape-5.webp)
![Landscape image description](./image-grid-demo/./landscape-6.webp)
:::
Landscape image one
Landscape image one
Landscape image two
Landscape image two
Landscape image three
Landscape image three
Landscape image four
Landscape image four
Landscape image five
Landscape image five
Landscape image six
Landscape image six

Three-Column Portraits: 3<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"}
![Portrait image description](./image-grid-demo/./portrait-1.webp)
![Portrait image description](./image-grid-demo/./portrait-2.webp)
![Portrait image description](./image-grid-demo/./portrait-3.webp)
![Portrait image description](./image-grid-demo/./portrait-4.webp)
![Portrait image description](./image-grid-demo/./portrait-5.webp)
![Portrait image description](./image-grid-demo/./portrait-6.webp)
:::
Portrait image one
Portrait image one
Portrait image two
Portrait image two
Portrait image three
Portrait image three
Portrait image four
Portrait image four
Portrait image five
Portrait image five
Portrait image six
Portrait image six

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"}
![Edge-critical content](./image-grid-demo/./critical-1.webp "Open the lightbox to view the complete edge content")
![Edge-critical content](./image-grid-demo/./critical-2.webp "Open the lightbox to view the complete edge content")
![Edge-critical content](./image-grid-demo/./critical-3.webp "Open the lightbox to view the complete edge content")
:::
First edge-critical image
Open the lightbox to view the complete edge content
Second edge-critical image
Open the lightbox to view the complete edge content
Third edge-critical image
Open the lightbox to view the complete edge content

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"}
![Complete screenshot description](./image-grid-demo/./wide-1.webp)
![Complete screenshot description](./image-grid-demo/./wide-2.webp)
![Complete screenshot description](./image-grid-demo/./wide-3.webp)
:::
Extreme-ratio image one
Extreme-ratio image one
Extreme-ratio image two
Extreme-ratio image two
Extreme-ratio image three
Extreme-ratio image three

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"}
![Transparent image description](./image-grid-demo/./transparent.webp)
:::
Transparent-background test image
Transparent-background test image

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> 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#

  1. Images in each grid have consistent dimensions, with captions below the cards.
  2. Images scale slightly on hover; after clicking, they can be zoomed, rotated, and navigated with the keyboard.
  3. Clicking “16<9> test image one” lets the lightbox browse only the other two landscape images in that section.
  4. Below 768px, grids use at most two columns; below 480px, they use one column.
  5. Portrait images in “Four Columns with contain” are fully visible with empty space and no cropping.
  6. 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:

  1. 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.

  2. 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.
  3. 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.
  4. 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: true is 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;
}
Terminal window
# Verify build and type checking
npx.cmd astro check
pnpm.cmd type-check
pnpm.cmd test

2.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]
    end

2.3 LaTeX Mathematical Formulas#

Inline formulas: Euler’s identity eiπ+1=0e^{i\pi} + 1 = 0 and Gaussian integral ∫−∞∞e−x2dx=π\int_{-\infty}^{\infty} e^{-x^2} dx = \sqrt{\pi}.

Display mathematical formulas with horizontal scroll containers:

f(x)=1σ2πexp⁡(−(x−μ)22σ2)f(x) = \frac{1}{\sigma \sqrt{2\pi}} \exp\left( -\frac{(x - \mu)^2}{2\sigma^2} \right)LAES-GCM=GHASHH(A∥C∥L)⊕AESK(J0)\mathcal{L}_{\text{AES-GCM}} = \text{GHASH}_H(A \parallel C \parallel L) \oplus \text{AES}_K(J_0)

2.4 Admonitions#

2.5 GitHub Repository Card#

withastroastro


3. Configuration Reference#

ParameterTypeRequiredDefaultDescription
encryptedbooleanNofalseExplicitly marks the post as encrypted. Implicitly true if password is set.
passwordstringYesNonePlaintext password used to encrypt at build time and unlock at runtime.
passwordHintstringNo""Optional hint displayed under the password input field.
hideHomeContentbooleanNotrueHides 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 Rendering

Footnotes#

  1. Footnote text goes here. ↩

功能演示合集
https://blog-c6q.pages.dev/posts/features/
作者
Hui_xuan_biao
发布于
2026-10-17
许可协议
CC BY-NC-SA 4.0

分享文章

生成精美分享图或复制链接,与更多人分享本文。