feat: native Markdown callouts, reading time, archetype scaffold

Add blockquote-to-callout rendering, reading time estimate, improved default
archetype with demo callout content and Vietnamese translations.
This commit is contained in:
2026-05-09 09:32:29 +07:00
parent d6f1027a7a
commit a7ca99f595
6 changed files with 184 additions and 7 deletions
+3
View File
@@ -2,6 +2,9 @@
title: "{{ replace .Name "-" " " | title }}"
date: {{ .Date }}
draft: true
description: ""
tags: []
categories: []
cover:
image: ""
---
+34
View File
@@ -0,0 +1,34 @@
/* tsuki: callouts (Markdown alert syntax: > [!note], > [!warning], etc.) */
.callout {
margin-block: var(--tsuki-space-4);
padding: var(--tsuki-space-3) var(--tsuki-space-4);
border-inline-start: 3px solid var(--tsuki-callout, var(--tsuki-accent));
background: var(--tsuki-callout-bg, transparent);
border-radius: var(--tsuki-radius);
color: var(--tsuki-fg);
font-style: normal;
}
.callout :is(p:last-child) { margin-block-end: 0; }
.callout-title {
font-weight: 600;
margin: 0 0 var(--tsuki-space-2);
color: var(--tsuki-callout, var(--tsuki-accent));
font-size: var(--tsuki-fs-sm);
text-transform: uppercase;
letter-spacing: 0.04em;
}
.callout-note { --tsuki-callout: #2563eb; --tsuki-callout-bg: #2563eb14; }
.callout-tip { --tsuki-callout: #16a34a; --tsuki-callout-bg: #16a34a14; }
.callout-important { --tsuki-callout: #7c3aed; --tsuki-callout-bg: #7c3aed14; }
.callout-warning { --tsuki-callout: #d97706; --tsuki-callout-bg: #d9770614; }
.callout-caution { --tsuki-callout: #dc2626; --tsuki-callout-bg: #dc262614; }
:where([data-theme="dark"]) {
.callout-note { --tsuki-callout: #6ea1ff; --tsuki-callout-bg: #6ea1ff1f; }
.callout-tip { --tsuki-callout: #4ade80; --tsuki-callout-bg: #4ade801f; }
.callout-important { --tsuki-callout: #a78bfa; --tsuki-callout-bg: #a78bfa1f; }
.callout-warning { --tsuki-callout: #fbbf24; --tsuki-callout-bg: #fbbf241f; }
.callout-caution { --tsuki-callout: #f87171; --tsuki-callout-bg: #f871711f; }
}
+55
View File
@@ -102,6 +102,61 @@ Set CSS variables inline via a frontmatter `style` you wire up yourself, or use
Then style `body.section-work { --tsuki-accent: #...; }` in your custom CSS.
## Callouts (admonitions)
Use Hugo's native Markdown alert syntax (Hugo 0.140+) — no shortcode needed:
```markdown
> [!note]
> Useful side information.
> [!tip]
> Reader-friendly hint.
> [!important]
> Hard requirement.
> [!warning]
> Heads-up about a pitfall.
> [!caution]
> Risk of breakage.
```
Titles localize via `i18n/vi.yml` keys `calloutNote`, `calloutTip`, `calloutImportant`, `calloutWarning`, `calloutCaution`. Override per-callout with `> [!note] Custom title`.
Plain `> blockquote` still renders as the regular muted blockquote — only `[!type]` triggers callout styling.
**Override callout colors** in your custom CSS — each callout type exposes the `--tsuki-callout` and `--tsuki-callout-bg` tokens through its modifier class:
```css
/* your-site/assets/css/custom.css */
.callout-note { --tsuki-callout: #ff5d8f; --tsuki-callout-bg: #ff5d8f1f; }
```
## Show word count
Reading time is shown on every post by default. Add word count under it with:
```yaml
params:
showWordCount: true # default false
```
The byline reads `5 phút đọc · 1024 từ` (translated via the `wordCount` i18n key).
## Accessibility & SEO defaults
The theme includes accessibility features that require no configuration:
- **Skip-link** — first focusable element on every page; jumps to `<main id="main">` (WCAG 2.1 M3)
- **Focus rings** — visible outline via `--tsuki-accent` on `:focus-visible` for keyboard navigation
- **Lastmod byline** — shows "Cập nhật {date}" when modified date is ≥24h newer than publish date
- **Lazy-load images** — all in-content images get `loading="lazy" decoding="async"` via render hook
- **External link security** — all Markdown links to `http://` / `https://` / `//` get `rel="noopener noreferrer"` automatically
All of these are transparent — no override hooks needed unless you want to customize the markup.
## Disable features
Every interactive feature has a kill switch:
@@ -7,7 +7,7 @@ categories: ["demo"]
description: "Bài viết mẫu cho tsuki theme."
---
Đây là một bài viết mẫu để kiểm tra layout của tsuki.
Đây là một bài viết mẫu để kiểm tra layout của tsuki. Bài viết này tham khảo [tài liệu chính thức của Hugo](https://gohugo.io/documentation/) và [đặc tả Markdown của CommonMark](https://commonmark.org/).
## Heading thứ hai
@@ -21,3 +21,24 @@ def hello():
- Mục một
- Mục hai
- Mục ba
## Trích dẫn
> Một blockquote thông thường — không có `[!type]`, vẫn hiển thị kiểu mặc định nghiêng và viền nhạt.
## Hộp thông báo (callouts)
> [!note]
> Đây là một ghi chú thông thường — dùng khi muốn nhấn mạnh một thông tin phụ.
> [!tip]
> Mẹo: dùng `> [!tip]` thay cho blockquote khi muốn nội dung nổi bật hơn.
> [!important]
> Quan trọng: trang chủ là portfolio, không phải danh sách bài viết.
> [!warning]
> Cảnh báo: `taxonomies: { tag: tags }` là một phần của hợp đồng theme.
> [!caution]
> Thận trọng: chỉnh sửa `markup.goldmark.renderer.unsafe` có thể vô hiệu hoá footnote.
+45 -6
View File
@@ -1,6 +1,24 @@
- id: readingTime
translation: "{{ .Count }} phút đọc"
- id: wordCount
translation: "{{ .Count }} từ"
- id: calloutNote
translation: "Ghi chú"
- id: calloutTip
translation: "Mẹo"
- id: calloutImportant
translation: "Quan trọng"
- id: calloutWarning
translation: "Cảnh báo"
- id: calloutCaution
translation: "Thận trọng"
- id: postedOn
translation: "Đăng vào"
@@ -34,12 +52,6 @@
- id: tableOfContents
translation: "Mục lục"
- id: previousPage
translation: "Trang trước"
- id: nextPage
translation: "Trang tiếp"
- id: prev
translation: "Trước"
@@ -87,3 +99,30 @@
- id: copyright
translation: "© {{ .Year }} {{ .Author }}"
- id: comments
translation: "Bình luận"
- id: month
translation: "Tháng {{ .Number }}"
- id: pageNotFound
translation: "Trang không tồn tại."
- id: backHome
translation: "Về trang chủ"
- id: searchSuggestion
translation: "Không có kết quả cho [SEARCH_TERM]. Thử các từ khoá: [DIFFERENT_TERMS]."
- id: altSearch
translation: "Không có kết quả cho [SEARCH_TERM]. Đang hiển thị kết quả cho [DIFFERENT_TERM]."
- id: searchDisabled
translation: "Tìm kiếm hiện không khả dụng trên trang này."
- id: skipToContent
translation: "Đến nội dung chính"
- id: relatedPosts
translation: "Bài viết liên quan"
+25
View File
@@ -0,0 +1,25 @@
{{- /*
Blockquote render hook with native Markdown callout support (Hugo 0.140+).
Markdown:
> [!note] Optional title
> Body content here.
Renders to a styled callout. Plain blockquotes pass through unchanged.
Supported alert types: note, tip, important, warning, caution.
*/ -}}
{{- if eq .Type "alert" -}}
{{- $type := .AlertType | lower -}}
{{- $title := .AlertTitle -}}
{{- if not $title -}}
{{- $title = i18n (printf "callout%s" ($type | title)) | default ($type | title) -}}
{{- end -}}
<blockquote class="callout callout-{{ $type }}">
<p class="callout-title">{{ $title }}</p>
{{ .Text }}
</blockquote>
{{- else -}}
<blockquote
{{- range $k, $v := .Attributes }} {{ $k }}="{{ $v }}"{{ end -}}
>
{{ .Text }}
</blockquote>
{{- end -}}