mirror of
https://github.com/tiennm99/tsuki.git
synced 2026-05-23 06:25:42 +00:00
feat: core layouts and partials (Phase 3)
- baseof.html with named blocks (head_extra, body_class, main, scripts) - single.html with data-pagefind-body wrapper - list.html using post-card partial - home.html placeholder (Phase 4 fills hero/projects/recent-posts) - 404.html with vi i18n - _partials: head (full meta + OG + Twitter Card + RSS autodiscovery + theme-flash script), header, nav, footer, meta, post-card, icon - Sample post fixture in exampleSite to validate render ci: bump GitHub Actions to Node 24-compatible versions - actions/checkout v4 → v6 - actions/setup-node v4 → v6, node 20 → 22 - actions/upload-pages-artifact v3 → v5 - actions/deploy-pages v4 → v5
This commit is contained in:
@@ -20,7 +20,7 @@ jobs:
|
||||
env:
|
||||
HUGO_VERSION: 0.154.0
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
@@ -32,9 +32,9 @@ jobs:
|
||||
extended: true
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 20
|
||||
node-version: 22
|
||||
cache: npm
|
||||
|
||||
- name: Install dependencies
|
||||
@@ -53,7 +53,7 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@v3
|
||||
uses: actions/upload-pages-artifact@v5
|
||||
with:
|
||||
path: exampleSite/public
|
||||
|
||||
@@ -65,4 +65,4 @@ jobs:
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
steps:
|
||||
- id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
uses: actions/deploy-pages@v5
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
---
|
||||
title: "Mẫu bài viết đầu tiên"
|
||||
date: 2026-05-07T19:00:00+07:00
|
||||
draft: false
|
||||
tags: ["hugo", "tsuki", "demo"]
|
||||
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.
|
||||
|
||||
## Heading thứ hai
|
||||
|
||||
Một đoạn văn ngắn với **chữ in đậm** và *chữ nghiêng* và `mã inline`.
|
||||
|
||||
```python
|
||||
def hello():
|
||||
print("Xin chào, tsuki!")
|
||||
```
|
||||
|
||||
- Mục một
|
||||
- Mục hai
|
||||
- Mục ba
|
||||
@@ -0,0 +1,9 @@
|
||||
{{ define "body_class" }}error-page{{ end }}
|
||||
|
||||
{{ define "main" }}
|
||||
<section class="error-404">
|
||||
<h1>404</h1>
|
||||
<p>{{ i18n "pageNotFound" | default "Trang không tồn tại." }}</p>
|
||||
<p><a href="{{ "/" | relURL }}">{{ i18n "backHome" | default "Về trang chủ" }}</a></p>
|
||||
</section>
|
||||
{{ end }}
|
||||
@@ -0,0 +1,11 @@
|
||||
<footer class="site-footer">
|
||||
<p class="copyright">
|
||||
© {{ now.Year }}
|
||||
{{- with site.Params.profile.name }} {{ . }}{{ else }} {{ site.Title }}{{ end }}.
|
||||
</p>
|
||||
<p class="powered-by">
|
||||
<span>{{ i18n "poweredBy" | default "Powered by" }}</span>
|
||||
<a href="https://gohugo.io" rel="noopener">Hugo</a> +
|
||||
<a href="https://github.com/tiennm99/tsuki" rel="noopener">tsuki</a>
|
||||
</p>
|
||||
</footer>
|
||||
@@ -0,0 +1,68 @@
|
||||
{{- $title := cond .IsHome site.Title (printf "%s · %s" .Title site.Title) -}}
|
||||
{{- $description := .Description | default .Summary | default site.Params.description | default site.Title -}}
|
||||
{{- $ogImage := .Params.image | default site.Params.profile.avatar -}}
|
||||
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="generator" content="Hugo {{ hugo.Version }} + tsuki">
|
||||
|
||||
<title>{{ $title }}</title>
|
||||
<meta name="description" content="{{ $description }}">
|
||||
<link rel="canonical" href="{{ .Permalink }}">
|
||||
|
||||
{{/* Theme flash prevention — must run before <body> */}}
|
||||
<script>
|
||||
(function () {
|
||||
try {
|
||||
var stored = localStorage.getItem('tsuki-theme');
|
||||
if (stored === 'dark' || stored === 'light') {
|
||||
document.documentElement.setAttribute('data-theme', stored);
|
||||
}
|
||||
} catch (e) {}
|
||||
})();
|
||||
</script>
|
||||
|
||||
{{/* OpenGraph */}}
|
||||
<meta property="og:title" content="{{ $title }}">
|
||||
<meta property="og:description" content="{{ $description }}">
|
||||
<meta property="og:url" content="{{ .Permalink }}">
|
||||
<meta property="og:type" content="{{ if .IsPage }}article{{ else }}website{{ end }}">
|
||||
<meta property="og:site_name" content="{{ site.Title }}">
|
||||
{{- with $ogImage }}
|
||||
<meta property="og:image" content="{{ . | absURL }}">
|
||||
{{- end }}
|
||||
{{- if and .IsPage .Date }}
|
||||
<meta property="article:published_time" content="{{ .Date.Format "2006-01-02T15:04:05Z07:00" }}">
|
||||
{{- with .Lastmod }}
|
||||
<meta property="article:modified_time" content="{{ .Format "2006-01-02T15:04:05Z07:00" }}">
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/* Twitter Card */}}
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="{{ $title }}">
|
||||
<meta name="twitter:description" content="{{ $description }}">
|
||||
{{- with $ogImage }}
|
||||
<meta name="twitter:image" content="{{ . | absURL }}">
|
||||
{{- end }}
|
||||
|
||||
{{/* RSS autodiscovery */}}
|
||||
{{- with .OutputFormats.Get "RSS" }}
|
||||
<link rel="alternate" type="application/rss+xml" title="{{ site.Title }}" href="{{ .Permalink }}">
|
||||
{{- end }}
|
||||
|
||||
{{/* Pagination prev/next for SEO — only on paginated kinds */}}
|
||||
{{- if or .IsHome (eq .Kind "section") (eq .Kind "taxonomy") (eq .Kind "term") }}
|
||||
{{- with .Paginator }}
|
||||
{{- if .HasPrev }}<link rel="prev" href="{{ .Prev.URL }}">{{ end }}
|
||||
{{- if .HasNext }}<link rel="next" href="{{ .Next.URL }}">{{ end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/* Favicon */}}
|
||||
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
|
||||
<link rel="icon" href="/favicon.ico" sizes="32x32">
|
||||
|
||||
{{/* Theme CSS — placeholder; Phase 6 wires resources.Get */}}
|
||||
{{/* {{ $css := resources.Get "css/tsuki.css" | minify | fingerprint }}
|
||||
<link rel="stylesheet" href="{{ $css.RelPermalink }}" integrity="{{ $css.Data.Integrity }}"> */}}
|
||||
@@ -0,0 +1,9 @@
|
||||
<header class="site-header">
|
||||
<a class="site-title" href="{{ "/" | relURL }}">
|
||||
{{- with site.Params.profile.name }}{{ . }}{{ else }}{{ site.Title }}{{ end -}}
|
||||
</a>
|
||||
{{ partial "nav.html" . }}
|
||||
<button type="button" class="theme-toggle" aria-label="{{ i18n "toggleTheme" | default "Toggle theme" }}" data-theme-toggle hidden>
|
||||
<span aria-hidden="true">◐</span>
|
||||
</button>
|
||||
</header>
|
||||
@@ -0,0 +1,4 @@
|
||||
{{- $name := . -}}
|
||||
{{- with resources.Get (printf "icons/%s.svg" $name) -}}
|
||||
{{ .Content | safeHTML }}
|
||||
{{- end -}}
|
||||
@@ -0,0 +1,20 @@
|
||||
<div class="post-meta">
|
||||
{{- with .Date }}
|
||||
<time datetime="{{ .Format "2006-01-02" }}" class="post-date">
|
||||
{{ . | time.Format ":date_long" }}
|
||||
</time>
|
||||
{{- end }}
|
||||
{{- if gt .ReadingTime 0 }}
|
||||
<span class="reading-time">
|
||||
{{ i18n "readingTime" (dict "Count" .ReadingTime) | default (printf "%d min" .ReadingTime) }}
|
||||
</span>
|
||||
{{- end }}
|
||||
{{- with .Params.tags }}
|
||||
<span class="post-tags">
|
||||
{{- range $i, $tag := . -}}
|
||||
{{- if $i }}, {{ end -}}
|
||||
<a href="{{ printf "/tags/%s/" (urlize $tag) | relURL }}">#{{ $tag }}</a>
|
||||
{{- end -}}
|
||||
</span>
|
||||
{{- end }}
|
||||
</div>
|
||||
@@ -0,0 +1,10 @@
|
||||
<nav class="site-nav" aria-label="Main">
|
||||
<ul>
|
||||
{{- range site.Menus.main }}
|
||||
{{- $current := or ($.IsMenuCurrent "main" .) ($.HasMenuCurrent "main" .) }}
|
||||
<li>
|
||||
<a href="{{ .URL }}"{{ if $current }} aria-current="page"{{ end }}>{{ .Name }}</a>
|
||||
</li>
|
||||
{{- end }}
|
||||
</ul>
|
||||
</nav>
|
||||
@@ -0,0 +1,14 @@
|
||||
{{- $page := . -}}
|
||||
<article class="post-card">
|
||||
<h3 class="post-card-title">
|
||||
<a href="{{ $page.RelPermalink }}">{{ $page.Title }}</a>
|
||||
</h3>
|
||||
{{- with $page.Date }}
|
||||
<time datetime="{{ .Format "2006-01-02" }}" class="post-card-date">
|
||||
{{ . | time.Format ":date_medium" }}
|
||||
</time>
|
||||
{{- end }}
|
||||
{{- with ($page.Summary | plainify) }}
|
||||
<p class="post-card-summary">{{ . | truncate 180 }}</p>
|
||||
{{- end }}
|
||||
</article>
|
||||
@@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ site.LanguageCode | default "vi" }}">
|
||||
<head>
|
||||
{{ partial "head.html" . }}
|
||||
{{ block "head_extra" . }}{{ end }}
|
||||
</head>
|
||||
<body class="{{ block "body_class" . }}{{ end }}">
|
||||
{{ partial "header.html" . }}
|
||||
<main>
|
||||
{{ block "main" . }}{{ end }}
|
||||
</main>
|
||||
{{ partial "footer.html" . }}
|
||||
{{ block "scripts" . }}{{ end }}
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,9 @@
|
||||
{{ define "body_class" }}home{{ end }}
|
||||
|
||||
{{ define "main" }}
|
||||
{{/* Phase 4 fills hero/projects/recent-posts here. Placeholder for now. */}}
|
||||
<section class="home-placeholder">
|
||||
<h1>{{ site.Title }}</h1>
|
||||
{{- with site.Params.description }}<p>{{ . }}</p>{{ end }}
|
||||
</section>
|
||||
{{ end }}
|
||||
@@ -0,0 +1,14 @@
|
||||
{{ define "body_class" }}list{{ end }}
|
||||
|
||||
{{ define "main" }}
|
||||
<header class="list-header">
|
||||
<h1 class="list-title">{{ .Title }}</h1>
|
||||
{{- with .Description }}<p class="list-description">{{ . }}</p>{{ end }}
|
||||
</header>
|
||||
|
||||
<div class="post-list">
|
||||
{{- range .Pages }}
|
||||
{{ partial "post-card.html" . }}
|
||||
{{- end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
@@ -0,0 +1,27 @@
|
||||
{{ define "body_class" }}post{{ end }}
|
||||
|
||||
{{ define "main" }}
|
||||
<article class="post">
|
||||
<header class="post-header">
|
||||
<h1 class="post-title">{{ .Title }}</h1>
|
||||
{{ partial "meta.html" . }}
|
||||
{{- with .Description }}
|
||||
<p class="post-description">{{ . }}</p>
|
||||
{{- end }}
|
||||
</header>
|
||||
|
||||
<div class="post-content" data-pagefind-body>
|
||||
{{ .Content }}
|
||||
</div>
|
||||
|
||||
<footer class="post-footer">
|
||||
{{- with .Params.tags }}
|
||||
<ul class="post-tag-list">
|
||||
{{- range . }}
|
||||
<li><a href="{{ printf "/tags/%s/" (urlize .) | relURL }}">#{{ . }}</a></li>
|
||||
{{- end }}
|
||||
</ul>
|
||||
{{- end }}
|
||||
</footer>
|
||||
</article>
|
||||
{{ end }}
|
||||
Reference in New Issue
Block a user