Add EN/CN/JA homepage flow and stabilize release assets

This commit is contained in:
Tw93
2026-04-23 17:03:31 +08:00
parent 75c20db79e
commit 5218dddb20
17 changed files with 2641 additions and 919 deletions
+5 -2
View File
@@ -12,13 +12,13 @@ Warm parchment canvas, ink-blue accent, serif-led hierarchy, and editorial white
| `references/design.md` | Design system spec, English-only source | Low |
| `references/writing.md` | Content strategy spec, English-only source | Low |
| `references/production.md` | WeasyPrint build and troubleshooting runbook, English-only source | Medium |
| `assets/templates/` | 6 document templates in 2 output languages | Medium |
| `assets/templates/` | 6 document templates in 2 base language families, plus Japanese best-effort mapping | Medium |
| `assets/demos/` | README showcase demos, regenerate after visual changes | Medium |
| `scripts/build.py` | PDF / PNG / PPTX build and verification script | Low |
| `scripts/package-skill.sh` | Claude Desktop ZIP packager, excluding large fonts | Low |
| `dist/kami.zip` | Claude Desktop ZIP artifact, updated from main | Medium |
Reference docs are English-only. Do not recreate `*.en.md` duplicates. Chinese / English output differences belong in the templates.
Reference docs are English-only. Do not recreate `*.en.md` duplicates. Chinese / English output differences belong in the templates. Japanese currently uses a best-effort CJK mapping, no dedicated `-ja` templates yet, and requires visual QA before shipping.
Do not use graphic emoticons in docs, template comments, or script output. Use `OK:` / `ERROR:` for status and `Use` / `Avoid` for comparisons.
## Verification
@@ -32,6 +32,8 @@ python3 scripts/build.py --check-placeholders path/to/filled.html
Expected page counts: one-pager 1 / letter 1 / resume 2 strict / long-doc 7 +/- 2 / portfolio 6 +/- 2 / slides 7 +/- 3
**PDF metadata**: `build.py` automatically sets `/Author` from `git config user.name` (or `KAMI_AUTHOR` env var) when the HTML template contains a placeholder like `{{作者}}` or `{{AUTHOR}}`. `/Producer` and `/Creator` are always set to `"Kami"`.
## Demo Screenshots
All demo PNG files use **1241x1754px** (first A4 portrait page at 150dpi).
@@ -98,5 +100,6 @@ Do not mix English and Chinese inside the same numbered item. Keep both lists al
`TsangerJinKai02-W04.ttf` is a commercial font. Commercial use requires a license from tsanger.cn.
Fallback without TsangerJinKai: Source Han Serif SC -> Noto Serif CJK SC -> Songti SC -> Georgia.
English templates use Newsreader serif.
Japanese output currently uses the CJK template path with Mincho-style fallbacks. Treat it as best-effort and verify rendering before delivery.
The Claude Desktop ZIP does not bundle TsangerJinKai TTF files. They are about 19MB each and can make upload or execution time out. Before building Chinese documents, the skill checks for missing fonts and downloads them from jsDelivr into `assets/fonts/`. WeasyPrint then uses the existing relative `@font-face` paths without changing HTML.
+8 -11
View File
@@ -8,15 +8,12 @@
<a href="https://twitter.com/HiTw93"><img src="https://img.shields.io/badge/follow-Tw93-red?style=flat-square&logo=Twitter" alt="Twitter"></a>
</div>
<br/>
## Why
Kami (紙, かみ) means paper: the surface where a finished idea lands. Most document design drifts into two failure modes: generic corporate gray, or gradient-heavy SaaS styling. Neither reads like something made with care.
Kami (紙, かみ) is a Japanese word for paper: the surface where a finished idea lands. Most AI-generated documents drift into generic corporate gray or inconsistent styling that shifts across sessions, requiring manual cleanup before they can be sent.
Kami holds one design idea across six document types: one-pagers, long docs, formal letters, portfolios, resumes, and slides. Each format shares a warm parchment canvas, a single ink-blue accent, serif for authority, sans for utility, and editorial whitespace tuned for print.
Kami is a document design system for the AI era: simple enough for agents to follow, strict enough to keep every output coherent, and practical enough to turn a prompt into a presentable PDF without design cleanup.
Kami is a document design system built for the AI era: one constraint language across six formats, simple enough for agents to execute reliably, strict enough to keep every output coherent and ready to ship.
It supports English and Chinese directly, with Japanese available via a best-effort CJK template path plus a final visual QA pass before delivery.
Part of a trilogy: [Kaku](https://github.com/tw93/Kaku) (書く) writes code, [Waza](https://github.com/tw93/Waza) (技) drills habits, [Kami](https://github.com/tw93/Kami) (紙) delivers documents.
@@ -41,8 +38,8 @@ Part of a trilogy: [Kaku](https://github.com/tw93/Kaku) (書く) writes code, [W
</td>
<td align="center" width="25%">
<a href="assets/demos/demo-kaku.pdf"><img src="assets/demos/demo-kaku.png" alt="Kaku portfolio"></a>
<br><b>Portfolio</b> · 中文
<br><sub>Kaku 项目作品集 · 6 </sub>
<br><b>Portfolio</b> · 日本語
<br><sub>Kaku 作品集 · 6 ページ</sub>
</td>
</tr>
</table>
@@ -75,7 +72,7 @@ The ZIP is intentionally lightweight: Chinese TsangerJinKai font files are loade
To update: download the same ZIP URL again, click "..." on the kami skill card, choose Replace, and upload the new ZIP.
The skill auto-triggers when you describe what you need, no slash command required. It supports one-pagers, long docs, formal letters, portfolios, resumes, and slides in both Chinese and English.
The skill auto-triggers when you describe what you need, no slash command required. It supports one-pagers, long docs, formal letters, portfolios, resumes, and slides in English and Chinese, with Japanese available as a best-effort path that should be visually QA-checked before delivery.
> make a one-pager for my startup / turn this research into a long doc / write a formal letter / make a portfolio showcasing my projects / build me a resume / design a slide deck for my talk / 帮我做一份一页纸 / 帮我排版一份长文档 / 帮我写一封正式信件 / 帮我做一份作品集 / 帮我做一份简历 / 帮我做一套演讲幻灯片
@@ -87,7 +84,7 @@ To include inline charts, mention the data and chart type in your prompt. Kami e
Warm parchment canvas, ink blue as the sole accent, serif carries hierarchy, no hard shadows or flashy palettes. This is not a UI framework; it is a constraint system for printed matter. Quality documents should read as composed pages, not dashboards.
Six document types (One-Pager, Long Doc, Letter, Portfolio, Resume, Slides), each with Chinese and English variants. Twelve inline SVG diagram types are included: architecture, flowchart, quadrant, bar chart, line chart, donut chart, state machine, timeline, swimlane, tree, layer stack, and Venn. Kami picks the right variant based on the language you write in.
Six document types (One-Pager, Long Doc, Letter, Portfolio, Resume, Slides), with dedicated English and Chinese templates and a best-effort Japanese path. Twelve inline SVG diagram types are included: architecture, flowchart, quadrant, bar chart, line chart, donut chart, state machine, timeline, swimlane, tree, layer stack, and Venn. Kami picks the right variant based on the language you write in.
| Element | Rule |
|---|---|
@@ -99,7 +96,7 @@ Six document types (One-Pager, Long Doc, Letter, Portfolio, Resume, Slides), eac
| Shadows | Ring or whisper only, no hard drop shadows |
| Tags | Solid hex backgrounds only. `rgba()` triggers a WeasyPrint double-rectangle bug |
**Fonts**: Chinese uses TsangerJinKai02 serif + Source Han Sans. TsangerJinKai is free for personal use, commercial use requires a license from [tsanger.cn](https://tsanger.cn). English uses Newsreader serif + Inter sans, both OFL open source.
**Fonts**: Chinese uses TsangerJinKai02 serif + Source Han Sans. Japanese currently follows the best-effort CJK path with Mincho-style fallbacks. TsangerJinKai is free for personal use, commercial use requires a license from [tsanger.cn](https://tsanger.cn). English uses Newsreader serif + Inter sans, both OFL open source.
Full spec: [design.md](references/design.md). Cheatsheet: [CHEATSHEET.md](CHEATSHEET.md).
+16 -3
View File
@@ -1,6 +1,6 @@
---
name: kami
description: 'Typeset any professional document: resumes, one-pagers, white papers, letters, portfolios, slide decks. Warm parchment design system with ink-blue accent, serif-led hierarchy, and tight editorial spacing. Bilingual document output: Chinese docs use TsangerJinKai02 + Source Han, English docs use Newsreader + Inter, with one shared English reference set. Triggers on "做 PDF / 排版 / 生成报告 / 一页纸 / 白皮书 / 作品集 / 正式信件 / 简历 / PPT / slides / 高质量文档 / 好看的排版", or "build me a resume / make a one-pager / design a slide deck / turn this into a PDF / make this presentable / polish typography", and when raw content is handed over to be "typeset, designed, made presentable".'
description: 'Typeset any professional document: resumes, one-pagers, white papers, letters, portfolios, slide decks. Warm parchment design system with ink-blue accent, serif-led hierarchy, and tight editorial spacing. Trilingual best-effort output: Chinese docs use TsangerJinKai02 + Source Han, English docs use Newsreader + Inter, Japanese currently maps to the CJK template path with Mincho fallbacks and requires manual visual QA before shipping, with one shared English reference set. Triggers on "做 PDF / 排版 / 生成报告 / 一页纸 / 白皮书 / 作品集 / 正式信件 / 简历 / PPT / slides / 高质量文档 / 好看的排版", or "build me a resume / make a one-pager / design a slide deck / turn this into a PDF / make this presentable / polish typography", and when raw content is handed over to be "typeset, designed, made presentable".'
---
# kami · 紙
@@ -13,7 +13,7 @@ Part of `Kaku · Waza · Kami` - Kaku writes code, Waza drills habits, **Kami de
## Step 1 · Decide the language
**Match the user's language**. If they write in Chinese -> use the Chinese templates (`.html` / `slides.py`). If they write in English -> use the English templates (`-en.html` / `slides-en.py`). The reference docs are shared English specs for both output languages.
**Match the user's language**. If they write in Chinese -> use the Chinese templates (`.html` / `slides.py`). If they write in English -> use the English templates (`-en.html` / `slides-en.py`). If they write in Japanese -> use the CJK template path (`.html` / `slides.py`) as best-effort, keep Japanese copy, and run visual QA before shipping. The reference docs are shared English specs for all output languages.
When ambiguous (e.g. a one-word command like "resume"), ask a one-liner rather than guess.
@@ -21,6 +21,8 @@ When ambiguous (e.g. a one-word command like "resume"), ask a one-liner rather t
|---|---|---|
| Chinese (primary) | `*.html` | `slides.py` |
| English | `*-en.html` | `slides-en.py` |
| Japanese (best-effort) | `*.html` | `slides.py` |
| Other languages (best-effort) | choose CJK or EN path by script coverage, then verify manually | choose `slides.py` or `slides-en.py`, then verify manually |
Always use `CHEATSHEET.md` and `references/*.md` for design, writing, production, and diagram guidance.
@@ -148,13 +150,20 @@ Every template has meta placeholders in `<head>`. Fill all four before building:
| Placeholder (CN) | Placeholder (EN) | Rule |
|---|---|---|
| `{{作者}}` | `{{AUTHOR}}` | Resume/letter/portfolio: use the person's name from the doc. All others: `"Kami"` |
| `{{作者}}` | `{{AUTHOR}}` | Resume/letter/portfolio: use the person's name from the doc. All others: leave as-is (build script infers from git config or env) |
| `{{摘要}}` | `{{DESCRIPTION}}` | Extract one sentence (≤150 chars) from the first 2 paragraphs |
| `{{关键词}}` | `{{KEYWORDS}}` | 3-5 keywords from the title + section headings, comma-separated |
| `{{文档标题}}` / `{{信件主题}}` etc. | `{{DOC_TITLE}}` / `{{LETTER_SUBJECT}}` etc. | Infer from the H1 or `.header .title` text |
`<meta name="generator" content="Kami">` is already fixed in the template; do not change it.
**Author inference**: `build.py` automatically sets PDF `/Author` metadata from:
1. `git config user.name` (primary)
2. `KAMI_AUTHOR` environment variable (fallback)
3. `"Kami"` (final fallback)
For personal documents (resume/letter/portfolio), the HTML `<meta name="author">` should match the person's name in the content. For non-personal documents (one-pager/long-doc), leave the placeholder as-is and let the build script infer it.
## Step 5 · Build & verify
```bash
@@ -177,6 +186,10 @@ Visual anomalies (tag double rectangle, font fallback, page break issues) -> `pr
- Both files are commercial fonts. Keep them available in the repository for local preview and CDN fallback, but do not bundle them inside Claude Desktop skill ZIPs
- Fallback chain baked into templates: Source Han Serif SC -> Noto Serif CJK SC -> Songti SC -> Georgia
**Japanese (best-effort)**
- Current routing uses the CJK template path (`*.html`, `slides.py`), no dedicated `-ja` templates yet
- Keep Japanese body copy, prefer Mincho-compatible fallback stacks, and visually verify line breaks, punctuation rhythm, and emphasis weight before shipping
**English**
- Main serif: Newsreader (Google Fonts, open source) - used for both headlines and body
- Sans: Inter (open source) - used for UI elements only (labels, eyebrows, meta)
+133 -133
View File
@@ -1,13 +1,13 @@
<!DOCTYPE html>
<!-- ==================================================================
PORTFOLIO TEMPLATE · kami design system
Kaku 终端模拟器作品集 · 中文
6 页:封面 + 关于 + 核心特性 + AI 助手 + 性能比 + 安装使用
Kaku ターミナルエミュレータ ポートフォリオ · 日本語
6 ページ: 表紙 + About + 主要機能 + AI アシスタント + 性能比 + 導入ガイド
================================================================== -->
<html lang="zh-CN">
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Kaku · 终端模拟器</title>
<title>Kaku · ターミナルエミュレータ</title>
<style>
@font-face {
font-family: "TsangerJinKai02";
@@ -46,13 +46,13 @@
body {
color: var(--near-black);
font-family: "TsangerJinKai02", Georgia, serif;
font-family: "TsangerJinKai02", "Hiragino Mincho ProN", "Yu Mincho", "Noto Serif CJK JP", "Source Han Serif JP", Georgia, serif;
font-size: 10pt;
line-height: 1.5;
}
.serif {
font-family: "TsangerJinKai02", Georgia, serif;
font-family: "TsangerJinKai02", "Hiragino Mincho ProN", "Yu Mincho", "Noto Serif CJK JP", "Source Han Serif JP", Georgia, serif;
}
/* ════════════ COVER ════════════ */
@@ -66,7 +66,7 @@
break-after: page;
}
.cover-top {
font-family: "TsangerJinKai02", Georgia, serif;
font-family: "TsangerJinKai02", "Hiragino Mincho ProN", "Yu Mincho", "Noto Serif CJK JP", "Source Han Serif JP", Georgia, serif;
font-size: 10pt;
color: var(--brand);
letter-spacing: 2pt;
@@ -77,8 +77,8 @@
gap: 14pt;
}
.cover-name {
font-family: "TsangerJinKai02", Georgia, serif;
font-size: 58pt;
font-family: "TsangerJinKai02", "Hiragino Mincho ProN", "Yu Mincho", "Noto Serif CJK JP", "Source Han Serif JP", Georgia, serif;
font-size: 64pt;
font-weight: 500;
line-height: 1.05;
color: var(--near-black);
@@ -120,7 +120,7 @@
margin-bottom: 8pt;
}
.section-title {
font-family: "TsangerJinKai02", Georgia, serif;
font-family: "TsangerJinKai02", "Hiragino Mincho ProN", "Yu Mincho", "Noto Serif CJK JP", "Source Han Serif JP", Georgia, serif;
font-size: 28pt;
font-weight: 500;
line-height: 1.2;
@@ -161,7 +161,7 @@
break-inside: avoid;
}
.feature-card h3 {
font-family: "TsangerJinKai02", Georgia, serif;
font-family: "TsangerJinKai02", "Hiragino Mincho ProN", "Yu Mincho", "Noto Serif CJK JP", "Source Han Serif JP", Georgia, serif;
font-size: 13pt;
font-weight: 500;
color: var(--near-black);
@@ -186,7 +186,7 @@
break-inside: avoid;
}
.ai-mode h3 {
font-family: "TsangerJinKai02", Georgia, serif;
font-family: "TsangerJinKai02", "Hiragino Mincho ProN", "Yu Mincho", "Noto Serif CJK JP", "Source Han Serif JP", Georgia, serif;
font-size: 13pt;
font-weight: 500;
color: var(--near-black);
@@ -199,8 +199,8 @@
margin-bottom: 6pt;
}
.ai-mode code {
font-family: "TsangerJinKai02", Georgia, serif;
font-size: 8.5pt;
font-family: "TsangerJinKai02", "Hiragino Mincho ProN", "Yu Mincho", "Noto Serif CJK JP", "Source Han Serif JP", Georgia, serif;
font-size: 9pt;
background: var(--tag-bg);
color: var(--brand);
padding: 1pt 4pt;
@@ -248,7 +248,7 @@
break-inside: avoid;
}
.install-num {
font-family: "TsangerJinKai02", Georgia, serif;
font-family: "TsangerJinKai02", "Hiragino Mincho ProN", "Yu Mincho", "Noto Serif CJK JP", "Source Han Serif JP", Georgia, serif;
font-size: 24pt;
font-weight: 500;
color: var(--brand);
@@ -256,7 +256,7 @@
min-width: 32pt;
}
.install-content h3 {
font-family: "TsangerJinKai02", Georgia, serif;
font-family: "TsangerJinKai02", "Hiragino Mincho ProN", "Yu Mincho", "Noto Serif CJK JP", "Source Han Serif JP", Georgia, serif;
font-size: 12pt;
font-weight: 500;
color: var(--near-black);
@@ -268,8 +268,8 @@
color: var(--charcoal);
}
.install-content code {
font-family: "TsangerJinKai02", Georgia, serif;
font-size: 8.5pt;
font-family: "TsangerJinKai02", "Hiragino Mincho ProN", "Yu Mincho", "Noto Serif CJK JP", "Source Han Serif JP", Georgia, serif;
font-size: 9pt;
background: var(--ivory);
border: 0.5pt solid var(--border);
padding: 8pt 10pt;
@@ -297,8 +297,8 @@
color: var(--charcoal);
}
.shortcut-item span:last-child {
font-family: "TsangerJinKai02", Georgia, serif;
font-size: 8.5pt;
font-family: "TsangerJinKai02", "Hiragino Mincho ProN", "Yu Mincho", "Noto Serif CJK JP", "Source Han Serif JP", Georgia, serif;
font-size: 9pt;
color: var(--brand);
}
@@ -307,7 +307,7 @@
padding: 20mm 0 10mm 0;
}
.contact-title {
font-family: "TsangerJinKai02", Georgia, serif;
font-family: "TsangerJinKai02", "Hiragino Mincho ProN", "Yu Mincho", "Noto Serif CJK JP", "Source Han Serif JP", Georgia, serif;
font-size: 32pt;
font-weight: 500;
color: var(--near-black);
@@ -354,7 +354,7 @@
text-align: center;
}
.about-metric-value {
font-family: "TsangerJinKai02", Georgia, serif;
font-family: "TsangerJinKai02", "Hiragino Mincho ProN", "Yu Mincho", "Noto Serif CJK JP", "Source Han Serif JP", Georgia, serif;
font-size: 18pt;
font-weight: 500;
color: var(--brand);
@@ -381,7 +381,7 @@
break-inside: avoid;
}
.tool-card h4 {
font-family: "TsangerJinKai02", Georgia, serif;
font-family: "TsangerJinKai02", "Hiragino Mincho ProN", "Yu Mincho", "Noto Serif CJK JP", "Source Han Serif JP", Georgia, serif;
font-size: 11pt;
font-weight: 500;
color: var(--near-black);
@@ -390,8 +390,8 @@
.tool-card p { font-size: 9pt; line-height: 1.45; color: var(--charcoal); margin-bottom: 7pt; }
.tool-shortcut {
display: inline-block;
font-family: "TsangerJinKai02", Georgia, serif;
font-size: 8pt;
font-family: "TsangerJinKai02", "Hiragino Mincho ProN", "Yu Mincho", "Noto Serif CJK JP", "Source Han Serif JP", Georgia, serif;
font-size: 9pt;
color: var(--brand);
background: var(--tag-bg);
padding: 1pt 5pt;
@@ -413,11 +413,11 @@
.config-table th {
font-weight: 500;
color: var(--stone);
font-size: 8.5pt;
font-size: 9pt;
}
.config-table td:first-child {
font-family: "TsangerJinKai02", Georgia, serif;
font-size: 8.5pt;
font-family: "TsangerJinKai02", "Hiragino Mincho ProN", "Yu Mincho", "Noto Serif CJK JP", "Source Han Serif JP", Georgia, serif;
font-size: 9pt;
color: var(--brand);
white-space: nowrap;
}
@@ -438,7 +438,7 @@
break-inside: avoid;
}
.opt-card h4 {
font-family: "TsangerJinKai02", Georgia, serif;
font-family: "TsangerJinKai02", "Hiragino Mincho ProN", "Yu Mincho", "Noto Serif CJK JP", "Source Han Serif JP", Georgia, serif;
font-size: 11pt;
font-weight: 500;
color: var(--near-black);
@@ -470,10 +470,10 @@
padding: 5pt 10pt;
border-bottom: 0.5pt solid var(--border-soft);
}
.cli-table th { font-weight: 500; color: var(--stone); font-size: 8.5pt; }
.cli-table th { font-weight: 500; color: var(--stone); font-size: 9pt; }
.cli-table td:first-child {
font-family: "TsangerJinKai02", Georgia, serif;
font-size: 8.5pt;
font-family: "TsangerJinKai02", "Hiragino Mincho ProN", "Yu Mincho", "Noto Serif CJK JP", "Source Han Serif JP", Georgia, serif;
font-size: 9pt;
color: var(--brand);
white-space: nowrap;
width: 120pt;
@@ -488,22 +488,22 @@
<div class="cover-top">TERMINAL EMULATOR · 2025</div>
<div class="cover-center">
<div class="cover-name">Kaku</div>
<div class="cover-tagline">AI 编码而生的快速终端模拟器,开箱即用的默认配置,完整的 Lua 自定义能力,轻量且高性能</div>
<div class="cover-tagline">AI コーディング向けに設計した高速ターミナルエミュレータ。初期設定のまま使え、Lua で深く拡張できる、軽量で高性能な実装です</div>
<div class="cover-line"></div>
</div>
<div class="cover-meta">
<div><strong>项目</strong> 终端模拟器</div>
<div><strong>术栈</strong> Rust · WezTerm · Lua</div>
<div><strong></strong> 开源 · 4800+ Stars</div>
<div><strong>カテゴリ</strong> ターミナルエミュレータ</div>
<div><strong></strong> Rust · WezTerm · Lua</div>
<div><strong></strong> OSS · 4800+ Stars</div>
</div>
</div>
<!-- ════════════ ABOUT ════════════ -->
<section>
<div class="section-eyebrow">01 · ABOUT</div>
<div class="section-title">关于 Kaku</div>
<div class="section-lead">Kaku(書く,かく)是日语"书写"的意思,代表将思想转化为形式的行为。这是一个深度定制的 WezTerm 分支,专注于实用的默认配置,同时保留完整的 Lua 自定义能力和快速轻量的体验</div>
<div class="section-title">Kaku とは</div>
<div class="section-lead">Kaku(書く)は、思考をかたちにする行為を指す言葉です。WezTerm をベースに深く最適化し、実用的なデフォルトと完全な Lua カスタマイズ性、軽快な操作感を両立したターミナルです</div>
<div class="about-metrics">
<div class="about-metric">
@@ -512,21 +512,21 @@
</div>
<div class="about-metric">
<div class="about-metric-value">Rust</div>
<div class="about-metric-label">主要</div>
<div class="about-metric-label">主要言</div>
</div>
<div class="about-metric">
<div class="about-metric-value">40 MB</div>
<div class="about-metric-label">可执行大小</div>
<div class="about-metric-label">実行ファイル</div>
</div>
<div class="about-metric">
<div class="about-metric-value">macOS</div>
<div class="about-metric-label">专注平台</div>
<div class="about-metric-label">重点プラットフォーム</div>
</div>
</div>
<div class="about-body">
<p>Kaku 三部曲的一部分:Kaku(書く)写代码,Waza(技)练习惯,Kami(紙)出文档,三者独立运作、彼此呼应。我长期依赖命令行工作,使用 Alacritty 多年后,随着工作流转向 AI 辅助编码,开始需要更强的标签页管理和内置 AI 交互能力。我尝试过 Kitty、Ghostty、WarpiTerm2,各有优势,但都不是我想要的平衡点:性能先、默认即好用、保留完整的 Lua 可定制性</p>
<p>WezTerm 是强大的引擎,Kaku 在此基础上做了两件事:裁减不必要体积,以及把 AI 工作流直接嵌入终端。配置体系完全兼容 WezTerm Lua API,存量配置无需迁移,并预置 Starship、Delta、Lazygit、Yazi 等工具集成,开箱即是完整工作环境。macOS 专属设计让 Kaku 深度利用平台特性:Traffic Light 嵌入标签栏、系统主题跟随、原生字体渲染、Apple 公证开箱即用</p>
<p>Kaku 三部作の一角です。Kaku(書く)がコードを書き、Waza(技)が習慣を鍛え、Kami(紙)が文書を仕上げます。私は長く CLI 中心で仕事をしてきました。Alacritty を長年使ったあと、AI 補助コーディングへ比重が移るにつれ、より強いタブ管理と内蔵 AI 連携が必要になりました。Kitty、Ghostty、WarpiTerm2 も試しましたが、性能先、デフォルトで使いやすい、Lua で全面的に拡張できる、というバランスに最も近かったのが Kaku です</p>
<p>WezTerm は強力なエンジンです。Kaku ではその上で 2 点に集中しました。不要な重量の削減と、AI ワークフローの直接統合です。設定は WezTerm Lua API と互換で、既存設定を移行せず再利用できます。Starship、Delta、Lazygit、Yazi も最初から連携済みです。macOS では、Traffic Light のタブバー統合、システム外観への追従、ネイティブフォント描画、Apple notarization まで含めて、導入直後から実用状態になります</p>
</div>
<div class="contact-screenshot">
<img src="../../../kaku/website/public/shots/kaku-light.webp" alt="Kaku terminal interface">
@@ -536,55 +536,55 @@
<!-- ════════════ FEATURES ════════════ -->
<section>
<div class="section-eyebrow">02 · FEATURES</div>
<div class="section-title">核心特性</div>
<div class="section-lead">Kaku 提供开箱即用的体验,无需复杂配置即可开始工作。同时保留完整的 WezTerm Lua API 兼容性,满足深度定制需求</div>
<div class="section-title">主要機能</div>
<div class="section-lead">Kaku は複雑な初期設定なしで使い始められます。同時に WezTerm Lua API 互換を維持し、深いカスタマイズにも対応します</div>
<div class="feature-grid">
<div class="feature-card">
<h3>零配置启动</h3>
<p>默认使用 JetBrains Mono 字体,macOS 字体渲染优化。自动检测显示器分辨率:低分辨率 15px,高分辨率 17px行高 1.28,无需任何手动设置</p>
<h3>ゼロ設定で起動</h3>
<p>標準フォントは JetBrains MonomacOS の描画特性に合わせて調整済みです。ディスプレイ解像度を自動判定し、低解像度は 15px、高解像度は 17px行高 1.28 を自動適用します</p>
</div>
<div class="feature-card">
<h3>主题自动切换</h3>
<p>跟随 macOS 系统外观自动切换 Kaku Dark / Kaku Light,同步更新选区颜色、字重、颜色覆盖表。也可通过 <code>color_scheme</code> 强制锁定任一模式</p>
<h3>テーマ自動切替</h3>
<p>macOS の外観設定に連動して Kaku Dark / Kaku Light を自動切替します。選択色、字重、色上書きテーブルも同時更新され、<code>color_scheme</code> で固定運用もできます</p>
</div>
<div class="feature-card">
<h3>精选 Shell 套件</h3>
<p>内置 zsh 插件自动加载:<code>z</code> 目录跳转<code>zsh-completions</code>语法高亮、自动建议、Smart Tab 智能补全。也支持 fish shell 集成</p>
<h3>厳選シェルツール</h3>
<p>zsh プラグインを自動読み込みします。<code>z</code> ディレクトリ移動<code>zsh-completions</code>構文ハイライト、補完候補、Smart Tab に対応し、fish 連携も可能です</p>
</div>
<div class="feature-card">
<h3>快速轻</h3>
<p>激进符号剥离 + 功能精简后二进制减小 40%67MB40MB,资源文件从 100MB 降至 80MB。即时初始化,Shell 引导时间减半200ms100ms)。</p>
<h3>高速で軽</h3>
<p>シンボル削減と機能整理でバイナリを 40% 圧縮しました67MB -> 40MB。リソースは 100MB から 80MB へ。JIT 初期化で Shell 初期化時間も半減します200ms -> 100ms)。</p>
</div>
<div class="feature-card">
<h3>WezTerm 兼容配置</h3>
<p>直接使用 WezTerm Lua 配置,完整 API 兼容,无需迁移<code>kaku.lua</code> 先加载内置 defaults,再应用用户 overrides,结构清晰不冲突</p>
<h3>WezTerm 互換設定</h3>
<p>WezTerm Lua 設定をそのまま利用でき、API 互換を保ちます。移行作業は不要です<code>kaku.lua</code> defaults を先に読み込み、その後 overrides を適用します</p>
</div>
<div class="feature-card">
<h3>精致的默认设置</h3>
<p>选中即复制、可点击文件路径、全屏应用历史记录查看、窗格输入广播、后台标签页完成视觉提示、Traffic Light 嵌入标签栏</p>
<h3>洗練されたデフォルト</h3>
<p>選択コピー、クリック可能なパス、履歴表示、ペイン入力ブロードキャスト、バックグラウンド完了表示、Traffic Light のタブバー統合を標準搭載します</p>
</div>
</div>
<div class="tools-row">
<div class="tool-card">
<h4>Lazygit 集成</h4>
<p>自动从 PATH Homebrew 路径检测 lazygit 二进制。有未提交改动时显示一次性提示</p>
<h4>Lazygit 連携</h4>
<p>PATH または Homebrew のパスから lazygit バイナリを自動検出します。未コミット変更がある場合は 1 回だけ通知します</p>
<span class="tool-shortcut">Cmd + Shift + G</span>
</div>
<div class="tool-card">
<h4>Yazi 文件管理器</h4>
<p>Shell wrapper <code>y</code> 启动 Yazi 并在退出时同步工作目录。自动同步 Kaku 配色 <code>yazi/theme.toml</code></p>
<h4>Yazi 連携</h4>
<p>Shell ラッパー <code>y</code> で起動し、終了時に作業ディレクトリを同期します。配色 <code>yazi/theme.toml</code> に自動反映されます</p>
<span class="tool-shortcut">Cmd + Shift + Y</span>
</div>
<div class="tool-card">
<h4>Remote FilesSSHFS</h4>
<p>从活跃 SSH 窗格自动检测目标主机,通过 sshfs 挂载远程文件系统并在 Yazi 中打开浏览</p>
<p>アクティブな SSH ペインから接続先を自動判定し、sshfs でリモートファイルシステムをマウントして Yazi で閲覧できます</p>
<span class="tool-shortcut">Cmd + Shift + R</span>
</div>
</div>
@@ -593,20 +593,20 @@
<!-- ════════════ AI ASSISTANT ════════════ -->
<section>
<div class="section-eyebrow">03 · AI ASSISTANT</div>
<div class="section-title">Kaku AI 助手</div>
<div class="section-lead">Kaku 内置 AI 助手,两种模式覆盖终端最高频的 AI 需求。运行 <code style="font-size:8.5pt;background:var(--tag-bg);color:var(--brand);padding:1pt 4pt;border-radius:2pt;">kaku ai</code> 打开设置面板,配置外部编码工具和 Kaku Assistant。</div>
<div class="section-title">Kaku AI アシスタント</div>
<div class="section-lead">Kaku には AI アシスタントを内蔵しています。2 つのモードで、ターミナル作業の主要な AI ユースケースをカバーします。<code style="font-size: 9pt;background:var(--tag-bg);color:var(--brand);padding:1pt 4pt;border-radius:2pt;">kaku ai</code> で設定パネルを開き、外部コーディングツールと Kaku Assistant を設定できます</div>
<div class="ai-modes">
<div class="ai-mode">
<h3>错误自动恢复</h3>
<p>命令失败时自动发送:失败命令 + 退出码 + 工作目录 + git 分支 → LLM,结果内联显示在终端。按 <code>Cmd + Shift + E</code> 直接应用修复</p>
<p>不触发的场景:<code>Ctrl+C</code> 退出、帮助 flag、裸包管理器调用、git pull 冲突、非 shell 前台进程。危险命令<code>rm -rf</code><code>git reset --hard</code>粘贴但从不自动执行</p>
<h3>エラー自動リカバリ</h3>
<p>コマンド失敗時に、失敗コマンド + 終了コード + 作業ディレクトリ + git ブランチを LLM へ送信し、修正案をターミナル内に表示します。<code>Cmd + Shift + E</code> でそのまま適用できます</p>
<p>未発火条件: <code>Ctrl+C</code> 中断、help フラグ、素のパッケージマネージャー実行、git pull 競合、非 shell 前景プロセス。危険コマンド<code>rm -rf</code><code>git reset --hard</code>は貼り付け可能ですが自動実行しません</p>
</div>
<div class="ai-mode">
<h3>自然语言转命令</h3>
<p>在提示符输入 <code># &lt;描述&gt;</code> 并回车,Kaku shell 看到之前拦截这一行,连同当前目录和 git 分支发给 LLM,将生成的命令注入回提示符供审查后运行</p>
<p style="font-size:8.5pt;color:var(--brand);line-height:1.7;margin-top:4pt;">
<h3>自然言語からコマンド</h3>
<p>プロンプトで <code># &lt;説明&gt;</code> を入力して Enter すると、Kaku shell に渡す前に行を取得し、現在のディレクトリと git ブランチを添えて LLM に送信します。生成されたコマンドはプロンプトへ戻り、確認してから実行できます</p>
<p style="font-size: 9pt;color:var(--brand);line-height:1.7;margin-top:4pt;">
# list all files modified in the last 7 days<br>
# find and kill the process on port 3000<br>
# compress the src folder excluding node_modules
@@ -616,20 +616,20 @@
<div style="display:grid;grid-template-columns:1fr 1fr;gap:16pt;margin-top:4pt;">
<div>
<div style="font-family:'TsangerJinKai02',Georgia,serif;font-size:11pt;font-weight:500;color:var(--near-black);margin-bottom:8pt;">assistant.toml 配置字段</div>
<div style="font-family:'TsangerJinKai02','Hiragino Mincho ProN','Yu Mincho','Noto Serif CJK JP','Source Han Serif JP',Georgia,serif;font-size:11pt;font-weight:500;color:var(--near-black);margin-bottom:8pt;">assistant.toml 設定項目</div>
<table class="config-table">
<tr><th>字段</th><th></th></tr>
<tr><td>enabled</td><td>true 启用 / false 禁用助手</td></tr>
<tr><td>api_key</td><td>服务商 API Key</td></tr>
<tr><td>model</td><td>模型标识,如 DeepSeek-V3.2</td></tr>
<tr><td>base_url</td><td>OpenAI 兼容 API 根地址</td></tr>
<tr><td>custom_headers</td><td>企业代理额外 HTTP </td></tr>
<tr><th>項目</th><th></th></tr>
<tr><td>enabled</td><td>true で有効 / false で無効</td></tr>
<tr><td>api_key</td><td>プロバイダー API Key</td></tr>
<tr><td>model</td><td>モデル識別子 (例: DeepSeek-V3.2)</td></tr>
<tr><td>base_url</td><td>OpenAI 互換 API のベース URL</td></tr>
<tr><td>custom_headers</td><td>プロキシ追加 HTTP ヘッダー</td></tr>
</table>
</div>
<div>
<div style="font-family:'TsangerJinKai02',Georgia,serif;font-size:11pt;font-weight:500;color:var(--near-black);margin-bottom:8pt;">支持的 AI 编码工具</div>
<div style="font-family:'TsangerJinKai02','Hiragino Mincho ProN','Yu Mincho','Noto Serif CJK JP','Source Han Serif JP',Georgia,serif;font-size:11pt;font-weight:500;color:var(--near-black);margin-bottom:8pt;">対応 AI コーディングツール</div>
<table class="config-table">
<tr><th>工具</th><th></th></tr>
<tr><th>ツール</th><th></th></tr>
<tr><td>claude</td><td>Claude Code · Anthropic</td></tr>
<tr><td>codex</td><td>OpenAI Codex CLI</td></tr>
<tr><td>gemini</td><td>Gemini CLI · Google</td></tr>
@@ -643,54 +643,54 @@
<!-- ════════════ PERFORMANCE ════════════ -->
<section>
<div class="section-eyebrow">04 · PERFORMANCE</div>
<div class="section-title">性能</div>
<div class="section-lead">通过激进的符号剥离、功能精简和资源优化,Kaku 在保持完整功能的同时显著减少体积和启动时间。macOS 专属设计带来平台级深度集成</div>
<div class="section-title">性能比</div>
<div class="section-lead">シンボル削減、機能整理、リソース最適化により、Kaku は機能を維持したままサイズと起動時間を大幅に改善しました。macOS 専用設計でプラットフォーム統合も深めています</div>
<table class="perf-table">
<tr>
<th></th>
<th> WezTerm</th>
<th></th>
<th> WezTerm</th>
<th>Kaku</th>
<th>优化方</th>
<th>最適化手</th>
</tr>
<tr>
<td>可执行文件大小</td>
<td>実行ファイルサイズ</td>
<td>~67 MB</td>
<td>~40 MB</td>
<td>激进符号剥离 + 功能精简</td>
<td>シンボル削減 + 機能整理</td>
</tr>
<tr>
<td>资源文件体积</td>
<td>リソース容量</td>
<td>~100 MB</td>
<td>~80 MB</td>
<td>资源优化 + 延迟加载</td>
<td>リソース最適化 + 遅延ロード</td>
</tr>
<tr>
<td>启动延迟</td>
<td>标准</td>
<td></td>
<td>即时初始化(JIT</td>
<td>起動遅延</td>
<td>標準</td>
<td></td>
<td>JIT 初期化</td>
</tr>
<tr>
<td>Shell 引导时间</td>
<td>Shell 初期化時間</td>
<td>~200ms</td>
<td>~100ms</td>
<td>优化的环境配置流程</td>
<td>環境設定フロー最適化</td>
</tr>
</table>
<div class="opt-grid">
<div class="opt-card">
<h4>二进制精简</h4>
<p>编译时裁掉上游未使用 feature flags,激进 symbol stripping,移除调试符号。Rust release profile 开启 LTO + codegen-units=1 最大化优化</p>
<h4>バイナリ最適化</h4>
<p>コンパイル時に未使用 feature flags を削除し、symbol stripping とデバッグ情報削減を実施。Rust release profile LTO + codegen-units=1 で最適化を最大化します</p>
</div>
<div class="opt-card">
<h4>延迟加载策</h4>
<p>Shell 套件Starship、Delta、Yazi 主题同步)采用 just-in-time 初始化,仅在首次进入 Kaku session 时触发,不影响系统级 shell 启动时间</p>
<h4>遅延ロード戦</h4>
<p>Shell ツール群Starship、Delta、Yazi テーマ同期)は just-in-time 初期化を採用。最初の Kaku セッションでのみ起動し、システム全体の shell 起動時間を悪化させません</p>
</div>
<div class="opt-card">
<h4>macOS 平台集成</h4>
<p>Traffic Light 嵌入标签栏INTEGRATED_BUTTONS|RESIZE)、系统外观跟随、Apple 公证(无需安全警告)、macOS 原生字体渲染栈</p>
<h4>macOS 統合</h4>
<p>Traffic Light のタブバー統合INTEGRATED_BUTTONS|RESIZE)、システム外観連動、Apple notarization(警告なし)、macOS ネイティブフォント描画スタックを提供します</p>
</div>
</div>
</section>
@@ -698,15 +698,15 @@
<!-- ════════════ INSTALLATION ════════════ -->
<section>
<div class="section-eyebrow">05 · INSTALLATION</div>
<div class="section-title">安装与使用</div>
<div class="section-lead">Kaku 提供简单的安装方式和直观的快捷键系统。首次启动时会自动设置 shell 环境</div>
<div class="section-title">インストールと使い方</div>
<div class="section-lead">Kaku はシンプルな導入手順と直感的なショートカット体系を提供します。初回起動時に shell 環境も自動設定されます</div>
<div class="install-steps">
<div class="install-step">
<div class="install-num">1</div>
<div class="install-content">
<h3>下载安装</h3>
<p>GitHub Releases 下载最新 DMG 文件并拖入应用程序文件夹,或使用 Homebrew 安装:</p>
<h3>インストール</h3>
<p>GitHub Releases から最新 DMG を取得してアプリケーションフォルダへ移動するか、Homebrew で導入します。</p>
<code>brew install tw93/tap/kaku</code>
</div>
</div>
@@ -714,59 +714,59 @@
<div class="install-step">
<div class="install-num">2</div>
<div class="install-content">
<h3>首次启动</h3>
<p>打开 Kaku,应用已通过 Apple 公证,无需安全警告。首次启动时 Kaku 会自动设置 shell 环境</p>
<h3>初回起動</h3>
<p>Kaku Apple notarization 済みです。セキュリティ警告なしで起動でき、初回起動時に shell 環境を自動設定します</p>
</div>
</div>
<div class="install-step">
<div class="install-num">3</div>
<div class="install-content">
<h3>配置定制</h3>
<p><span style="font-size:8.5pt;color:var(--brand);">Cmd + ,</span> 打开设置面板,或运行 <span style="font-size:8.5pt;color:var(--brand);">kaku config</span> 编辑 Lua 配置文件</p>
<h3>設定カスタマイズ</h3>
<p><span style="font-size: 9pt;color:var(--brand);">Cmd + ,</span> で設定パネルを開くか、<span style="font-size: 9pt;color:var(--brand);">kaku config</span> Lua 設定ファイルを編集します</p>
</div>
</div>
</div>
<div class="section-lead" style="margin-bottom: 10pt;">常用快捷键:</div>
<div class="section-lead" style="margin-bottom: 10pt;">主要ショートカット:</div>
<div class="shortcuts">
<div class="shortcut-item"><span>建标签页</span><span>Cmd + T</span></div>
<div class="shortcut-item"><span>建窗口</span><span>Cmd + N</span></div>
<div class="shortcut-item"><span>关闭标签页/窗格</span><span>Cmd + W</span></div>
<div class="shortcut-item"><span>切换标签页</span><span>Cmd + Shift + [ / ]</span></div>
<div class="shortcut-item"><span>垂直分割窗格</span><span>Cmd + D</span></div>
<div class="shortcut-item"><span>水平分割窗格</span><span>Cmd + Shift + D</span></div>
<div class="shortcut-item"><span>导航窗格</span><span>Cmd + Opt + 方向键</span></div>
<div class="shortcut-item"><span>打开 Lazygit</span><span>Cmd + Shift + G</span></div>
<div class="shortcut-item"><span>打开 Yazi 文件管理器</span><span>Cmd + Shift + Y</span></div>
<div class="shortcut-item"><span>AI 面板</span><span>Cmd + Shift + A</span></div>
<div class="shortcut-item"><span>应用 AI 建议</span><span>Cmd + Shift + E</span></div>
<div class="shortcut-item"><span>清屏</span><span>Cmd + K</span></div>
<div class="shortcut-item"><span>規タブ</span><span>Cmd + T</span></div>
<div class="shortcut-item"><span>規ウィンドウ</span><span>Cmd + N</span></div>
<div class="shortcut-item"><span>タブ/ペインを閉じる</span><span>Cmd + W</span></div>
<div class="shortcut-item"><span>タブ切替</span><span>Cmd + Shift + [ / ]</span></div>
<div class="shortcut-item"><span>ペイン縦分割</span><span>Cmd + D</span></div>
<div class="shortcut-item"><span>ペイン横分割</span><span>Cmd + Shift + D</span></div>
<div class="shortcut-item"><span>ペイン移動</span><span>Cmd + Opt + 矢印</span></div>
<div class="shortcut-item"><span>Lazygit を開く</span><span>Cmd + Shift + G</span></div>
<div class="shortcut-item"><span>Yazi を開く</span><span>Cmd + Shift + Y</span></div>
<div class="shortcut-item"><span>AI パネル</span><span>Cmd + Shift + A</span></div>
<div class="shortcut-item"><span>AI 提案を適用</span><span>Cmd + Shift + E</span></div>
<div class="shortcut-item"><span>画面クリア</span><span>Cmd + K</span></div>
</div>
<div style="font-family:'TsangerJinKai02',Georgia,serif;font-size:11pt;font-weight:500;color:var(--near-black);margin:10pt 0 6pt;">CLI 命令参考</div>
<div style="font-family:'TsangerJinKai02','Hiragino Mincho ProN','Yu Mincho','Noto Serif CJK JP','Source Han Serif JP',Georgia,serif;font-size:11pt;font-weight:500;color:var(--near-black);margin:10pt 0 6pt;">CLI コマンド一覧</div>
<table class="cli-table">
<tr><th>命令</th><th></th></tr>
<tr><td>kaku ai</td><td>打开 AI 设置面板,配置 Kaku Assistant 外部编码工具</td></tr>
<tr><td>kaku config</td><td>在默认编辑器中打开 <code style="font-size:8pt;color:var(--brand);">~/.config/kaku/kaku.lua</code></td></tr>
<tr><td>kaku doctor</td><td>运行诊断,验证 shell 集成、PATH 和可选工具安装状态</td></tr>
<tr><td>kaku update</td><td>检查并安装最新版本的 Kaku</td></tr>
<tr><td>kaku init</td><td>设置 shell 集成,可选安装 Starship / Delta / Lazygit / Yazi</td></tr>
<tr><th>コマンド</th><th></th></tr>
<tr><td>kaku ai</td><td>AI 設定パネルを開き、Kaku Assistant 外部コーディングツールを設定</td></tr>
<tr><td>kaku config</td><td>既定エディタで <code style="font-size: 9pt;color:var(--brand);">~/.config/kaku/kaku.lua</code> を開く</td></tr>
<tr><td>kaku doctor</td><td>診断を実行し、shell 連携、PATH、任意ツールの導入状態を確認</td></tr>
<tr><td>kaku update</td><td>最新バージョンの確認と更新を実行</td></tr>
<tr><td>kaku init</td><td>shell 連携を設定し、Starship / Delta / Lazygit / Yazi を任意で導入</td></tr>
</table>
</section>
<!-- ════════════ CONTACT ════════════ -->
<section class="contact">
<div class="section-eyebrow">06 · GET KAKU</div>
<div class="contact-title">Kaku 是开源项目</div>
<div class="contact-title">Kaku はオープンソースです</div>
<div class="contact-line"></div>
<div class="contact-list">
<div><strong>GitHub</strong><a href="https://github.com/tw93/Kaku">github.com/tw93/Kaku</a></div>
<div><strong>官网</strong><a href="https://kaku.app">kaku.app</a></div>
<div><strong>安装</strong>brew install tw93/tap/kaku</div>
<div><strong>Stars</strong>4800+ 持续增长</div>
<div><strong>文档</strong><a href="https://github.com/tw93/Kaku/tree/main/docs">整文档和 FAQ</a></div>
<div><strong>公式サイト</strong><a href="https://kaku.app">kaku.app</a></div>
<div><strong>インストール</strong>brew install tw93/tap/kaku</div>
<div><strong>Stars</strong>4800+ 増加</div>
<div><strong>ドキュメント</strong><a href="https://github.com/tw93/Kaku/tree/main/docs">全ドキュメントと FAQ</a></div>
<div><strong>作者</strong><a href="https://twitter.com/HiTw93">@HiTw93</a></div>
</div>
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

After

Width:  |  Height:  |  Size: 86 KiB

BIN
View File
Binary file not shown.
+8 -553
View File
@@ -2,561 +2,16 @@
<html lang="en">
<head>
<meta charset="utf-8">
<title>Kami · Design System</title>
<title>Kami · Redirect</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Kami - a warm parchment design system for AI-assisted professional documents, shaped by one accent color, serif-led hierarchy, and editorial whitespace.">
<link rel="icon" href="https://gw.alipayobjects.com/zos/k/vl/logo.svg" type="image/svg+xml">
<link rel="stylesheet" href="./styles.css">
<meta http-equiv="refresh" content="0; url=./">
<meta name="robots" content="noindex">
<link rel="canonical" href="./">
</head>
<body>
<nav class="lang-switch"><a href="./index.html"></a><a href="./index-en.html" class="active">EN</a></nav>
<main class="page">
<!-- ============ HERO ============ -->
<header class="hero">
<div class="eyebrow">
<span>Design System · v1.1.0<span class="eyebrow-date"> · 2026.04</span></span>
<span class="hero-links">
<a href="https://github.com/tw93/kami" target="_blank" rel="noopener" aria-label="GitHub">
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0016 8c0-4.42-3.58-8-8-8z"/>
</svg>
</a>
<a href="https://x.com/HiTw93" target="_blank" rel="noopener" aria-label="X">
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/>
</svg>
</a>
</span>
</div>
<h1>Kami<span class="cn"></span></h1>
<p class="tagline">Good content deserves good paper. Kami is a layout design system for the AI era, making documents clear, readable, and memorable.</p>
<div class="hero-tokens">
<span><b>Canvas</b>#f5f4ed</span>
<span><b>Accent</b>#1B365D</span>
<span><b>Serif</b>Newsreader / TsangerJinKai</span>
<span><b>Sans</b>Inter / Source Han Sans</span>
</div>
</header>
<!-- ============ 00 DEMOS ============ -->
<section>
<div class="section-head">
<p class="section-num">00 · See it</p>
<h2 class="section-title">Output Samples</h2>
<p class="section-lede">Give Claude the brief: one-pagers, long docs, formal letters, portfolios, resumes, and slides become polished layouts through a small, reliable rule set.</p>
</div>
<div class="demo-grid">
<div class="demo-card">
<a href="assets/demos/demo-tesla.pdf" target="_blank"><img src="assets/demos/demo-tesla.png" alt="Tesla company one-pager"></a>
<p class="demo-title">One-Pager</p>
<p class="demo-desc">Tesla company profile, single A4</p>
</div>
<div class="demo-card">
<a href="assets/demos/demo-agent-slides.pdf" target="_blank"><img src="assets/demos/demo-agent-slides.png" alt="Agent slides"></a>
<p class="demo-title">Slides</p>
<p class="demo-desc">Agent keynote, 6 slides</p>
</div>
<div class="demo-card">
<a href="assets/demos/demo-musk-resume.pdf" target="_blank"><img src="assets/demos/demo-musk-resume.png" alt="Musk resume"></a>
<p class="demo-title">Resume</p>
<p class="demo-desc">Founder CV, 2 pages</p>
</div>
<div class="demo-card">
<a href="assets/demos/demo-kaku.pdf" target="_blank"><img src="assets/demos/demo-kaku.png" alt="Kaku portfolio"></a>
<p class="demo-title">Portfolio</p>
<p class="demo-desc">Kaku terminal, 6 pages</p>
</div>
</div>
</section>
<!-- ============ 01 INSTALL ============ -->
<section>
<div class="section-head">
<p class="section-num">01 · Usage</p>
<h2 class="section-title">Install &amp; Invoke</h2>
<p class="section-lede">Tell Claude what you need, for example "build me a resume", "make a one-pager for my startup", or "design a slide deck for my talk". The skill auto-triggers, no slash command needed.</p>
</div>
<pre class="code"><span class="c"># Claude Code</span>
npx skills add tw93/kami -a claude-code -g -y
<span class="c"># Codex</span>
npx skills add tw93/kami -a codex -g -y</pre>
<p class="section-lede" style="font-size:14px;">Claude Desktop: download <a href="https://cdn.jsdelivr.net/gh/tw93/kami@main/dist/kami.zip" style="color:var(--brand); text-decoration:none; font-weight:500;">kami.zip</a>, then upload it in Customize &gt; Skills &gt; "+" &gt; Create skill.</p>
</section>
<!-- ============ 02 MANIFESTO ============ -->
<section>
<div class="section-head">
<p class="section-num">02 · Manifesto</p>
<h2 class="section-title">Design Principles</h2>
</div>
<p class="manifesto">Warm parchment canvas, <em>ink blue</em> as the sole accent, serif carries hierarchy, while hard shadows and flashy palettes recede. This system is built for printed matter: stable, clear, and composed.</p>
<ol class="rules">
<li><span class="n">1</span><p>Page background is parchment <code>#f5f4ed</code>, never pure white</p></li>
<li><span class="n">2</span><p>Accent color is ink blue <code>#1B365D</code> only; no second chromatic hue</p></li>
<li><span class="n">3</span><p>All grays are warm, yellow-brown undertone; no cool blue-grays</p></li>
<li><span class="n">4</span><p>English uses serif for headlines and body; Chinese uses serif headlines and sans body</p></li>
<li><span class="n">5</span><p>Serif body at 400, headings at 500. Avoid synthetic bold</p></li>
<li><span class="n">6</span><p>Three line-height bands: tight titles 1.1-1.3 / dense 1.4-1.45 / reading 1.5-1.55</p></li>
<li><span class="n">7</span><p>Tag backgrounds must be solid hex; no rgba, WeasyPrint double-rectangle bug</p></li>
<li><span class="n">8</span><p>Shadows: ring or whisper only, no hard drop shadows</p></li>
</ol>
</section>
<!-- ============ 03 COLOR ============ -->
<section>
<div class="section-head">
<p class="section-num">03 · Color</p>
<h2 class="section-title">Warm Restraint</h2>
<p class="section-lede">One accent + warm neutrals + zero cool colors. Ink blue covers no more than <b>5%</b> of any page. Beyond that is clutter, not restraint.</p>
</div>
<h3 class="subhead">Canvas</h3>
<div class="swatches">
<div class="swatch"><div class="chip" style="background:#f5f4ed"></div><div class="info"><p class="name">Parchment</p><p class="role">Page background, the emotional foundation</p><span class="hex">#F5F4ED</span></div></div>
<div class="swatch"><div class="chip" style="background:#faf9f5"></div><div class="info"><p class="name">Ivory</p><p class="role">Cards / elevated surfaces</p><span class="hex">#FAF9F5</span></div></div>
<div class="swatch"><div class="chip" style="background:#e8e6dc"></div><div class="info"><p class="name">Warm Sand</p><p class="role">Button default / interactive surfaces</p><span class="hex">#E8E6DC</span></div></div>
<div class="swatch dark"><div class="chip" style="background:#141413"></div><div class="info"><p class="name" style="color:var(--deep-dark)">Deep Dark</p><p class="role">Dark theme page base, not pure black</p><span class="hex">#141413</span></div></div>
</div>
<h3 class="subhead">Brand</h3>
<div class="swatches">
<div class="swatch brand"><div class="chip" style="background:#1B365D"></div><div class="info"><p class="name">Ink Blue</p><p class="role">Primary color · CTA · quote bar · section overline</p><span class="hex">#1B365D</span></div></div>
<div class="swatch"><div class="chip" style="background:#2D5A8A"></div><div class="info"><p class="name">Ink Light</p><p class="role">Links on dark surfaces · lighter variant</p><span class="hex">#2D5A8A</span></div></div>
<div class="swatch"><div class="chip" style="background:#30302e"></div><div class="info"><p class="name" style="color:var(--deep-dark)">Dark Surface</p><p class="role">Dark theme container · warm charcoal</p><span class="hex">#30302E</span></div></div>
<div class="swatch"><div class="chip" style="background:#b53333"></div><div class="info"><p class="name">Error</p><p class="role">Error state, deep warm red</p><span class="hex">#B53333</span></div></div>
</div>
<h3 class="subhead">Warm Neutrals</h3>
<div class="swatches" style="grid-template-columns: repeat(6, 1fr);">
<div class="swatch"><div class="chip" style="background:#141413"></div><div class="info"><p class="name">Near Black</p><span class="hex">#141413</span></div></div>
<div class="swatch"><div class="chip" style="background:#3d3d3a"></div><div class="info"><p class="name">Dark Warm</p><span class="hex">#3D3D3A</span></div></div>
<div class="swatch"><div class="chip" style="background:#4d4c48"></div><div class="info"><p class="name">Charcoal</p><span class="hex">#4D4C48</span></div></div>
<div class="swatch"><div class="chip" style="background:#5e5d59"></div><div class="info"><p class="name">Olive</p><span class="hex">#5E5D59</span></div></div>
<div class="swatch"><div class="chip" style="background:#87867f"></div><div class="info"><p class="name">Stone</p><span class="hex">#87867F</span></div></div>
<div class="swatch"><div class="chip" style="background:#b0aea5"></div><div class="info"><p class="name">Warm Silver</p><span class="hex">#B0AEA5</span></div></div>
</div>
<h3 class="subhead">Tag Tints: rgba to solid hex</h3>
<p style="font-size:14px; color: var(--olive); max-width: 720px; margin: 0 0 16px; line-height: 1.55;">These are the equivalent solid hex values for ink blue <code style="font-family:var(--mono);font-size:12px;background:#EEF2F7;color:var(--brand);padding:1px 5px;border-radius:2px">#1B365D</code> layered on parchment. Tag backgrounds avoid <code style="font-family:var(--mono);font-size:12px;background:#EEF2F7;color:var(--brand);padding:1px 5px;border-radius:2px">rgba()</code> so WeasyPrint will not render double rectangles.</p>
<div class="tint-strip">
<div class="tint"><span class="flag">&nbsp;</span><span class="sample" style="background:#EEF2F7">Tag sample</span><p class="opacity">0.08</p><span class="hex">#EEF2F7</span></div>
<div class="tint"><span class="flag">&nbsp;</span><span class="sample" style="background:#E4ECF5">Tag sample</span><p class="opacity">0.14</p><span class="hex">#E4ECF5</span></div>
<div class="tint default"><span class="flag">Default</span><span class="sample" style="background:#E4ECF5">Tag sample</span><p class="opacity">0.18</p><span class="hex">#E4ECF5</span></div>
<div class="tint"><span class="flag">&nbsp;</span><span class="sample" style="background:#D0DCE9">Tag sample</span><p class="opacity">0.22</p><span class="hex">#D0DCE9</span></div>
<div class="tint"><span class="flag">&nbsp;</span><span class="sample" style="background:#D6E1EE">Tag sample</span><p class="opacity">0.30</p><span class="hex">#D6E1EE</span></div>
</div>
</section>
<!-- ============ 04 TYPE ============ -->
<section>
<div class="section-head">
<p class="section-num">04 · Typography</p>
<h2 class="section-title">Type System</h2>
<p class="section-lede">Serif carries hierarchy, sans carries function. Serif body at 400, headings at 500.</p>
</div>
<div class="family-grid">
<div class="family serif">
<p class="glyph">Aa</p>
<p class="role">Serif · Headlines + Body</p>
<p class="name">Newsreader / TsangerJinKai02</p>
<p class="use">Used for headlines, body text, pull quotes, and numeric emphasis. English uses Newsreader, Chinese uses TsangerJinKai02.</p>
</div>
<div class="family sans">
<p class="glyph">Aa</p>
<p class="role">Sans · UI</p>
<p class="name">Inter / Source Han Sans</p>
<p class="use">Labels, buttons, metadata, eyebrows. Default 400, labels use 500-600. Chinese body text also uses sans.</p>
</div>
<div class="family mono">
<p class="glyph">&lt;/&gt;</p>
<p class="role">Mono · Code</p>
<p class="name">JetBrains Mono</p>
<p class="use">Code blocks, version numbers, hex values, tabular figures.</p>
</div>
</div>
<h3 class="subhead">Scale (print pt; screen px = pt x 1.33)</h3>
<div class="type-grid">
<div class="type-row">
<div class="type-label">Display</div>
<div class="type-sample display">Good docs read<br>clear, stable, poised</div>
<div class="type-specs"><b>36-48 pt</b><br>weight 500<br>line 1.10</div>
</div>
<div class="type-row">
<div class="type-label">H1 Section</div>
<div class="type-sample h1">Color System · Palette</div>
<div class="type-specs"><b>18-22 pt</b><br>weight 500<br>line 1.20</div>
</div>
<div class="type-row">
<div class="type-label">H2 Subsection</div>
<div class="type-sample h2">Canvas · Warm Neutrals</div>
<div class="type-specs"><b>14-16 pt</b><br>weight 500<br>line 1.25</div>
</div>
<div class="type-row">
<div class="type-label">H3 Item</div>
<div class="type-sample h3">Tag Tints · Solid Hex</div>
<div class="type-specs"><b>12-13 pt</b><br>weight 500<br>line 1.30</div>
</div>
<div class="type-row">
<div class="type-label">Body</div>
<div class="type-sample body">Ink blue covers no more than 5% of any page. Beyond that is clutter, not restraint. All grays have a yellow-brown undertone: R ≈ G > B is the rule of thumb.</div>
<div class="type-specs"><b>9.5-10 pt</b><br>weight 400<br>line 1.55</div>
</div>
<div class="type-row">
<div class="type-label">Caption</div>
<div class="type-sample caption">Figure notes, footnotes, annotation text. Color drops to olive or stone.</div>
<div class="type-specs"><b>8.5-9 pt</b><br>weight 400<br>line 1.45</div>
</div>
<div class="type-row">
<div class="type-label">Label</div>
<div class="type-sample label">Design System · v1.1.0</div>
<div class="type-specs"><b>7.5-8 pt</b><br>weight 600<br>line 1.35</div>
</div>
</div>
</section>
<!-- ============ 05 SPACING ============ -->
<section>
<div class="section-head">
<p class="section-num">05 · Spacing &amp; Shape</p>
<h2 class="section-title">Rhythm &amp; Form</h2>
<p class="section-lede">Base unit: 4pt. Denser layouts get smaller margins; more formal documents get larger ones.</p>
</div>
<table class="space-table">
<tr><th>Scale</th><th>Value</th><th>Use</th><th></th></tr>
<tr><td class="name">xs</td><td class="val">2-3 pt</td><td class="use">Inline elements</td><td class="bar"><span style="width:6px"></span></td></tr>
<tr><td class="name">sm</td><td class="val">4-5 pt</td><td class="use">Tag padding · tight layout</td><td class="bar"><span style="width:12px"></span></td></tr>
<tr><td class="name">md</td><td class="val">8-10 pt</td><td class="use">Component internals</td><td class="bar"><span style="width:24px"></span></td></tr>
<tr><td class="name">lg</td><td class="val">16-20 pt</td><td class="use">Between components · card padding</td><td class="bar"><span style="width:48px"></span></td></tr>
<tr><td class="name">xl</td><td class="val">24-32 pt</td><td class="use">Section title margins</td><td class="bar"><span style="width:72px"></span></td></tr>
<tr><td class="name">2xl</td><td class="val">40-60 pt</td><td class="use">Between major sections</td><td class="bar"><span style="width:140px"></span></td></tr>
<tr><td class="name">3xl</td><td class="val">80-120 pt</td><td class="use">Between long-doc chapters</td><td class="bar"><span style="width:240px"></span></td></tr>
</table>
<h3 class="subhead">Radii</h3>
<div class="radii">
<div class="radius"><div class="box" style="border-radius:4px"></div><div class="r"><b>4</b> pt</div><div class="label">Tight</div></div>
<div class="radius"><div class="box" style="border-radius:6px"></div><div class="r"><b>6</b> pt</div><div class="label">Code block</div></div>
<div class="radius"><div class="box" style="border-radius:8px"></div><div class="r"><b>8</b> pt</div><div class="label">Default card</div></div>
<div class="radius"><div class="box" style="border-radius:12px"></div><div class="r"><b>12</b> pt</div><div class="label">Container</div></div>
<div class="radius"><div class="box" style="border-radius:16px"></div><div class="r"><b>16</b> pt</div><div class="label">Feature card</div></div>
<div class="radius"><div class="box" style="border-radius:24px"></div><div class="r"><b>24</b> pt</div><div class="label">Large container</div></div>
<div class="radius"><div class="box" style="border-radius:32px"></div><div class="r"><b>32</b> pt</div><div class="label">Hero</div></div>
</div>
<h3 class="subhead">Depth: Three Shadow Methods</h3>
<p style="font-size:14px; color: var(--olive); max-width: 720px; margin: 0 0 12px; line-height: 1.55;">Kami avoids traditional hard shadows. Depth comes from ring shadows, whisper shadows, and light-dark alternation.</p>
<div class="shadow-row">
<div class="shadow-demo sd-ring">
<p class="t">Ring Shadow</p>
<p class="s">0 0 0 1pt var(--ring-warm)<br>&nbsp;<br>Buttons · card hover</p>
</div>
<div class="shadow-demo sd-whisper">
<p class="t">Whisper Shadow</p>
<p class="s">0 4pt 24pt rgba(0,0,0,0.05)<br>&nbsp;<br>Gentle elevation · feature cards</p>
</div>
<div class="shadow-demo sd-alt">
<p class="t">Light ⇌ Dark</p>
<p class="s">parchment ⇌ deep-dark<br>&nbsp;<br>Section-level · strongest contrast</p>
</div>
</div>
</section>
<!-- ============ 06 COMPONENTS ============ -->
<section>
<div class="section-head">
<p class="section-num">06 · Components</p>
<h2 class="section-title">Atomic Modules</h2>
<p class="section-lede">A small fixed set, kept only where it solves a concrete document problem.</p>
</div>
<div class="comp-grid">
<div class="comp">
<h4>Buttons</h4>
<p class="hint">ring shadow instead of hard drop · 8pt radius</p>
<div class="demo" style="display:flex; gap:10px; flex-wrap:wrap;">
<span class="btn btn-primary">Primary CTA</span>
<span class="btn btn-secondary">Secondary</span>
<span class="btn btn-ghost">Ghost</span>
</div>
</div>
<div class="comp">
<h4>Tags, three levels</h4>
<p class="hint">solid hex · pick from weak to strong</p>
<div class="demo" style="display:flex; gap:10px; flex-wrap:wrap; align-items:center;">
<span class="tag calm">Light 0.08</span>
<span class="tag standard">Standard 0.18</span>
<span class="tag brush">Brush gradient</span>
</div>
</div>
<div class="comp">
<h4>Quote</h4>
<p class="hint">left 2pt brand solid + olive text</p>
<blockquote class="quote">A thousand no's for every yes, prefer clarity over decoration.</blockquote>
</div>
<div class="comp">
<h4>Metric</h4>
<p class="hint">serif number + sans label · tabular-nums</p>
<div class="metrics demo">
<div class="metric"><span class="metric-value">6</span><span class="metric-label">Doc types</span></div>
<div class="metric"><span class="metric-value">1</span><span class="metric-label">Accent</span></div>
<div class="metric"><span class="metric-value">8</span><span class="metric-label">Rules</span></div>
</div>
</div>
<div class="comp">
<h4>Section Title</h4>
<p class="hint">serif 500 · 15pt · size-led hierarchy, no decoration</p>
<div class="demo">
<span class="sample-section-title">Selected Works · Projects</span>
</div>
</div>
<div class="comp">
<h4>Dash List</h4>
<p class="hint">dash instead of bullet · editorial tone</p>
<ul class="dash demo">
<li>Warm parchment, never pure white</li>
<li>Ink blue accent, no second color</li>
<li>Serif carries authority</li>
</ul>
</div>
<div class="comp">
<h4>Code Block</h4>
<p class="hint">ivory bg + 0.5pt border + 6pt radius</p>
<pre class="code"><span class="c">// warmth from restraint, not from color</span>
canvas = parchment; accent = <span class="k">ink_blue</span>
palette = <span class="k">warm_neutrals</span> cool_grays
document = <span class="k">serif</span> × hierarchy + generous_space
beauty = constraints × <span class="k">intention</span> ÷ noise</pre>
</div>
<div class="comp">
<h4>Featured Card</h4>
<p class="hint">whisper shadow + 16pt radius</p>
<div class="demo" style="background:var(--ivory); border-radius:16px; padding:18px 20px; box-shadow: 0 4px 24px rgba(0,0,0,0.05); border: 1px solid var(--border-cream);">
<p style="font-family:var(--serif); font-weight:500; font-size:15px; margin:0 0 6px; color:var(--near-black);">Tesla Company Profile · One-Pager</p>
<p style="font-size:12px; color:var(--olive); margin:0 0 10px; line-height:1.5;">Single A4 page · 4 metric cards + 3 body sections + timeline</p>
<div style="display:flex; gap:8px; flex-wrap:wrap;">
<span class="tag calm">English</span>
<span class="tag calm">A4 x 1</span>
<span class="tag calm">Ink Blue</span>
</div>
</div>
</div>
</div>
</section>
<!-- ============ 07 DIAGRAMS ============ -->
<section>
<div class="section-head">
<p class="section-num">07 · Diagrams</p>
<h2 class="section-title">Inline Charts</h2>
<p class="section-lede">Twelve inline SVG diagram types covering architecture, process, and data chart scenarios. Tell Claude which type you need and it embeds directly into the document, colors and fonts following the Kami design language.</p>
</div>
<div class="chart-showcase">
<!-- Architecture mini -->
<div class="chart-card">
<div class="chart-label">Architecture · 架构图</div>
<svg viewBox="0 0 280 160" xmlns="http://www.w3.org/2000/svg" style="font-family:'Inter','TsangerJinKai02',sans-serif">
<rect width="280" height="160" fill="#f5f4ed"/>
<!-- Browser → API -->
<path d="M 84,79.5 L 103,79.5 L 103,78 L 107,80 L 103,82 L 103,80.5 L 84,80.5 Z" fill="#87867f"/>
<!-- API → Database -->
<path d="M 172,79.5 L 191,79.5 L 191,78 L 195,80 L 191,82 L 191,80.5 L 172,80.5 Z" fill="#5e5d59"/>
<!-- Node: Client -->
<rect x="20" y="58" width="64" height="44" rx="3" fill="#f5f4ed" stroke="#87867f" stroke-width="0.8"/>
<text x="52" y="76" text-anchor="middle" font-size="7" letter-spacing="0.5" fill="#87867f">CLIENT</text>
<text x="52" y="91" text-anchor="middle" font-size="9" font-weight="600" fill="#141413">Browser</text>
<!-- Node: API (focal) -->
<rect x="108" y="58" width="64" height="44" rx="3" fill="#EEF2F7" stroke="#1B365D" stroke-width="1"/>
<text x="140" y="76" text-anchor="middle" font-size="7" letter-spacing="0.5" fill="#1B365D">SERVICE</text>
<text x="140" y="91" text-anchor="middle" font-size="9" font-weight="600" fill="#141413">API</text>
<!-- Node: Database -->
<rect x="196" y="58" width="64" height="44" rx="3" fill="#f5f4ed" stroke="#5e5d59" stroke-width="0.8"/>
<text x="228" y="76" text-anchor="middle" font-size="7" letter-spacing="0.5" fill="#87867f">DATA</text>
<text x="228" y="91" text-anchor="middle" font-size="9" font-weight="600" fill="#141413">Database</text>
</svg>
<p class="chart-caption">System components and connections, one focal node</p>
</div>
<!-- Flowchart mini -->
<div class="chart-card">
<div class="chart-label">Flowchart · 流程图</div>
<svg viewBox="0 0 280 160" xmlns="http://www.w3.org/2000/svg" style="font-family:'Inter','TsangerJinKai02',sans-serif">
<rect width="280" height="160" fill="#f5f4ed"/>
<rect x="100" y="8" width="80" height="28" rx="14" fill="#141413"/>
<text x="140" y="27" text-anchor="middle" font-size="9" font-weight="600" fill="#f5f4ed">Start</text>
<!-- Start → diamond -->
<path d="M 139.5,36 L 139.5,48 L 138,48 L 140,52 L 142,48 L 140.5,48 L 140.5,36 Z" fill="#5e5d59"/>
<polygon points="140,52 172,72 140,92 108,72" fill="#f5f4ed" stroke="#141413" stroke-width="1"/>
<text x="140" y="76" text-anchor="middle" font-size="8" fill="#141413">Valid?</text>
<!-- Yes right -->
<path d="M 172,71.5 L 204,71.5 L 204,70 L 208,72 L 204,74 L 204,72.5 L 172,72.5 Z" fill="#1B365D"/>
<text x="186" y="68" text-anchor="middle" font-size="8" fill="#1B365D">Yes</text>
<rect x="208" y="56" width="64" height="32" rx="4" fill="#EEF2F7" stroke="#1B365D" stroke-width="1"/>
<text x="240" y="77" text-anchor="middle" font-size="9" font-weight="600" fill="#141413">Process</text>
<!-- No down -->
<path d="M 139.5,92 L 139.5,112 L 138,112 L 140,116 L 142,112 L 140.5,112 L 140.5,92 Z" fill="#5e5d59"/>
<text x="151" y="106" font-size="8" fill="#5e5d59">No</text>
<rect x="96" y="116" width="88" height="32" rx="4" fill="#f5f4ed" stroke="#87867f" stroke-width="0.8"/>
<text x="140" y="136" text-anchor="middle" font-size="9" font-weight="600" fill="#5e5d59">Reject</text>
</svg>
<p class="chart-caption">Decision branches, yes/no paths, focal on success</p>
</div>
<!-- Bar chart (shifted left) -->
<div class="chart-card">
<div class="chart-label">Bar Chart · 柱状图</div>
<svg viewBox="0 0 280 160" xmlns="http://www.w3.org/2000/svg" style="font-family:'Inter','TsangerJinKai02',sans-serif">
<rect width="280" height="160" fill="#f5f4ed"/>
<rect x="18" y="78" width="42" height="50" rx="3" fill="#87867f"/>
<rect x="78" y="62" width="42" height="66" rx="3" fill="#5e5d59"/>
<rect x="138" y="44" width="42" height="84" rx="3" fill="#2d4e7a"/>
<rect x="198" y="30" width="42" height="98" rx="3" fill="#1B365D"/>
<text x="39" y="145" text-anchor="middle" font-size="10" fill="#87867f">Q1</text>
<text x="99" y="145" text-anchor="middle" font-size="10" fill="#5e5d59">Q2</text>
<text x="159" y="145" text-anchor="middle" font-size="10" fill="#4d4c48">Q3</text>
<text x="219" y="145" text-anchor="middle" font-size="10" fill="#4d4c48">Q4</text>
<text x="39" y="73" text-anchor="middle" font-size="9" font-weight="600" fill="#87867f">180</text>
<text x="99" y="57" text-anchor="middle" font-size="9" font-weight="600" fill="#5e5d59">220</text>
<text x="159" y="39" text-anchor="middle" font-size="9" font-weight="600" fill="#2d4e7a">195</text>
<text x="219" y="25" text-anchor="middle" font-size="9" font-weight="600" fill="#1B365D">240</text>
</svg>
<p class="chart-caption">Quarterly revenue comparison, gradient or single color</p>
</div>
<!-- Donut chart -->
<div class="chart-card">
<div class="chart-label">Donut Chart · 环形图</div>
<svg viewBox="0 0 280 160" xmlns="http://www.w3.org/2000/svg" style="font-family:'Inter','TsangerJinKai02',sans-serif">
<rect width="280" height="160" fill="#f5f4ed"/>
<path d="M 80,25 A 55,55 0 1,1 47.7,124.5 L 62.4,104.3 A 30,30 0 1,0 80,50 Z" fill="#1B365D" stroke="white" stroke-width="1.5"/>
<path d="M 47.7,124.5 A 55,55 0 0,1 35.5,47.7 L 55.7,62.4 A 30,30 0 0,0 62.4,104.3 Z" fill="#5e5d59" stroke="white" stroke-width="1.5"/>
<path d="M 35.5,47.7 A 55,55 0 0,1 80,25 L 80,50 A 30,30 0 0,0 55.7,62.4 Z" fill="#87867f" stroke="white" stroke-width="1.5"/>
<text x="80" y="77" text-anchor="middle" font-size="13" font-weight="600" fill="#1B365D">60%</text>
<text x="80" y="92" text-anchor="middle" font-size="10" fill="#5e5d59">Primary</text>
<rect x="150" y="46" width="10" height="10" rx="2" fill="#1B365D"/>
<text x="165" y="55" font-size="11" fill="#4d4c48">Core revenue · 60%</text>
<rect x="150" y="72" width="10" height="10" rx="2" fill="#5e5d59"/>
<text x="165" y="81" font-size="11" fill="#4d4c48">Services · 25%</text>
<rect x="150" y="98" width="10" height="10" rx="2" fill="#87867f"/>
<text x="165" y="107" font-size="11" fill="#4d4c48">Other · 15%</text>
</svg>
<p class="chart-caption">Revenue structure breakdown, supports 36 segments</p>
</div>
</div>
</section>
<!-- ============ 07 DECISION TABLE ============ -->
<section>
<div class="section-head">
<p class="section-num">08 · Decision Lookup</p>
<h2 class="section-title">Quick Reference</h2>
<p class="section-lede">When in doubt about what to use, consult this table. If it's not here, go back to first principles.</p>
</div>
<table class="decision">
<tr><th>Task</th><th>How</th></tr>
<tr><td class="task">Large headline</td><td class="how">serif <code>500</code>, size by hierarchy, line-height <code>1.10-1.30</code></td></tr>
<tr><td class="task">Reading body</td><td class="how">serif <code>400</code>, <code>9.5-10 pt</code>, line-height <code>1.55</code></td></tr>
<tr><td class="task">Emphasize a number</td><td class="how"><code>color: var(--brand)</code>, not bold</td></tr>
<tr><td class="task">Separate two sections</td><td class="how"><code>2.5pt</code> brand left bar, or <code>0.5pt</code> warm-gray dashed</td></tr>
<tr><td class="task">Quote someone</td><td class="how">Left <code>2pt</code> brand solid + olive text</td></tr>
<tr><td class="task">Show code</td><td class="how">Ivory bg + <code>0.5pt</code> border + <code>6pt</code> radius + mono font</td></tr>
<tr><td class="task">Primary vs secondary</td><td class="how">Primary: brand fill + ivory text; Secondary: warm-sand + charcoal</td></tr>
<tr><td class="task">Mark a special card</td><td class="how"><code>border: 0.5pt solid var(--brand)</code> or <code>border-left: 3pt</code></td></tr>
<tr><td class="task">Begin a section</td><td class="how">Serif <code>500</code> · <code>15pt</code> · size-led hierarchy, no decoration needed</td></tr>
<tr><td class="task">Document cover</td><td class="how">Single page, Display size + right-aligned author/date + generous whitespace</td></tr>
<tr><td class="task">Data card</td><td class="how">Ivory bg + <code>8pt</code> radius + serif large number, ink blue + sans small label</td></tr>
</table>
</section>
<!-- ============ 08 ANTI ============ -->
<section>
<div class="section-head">
<p class="section-num">08 · Anti-Patterns</p>
<h2 class="section-title">What to Avoid</h2>
<p class="section-lede">Exceptions are allowed, but the reason should be explicit.</p>
</div>
<div class="anti">
<div class="no">
<h5>Don't</h5>
<p>Use <code>#ffffff</code> pure white or <code>#f3f4f6</code> cool gray as page background. It makes the page feel brittle and weakens the warm parchment character.</p>
</div>
<div class="yes">
<h5>Do</h5>
<p>Always use <code>#f5f4ed</code> parchment. Set <code>@page background</code> to the same color to avoid white edges when printing.</p>
</div>
<div class="no">
<h5>Don't</h5>
<p>Use <code>rgba(27,54,93,0.18)</code> for tags. WeasyPrint renders padding and glyph areas at different opacity, creating double rectangles.</p>
</div>
<div class="yes">
<h5>Do</h5>
<p>Use the tint lookup table for equivalent solid hex <code>#E4ECF5</code>. The rendering stays clean and the color remains stable.</p>
</div>
<div class="no">
<h5>Don't</h5>
<p>Set headlines to <code>font-weight: 600</code> or heavier synthetic bold. Synthetic bold blurs strokes and degrades typographic quality.</p>
</div>
<div class="yes">
<h5>Do</h5>
<p>Body 400, headings 500 (real W05). For more presence, use size or a brand left bar, never synthetic bold.</p>
</div>
<div class="no">
<h5>Don't</h5>
<p>Use hard drop shadow <code>0 2px 8px rgba(0,0,0,0.3)</code>. Visually heavy and likely to print as dark blobs.</p>
</div>
<div class="yes">
<h5>Do</h5>
<p>Ring shadow <code>0 0 0 1pt</code> or whisper <code>rgba 0.05</code>, or simply alternate light and dark sections.</p>
</div>
</div>
</section>
<!-- ============ 09 BACKGROUND ============ -->
<section>
<div class="section-head">
<p class="section-num">09 · Background</p>
<h2 class="section-title">Design Origins</h2>
</div>
<div class="background-text">
<p>I invest in US equities and often ask AI to generate analysis reports. The earliest drafts looked like default Google Docs: plain, inconsistent, and easy to forget. I did not want each document to arrive with a different face, so I kept refining the typography, palette, and spacing until the page became something I wanted to keep reading.</p>
<p>Later, I was invited to give a talk and needed a slide deck that shared the same visual standard. That iteration pushed the system further: inline SVG diagrams, a unified warm palette, and a tighter editorial rhythm. Over time it covered the document formats I use most, so it became kami, a visual language I can reuse with confidence and send out without hesitation.</p>
</div>
</section>
<!-- ============ FOOTER ============ -->
<footer class="footer">
<div class="mark">
<svg width="56" height="56" viewBox="0 0 80 80" xmlns="http://www.w3.org/2000/svg" aria-label="Kami mark">
<rect x="6" y="6" width="68" height="68" rx="10" fill="#faf9f5" stroke="#1B365D" stroke-width="1.25"/>
<line x1="16" y1="26" x2="64" y2="26" stroke="#1B365D" stroke-width="1.25"/>
<text x="40" y="56" font-family="TsangerJinKai02, Georgia, serif" font-size="34" font-weight="500" fill="#141413" text-anchor="middle"></text>
</svg>
<div>
<p class="wm-name">Kami · 紙</p>
<p class="wm-sub">Good content deserves good paper.</p>
</div>
</div>
<div class="colophon">
<div><a href="https://github.com/tw93/kami" style="color:var(--brand); text-decoration:none; font-weight:500;">GitHub</a>&nbsp;&nbsp;·&nbsp;&nbsp;One-Pager · Long Doc · Letter · Portfolio · Resume · Slides</div>
<div style="margin-top:8px; color: var(--olive); font-family: var(--serif);">
Serif carries authority, sans function, warm grays rhythm, ink blue focus.
</div>
</div>
</footer>
</main>
<script>
(function(){
var sw=document.querySelector('.lang-switch'),last=0;
window.addEventListener('scroll',function(){
var y=window.scrollY;
if(y>80&&y>last)sw.classList.add('hidden');
else sw.classList.remove('hidden');
last=y;
},{passive:true});
})();
</script>
<script>
window.location.replace("./");
</script>
<p>Redirecting to the homepage. If it does not redirect, open <a href="./">Kami homepage</a>.</p>
</body>
</html>
+562
View File
@@ -0,0 +1,562 @@
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>Kami · Design System</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Kami(紙)は、AI時代の文書制作に向けた、あたたかな紙面デザインシステムです。単一アクセントカラー、serif中心の階層、編集的な余白で一貫した可読性をつくります。">
<link rel="icon" href="https://gw.alipayobjects.com/zos/k/vl/logo.svg" type="image/svg+xml">
<link rel="stylesheet" href="./styles.css">
</head>
<body>
<nav class="lang-switch"><a href="./index.html">EN</a><a href="./index-zh.html"></a><a href="./index-ja.html" class="active"></a></nav>
<main class="page">
<!-- ============ HERO ============ -->
<header class="hero">
<div class="eyebrow">
<span>Design System · v1.1.0<span class="eyebrow-date"> · 2026.04</span></span>
<span class="hero-links">
<a href="https://github.com/tw93/kami" target="_blank" rel="noopener" aria-label="GitHub">
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0016 8c0-4.42-3.58-8-8-8z"/>
</svg>
</a>
<a href="https://x.com/HiTw93" target="_blank" rel="noopener" aria-label="X">
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/>
</svg>
</a>
</span>
</div>
<h1>Kami<span class="cn"></span></h1>
<p class="tagline">よい内容には、よい版面を。Kami は AI 時代のための文書組版デザインシステム。文書を明快で読みやすく、記憶に残る形へ整えます。</p>
<div class="hero-tokens">
<span><b>Canvas</b>#f5f4ed</span>
<span><b>Accent</b>#1B365D</span>
<span><b>Serif</b>Newsreader / TsangerJinKai</span>
<span><b>Sans</b>Inter / Source Han Sans</span>
</div>
</header>
<!-- ============ 00 DEMOS ============ -->
<section>
<div class="section-head">
<p class="section-num">00 · See it</p>
<h2 class="section-title">出力サンプル</h2>
<p class="section-lede section-lede-nowrap">Claude に要件を渡すだけで、一枚資料、長文、正式書簡、ポートフォリオ、履歴書、スライドまで、少数の明確なルールで整った版面に仕上がります。</p>
</div>
<div class="demo-grid">
<div class="demo-card">
<a href="assets/demos/demo-tesla.pdf" target="_blank"><img src="assets/demos/demo-tesla.png" alt="Tesla company one-pager"></a>
<p class="demo-title">One-Pager</p>
<p class="demo-desc">Tesla 企業紹介、A4 1ページ</p>
</div>
<div class="demo-card">
<a href="assets/demos/demo-musk-resume.pdf" target="_blank"><img src="assets/demos/demo-musk-resume.png" alt="Musk resume"></a>
<p class="demo-title">Resume</p>
<p class="demo-desc">創業者 CV、2ページ</p>
</div>
<div class="demo-card">
<a href="assets/demos/demo-kaku.pdf" target="_blank"><img src="assets/demos/demo-kaku.png" alt="Kaku portfolio"></a>
<p class="demo-title">Portfolio</p>
<p class="demo-desc demo-desc-nowrap">Kaku プログラミング向け端末、6ページ</p>
</div>
<div class="demo-card">
<a href="assets/demos/demo-agent-slides.pdf" target="_blank"><img src="assets/demos/demo-agent-slides.png" alt="Agent slides"></a>
<p class="demo-title">Slides</p>
<p class="demo-desc">Agent キーノート、6枚</p>
</div>
</div>
</section>
<!-- ============ 01 INSTALL ============ -->
<section>
<div class="section-head">
<p class="section-num">01 · Usage</p>
<h2 class="section-title">インストールと呼び出し</h2>
<p class="section-lede">「履歴書を作って」「スタートアップ紹介の一枚資料を作って」「登壇用スライドを作って」のように要件を伝えるだけで、skill が自動起動します。スラッシュコマンドは不要です。</p>
</div>
<pre class="code"><span class="c"># Claude Code</span>
npx skills add tw93/kami -a claude-code -g -y
<span class="c"># Codex</span>
npx skills add tw93/kami -a codex -g -y</pre>
<p class="section-lede" style="font-size:14px;">Claude Desktop の場合は <a href="https://cdn.jsdelivr.net/gh/tw93/kami@main/dist/kami.zip" style="color:var(--brand); text-decoration:none; font-weight:500;">kami.zip</a> をダウンロードし、Customize &gt; Skills &gt; "+" &gt; Create skill からそのままアップロードします。</p>
</section>
<!-- ============ 02 MANIFESTO ============ -->
<section>
<div class="section-head">
<p class="section-num">02 · Manifesto</p>
<h2 class="section-title">設計原則</h2>
</div>
<p class="manifesto">暖かい紙色を土台に、強調色は <em>ink blue</em> のみ。階層は serif で作り、強い影や派手な配色は退かせます。印刷文書に向けて、安定し、明快で、読みやすい紙面を保つための設計です。</p>
<ol class="rules">
<li><span class="n">1</span><p>ページ背景は parchment <code>#f5f4ed</code>、純白は使わない</p></li>
<li><span class="n">2</span><p>強調色は ink blue <code>#1B365D</code> のみ、第二の彩色は入れない</p></li>
<li><span class="n">3</span><p>グレーはすべて暖色寄り、冷たいブルーグレーは禁止</p></li>
<li><span class="n">4</span><p>英語は見出しと本文を serif、中国語は見出しを serif、本文は読みやすい本文体</p></li>
<li><span class="n">5</span><p>serif 本文 400、見出し 500、合成ボールドは使わない</p></li>
<li><span class="n">6</span><p>行間は 2 段階、見出し 1.1-1.3 / 本文 1.4-1.45</p></li>
<li><span class="n">7</span><p>タグ背景は hex の実色のみ、rgba は使わない。WeasyPrint で二重矩形が出るためです</p></li>
<li><span class="n">8</span><p>影は ring か whisper のみ、強い drop shadow は使わない</p></li>
</ol>
</section>
<!-- ============ 03 COLOR ============ -->
<section>
<div class="section-head">
<p class="section-num">03 · Color</p>
<h2 class="section-title">暖色で抑制する</h2>
<p class="section-lede">強調色は 1 つ、ニュートラルは暖色のみ、寒色は入れない。Ink blue の使用面積はページ全体の <b>5%</b> 以内に抑えます。超えると統制ではなく過剰になります。</p>
</div>
<h3 class="subhead">Canvas</h3>
<div class="swatches">
<div class="swatch"><div class="chip" style="background:#f5f4ed"></div><div class="info"><p class="name">Parchment</p><p class="role">Page background, the emotional foundation</p><span class="hex">#F5F4ED</span></div></div>
<div class="swatch"><div class="chip" style="background:#faf9f5"></div><div class="info"><p class="name">Ivory</p><p class="role">Cards / elevated surfaces</p><span class="hex">#FAF9F5</span></div></div>
<div class="swatch"><div class="chip" style="background:#e8e6dc"></div><div class="info"><p class="name">Warm Sand</p><p class="role">Button default / interactive surfaces</p><span class="hex">#E8E6DC</span></div></div>
<div class="swatch dark"><div class="chip" style="background:#141413"></div><div class="info"><p class="name" style="color:var(--deep-dark)">Deep Dark</p><p class="role">Dark theme page base, not pure black</p><span class="hex">#141413</span></div></div>
</div>
<h3 class="subhead">Brand</h3>
<div class="swatches">
<div class="swatch brand"><div class="chip" style="background:#1B365D"></div><div class="info"><p class="name">Ink Blue</p><p class="role">Primary color · CTA · quote bar · section overline</p><span class="hex">#1B365D</span></div></div>
<div class="swatch"><div class="chip" style="background:#2D5A8A"></div><div class="info"><p class="name">Ink Light</p><p class="role">Links on dark surfaces · lighter variant</p><span class="hex">#2D5A8A</span></div></div>
<div class="swatch"><div class="chip" style="background:#30302e"></div><div class="info"><p class="name" style="color:var(--deep-dark)">Dark Surface</p><p class="role">Dark theme container · warm charcoal</p><span class="hex">#30302E</span></div></div>
<div class="swatch"><div class="chip" style="background:#b53333"></div><div class="info"><p class="name">Error</p><p class="role">Error state, deep warm red</p><span class="hex">#B53333</span></div></div>
</div>
<h3 class="subhead">Warm Neutrals</h3>
<div class="swatches" style="grid-template-columns: repeat(6, 1fr);">
<div class="swatch"><div class="chip" style="background:#141413"></div><div class="info"><p class="name">Near Black</p><span class="hex">#141413</span></div></div>
<div class="swatch"><div class="chip" style="background:#3d3d3a"></div><div class="info"><p class="name">Dark Warm</p><span class="hex">#3D3D3A</span></div></div>
<div class="swatch"><div class="chip" style="background:#4d4c48"></div><div class="info"><p class="name">Charcoal</p><span class="hex">#4D4C48</span></div></div>
<div class="swatch"><div class="chip" style="background:#5e5d59"></div><div class="info"><p class="name">Olive</p><span class="hex">#5E5D59</span></div></div>
<div class="swatch"><div class="chip" style="background:#87867f"></div><div class="info"><p class="name">Stone</p><span class="hex">#87867F</span></div></div>
<div class="swatch"><div class="chip" style="background:#b0aea5"></div><div class="info"><p class="name">Warm Silver</p><span class="hex">#B0AEA5</span></div></div>
</div>
<h3 class="subhead">Tag Tints: rgba から実色 hex へ</h3>
<p style="font-size:14px; color: var(--olive); margin: 0 0 16px; line-height: 1.55;">下記は ink blue <code style="font-family:var(--mono);font-size:12px;background:#EEF2F7;color:var(--brand);padding:1px 5px;border-radius:2px">#1B365D</code> を parchment 上に重ねたときの等価な実色 hex です。タグ背景は <code style="font-family:var(--mono);font-size:12px;background:#EEF2F7;color:var(--brand);padding:1px 5px;border-radius:2px">rgba()</code> を避け、WeasyPrint の二重矩形を防ぎます。</p>
<div class="tint-strip">
<div class="tint"><span class="flag">&nbsp;</span><span class="sample" style="background:#EEF2F7">Tag sample</span><p class="opacity">0.08</p><span class="hex">#EEF2F7</span></div>
<div class="tint"><span class="flag">&nbsp;</span><span class="sample" style="background:#E4ECF5">Tag sample</span><p class="opacity">0.14</p><span class="hex">#E4ECF5</span></div>
<div class="tint default"><span class="flag">Default</span><span class="sample" style="background:#E4ECF5">Tag sample</span><p class="opacity">0.18</p><span class="hex">#E4ECF5</span></div>
<div class="tint"><span class="flag">&nbsp;</span><span class="sample" style="background:#D0DCE9">Tag sample</span><p class="opacity">0.22</p><span class="hex">#D0DCE9</span></div>
<div class="tint"><span class="flag">&nbsp;</span><span class="sample" style="background:#D6E1EE">Tag sample</span><p class="opacity">0.30</p><span class="hex">#D6E1EE</span></div>
</div>
</section>
<!-- ============ 04 TYPE ============ -->
<section>
<div class="section-head">
<p class="section-num">04 · Typography</p>
<h2 class="section-title">タイポグラフィ体系</h2>
<p class="section-lede">階層は serif、機能要素は sans。serif は本文 400、見出し 500 を基本にします。</p>
</div>
<div class="family-grid">
<div class="family serif">
<p class="glyph">Aa</p>
<p class="role">Serif · 見出し + 本文</p>
<p class="name">Newsreader / TsangerJinKai02</p>
<p class="use">見出し、本文、引用、数値強調に使用。英語は Newsreader、中国語は TsangerJinKai02 を使用。</p>
</div>
<div class="family sans">
<p class="glyph">Aa</p>
<p class="role">Sans · UI</p>
<p class="name">Inter / Source Han Sans</p>
<p class="use">ラベル、ボタン、メタ情報、eyebrow に使用。標準は 400、ラベルは 500-600。中国語本文も sans を使用。</p>
</div>
<div class="family mono">
<p class="glyph">&lt;/&gt;</p>
<p class="role">Mono · コード</p>
<p class="name">JetBrains Mono</p>
<p class="use">コードブロック、バージョン番号、hex 値、等幅数字に使用。</p>
</div>
</div>
<h3 class="subhead">スケール(印刷 pt、画面 px = pt x 1.33</h3>
<div class="type-grid">
<div class="type-row">
<div class="type-label">Display</div>
<div class="type-sample display">よい文書は<br><span class="display-line-nowrap">明快で、安定し、読みやすい</span></div>
<div class="type-specs"><b>36-48 pt</b><br>weight 500<br>line 1.10</div>
</div>
<div class="type-row">
<div class="type-label">H1 Section</div>
<div class="type-sample h1">Color System · Palette</div>
<div class="type-specs"><b>18-22 pt</b><br>weight 500<br>line 1.20</div>
</div>
<div class="type-row">
<div class="type-label">H2 Subsection</div>
<div class="type-sample h2">Canvas · Warm Neutrals</div>
<div class="type-specs"><b>14-16 pt</b><br>weight 500<br>line 1.25</div>
</div>
<div class="type-row">
<div class="type-label">H3 Item</div>
<div class="type-sample h3">Tag Tints · Solid Hex</div>
<div class="type-specs"><b>12-13 pt</b><br>weight 500<br>line 1.30</div>
</div>
<div class="type-row">
<div class="type-label">Body</div>
<div class="type-sample body">Ink blue は各ページで 5% 以内。超えると抑制ではなく過剰になります。グレーは黄褐色の下地を持ち、R ≈ G > B を目安にします。</div>
<div class="type-specs"><b>9.5-10 pt</b><br>weight 400<br>line 1.55</div>
</div>
<div class="type-row">
<div class="type-label">Caption</div>
<div class="type-sample caption">図注、脚注、補足説明。色は olive または stone へ一段落とします。</div>
<div class="type-specs"><b>8.5-9 pt</b><br>weight 400<br>line 1.45</div>
</div>
<div class="type-row">
<div class="type-label">Label</div>
<div class="type-sample label">Design System · v1.1.0</div>
<div class="type-specs"><b>7.5-8 pt</b><br>weight 600<br>line 1.35</div>
</div>
</div>
</section>
<!-- ============ 05 SPACING ============ -->
<section>
<div class="section-head">
<p class="section-num">05 · Spacing &amp; Shape</p>
<h2 class="section-title">リズムと形</h2>
<p class="section-lede">基本単位は 4pt。情報密度が高いほどマージンを詰め、フォーマルな文書ほど広く取ります。</p>
</div>
<table class="space-table">
<tr><th>Scale</th><th>Value</th><th>Use</th><th></th></tr>
<tr><td class="name">xs</td><td class="val">2-3 pt</td><td class="use">Inline elements</td><td class="bar"><span style="width:6px"></span></td></tr>
<tr><td class="name">sm</td><td class="val">4-5 pt</td><td class="use">Tag padding · tight layout</td><td class="bar"><span style="width:12px"></span></td></tr>
<tr><td class="name">md</td><td class="val">8-10 pt</td><td class="use">Component internals</td><td class="bar"><span style="width:24px"></span></td></tr>
<tr><td class="name">lg</td><td class="val">16-20 pt</td><td class="use">Between components · card padding</td><td class="bar"><span style="width:48px"></span></td></tr>
<tr><td class="name">xl</td><td class="val">24-32 pt</td><td class="use">Section title margins</td><td class="bar"><span style="width:72px"></span></td></tr>
<tr><td class="name">2xl</td><td class="val">40-60 pt</td><td class="use">Between major sections</td><td class="bar"><span style="width:140px"></span></td></tr>
<tr><td class="name">3xl</td><td class="val">80-120 pt</td><td class="use">Between long-doc chapters</td><td class="bar"><span style="width:240px"></span></td></tr>
</table>
<h3 class="subhead">Radii</h3>
<div class="radii">
<div class="radius"><div class="box" style="border-radius:4px"></div><div class="r"><b>4</b> pt</div><div class="label">Tight</div></div>
<div class="radius"><div class="box" style="border-radius:6px"></div><div class="r"><b>6</b> pt</div><div class="label">Code block</div></div>
<div class="radius"><div class="box" style="border-radius:8px"></div><div class="r"><b>8</b> pt</div><div class="label">Default card</div></div>
<div class="radius"><div class="box" style="border-radius:12px"></div><div class="r"><b>12</b> pt</div><div class="label">Container</div></div>
<div class="radius"><div class="box" style="border-radius:16px"></div><div class="r"><b>16</b> pt</div><div class="label">Feature card</div></div>
<div class="radius"><div class="box" style="border-radius:24px"></div><div class="r"><b>24</b> pt</div><div class="label">Large container</div></div>
<div class="radius"><div class="box" style="border-radius:32px"></div><div class="r"><b>32</b> pt</div><div class="label">Hero</div></div>
</div>
<h3 class="subhead">奥行き: 3 つの影の方法</h3>
<p style="font-size:14px; color: var(--olive); margin: 0 0 12px; line-height: 1.55;">Kami は強い影を使いません。奥行きは ring shadow、whisper shadow、明暗切り替えで作ります。</p>
<div class="shadow-row">
<div class="shadow-demo sd-ring">
<p class="t">Ring Shadow</p>
<p class="s">0 0 0 1pt var(--ring-warm)<br>&nbsp;<br>ボタン・カード hover</p>
</div>
<div class="shadow-demo sd-whisper">
<p class="t">Whisper Shadow</p>
<p class="s">0 4pt 24pt rgba(0,0,0,0.05)<br>&nbsp;<br>軽い浮き上がり・特集カード</p>
</div>
<div class="shadow-demo sd-alt">
<p class="t">Light ⇌ Dark</p>
<p class="s">parchment ⇌ deep-dark<br>&nbsp;<br>セクション単位・最も強い対比</p>
</div>
</div>
</section>
<!-- ============ 06 COMPONENTS ============ -->
<section>
<div class="section-head">
<p class="section-num">06 · Components</p>
<h2 class="section-title">モジュール</h2>
<p class="section-lede">必要最小限の固定セットを使い、文書上の具体的な課題に効く場面だけで使います。</p>
</div>
<div class="comp-grid">
<div class="comp">
<h4>Buttons</h4>
<p class="hint">強い drop shadow ではなく ring shadow ・角丸 8pt</p>
<div class="demo" style="display:flex; gap:10px; flex-wrap:wrap;">
<span class="btn btn-primary">主要 CTA</span>
<span class="btn btn-secondary">副次 CTA</span>
<span class="btn btn-ghost">Ghost</span>
</div>
</div>
<div class="comp">
<h4>タグ(3 段階)</h4>
<p class="hint">実色 hex ・弱から強へ選択</p>
<div class="demo" style="display:flex; gap:10px; flex-wrap:wrap; align-items:center;">
<span class="tag calm">Light 0.08</span>
<span class="tag standard">Standard 0.18</span>
<span class="tag brush">Brush gradient</span>
</div>
</div>
<div class="comp">
<h4>引用</h4>
<p class="hint">左 2pt のブランド実線 + olive テキスト</p>
<blockquote class="quote">装飾より明快さを優先する。ひとつの Yes のために多くの No を選ぶ。</blockquote>
</div>
<div class="comp">
<h4>指標カード</h4>
<p class="hint">serif 数字 + sans ラベル ・ tabular-nums</p>
<div class="metrics demo">
<div class="metric"><span class="metric-value">6</span><span class="metric-label">文書タイプ</span></div>
<div class="metric"><span class="metric-value">1</span><span class="metric-label">強調色</span></div>
<div class="metric"><span class="metric-value">8</span><span class="metric-label">基本ルール</span></div>
</div>
</div>
<div class="comp">
<h4>セクション見出し</h4>
<p class="hint">serif 500 ・ 15pt ・サイズ主導の階層、装飾は不要</p>
<div class="demo">
<span class="sample-section-title">Selected Works · Projects</span>
</div>
</div>
<div class="comp">
<h4>Dash List</h4>
<p class="hint">丸点ではなくダッシュ ・ 編集文体</p>
<ul class="dash demo">
<li>紙色は暖色、純白は使わない</li>
<li>強調色は ink blue だけ、第二色は入れない</li>
<li>階層は serif で担保する</li>
</ul>
</div>
<div class="comp">
<h4>コードブロック</h4>
<p class="hint">ivory 背景 + 0.5pt 枠線 + 6pt 角丸</p>
<pre class="code"><span class="c">// 温かみは色数ではなく、抑制で作る</span>
canvas = parchment; accent = <span class="k">ink_blue</span>
palette = <span class="k">warm_neutrals</span> cool_grays
document = <span class="k">serif</span> × hierarchy + generous_space
beauty = constraints × <span class="k">intention</span> ÷ noise</pre>
</div>
<div class="comp">
<h4>特集カード</h4>
<p class="hint">whisper shadow + 16pt 角丸</p>
<div class="demo" style="background:var(--ivory); border-radius:16px; padding:18px 20px; box-shadow: 0 4px 24px rgba(0,0,0,0.05); border: 1px solid var(--border-cream);">
<p style="font-family:var(--serif); font-weight:500; font-size:15px; margin:0 0 6px; color:var(--near-black);">Tesla 企業紹介 · One-Pager</p>
<p style="font-size:12px; color:var(--olive); margin:0 0 10px; line-height:1.5;">A4 1ページ ・ 指標カード 4 枚 + 本文 3 セクション + タイムライン</p>
<div style="display:flex; gap:8px; flex-wrap:wrap;">
<span class="tag calm">日本語</span>
<span class="tag calm">A4 x 1</span>
<span class="tag calm">Ink Blue</span>
</div>
</div>
</div>
</div>
</section>
<!-- ============ 07 DIAGRAMS ============ -->
<section>
<div class="section-head">
<p class="section-num">07 · Diagrams</p>
<h2 class="section-title">インライン図表</h2>
<p class="section-lede">アーキテクチャ、フロー、データ可視化をカバーする 12 種の SVG 図表を用意。必要な種類を Claude に伝えると、Kami の色と書体規則に沿って文書へ直接埋め込みます。</p>
</div>
<div class="chart-showcase">
<!-- Architecture mini -->
<div class="chart-card">
<div class="chart-label">アーキテクチャ図 · Architecture</div>
<svg viewBox="0 0 280 160" xmlns="http://www.w3.org/2000/svg" style="font-family:'Inter','TsangerJinKai02',sans-serif">
<rect width="280" height="160" fill="#f5f4ed"/>
<!-- Browser → API -->
<path d="M 84,79.5 L 103,79.5 L 103,78 L 107,80 L 103,82 L 103,80.5 L 84,80.5 Z" fill="#87867f"/>
<!-- API → Database -->
<path d="M 172,79.5 L 191,79.5 L 191,78 L 195,80 L 191,82 L 191,80.5 L 172,80.5 Z" fill="#5e5d59"/>
<!-- Node: Client -->
<rect x="20" y="58" width="64" height="44" rx="3" fill="#f5f4ed" stroke="#87867f" stroke-width="0.8"/>
<text x="52" y="76" text-anchor="middle" font-size="7" letter-spacing="0.5" fill="#87867f">CLIENT</text>
<text x="52" y="91" text-anchor="middle" font-size="9" font-weight="600" fill="#141413">Browser</text>
<!-- Node: API (focal) -->
<rect x="108" y="58" width="64" height="44" rx="3" fill="#EEF2F7" stroke="#1B365D" stroke-width="1"/>
<text x="140" y="76" text-anchor="middle" font-size="7" letter-spacing="0.5" fill="#1B365D">SERVICE</text>
<text x="140" y="91" text-anchor="middle" font-size="9" font-weight="600" fill="#141413">API</text>
<!-- Node: Database -->
<rect x="196" y="58" width="64" height="44" rx="3" fill="#f5f4ed" stroke="#5e5d59" stroke-width="0.8"/>
<text x="228" y="76" text-anchor="middle" font-size="7" letter-spacing="0.5" fill="#87867f">DATA</text>
<text x="228" y="91" text-anchor="middle" font-size="9" font-weight="600" fill="#141413">Database</text>
</svg>
<p class="chart-caption">システム構成と接続関係、焦点ノードを 1 つに絞る</p>
</div>
<!-- Flowchart mini -->
<div class="chart-card">
<div class="chart-label">フローチャート · Flowchart</div>
<svg viewBox="0 0 280 160" xmlns="http://www.w3.org/2000/svg" style="font-family:'Inter','TsangerJinKai02',sans-serif">
<rect width="280" height="160" fill="#f5f4ed"/>
<rect x="100" y="8" width="80" height="28" rx="14" fill="#141413"/>
<text x="140" y="27" text-anchor="middle" font-size="9" font-weight="600" fill="#f5f4ed">Start</text>
<!-- Start → diamond -->
<path d="M 139.5,36 L 139.5,48 L 138,48 L 140,52 L 142,48 L 140.5,48 L 140.5,36 Z" fill="#5e5d59"/>
<polygon points="140,52 172,72 140,92 108,72" fill="#f5f4ed" stroke="#141413" stroke-width="1"/>
<text x="140" y="76" text-anchor="middle" font-size="8" fill="#141413">Valid?</text>
<!-- Yes right -->
<path d="M 172,71.5 L 204,71.5 L 204,70 L 208,72 L 204,74 L 204,72.5 L 172,72.5 Z" fill="#1B365D"/>
<text x="186" y="68" text-anchor="middle" font-size="8" fill="#1B365D">Yes</text>
<rect x="208" y="56" width="64" height="32" rx="4" fill="#EEF2F7" stroke="#1B365D" stroke-width="1"/>
<text x="240" y="77" text-anchor="middle" font-size="9" font-weight="600" fill="#141413">Process</text>
<!-- No down -->
<path d="M 139.5,92 L 139.5,112 L 138,112 L 140,116 L 142,112 L 140.5,112 L 140.5,92 Z" fill="#5e5d59"/>
<text x="151" y="106" font-size="8" fill="#5e5d59">No</text>
<rect x="96" y="116" width="88" height="32" rx="4" fill="#f5f4ed" stroke="#87867f" stroke-width="0.8"/>
<text x="140" y="136" text-anchor="middle" font-size="9" font-weight="600" fill="#5e5d59">Reject</text>
</svg>
<p class="chart-caption">分岐と yes/no の流れ、成功側を主軸に配置</p>
</div>
<!-- Bar chart (shifted left) -->
<div class="chart-card">
<div class="chart-label">棒グラフ · Bar Chart</div>
<svg viewBox="0 0 280 160" xmlns="http://www.w3.org/2000/svg" style="font-family:'Inter','TsangerJinKai02',sans-serif">
<rect width="280" height="160" fill="#f5f4ed"/>
<rect x="18" y="78" width="42" height="50" rx="3" fill="#87867f"/>
<rect x="78" y="62" width="42" height="66" rx="3" fill="#5e5d59"/>
<rect x="138" y="44" width="42" height="84" rx="3" fill="#2d4e7a"/>
<rect x="198" y="30" width="42" height="98" rx="3" fill="#1B365D"/>
<text x="39" y="145" text-anchor="middle" font-size="10" fill="#87867f">Q1</text>
<text x="99" y="145" text-anchor="middle" font-size="10" fill="#5e5d59">Q2</text>
<text x="159" y="145" text-anchor="middle" font-size="10" fill="#4d4c48">Q3</text>
<text x="219" y="145" text-anchor="middle" font-size="10" fill="#4d4c48">Q4</text>
<text x="39" y="73" text-anchor="middle" font-size="9" font-weight="600" fill="#87867f">180</text>
<text x="99" y="57" text-anchor="middle" font-size="9" font-weight="600" fill="#5e5d59">220</text>
<text x="159" y="39" text-anchor="middle" font-size="9" font-weight="600" fill="#2d4e7a">195</text>
<text x="219" y="25" text-anchor="middle" font-size="9" font-weight="600" fill="#1B365D">240</text>
</svg>
<p class="chart-caption">四半期売上の比較、単色または段階色で表現</p>
</div>
<!-- Donut chart -->
<div class="chart-card">
<div class="chart-label">ドーナツチャート · Donut Chart</div>
<svg viewBox="0 0 280 160" xmlns="http://www.w3.org/2000/svg" style="font-family:'Inter','TsangerJinKai02',sans-serif">
<rect width="280" height="160" fill="#f5f4ed"/>
<path d="M 80,25 A 55,55 0 1,1 47.7,124.5 L 62.4,104.3 A 30,30 0 1,0 80,50 Z" fill="#1B365D" stroke="white" stroke-width="1.5"/>
<path d="M 47.7,124.5 A 55,55 0 0,1 35.5,47.7 L 55.7,62.4 A 30,30 0 0,0 62.4,104.3 Z" fill="#5e5d59" stroke="white" stroke-width="1.5"/>
<path d="M 35.5,47.7 A 55,55 0 0,1 80,25 L 80,50 A 30,30 0 0,0 55.7,62.4 Z" fill="#87867f" stroke="white" stroke-width="1.5"/>
<text x="80" y="77" text-anchor="middle" font-size="13" font-weight="600" fill="#1B365D">60%</text>
<text x="80" y="92" text-anchor="middle" font-size="10" fill="#5e5d59">Primary</text>
<rect x="150" y="46" width="10" height="10" rx="2" fill="#1B365D"/>
<text x="165" y="55" font-size="11" fill="#4d4c48">Core revenue · 60%</text>
<rect x="150" y="72" width="10" height="10" rx="2" fill="#5e5d59"/>
<text x="165" y="81" font-size="11" fill="#4d4c48">Services · 25%</text>
<rect x="150" y="98" width="10" height="10" rx="2" fill="#87867f"/>
<text x="165" y="107" font-size="11" fill="#4d4c48">Other · 15%</text>
</svg>
<p class="chart-caption">売上構成の内訳、3-6 セグメントに対応</p>
</div>
</div>
</section>
<!-- ============ 07 DECISION TABLE ============ -->
<section>
<div class="section-head">
<p class="section-num">08 · Decision Lookup</p>
<h2 class="section-title">クイック参照</h2>
<p class="section-lede">どの手法を使うか迷ったらこの表を参照。該当がなければ基本原則に戻って判断します。</p>
</div>
<table class="decision">
<tr><th>場面</th><th>処方</th></tr>
<tr><td class="task">大きな見出し</td><td class="how">serif <code>500</code>、サイズで階層化、行間 <code>1.10-1.30</code></td></tr>
<tr><td class="task">本文(可読重視)</td><td class="how">serif <code>400</code><code>9.5-10 pt</code>、行間 <code>1.55</code></td></tr>
<tr><td class="task">数値を強調</td><td class="how"><code>color: var(--brand)</code> を使い、太字には頼らない</td></tr>
<tr><td class="task">セクション分離</td><td class="how"><code>2.5pt</code> の brand 実線、または <code>0.5pt</code> 暖色破線</td></tr>
<tr><td class="task">引用を置く</td><td class="how"><code>2pt</code> brand 実線 + olive テキスト</td></tr>
<tr><td class="task">コードを載せる</td><td class="how">ivory 背景 + <code>0.5pt</code> 枠線 + <code>6pt</code> 角丸 + mono 書体</td></tr>
<tr><td class="task">主ボタンと副ボタン</td><td class="how">主: brand 塗り + ivory 文字、副: warm-sand + charcoal</td></tr>
<tr><td class="task">特別カードを示す</td><td class="how"><code>border: 0.5pt solid var(--brand)</code> または <code>border-left: 3pt</code></td></tr>
<tr><td class="task">セクション開始</td><td class="how">serif <code>500</code><code>15pt</code> ・サイズ主導の階層、装飾は不要</td></tr>
<tr><td class="task">表紙を作る</td><td class="how">1 ページ構成、Display サイズ + 右寄せの著者/日付 + 広い余白</td></tr>
<tr><td class="task">データカード</td><td class="how">ivory 背景 + <code>8pt</code> 角丸 + serif 大数字 + sans 小ラベル</td></tr>
</table>
</section>
<!-- ============ 08 ANTI ============ -->
<section>
<div class="section-head">
<p class="section-num">08 · Anti-Patterns</p>
<h2 class="section-title">避けるべきこと</h2>
<p class="section-lede">例外は許容できますが、理由を明示できる場合に限ります。</p>
</div>
<div class="anti">
<div class="no">
<h5>Don't</h5>
<p>ページ背景に <code>#ffffff</code> の純白や <code>#f3f4f6</code> の寒色グレーを使わない。紙面が硬く見え、温かい紙質感が崩れます。</p>
</div>
<div class="yes">
<h5>Do</h5>
<p>常に <code>#f5f4ed</code> の parchment を使う。印刷時の白フチを防ぐため、<code>@page background</code> も同色に設定します。</p>
</div>
<div class="no">
<h5>Don't</h5>
<p>タグに <code>rgba(27,54,93,0.18)</code> を使わない。WeasyPrint で文字領域と余白領域の不透明度がずれ、二重矩形が出ます。</p>
</div>
<div class="yes">
<h5>Do</h5>
<p>tint 対照表の実色 hex <code>#E4ECF5</code> を使う。描画が安定し、色ぶれを防げます。</p>
</div>
<div class="no">
<h5>Don't</h5>
<p>見出しに <code>font-weight: 600</code> 以上の合成ボールドを使わない。線が潰れて可読性と品位が落ちます。</p>
</div>
<div class="yes">
<h5>Do</h5>
<p>本文 400、見出し 500(実フォント W05)。強調が必要ならサイズや左バーで対応し、合成ボールドは使わない。</p>
</div>
<div class="no">
<h5>Don't</h5>
<p>強い drop shadow <code>0 2px 8px rgba(0,0,0,0.3)</code> を使わない。重く見え、印刷時に黒く潰れやすい。</p>
</div>
<div class="yes">
<h5>Do</h5>
<p>ring shadow <code>0 0 0 1pt</code> か whisper <code>rgba 0.05</code> を使う。あるいは明暗の切り替えで階層を作る。</p>
</div>
</div>
</section>
<!-- ============ 09 BACKGROUND ============ -->
<section>
<div class="section-head">
<p class="section-num">09 · Background</p>
<h2 class="section-title">設計の背景</h2>
</div>
<div class="background-text">
<p>私は米国株投資をしており、AI に分析レポートを作らせることが多くあります。初期の出力は既定の Google Docs のように平板で、統一感がなく、記憶にも残りませんでした。文書ごとに顔が変わる状態を避けるため、書体、配色、余白を繰り返し調整し、読み続けたくなる紙面へ寄せていきました。</p>
<p>その後、登壇の機会で同じ視覚基準のスライドが必要になり、設計をさらに前進させました。inline SVG 図表、暖色パレットの統一、編集リズムの引き締めです。使う文書形式を横断して再利用できるようになり、Kami は安心して使い続けられる視覚言語になりました。</p>
</div>
</section>
<!-- ============ FOOTER ============ -->
<footer class="footer">
<div class="mark">
<svg width="56" height="56" viewBox="0 0 80 80" xmlns="http://www.w3.org/2000/svg" aria-label="Kami mark">
<rect x="6" y="6" width="68" height="68" rx="10" fill="#faf9f5" stroke="#1B365D" stroke-width="1.25"/>
<line x1="16" y1="26" x2="64" y2="26" stroke="#1B365D" stroke-width="1.25"/>
<text x="40" y="56" font-family="TsangerJinKai02, Georgia, serif" font-size="34" font-weight="500" fill="#141413" text-anchor="middle"></text>
</svg>
<div>
<p class="wm-name">Kami · 紙</p>
<p class="wm-sub">よい内容には、よい版面を。</p>
</div>
</div>
<div class="colophon">
<div><a href="https://github.com/tw93/kami" style="color:var(--brand); text-decoration:none; font-weight:500;">GitHub</a>&nbsp;&nbsp;·&nbsp;&nbsp;One-Pager · Long Doc · Letter · Portfolio · Resume · Slides</div>
<div style="margin-top:8px; color: var(--olive); font-family: var(--serif);">
Serif が階層を担い、sans が機能を担い、暖色グレーがリズムを作り、ink blue が焦点を作ります。
</div>
</div>
</footer>
</main>
<script>
(function(){
var sw=document.querySelector('.lang-switch'),last=0;
window.addEventListener('scroll',function(){
var y=window.scrollY;
if(y>80&&y>last)sw.classList.add('hidden');
else sw.classList.remove('hidden');
last=y;
},{passive:true});
})();
</script>
</body>
</html>
+592
View File
@@ -0,0 +1,592 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<title>Kami · Design System</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Kami 紙:面向 AI 文档生成的温暖纸质感设计系统,以单一强调色、serif 层级与编辑级留白,收束成稳定的排版。">
<link rel="icon" href="https://gw.alipayobjects.com/zos/k/vl/logo.svg" type="image/svg+xml">
<link rel="stylesheet" href="./styles.css">
</head>
<body>
<nav class="lang-switch"><a href="./index.html">EN</a><a href="./index-zh.html" class="active"></a><a href="./index-ja.html"></a></nav>
<main class="page">
<!-- ============ HERO ============ -->
<header class="hero">
<div class="eyebrow">
<span>Design System · v1.1.0<span class="eyebrow-date"> · 2026.04</span></span>
<span class="hero-links">
<a href="https://github.com/tw93/kami" target="_blank" rel="noopener" aria-label="GitHub">
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0016 8c0-4.42-3.58-8-8-8z"/>
</svg>
</a>
<a href="https://x.com/HiTw93" target="_blank" rel="noopener" aria-label="X">
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/>
</svg>
</a>
</span>
</div>
<h1>Kami<span class="cn"></span></h1>
<p class="tagline">好内容,值得好版面。Kami 是一套适合 AI 时代的排版设计系统,让文档更清晰好读,也容易记住。</p>
<div class="hero-tokens">
<span><b>Canvas</b>#f5f4ed</span>
<span><b>Accent</b>#1B365D</span>
<span><b>Serif</b>TsangerJinKai / Newsreader</span>
<span><b>Sans</b>Source Han Sans / Inter</span>
</div>
</header>
<!-- ============ 00 DEMOS ============ -->
<section>
<div class="section-head">
<p class="section-num">00 · See it</p>
<h2 class="section-title">输出样本</h2>
<p class="section-lede">把文档需求交给 Claude。无论是一页纸、长文、正式信件、作品集、简历还是幻灯片,Kami 都用少量清晰规则,把提示词变成可交付的排版。</p>
</div>
<div class="demo-grid">
<div class="demo-card">
<a href="assets/demos/demo-tesla.pdf" target="_blank"><img src="assets/demos/demo-tesla.png" alt="Tesla 公司介绍"></a>
<p class="demo-title">One-Pager · 中文</p>
<p class="demo-desc">Tesla 公司介绍,单页 A4</p>
</div>
<div class="demo-card">
<a href="assets/demos/demo-musk-resume.pdf" target="_blank"><img src="assets/demos/demo-musk-resume.png" alt="Musk resume"></a>
<p class="demo-title">Resume · English</p>
<p class="demo-desc">Founder CV2 页</p>
</div>
<div class="demo-card">
<a href="assets/demos/demo-kaku.pdf" target="_blank"><img src="assets/demos/demo-kaku.png" alt="Kaku 作品集"></a>
<p class="demo-title">作品集 · 日本語</p>
<p class="demo-desc">Kaku 终端模拟器(日文版),6 页</p>
</div>
<div class="demo-card">
<a href="assets/demos/demo-agent-slides.pdf" target="_blank"><img src="assets/demos/demo-agent-slides.png" alt="Agent slides"></a>
<p class="demo-title">Slides · English</p>
<p class="demo-desc">Agent keynote, 6 slides</p>
</div>
</div>
</section>
<!-- ============ 01 INSTALL ============ -->
<section>
<div class="section-head">
<p class="section-num">01 · Usage</p>
<h2 class="section-title">安装与调用</h2>
<p class="section-lede">直接告诉 Claude 你要什么,比如「帮我排版一份白皮书」「做一份简历」「帮我做一份作品集」,skill 会自动触发,无需斜杠命令。</p>
</div>
<pre class="code"><span class="c"># Claude Code</span>
npx skills add tw93/kami -a claude-code -g -y
<span class="c"># Codex</span>
npx skills add tw93/kami -a codex -g -y</pre>
<p class="section-lede" style="font-size:14px;">Claude Desktop:下载 <a href="https://cdn.jsdelivr.net/gh/tw93/kami@main/dist/kami.zip" style="color:var(--brand); text-decoration:none; font-weight:500;">kami.zip</a>,在 Customize &gt; Skills &gt; "+" &gt; Create skill 中直接上传。</p>
</section>
<!-- ============ 02 MANIFESTO ============ -->
<section>
<div class="section-head">
<p class="section-num">02 · Manifesto</p>
<h2 class="section-title">设计原则</h2>
</div>
<p class="manifesto">暖米纸底,<em>油墨蓝</em>点缀,serif 承担层级,硬阴影与花哨配色都退后;这套系统面向印刷文档,负责把内容收束成稳定、清晰、易读的版面。</p>
<ol class="rules">
<li><span class="n">1</span><p>页面背景 parchment <code>#f5f4ed</code>,不用纯白</p></li>
<li><span class="n">2</span><p>强调色只有油墨蓝 <code>#1B365D</code>,不引入第二种彩色</p></li>
<li><span class="n">3</span><p>所有灰色暖调 (yellow-brown undertone),禁止冷蓝灰</p></li>
<li><span class="n">4</span><p>英文 serif 通吃标题和正文,中文标题用 serif、正文用 sans</p></li>
<li><span class="n">5</span><p>Serif 正文 400,标题 500,不用合成 bold</p></li>
<li><span class="n">6</span><p>行距三档:紧凑标题 1.11.3 / 密排 1.41.45 / 阅读 1.51.55</p></li>
<li><span class="n">7</span><p>Tag 背景必须实色 hex,禁止 rgbaWeasyPrint 双层矩形 bug</p></li>
<li><span class="n">8</span><p>阴影只用 ring 或 whisper shadow,不用硬 drop shadow</p></li>
</ol>
</section>
<!-- ============ 01 COLOR ============ -->
<section>
<div class="section-head">
<p class="section-num">03 · Color</p>
<h2 class="section-title">暖调克制</h2>
<p class="section-lede">唯一强调色、纯暖调中性灰、零冷色;油墨蓝全文档不超过 <b>5%</b> 面积,超过就从克制变成堆砌。</p>
</div>
<h3 class="subhead">画布 · Canvas</h3>
<div class="swatches">
<div class="swatch"><div class="chip" style="background:#f5f4ed"></div><div class="info"><p class="name">Parchment</p><p class="role">页面底色,也是整套设计的温度来源</p><span class="hex">#F5F4ED</span></div></div>
<div class="swatch"><div class="chip" style="background:#faf9f5"></div><div class="info"><p class="name">Ivory</p><p class="role">卡片 · 浮起容器</p><span class="hex">#FAF9F5</span></div></div>
<div class="swatch"><div class="chip" style="background:#e8e6dc"></div><div class="info"><p class="name">Warm Sand</p><p class="role">按钮默认背景 · 交互面</p><span class="hex">#E8E6DC</span></div></div>
<div class="swatch dark"><div class="chip" style="background:#141413"></div><div class="info"><p class="name" style="color:var(--deep-dark)">Deep Dark</p><p class="role">深色主题页面底,不取纯黑,保留一点橄榄底色</p><span class="hex">#141413</span></div></div>
</div>
<h3 class="subhead">主调 · Brand</h3>
<div class="swatches">
<div class="swatch brand"><div class="chip" style="background:#1B365D"></div><div class="info"><p class="name">Ink Blue</p><p class="role">主色 · CTA · quote 竖线 · 章节标题</p><span class="hex">#1B365D</span></div></div>
<div class="swatch"><div class="chip" style="background:#2D5A8A"></div><div class="info"><p class="name">Ink Light</p><p class="role">深色底上的链接 · 亮变体</p><span class="hex">#2D5A8A</span></div></div>
<div class="swatch"><div class="chip" style="background:#30302e"></div><div class="info"><p class="name" style="color:var(--deep-dark)">Dark Surface</p><p class="role">深色主题容器 · 暖炭灰</p><span class="hex">#30302E</span></div></div>
<div class="swatch"><div class="chip" style="background:#b53333"></div><div class="info"><p class="name">Error</p><p class="role">错误,深暖红,不刺眼</p><span class="hex">#B53333</span></div></div>
</div>
<h3 class="subhead">中性 · Warm Neutrals</h3>
<div class="swatches" style="grid-template-columns: repeat(6, 1fr);">
<div class="swatch"><div class="chip" style="background:#141413"></div><div class="info"><p class="name">Near Black</p><span class="hex">#141413</span></div></div>
<div class="swatch"><div class="chip" style="background:#3d3d3a"></div><div class="info"><p class="name">Dark Warm</p><span class="hex">#3D3D3A</span></div></div>
<div class="swatch"><div class="chip" style="background:#4d4c48"></div><div class="info"><p class="name">Charcoal</p><span class="hex">#4D4C48</span></div></div>
<div class="swatch"><div class="chip" style="background:#5e5d59"></div><div class="info"><p class="name">Olive</p><span class="hex">#5E5D59</span></div></div>
<div class="swatch"><div class="chip" style="background:#87867f"></div><div class="info"><p class="name">Stone</p><span class="hex">#87867F</span></div></div>
<div class="swatch"><div class="chip" style="background:#b0aea5"></div><div class="info"><p class="name">Warm Silver</p><span class="hex">#B0AEA5</span></div></div>
</div>
<h3 class="subhead">Tag Tints · rgba → 实色对照</h3>
<p style="font-size:14px; color: var(--olive); margin: 0 0 16px; line-height: 1.55;">这里列的是油墨蓝 <code style="font-family:var(--mono);font-size:12px;background:#EEF2F7;color:var(--brand);padding:1px 5px;border-radius:2px">#1B365D</code> 叠加在 parchment 上的等效实色;Tag 背景不使用 <code style="font-family:var(--mono);font-size:12px;background:#EEF2F7;color:var(--brand);padding:1px 5px;border-radius:2px">rgba()</code>,避免 WeasyPrint 的双层矩形问题。</p>
<div class="tint-strip">
<div class="tint"><span class="flag">&nbsp;</span><span class="sample" style="background:#EEF2F7">Tag sample</span><p class="opacity">0.08</p><span class="hex">#EEF2F7</span></div>
<div class="tint"><span class="flag">&nbsp;</span><span class="sample" style="background:#E4ECF5">Tag sample</span><p class="opacity">0.14</p><span class="hex">#E4ECF5</span></div>
<div class="tint default"><span class="flag">Default</span><span class="sample" style="background:#E4ECF5">Tag sample</span><p class="opacity">0.18</p><span class="hex">#E4ECF5</span></div>
<div class="tint"><span class="flag">&nbsp;</span><span class="sample" style="background:#D0DCE9">Tag sample</span><p class="opacity">0.22</p><span class="hex">#D0DCE9</span></div>
<div class="tint"><span class="flag">&nbsp;</span><span class="sample" style="background:#D6E1EE">Tag sample</span><p class="opacity">0.30</p><span class="hex">#D6E1EE</span></div>
</div>
</section>
<!-- ============ 02 TYPE ============ -->
<section>
<div class="section-head">
<p class="section-num">04 · Typography</p>
<h2 class="section-title">字体系统</h2>
<p class="section-lede">Serif 承担层级,sans 承担功能;serif 正文 400,标题 500。</p>
</div>
<div class="family-grid">
<div class="family serif">
<p class="glyph">紙 Aa</p>
<p class="role">Serif · 标题</p>
<p class="name">TsangerJinKai02 / Newsreader</p>
<p class="use">承担标题、引语与数字强调,中文取仓耳今楷,英文取 Newsreader。</p>
</div>
<div class="family sans">
<p class="glyph">文 Aa</p>
<p class="role">Sans · 正文 / UI</p>
<p class="name">Source Han Sans / Inter</p>
<p class="use">用于正文、标签、按钮与 metadata,默认 400,标签标题用 500600。</p>
</div>
<div class="family mono">
<p class="glyph">&lt;/&gt;</p>
<p class="role">Mono · 代码</p>
<p class="name">JetBrains Mono</p>
<p class="use">用于代码块、版本号、hex 值与等宽数字,字号较正文低一档。</p>
</div>
</div>
<h3 class="subhead">层级(印刷品 pt · 屏幕 px ≈ pt × 1.33</h3>
<div class="type-grid">
<div class="type-row">
<div class="type-label">Display</div>
<div class="type-sample display">高质量文档读起来<br>清晰、稳定、有余韵</div>
<div class="type-specs"><b>3648 pt</b><br>weight 500<br>line 1.10</div>
</div>
<div class="type-row">
<div class="type-label">Section H1</div>
<div class="type-sample h1">色彩系统 · Color</div>
<div class="type-specs"><b>1822 pt</b><br>weight 500<br>line 1.20</div>
</div>
<div class="type-row">
<div class="type-label">Subsection H2</div>
<div class="type-sample h2">画布 · Canvas</div>
<div class="type-specs"><b>1416 pt</b><br>weight 500<br>line 1.25</div>
</div>
<div class="type-row">
<div class="type-label">Item H3</div>
<div class="type-sample h3">Tag Tints · rgba 对照</div>
<div class="type-specs"><b>1213 pt</b><br>weight 500<br>line 1.30</div>
</div>
<div class="type-row">
<div class="type-label">Body Lead</div>
<div class="type-sample lede">面向印刷文档的约束系统,目标是稳定、清晰、易读。</div>
<div class="type-specs"><b>11 pt</b><br>weight 400<br>line 1.55</div>
</div>
<div class="type-row">
<div class="type-label">Body</div>
<div class="type-sample body">油墨蓝全文档不超过 5% 面积,超过就从克制变成堆砌;所有灰色都有暖黄底色,R ≈ G > B 是基本规律。</div>
<div class="type-specs"><b>9.510 pt</b><br>weight 400<br>line 1.55</div>
</div>
<div class="type-row">
<div class="type-label">Body Dense</div>
<div class="type-sample dense">密排正文用于简历、一页纸、名片、索引卡,中文 pt 字号下 1.4–1.45 的行距最舒服,英文网页的 1.6 放进来会显得松散。</div>
<div class="type-specs"><b>99.2 pt</b><br>weight 400<br>line 1.40</div>
</div>
<div class="type-row">
<div class="type-label">Caption</div>
<div class="type-sample caption">图注、脚注、说明文字,颜色用 olive 或 stone 降一级。</div>
<div class="type-specs"><b>8.59 pt</b><br>weight 400<br>line 1.45</div>
</div>
<div class="type-row">
<div class="type-label">Label</div>
<div class="type-sample label">Design System · v1.1.0</div>
<div class="type-specs"><b>7.58 pt</b><br>weight 600<br>line 1.35</div>
</div>
</div>
</section>
<!-- ============ 03 SPACING ============ -->
<section>
<div class="section-head">
<p class="section-num">05 · Spacing &amp; Shape</p>
<h2 class="section-title">节奏与形</h2>
<p class="section-lede">基础单位 4pt,密度越高 margin 越小,场合越正式 margin 越大。</p>
</div>
<table class="space-table">
<tr>
<th>Scale</th><th>Value</th><th>Use</th><th></th>
</tr>
<tr><td class="name">xs</td><td class="val">23 pt</td><td class="use">同行内元素</td><td class="bar"><span style="width:6px"></span></td></tr>
<tr><td class="name">sm</td><td class="val">45 pt</td><td class="use">tag padding · 紧凑布局</td><td class="bar"><span style="width:12px"></span></td></tr>
<tr><td class="name">md</td><td class="val">810 pt</td><td class="use">组件内部</td><td class="bar"><span style="width:24px"></span></td></tr>
<tr><td class="name">lg</td><td class="val">1620 pt</td><td class="use">组件之间 · 卡片 padding</td><td class="bar"><span style="width:48px"></span></td></tr>
<tr><td class="name">xl</td><td class="val">2432 pt</td><td class="use">section 标题 margin</td><td class="bar"><span style="width:72px"></span></td></tr>
<tr><td class="name">2xl</td><td class="val">4060 pt</td><td class="use">大 section 之间</td><td class="bar"><span style="width:140px"></span></td></tr>
<tr><td class="name">3xl</td><td class="val">80120 pt</td><td class="use">长文档章节之间</td><td class="bar"><span style="width:240px"></span></td></tr>
</table>
<h3 class="subhead">圆角尺度 · Radii</h3>
<div class="radii">
<div class="radius"><div class="box" style="border-radius:4px"></div><div class="r"><b>4</b> pt</div><div class="label">极紧</div></div>
<div class="radius"><div class="box" style="border-radius:6px"></div><div class="r"><b>6</b> pt</div><div class="label">代码块</div></div>
<div class="radius"><div class="box" style="border-radius:8px"></div><div class="r"><b>8</b> pt</div><div class="label">默认卡片</div></div>
<div class="radius"><div class="box" style="border-radius:12px"></div><div class="r"><b>12</b> pt</div><div class="label">容器</div></div>
<div class="radius"><div class="box" style="border-radius:16px"></div><div class="r"><b>16</b> pt</div><div class="label">特色卡片</div></div>
<div class="radius"><div class="box" style="border-radius:24px"></div><div class="r"><b>24</b> pt</div><div class="label">大容器</div></div>
<div class="radius"><div class="box" style="border-radius:32px"></div><div class="r"><b>32</b> pt</div><div class="label">Hero</div></div>
</div>
<h3 class="subhead">深度 · 阴影三法</h3>
<p style="font-size:14px; color: var(--olive); margin: 0 0 12px; line-height: 1.55;">Kami 不用传统硬阴影,深度交给 ring shadow、whisper shadow 与明暗交替。</p>
<div class="shadow-row">
<div class="shadow-demo sd-ring">
<p class="t">Ring Shadow</p>
<p class="s">0 0 0 1pt var(--ring-warm)<br>&nbsp;<br>按钮 · 卡片 hover</p>
</div>
<div class="shadow-demo sd-whisper">
<p class="t">Whisper Shadow</p>
<p class="s">0 4pt 24pt rgba(0,0,0,0.05)<br>&nbsp;<br>极轻浮起 · 特色卡</p>
</div>
<div class="shadow-demo sd-alt">
<p class="t">Light ⇌ Dark</p>
<p class="s">parchment ⇌ deep-dark<br>&nbsp;<br>section 级别 · 对比最明显</p>
</div>
</div>
</section>
<!-- ============ 04 COMPONENTS ============ -->
<section>
<div class="section-head">
<p class="section-num">06 · Components</p>
<h2 class="section-title">原子组件</h2>
<p class="section-lede">组件不求多,只保留能解决具体问题的一组。</p>
</div>
<div class="comp-grid">
<div class="comp">
<h4>Buttons</h4>
<p class="hint">ring shadow 替代硬投影 · 8pt 圆角</p>
<div class="demo" style="display:flex; gap:10px; flex-wrap:wrap;">
<span class="btn btn-primary">Primary CTA</span>
<span class="btn btn-secondary">Secondary</span>
<span class="btn btn-ghost">Ghost</span>
</div>
</div>
<div class="comp">
<h4>Tags · 三档</h4>
<p class="hint">实色 hex · 从弱到强选</p>
<div class="demo" style="display:flex; gap:10px; flex-wrap:wrap; align-items:center;">
<span class="tag calm">极淡 0.08</span>
<span class="tag standard">标准 0.18</span>
<span class="tag brush">笔刷渐变</span>
</div>
</div>
<div class="comp">
<h4>Quote</h4>
<p class="hint">左 2pt 品牌实线 + olive 色</p>
<blockquote class="quote">一千个 no 换一个 yes,宁可清淡,不可浓艳。</blockquote>
</div>
<div class="comp">
<h4>Metric</h4>
<p class="hint">serif 数字 + sans 标签 · tabular-nums</p>
<div class="metrics demo">
<div class="metric"><span class="metric-value">6</span><span class="metric-label">文档类型</span></div>
<div class="metric"><span class="metric-value">1</span><span class="metric-label">强调色</span></div>
<div class="metric"><span class="metric-value">8</span><span class="metric-label">条规则</span></div>
</div>
</div>
<div class="comp">
<h4>Section Title</h4>
<p class="hint">serif 500 · 15pt · 纯字号层级,无装饰</p>
<div class="demo">
<span class="sample-section-title">核心项目 · Selected Works</span>
</div>
</div>
<div class="comp">
<h4>Dash List</h4>
<p class="hint">短横线代替圆点 · 更书卷气</p>
<ul class="dash demo">
<li>暖米纸底,不用纯白</li>
<li>油墨蓝点缀,不引第二色</li>
<li>Serif 承担权威</li>
</ul>
</div>
<div class="comp">
<h4>Code Block</h4>
<p class="hint">ivory 底 + 0.5pt border + 6pt 圆角 + mono 字体</p>
<pre class="code"><span class="c">// 暖米纸 · 一种克制的温度</span>
canvas = parchment; accent = <span class="k">ink_blue</span>
palette = <span class="k">warm_neutrals</span> cool_grays
document = <span class="k">serif</span> × hierarchy + generous_space
beauty = constraints × <span class="k">intention</span> ÷ noise</pre>
</div>
<div class="comp">
<h4>Featured Card</h4>
<p class="hint">whisper shadow + 16pt 圆角</p>
<div class="demo" style="background:var(--ivory); border-radius:16px; padding:18px 20px; box-shadow: 0 4px 24px rgba(0,0,0,0.05); border: 1px solid var(--border-cream);">
<p style="font-family:var(--serif); font-weight:500; font-size:15px; margin:0 0 6px; color:var(--near-black);">Tesla 公司介绍 · One-Pager</p>
<p style="font-size:12px; color:var(--olive); margin:0 0 10px; line-height:1.5;">单页 A4 · 中文版 · 4 张指标卡 + 3 段正文 + 时间线</p>
<div style="display:flex; gap:8px; flex-wrap:wrap;">
<span class="tag calm">中文</span>
<span class="tag calm">A4 × 1</span>
<span class="tag calm">Ink Blue</span>
</div>
</div>
</div>
</div>
</section>
<!-- ============ 05b DIAGRAMS ============ -->
<section>
<div class="section-head">
<p class="section-num">07 · Diagrams</p>
<h2 class="section-title">内联图表</h2>
<p class="section-lede">十二种内联 SVG 图表,覆盖架构、流程、数据三大场景。直接告诉 Claude 需要哪种图,会自动嵌入文档,配色与字体沿用 Kami 设计语言。</p>
</div>
<div class="chart-showcase">
<!-- Architecture mini -->
<div class="chart-card">
<div class="chart-label">架构图 · Architecture</div>
<svg viewBox="0 0 280 160" xmlns="http://www.w3.org/2000/svg" style="font-family:'Inter','TsangerJinKai02',sans-serif">
<rect width="280" height="160" fill="#f5f4ed"/>
<!-- Browser → API -->
<path d="M 84,79.5 L 103,79.5 L 103,78 L 107,80 L 103,82 L 103,80.5 L 84,80.5 Z" fill="#87867f"/>
<!-- API → Database -->
<path d="M 172,79.5 L 191,79.5 L 191,78 L 195,80 L 191,82 L 191,80.5 L 172,80.5 Z" fill="#5e5d59"/>
<!-- Node: Client -->
<rect x="20" y="58" width="64" height="44" rx="3" fill="#f5f4ed" stroke="#87867f" stroke-width="0.8"/>
<text x="52" y="76" text-anchor="middle" font-size="7" letter-spacing="0.5" fill="#87867f">CLIENT</text>
<text x="52" y="91" text-anchor="middle" font-size="9" font-weight="600" fill="#141413">Browser</text>
<!-- Node: API (focal) -->
<rect x="108" y="58" width="64" height="44" rx="3" fill="#EEF2F7" stroke="#1B365D" stroke-width="1"/>
<text x="140" y="76" text-anchor="middle" font-size="7" letter-spacing="0.5" fill="#1B365D">SERVICE</text>
<text x="140" y="91" text-anchor="middle" font-size="9" font-weight="600" fill="#141413">API</text>
<!-- Node: Database -->
<rect x="196" y="58" width="64" height="44" rx="3" fill="#f5f4ed" stroke="#5e5d59" stroke-width="0.8"/>
<text x="228" y="76" text-anchor="middle" font-size="7" letter-spacing="0.5" fill="#87867f">DATA</text>
<text x="228" y="91" text-anchor="middle" font-size="9" font-weight="600" fill="#141413">Database</text>
</svg>
<p class="chart-caption">系统组件与连接关系,一个焦点节点</p>
</div>
<!-- Flowchart mini -->
<div class="chart-card">
<div class="chart-label">流程图 · Flowchart</div>
<svg viewBox="0 0 280 160" xmlns="http://www.w3.org/2000/svg" style="font-family:'Inter','TsangerJinKai02',sans-serif">
<rect width="280" height="160" fill="#f5f4ed"/>
<!-- Start pill -->
<rect x="100" y="8" width="80" height="28" rx="14" fill="#141413"/>
<text x="140" y="27" text-anchor="middle" font-size="9" font-weight="600" fill="#f5f4ed">Start</text>
<!-- Start → diamond (arrow down) -->
<path d="M 139.5,36 L 139.5,48 L 138,48 L 140,52 L 142,48 L 140.5,48 L 140.5,36 Z" fill="#5e5d59"/>
<!-- Decision diamond -->
<polygon points="140,52 172,72 140,92 108,72" fill="#f5f4ed" stroke="#141413" stroke-width="1"/>
<text x="140" y="76" text-anchor="middle" font-size="8" fill="#141413">Valid?</text>
<!-- Yes arrow right -->
<path d="M 172,71.5 L 204,71.5 L 204,70 L 208,72 L 204,74 L 204,72.5 L 172,72.5 Z" fill="#1B365D"/>
<text x="186" y="68" text-anchor="middle" font-size="8" fill="#1B365D">Yes</text>
<!-- Success node (focal) -->
<rect x="208" y="56" width="64" height="32" rx="4" fill="#EEF2F7" stroke="#1B365D" stroke-width="1"/>
<text x="240" y="77" text-anchor="middle" font-size="9" font-weight="600" fill="#141413">Process</text>
<!-- No arrow down -->
<path d="M 139.5,92 L 139.5,112 L 138,112 L 140,116 L 142,112 L 140.5,112 L 140.5,92 Z" fill="#5e5d59"/>
<text x="151" y="106" font-size="8" fill="#5e5d59">No</text>
<!-- Reject node -->
<rect x="96" y="116" width="88" height="32" rx="4" fill="#f5f4ed" stroke="#87867f" stroke-width="0.8"/>
<text x="140" y="136" text-anchor="middle" font-size="9" font-weight="600" fill="#5e5d59">Reject</text>
</svg>
<p class="chart-caption">决策分支,Yes / No 路径,焦点为成功路径</p>
</div>
<!-- Bar chart (shifted left) -->
<div class="chart-card">
<div class="chart-label">柱状图 · Bar Chart</div>
<svg viewBox="0 0 280 160" xmlns="http://www.w3.org/2000/svg" style="font-family:'Inter','TsangerJinKai02',sans-serif">
<rect width="280" height="160" fill="#f5f4ed"/>
<rect x="18" y="78" width="42" height="50" rx="3" fill="#87867f"/>
<rect x="78" y="62" width="42" height="66" rx="3" fill="#5e5d59"/>
<rect x="138" y="44" width="42" height="84" rx="3" fill="#2d4e7a"/>
<rect x="198" y="30" width="42" height="98" rx="3" fill="#1B365D"/>
<text x="39" y="145" text-anchor="middle" font-size="10" fill="#87867f">Q1</text>
<text x="99" y="145" text-anchor="middle" font-size="10" fill="#5e5d59">Q2</text>
<text x="159" y="145" text-anchor="middle" font-size="10" fill="#4d4c48">Q3</text>
<text x="219" y="145" text-anchor="middle" font-size="10" fill="#4d4c48">Q4</text>
<text x="39" y="73" text-anchor="middle" font-size="9" font-weight="600" fill="#87867f">180</text>
<text x="99" y="57" text-anchor="middle" font-size="9" font-weight="600" fill="#5e5d59">220</text>
<text x="159" y="39" text-anchor="middle" font-size="9" font-weight="600" fill="#2d4e7a">195</text>
<text x="219" y="25" text-anchor="middle" font-size="9" font-weight="600" fill="#1B365D">240</text>
</svg>
<p class="chart-caption">季度营收对比,可选颜色渐变或单色</p>
</div>
<!-- Donut chart -->
<div class="chart-card">
<div class="chart-label">环形图 · Donut Chart</div>
<svg viewBox="0 0 280 160" xmlns="http://www.w3.org/2000/svg" style="font-family:'Inter','TsangerJinKai02',sans-serif">
<rect width="280" height="160" fill="#f5f4ed"/>
<path d="M 80,25 A 55,55 0 1,1 47.7,124.5 L 62.4,104.3 A 30,30 0 1,0 80,50 Z" fill="#1B365D" stroke="white" stroke-width="1.5"/>
<path d="M 47.7,124.5 A 55,55 0 0,1 35.5,47.7 L 55.7,62.4 A 30,30 0 0,0 62.4,104.3 Z" fill="#5e5d59" stroke="white" stroke-width="1.5"/>
<path d="M 35.5,47.7 A 55,55 0 0,1 80,25 L 80,50 A 30,30 0 0,0 55.7,62.4 Z" fill="#87867f" stroke="white" stroke-width="1.5"/>
<text x="80" y="77" text-anchor="middle" font-size="13" font-weight="600" fill="#1B365D">60%</text>
<text x="80" y="92" text-anchor="middle" font-size="10" fill="#5e5d59">主营</text>
<rect x="150" y="46" width="10" height="10" rx="2" fill="#1B365D"/>
<text x="165" y="55" font-size="11" fill="#4d4c48">主营业务 · 60%</text>
<rect x="150" y="72" width="10" height="10" rx="2" fill="#5e5d59"/>
<text x="165" y="81" font-size="11" fill="#4d4c48">服务收入 · 25%</text>
<rect x="150" y="98" width="10" height="10" rx="2" fill="#87867f"/>
<text x="165" y="107" font-size="11" fill="#4d4c48">其他来源 · 15%</text>
</svg>
<p class="chart-caption">营收结构拆解,支持 36 个分段</p>
</div>
</div>
</section>
<!-- ============ 05 DECISION TABLE ============ -->
<section>
<div class="section-head">
<p class="section-num">08 · Decision Lookup</p>
<h2 class="section-title">决策速查</h2>
<p class="section-lede">遇到“该用什么”时先看这张表,不在表里就回到基础原则。</p>
</div>
<table class="decision">
<tr><th>要做什么</th><th>怎么做</th></tr>
<tr><td class="task">大标题</td><td class="how">serif <code>500</code>,字号按层级,line-height <code>1.101.30</code></td></tr>
<tr><td class="task">正文阅读</td><td class="how">sans <code>400</code><code>9.510 pt</code>line-height <code>1.55</code></td></tr>
<tr><td class="task">强调一个数字</td><td class="how"><code>color: var(--brand)</code>,不要粗体</td></tr>
<tr><td class="task">分隔两段内容</td><td class="how"><code>2.5pt</code> 品牌色左侧竖线,或 <code>0.5pt</code> 暖灰虚线</td></tr>
<tr><td class="task">引用某人的话</td><td class="how"><code>2pt</code> 品牌色实线 + olive 色</td></tr>
<tr><td class="task">展示代码</td><td class="how">ivory 底 + <code>0.5pt</code> border + <code>6pt</code> 圆角 + mono 字体</td></tr>
<tr><td class="task">区分主次按钮</td><td class="how">Primary 品牌色填充 + ivory 字;Secondary warm-sand + charcoal</td></tr>
<tr><td class="task">标注某张特殊卡片</td><td class="how"><code>border: 0.5pt solid var(--brand)</code><code>border-left: 3pt</code></td></tr>
<tr><td class="task">章节开始</td><td class="how">serif <code>500</code> · <code>15pt</code> · 纯字号层级,无额外装饰</td></tr>
<tr><td class="task">文档封面</td><td class="how">单页 Display 字号 + 右对齐作者/日期 + 大量留白</td></tr>
<tr><td class="task">数据卡</td><td class="how">ivory 底 + <code>8pt</code> 圆角 + serif 大数字(油墨蓝) + sans 小标签</td></tr>
</table>
</section>
<!-- ============ 06 ANTI ============ -->
<section>
<div class="section-head">
<p class="section-num">09 · Anti-Patterns</p>
<h2 class="section-title">反面示例</h2>
<p class="section-lede">破例可以,但理由要足够清楚。</p>
</div>
<div class="anti">
<div class="no">
<h5>Don't</h5>
<p>页面背景用 <code>#ffffff</code> 纯白或 <code>#f3f4f6</code> 冷灰,会让版面变硬,也削弱 Kami 的暖米纸气质。</p>
</div>
<div class="yes">
<h5>Do</h5>
<p>始终使用 <code>#f5f4ed</code> parchment,打印时把 <code>@page background</code> 也设为同色,避免白边。</p>
</div>
<div class="no">
<h5>Don't</h5>
<p>Tag 用 <code>rgba(201,100,66,0.18)</code>WeasyPrint 会让 padding 区与字形区透明度叠加,形成双层矩形。</p>
</div>
<div class="yes">
<h5>Do</h5>
<p>查 tint 对照表,改用等效实色 <code>#E4ECF5</code>,颜色稳定,PDF 也干净。</p>
</div>
<div class="no">
<h5>Don't</h5>
<p>标题用 <code>font-weight: 600</code> 或更粗的合成 bold,笔画容易变糊,版面也会显得粗重。</p>
</div>
<div class="yes">
<h5>Do</h5>
<p>正文 400,标题 500(真实 W05 字重);需要更强存在感时,用字号或品牌色左侧竖线,不靠合成粗体。</p>
</div>
<div class="no">
<h5>Don't</h5>
<p>box-shadow 硬投影 <code>0 2px 8px rgba(0,0,0,0.3)</code> 观感偏硬,印刷时也容易发黑。</p>
</div>
<div class="yes">
<h5>Do</h5>
<p>用 ring shadow (<code>0 0 0 1pt</code>)、whisper (<code>rgba 0.05</code>),或直接用明暗交替建立层次。</p>
</div>
</div>
</section>
<!-- ============ 09 BACKGROUND ============ -->
<section>
<div class="section-head">
<p class="section-num">10 · Background</p>
<h2 class="section-title">设计缘起</h2>
</div>
<div class="background-text">
<p>我做美股投资,经常让 AI 生成分析报告,最初的输出总像默认 Google Doc,平淡、不一致,也没有记忆点;我不喜欢文档风格总在变,便反复调字体、配色与间距,直到它变成一份自己愿意读下去的文档。</p>
<p>后来受邀做一次演讲,需要一套和文档风格一致的 slides,这轮迭代把系统继续往前推:加入 inline SVG 图表,统一暖色调,也收紧编辑节奏;做着做着,它已经覆盖了我常用的文档场景,于是独立成 kami,一套我愿意反复使用、也愿意发出去的视觉语言。</p>
</div>
</section>
<!-- ============ FOOTER ============ -->
<footer class="footer">
<div class="mark">
<svg width="56" height="56" viewBox="0 0 80 80" xmlns="http://www.w3.org/2000/svg" aria-label="Kami mark">
<rect x="6" y="6" width="68" height="68" rx="10" fill="#faf9f5" stroke="#1B365D" stroke-width="1.25"/>
<line x1="16" y1="26" x2="64" y2="26" stroke="#1B365D" stroke-width="1.25"/>
<text x="40" y="56" font-family="TsangerJinKai02, Georgia, serif" font-size="34" font-weight="500" fill="#141413" text-anchor="middle"></text>
</svg>
<div>
<p class="wm-name">Kami · 紙</p>
<p class="wm-sub">好内容,值得好版面。</p>
</div>
</div>
<div class="colophon">
<div><a href="https://github.com/tw93/kami" style="color:var(--brand); text-decoration:none; font-weight:500;">GitHub</a>&nbsp;&nbsp;·&nbsp;&nbsp;One-Pager · Long Doc · Letter · 作品集 · Resume · Slides</div>
<div style="margin-top:8px; color: var(--olive); font-family: var(--serif);">
Serif 承担权威,sans 承担功能,暖灰承担节奏,油墨蓝承担焦点。
</div>
</div>
</footer>
</main>
<script>
(function(){
var sw=document.querySelector('.lang-switch'),last=0;
window.addEventListener('scroll',function(){
var y=window.scrollY;
if(y>80&&y>last)sw.classList.add('hidden');
else sw.classList.remove('hidden');
last=y;
},{passive:true});
})();
</script>
</body>
</html>
+183 -213
View File
@@ -1,15 +1,15 @@
<!doctype html>
<html lang="zh-CN">
<html lang="en">
<head>
<meta charset="utf-8">
<title>Kami · Design System</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Kami 紙:面向 AI 文档生成的温暖纸质感设计系统,以单一强调色、serif 层级与编辑级留白,收束成稳定的排版。">
<meta name="description" content="Kami - a warm parchment design system for AI-assisted professional documents, shaped by one accent color, serif-led hierarchy, and editorial whitespace.">
<link rel="icon" href="https://gw.alipayobjects.com/zos/k/vl/logo.svg" type="image/svg+xml">
<link rel="stylesheet" href="./styles.css">
</head>
<body>
<nav class="lang-switch"><a href="./index.html" class="active"></a><a href="./index-en.html">EN</a></nav>
<nav class="lang-switch"><a href="./index.html" class="active">EN</a><a href="./index-zh.html"></a><a href="./index-ja.html"></a></nav>
<main class="page">
<!-- ============ HERO ============ -->
@@ -30,12 +30,12 @@
</span>
</div>
<h1>Kami<span class="cn"></span></h1>
<p class="tagline">好内容,值得好版面。Kami 是一套适合 AI 时代的排版设计系统,让文档更清晰好读,也容易记住。</p>
<p class="tagline">Good content deserves good paper. Kami is a layout design system for the AI era, making documents clear, readable, and memorable.</p>
<div class="hero-tokens">
<span><b>Canvas</b>#f5f4ed</span>
<span><b>Accent</b>#1B365D</span>
<span><b>Serif</b>TsangerJinKai / Newsreader</span>
<span><b>Sans</b>Source Han Sans / Inter</span>
<span><b>Serif</b>Newsreader / TsangerJinKai</span>
<span><b>Sans</b>Inter / Source Han Sans</span>
</div>
</header>
@@ -43,29 +43,29 @@
<section>
<div class="section-head">
<p class="section-num">00 · See it</p>
<h2 class="section-title">输出样本</h2>
<p class="section-lede">把文档需求交给 Claude。无论是一页纸、长文、正式信件、作品集、简历还是幻灯片,Kami 都用少量清晰规则,把提示词变成可交付的排版。</p>
<h2 class="section-title">Output Samples</h2>
<p class="section-lede">Give Claude the brief: one-pagers, long docs, formal letters, portfolios, resumes, and slides become polished layouts through a small, reliable rule set.</p>
</div>
<div class="demo-grid">
<div class="demo-card">
<a href="assets/demos/demo-tesla.pdf" target="_blank"><img src="assets/demos/demo-tesla.png" alt="Tesla 公司介绍"></a>
<p class="demo-title">One-Pager · 中文</p>
<p class="demo-desc">Tesla 公司介绍,单页 A4</p>
</div>
<div class="demo-card">
<a href="assets/demos/demo-agent-slides.pdf" target="_blank"><img src="assets/demos/demo-agent-slides.png" alt="Agent slides"></a>
<p class="demo-title">Slides · English</p>
<p class="demo-desc">Agent keynote, 6 slides</p>
<a href="assets/demos/demo-tesla.pdf" target="_blank"><img src="assets/demos/demo-tesla.png" alt="Tesla company one-pager"></a>
<p class="demo-title">One-Pager</p>
<p class="demo-desc">Tesla company profile, single A4</p>
</div>
<div class="demo-card">
<a href="assets/demos/demo-musk-resume.pdf" target="_blank"><img src="assets/demos/demo-musk-resume.png" alt="Musk resume"></a>
<p class="demo-title">Resume · English</p>
<p class="demo-desc">Founder CV2 页</p>
<p class="demo-title">Resume</p>
<p class="demo-desc">Founder CV, 2 pages</p>
</div>
<div class="demo-card">
<a href="assets/demos/demo-kaku.pdf" target="_blank"><img src="assets/demos/demo-kaku.png" alt="Kaku 作品集"></a>
<p class="demo-title">作品集 · 中文</p>
<p class="demo-desc">Kaku 终端模拟器,6 页</p>
<a href="assets/demos/demo-kaku.pdf" target="_blank"><img src="assets/demos/demo-kaku.png" alt="Kaku portfolio"></a>
<p class="demo-title">Portfolio</p>
<p class="demo-desc">Kaku terminal portfolio, Japanese, 6 pages</p>
</div>
<div class="demo-card">
<a href="assets/demos/demo-agent-slides.pdf" target="_blank"><img src="assets/demos/demo-agent-slides.png" alt="Agent slides"></a>
<p class="demo-title">Slides</p>
<p class="demo-desc">Agent keynote, 6 slides</p>
</div>
</div>
</section>
@@ -74,61 +74,61 @@
<section>
<div class="section-head">
<p class="section-num">01 · Usage</p>
<h2 class="section-title">安装与调用</h2>
<p class="section-lede">直接告诉 Claude 你要什么,比如「帮我排版一份白皮书」「做一份简历」「帮我做一份作品集」,skill 会自动触发,无需斜杠命令。</p>
<h2 class="section-title">Install &amp; Invoke</h2>
<p class="section-lede">Tell Claude what you need, for example "build me a resume", "make a one-pager for my startup", or "design a slide deck for my talk". The skill auto-triggers, no slash command needed.</p>
</div>
<pre class="code"><span class="c"># Claude Code</span>
npx skills add tw93/kami -a claude-code -g -y
<span class="c"># Codex</span>
npx skills add tw93/kami -a codex -g -y</pre>
<p class="section-lede" style="font-size:14px;">Claude Desktop:下载 <a href="https://cdn.jsdelivr.net/gh/tw93/kami@main/dist/kami.zip" style="color:var(--brand); text-decoration:none; font-weight:500;">kami.zip</a>,在 Customize &gt; Skills &gt; "+" &gt; Create skill 中直接上传。</p>
<p class="section-lede" style="font-size:14px;">Claude Desktop: download <a href="https://cdn.jsdelivr.net/gh/tw93/kami@main/dist/kami.zip" style="color:var(--brand); text-decoration:none; font-weight:500;">kami.zip</a>, then upload it in Customize &gt; Skills &gt; "+" &gt; Create skill.</p>
</section>
<!-- ============ 02 MANIFESTO ============ -->
<section>
<div class="section-head">
<p class="section-num">02 · Manifesto</p>
<h2 class="section-title">设计原则</h2>
<h2 class="section-title">Design Principles</h2>
</div>
<p class="manifesto">暖米纸底,<em>油墨蓝</em>点缀,serif 承担层级,硬阴影与花哨配色都退后;这套系统面向印刷文档,负责把内容收束成稳定、清晰、易读的版面。</p>
<p class="manifesto">Warm parchment canvas, <em>ink blue</em> as the sole accent, serif carries hierarchy, while hard shadows and flashy palettes recede. This system is built for printed matter: stable, clear, and composed.</p>
<ol class="rules">
<li><span class="n">1</span><p>页面背景 parchment <code>#f5f4ed</code>,不用纯白</p></li>
<li><span class="n">2</span><p>强调色只有油墨蓝 <code>#1B365D</code>,不引入第二种彩色</p></li>
<li><span class="n">3</span><p>所有灰色暖调 (yellow-brown undertone),禁止冷蓝灰</p></li>
<li><span class="n">4</span><p>英文 serif 通吃标题和正文,中文标题用 serif、正文用 sans</p></li>
<li><span class="n">5</span><p>Serif 正文 400,标题 500,不用合成 bold</p></li>
<li><span class="n">6</span><p>行距三档:紧凑标题 1.11.3 / 密排 1.41.45 / 阅读 1.51.55</p></li>
<li><span class="n">7</span><p>Tag 背景必须实色 hex,禁止 rgbaWeasyPrint 双层矩形 bug</p></li>
<li><span class="n">8</span><p>阴影只用 ring whisper shadow,不用硬 drop shadow</p></li>
<li><span class="n">1</span><p>Page background is parchment <code>#f5f4ed</code>, never pure white</p></li>
<li><span class="n">2</span><p>Accent color is ink blue <code>#1B365D</code> only; no second chromatic hue</p></li>
<li><span class="n">3</span><p>All grays are warm, yellow-brown undertone; no cool blue-grays</p></li>
<li><span class="n">4</span><p>English uses serif for headlines and body; Chinese uses serif headlines and sans body</p></li>
<li><span class="n">5</span><p>Serif body at 400, headings at 500. Avoid synthetic bold</p></li>
<li><span class="n">6</span><p>Three line-height bands: tight titles 1.1-1.3 / dense 1.4-1.45 / reading 1.5-1.55</p></li>
<li><span class="n">7</span><p>Tag backgrounds must be solid hex; no rgba, WeasyPrint double-rectangle bug</p></li>
<li><span class="n">8</span><p>Shadows: ring or whisper only, no hard drop shadows</p></li>
</ol>
</section>
<!-- ============ 01 COLOR ============ -->
<!-- ============ 03 COLOR ============ -->
<section>
<div class="section-head">
<p class="section-num">03 · Color</p>
<h2 class="section-title">暖调克制</h2>
<p class="section-lede">唯一强调色、纯暖调中性灰、零冷色;油墨蓝全文档不超过 <b>5%</b> 面积,超过就从克制变成堆砌。</p>
<h2 class="section-title">Warm Restraint</h2>
<p class="section-lede">One accent + warm neutrals + zero cool colors. Ink blue covers no more than <b>5%</b> of any page. Beyond that is clutter, not restraint.</p>
</div>
<h3 class="subhead">画布 · Canvas</h3>
<h3 class="subhead">Canvas</h3>
<div class="swatches">
<div class="swatch"><div class="chip" style="background:#f5f4ed"></div><div class="info"><p class="name">Parchment</p><p class="role">页面底色,也是整套设计的温度来源</p><span class="hex">#F5F4ED</span></div></div>
<div class="swatch"><div class="chip" style="background:#faf9f5"></div><div class="info"><p class="name">Ivory</p><p class="role">卡片 · 浮起容器</p><span class="hex">#FAF9F5</span></div></div>
<div class="swatch"><div class="chip" style="background:#e8e6dc"></div><div class="info"><p class="name">Warm Sand</p><p class="role">按钮默认背景 · 交互面</p><span class="hex">#E8E6DC</span></div></div>
<div class="swatch dark"><div class="chip" style="background:#141413"></div><div class="info"><p class="name" style="color:var(--deep-dark)">Deep Dark</p><p class="role">深色主题页面底,不取纯黑,保留一点橄榄底色</p><span class="hex">#141413</span></div></div>
<div class="swatch"><div class="chip" style="background:#f5f4ed"></div><div class="info"><p class="name">Parchment</p><p class="role">Page background, the emotional foundation</p><span class="hex">#F5F4ED</span></div></div>
<div class="swatch"><div class="chip" style="background:#faf9f5"></div><div class="info"><p class="name">Ivory</p><p class="role">Cards / elevated surfaces</p><span class="hex">#FAF9F5</span></div></div>
<div class="swatch"><div class="chip" style="background:#e8e6dc"></div><div class="info"><p class="name">Warm Sand</p><p class="role">Button default / interactive surfaces</p><span class="hex">#E8E6DC</span></div></div>
<div class="swatch dark"><div class="chip" style="background:#141413"></div><div class="info"><p class="name" style="color:var(--deep-dark)">Deep Dark</p><p class="role">Dark theme page base, not pure black</p><span class="hex">#141413</span></div></div>
</div>
<h3 class="subhead">主调 · Brand</h3>
<h3 class="subhead">Brand</h3>
<div class="swatches">
<div class="swatch brand"><div class="chip" style="background:#1B365D"></div><div class="info"><p class="name">Ink Blue</p><p class="role">主色 · CTA · quote 竖线 · 章节标题</p><span class="hex">#1B365D</span></div></div>
<div class="swatch"><div class="chip" style="background:#2D5A8A"></div><div class="info"><p class="name">Ink Light</p><p class="role">深色底上的链接 · 亮变体</p><span class="hex">#2D5A8A</span></div></div>
<div class="swatch"><div class="chip" style="background:#30302e"></div><div class="info"><p class="name" style="color:var(--deep-dark)">Dark Surface</p><p class="role">深色主题容器 · 暖炭灰</p><span class="hex">#30302E</span></div></div>
<div class="swatch"><div class="chip" style="background:#b53333"></div><div class="info"><p class="name">Error</p><p class="role">错误,深暖红,不刺眼</p><span class="hex">#B53333</span></div></div>
<div class="swatch brand"><div class="chip" style="background:#1B365D"></div><div class="info"><p class="name">Ink Blue</p><p class="role">Primary color · CTA · quote bar · section overline</p><span class="hex">#1B365D</span></div></div>
<div class="swatch"><div class="chip" style="background:#2D5A8A"></div><div class="info"><p class="name">Ink Light</p><p class="role">Links on dark surfaces · lighter variant</p><span class="hex">#2D5A8A</span></div></div>
<div class="swatch"><div class="chip" style="background:#30302e"></div><div class="info"><p class="name" style="color:var(--deep-dark)">Dark Surface</p><p class="role">Dark theme container · warm charcoal</p><span class="hex">#30302E</span></div></div>
<div class="swatch"><div class="chip" style="background:#b53333"></div><div class="info"><p class="name">Error</p><p class="role">Error state, deep warm red</p><span class="hex">#B53333</span></div></div>
</div>
<h3 class="subhead">中性 · Warm Neutrals</h3>
<h3 class="subhead">Warm Neutrals</h3>
<div class="swatches" style="grid-template-columns: repeat(6, 1fr);">
<div class="swatch"><div class="chip" style="background:#141413"></div><div class="info"><p class="name">Near Black</p><span class="hex">#141413</span></div></div>
<div class="swatch"><div class="chip" style="background:#3d3d3a"></div><div class="info"><p class="name">Dark Warm</p><span class="hex">#3D3D3A</span></div></div>
@@ -138,8 +138,8 @@ npx skills add tw93/kami -a codex -g -y</pre>
<div class="swatch"><div class="chip" style="background:#b0aea5"></div><div class="info"><p class="name">Warm Silver</p><span class="hex">#B0AEA5</span></div></div>
</div>
<h3 class="subhead">Tag Tints · rgba → 实色对照</h3>
<p style="font-size:14px; color: var(--olive); max-width: 720px; margin: 0 0 16px; line-height: 1.55;">这里列的是油墨蓝 <code style="font-family:var(--mono);font-size:12px;background:#EEF2F7;color:var(--brand);padding:1px 5px;border-radius:2px">#1B365D</code> 叠加在 parchment 上的等效实色;Tag 背景不使用 <code style="font-family:var(--mono);font-size:12px;background:#EEF2F7;color:var(--brand);padding:1px 5px;border-radius:2px">rgba()</code>,避免 WeasyPrint 的双层矩形问题。</p>
<h3 class="subhead">Tag Tints: rgba to solid hex</h3>
<p style="font-size:14px; color: var(--olive); margin: 0 0 16px; line-height: 1.55;">These are the equivalent solid hex values for ink blue <code style="font-family:var(--mono);font-size:12px;background:#EEF2F7;color:var(--brand);padding:1px 5px;border-radius:2px">#1B365D</code> layered on parchment. Tag backgrounds avoid <code style="font-family:var(--mono);font-size:12px;background:#EEF2F7;color:var(--brand);padding:1px 5px;border-radius:2px">rgba()</code> so WeasyPrint will not render double rectangles.</p>
<div class="tint-strip">
<div class="tint"><span class="flag">&nbsp;</span><span class="sample" style="background:#EEF2F7">Tag sample</span><p class="opacity">0.08</p><span class="hex">#EEF2F7</span></div>
<div class="tint"><span class="flag">&nbsp;</span><span class="sample" style="background:#E4ECF5">Tag sample</span><p class="opacity">0.14</p><span class="hex">#E4ECF5</span></div>
@@ -149,239 +149,216 @@ npx skills add tw93/kami -a codex -g -y</pre>
</div>
</section>
<!-- ============ 02 TYPE ============ -->
<!-- ============ 04 TYPE ============ -->
<section>
<div class="section-head">
<p class="section-num">04 · Typography</p>
<h2 class="section-title">字体系统</h2>
<p class="section-lede">Serif 承担层级,sans 承担功能;serif 正文 400,标题 500</p>
<h2 class="section-title">Type System</h2>
<p class="section-lede">Serif carries hierarchy, sans carries function. Serif body at 400, headings at 500.</p>
</div>
<div class="family-grid">
<div class="family serif">
<p class="glyph">Aa</p>
<p class="role">Serif · 标题</p>
<p class="name">TsangerJinKai02 / Newsreader</p>
<p class="use">承担标题、引语与数字强调,中文取仓耳今楷,英文取 Newsreader。</p>
<p class="glyph">Aa</p>
<p class="role">Serif · Headlines + Body</p>
<p class="name">Newsreader / TsangerJinKai02</p>
<p class="use">Used for headlines, body text, pull quotes, and numeric emphasis. English uses Newsreader, Chinese uses TsangerJinKai02.</p>
</div>
<div class="family sans">
<p class="glyph">Aa</p>
<p class="role">Sans · 正文 / UI</p>
<p class="name">Source Han Sans / Inter</p>
<p class="use">用于正文、标签、按钮与 metadata,默认 400,标签标题用 500600</p>
<p class="glyph">Aa</p>
<p class="role">Sans · UI</p>
<p class="name">Inter / Source Han Sans</p>
<p class="use">Labels, buttons, metadata, eyebrows. Default 400, labels use 500-600. Chinese body text also uses sans.</p>
</div>
<div class="family mono">
<p class="glyph">&lt;/&gt;</p>
<p class="role">Mono · 代码</p>
<p class="role">Mono · Code</p>
<p class="name">JetBrains Mono</p>
<p class="use">用于代码块、版本号、hex 值与等宽数字,字号较正文低一档。</p>
<p class="use">Code blocks, version numbers, hex values, tabular figures.</p>
</div>
</div>
<h3 class="subhead">层级(印刷品 pt · 屏幕 px pt × 1.33</h3>
<h3 class="subhead">Scale (print pt; screen px = pt x 1.33)</h3>
<div class="type-grid">
<div class="type-row">
<div class="type-label">Display</div>
<div class="type-sample display">高质量文档读起来<br>清晰、稳定、有余韵</div>
<div class="type-specs"><b>3648 pt</b><br>weight 500<br>line 1.10</div>
<div class="type-sample display">Good docs read<br>clear, stable, poised</div>
<div class="type-specs"><b>36-48 pt</b><br>weight 500<br>line 1.10</div>
</div>
<div class="type-row">
<div class="type-label">Section H1</div>
<div class="type-sample h1">色彩系统 · Color</div>
<div class="type-specs"><b>1822 pt</b><br>weight 500<br>line 1.20</div>
<div class="type-label">H1 Section</div>
<div class="type-sample h1">Color System · Palette</div>
<div class="type-specs"><b>18-22 pt</b><br>weight 500<br>line 1.20</div>
</div>
<div class="type-row">
<div class="type-label">Subsection H2</div>
<div class="type-sample h2">画布 · Canvas</div>
<div class="type-specs"><b>1416 pt</b><br>weight 500<br>line 1.25</div>
<div class="type-label">H2 Subsection</div>
<div class="type-sample h2">Canvas · Warm Neutrals</div>
<div class="type-specs"><b>14-16 pt</b><br>weight 500<br>line 1.25</div>
</div>
<div class="type-row">
<div class="type-label">Item H3</div>
<div class="type-sample h3">Tag Tints · rgba 对照</div>
<div class="type-specs"><b>1213 pt</b><br>weight 500<br>line 1.30</div>
</div>
<div class="type-row">
<div class="type-label">Body Lead</div>
<div class="type-sample lede">面向印刷文档的约束系统,目标是稳定、清晰、易读。</div>
<div class="type-specs"><b>11 pt</b><br>weight 400<br>line 1.55</div>
<div class="type-label">H3 Item</div>
<div class="type-sample h3">Tag Tints · Solid Hex</div>
<div class="type-specs"><b>12-13 pt</b><br>weight 500<br>line 1.30</div>
</div>
<div class="type-row">
<div class="type-label">Body</div>
<div class="type-sample body">油墨蓝全文档不超过 5% 面积,超过就从克制变成堆砌;所有灰色都有暖黄底色,R ≈ G > B 是基本规律。</div>
<div class="type-specs"><b>9.510 pt</b><br>weight 400<br>line 1.55</div>
</div>
<div class="type-row">
<div class="type-label">Body Dense</div>
<div class="type-sample dense">密排正文用于简历、一页纸、名片、索引卡,中文 pt 字号下 1.4–1.45 的行距最舒服,英文网页的 1.6 放进来会显得松散。</div>
<div class="type-specs"><b>99.2 pt</b><br>weight 400<br>line 1.40</div>
<div class="type-sample body">Ink blue covers no more than 5% of any page. Beyond that is clutter, not restraint. All grays have a yellow-brown undertone: R ≈ G > B is the rule of thumb.</div>
<div class="type-specs"><b>9.5-10 pt</b><br>weight 400<br>line 1.55</div>
</div>
<div class="type-row">
<div class="type-label">Caption</div>
<div class="type-sample caption">图注、脚注、说明文字,颜色用 olive stone 降一级。</div>
<div class="type-specs"><b>8.59 pt</b><br>weight 400<br>line 1.45</div>
<div class="type-sample caption">Figure notes, footnotes, annotation text. Color drops to olive or stone.</div>
<div class="type-specs"><b>8.5-9 pt</b><br>weight 400<br>line 1.45</div>
</div>
<div class="type-row">
<div class="type-label">Label</div>
<div class="type-sample label">Design System · v1.1.0</div>
<div class="type-specs"><b>7.58 pt</b><br>weight 600<br>line 1.35</div>
<div class="type-specs"><b>7.5-8 pt</b><br>weight 600<br>line 1.35</div>
</div>
</div>
</section>
<!-- ============ 03 SPACING ============ -->
<!-- ============ 05 SPACING ============ -->
<section>
<div class="section-head">
<p class="section-num">05 · Spacing &amp; Shape</p>
<h2 class="section-title">节奏与形</h2>
<p class="section-lede">基础单位 4pt,密度越高 margin 越小,场合越正式 margin 越大。</p>
<h2 class="section-title">Rhythm &amp; Form</h2>
<p class="section-lede">Base unit: 4pt. Denser layouts get smaller margins; more formal documents get larger ones.</p>
</div>
<table class="space-table">
<tr>
<th>Scale</th><th>Value</th><th>Use</th><th></th>
</tr>
<tr><td class="name">xs</td><td class="val">23 pt</td><td class="use">同行内元素</td><td class="bar"><span style="width:6px"></span></td></tr>
<tr><td class="name">sm</td><td class="val">45 pt</td><td class="use">tag padding · 紧凑布局</td><td class="bar"><span style="width:12px"></span></td></tr>
<tr><td class="name">md</td><td class="val">810 pt</td><td class="use">组件内部</td><td class="bar"><span style="width:24px"></span></td></tr>
<tr><td class="name">lg</td><td class="val">1620 pt</td><td class="use">组件之间 · 卡片 padding</td><td class="bar"><span style="width:48px"></span></td></tr>
<tr><td class="name">xl</td><td class="val">2432 pt</td><td class="use">section 标题 margin</td><td class="bar"><span style="width:72px"></span></td></tr>
<tr><td class="name">2xl</td><td class="val">4060 pt</td><td class="use">大 section 之间</td><td class="bar"><span style="width:140px"></span></td></tr>
<tr><td class="name">3xl</td><td class="val">80120 pt</td><td class="use">长文档章节之间</td><td class="bar"><span style="width:240px"></span></td></tr>
<tr><th>Scale</th><th>Value</th><th>Use</th><th></th></tr>
<tr><td class="name">xs</td><td class="val">2-3 pt</td><td class="use">Inline elements</td><td class="bar"><span style="width:6px"></span></td></tr>
<tr><td class="name">sm</td><td class="val">4-5 pt</td><td class="use">Tag padding · tight layout</td><td class="bar"><span style="width:12px"></span></td></tr>
<tr><td class="name">md</td><td class="val">8-10 pt</td><td class="use">Component internals</td><td class="bar"><span style="width:24px"></span></td></tr>
<tr><td class="name">lg</td><td class="val">16-20 pt</td><td class="use">Between components · card padding</td><td class="bar"><span style="width:48px"></span></td></tr>
<tr><td class="name">xl</td><td class="val">24-32 pt</td><td class="use">Section title margins</td><td class="bar"><span style="width:72px"></span></td></tr>
<tr><td class="name">2xl</td><td class="val">40-60 pt</td><td class="use">Between major sections</td><td class="bar"><span style="width:140px"></span></td></tr>
<tr><td class="name">3xl</td><td class="val">80-120 pt</td><td class="use">Between long-doc chapters</td><td class="bar"><span style="width:240px"></span></td></tr>
</table>
<h3 class="subhead">圆角尺度 · Radii</h3>
<h3 class="subhead">Radii</h3>
<div class="radii">
<div class="radius"><div class="box" style="border-radius:4px"></div><div class="r"><b>4</b> pt</div><div class="label">极紧</div></div>
<div class="radius"><div class="box" style="border-radius:6px"></div><div class="r"><b>6</b> pt</div><div class="label">代码块</div></div>
<div class="radius"><div class="box" style="border-radius:8px"></div><div class="r"><b>8</b> pt</div><div class="label">默认卡片</div></div>
<div class="radius"><div class="box" style="border-radius:12px"></div><div class="r"><b>12</b> pt</div><div class="label">容器</div></div>
<div class="radius"><div class="box" style="border-radius:16px"></div><div class="r"><b>16</b> pt</div><div class="label">特色卡片</div></div>
<div class="radius"><div class="box" style="border-radius:24px"></div><div class="r"><b>24</b> pt</div><div class="label">大容器</div></div>
<div class="radius"><div class="box" style="border-radius:4px"></div><div class="r"><b>4</b> pt</div><div class="label">Tight</div></div>
<div class="radius"><div class="box" style="border-radius:6px"></div><div class="r"><b>6</b> pt</div><div class="label">Code block</div></div>
<div class="radius"><div class="box" style="border-radius:8px"></div><div class="r"><b>8</b> pt</div><div class="label">Default card</div></div>
<div class="radius"><div class="box" style="border-radius:12px"></div><div class="r"><b>12</b> pt</div><div class="label">Container</div></div>
<div class="radius"><div class="box" style="border-radius:16px"></div><div class="r"><b>16</b> pt</div><div class="label">Feature card</div></div>
<div class="radius"><div class="box" style="border-radius:24px"></div><div class="r"><b>24</b> pt</div><div class="label">Large container</div></div>
<div class="radius"><div class="box" style="border-radius:32px"></div><div class="r"><b>32</b> pt</div><div class="label">Hero</div></div>
</div>
<h3 class="subhead">深度 · 阴影三法</h3>
<p style="font-size:14px; color: var(--olive); max-width: 720px; margin: 0 0 12px; line-height: 1.55;">Kami 不用传统硬阴影,深度交给 ring shadowwhisper shadow 与明暗交替。</p>
<h3 class="subhead">Depth: Three Shadow Methods</h3>
<p style="font-size:14px; color: var(--olive); margin: 0 0 12px; line-height: 1.55;">Kami avoids traditional hard shadows. Depth comes from ring shadows, whisper shadows, and light-dark alternation.</p>
<div class="shadow-row">
<div class="shadow-demo sd-ring">
<p class="t">Ring Shadow</p>
<p class="s">0 0 0 1pt var(--ring-warm)<br>&nbsp;<br>按钮 · 卡片 hover</p>
<p class="s">0 0 0 1pt var(--ring-warm)<br>&nbsp;<br>Buttons · card hover</p>
</div>
<div class="shadow-demo sd-whisper">
<p class="t">Whisper Shadow</p>
<p class="s">0 4pt 24pt rgba(0,0,0,0.05)<br>&nbsp;<br>极轻浮起 · 特色卡</p>
<p class="s">0 4pt 24pt rgba(0,0,0,0.05)<br>&nbsp;<br>Gentle elevation · feature cards</p>
</div>
<div class="shadow-demo sd-alt">
<p class="t">Light ⇌ Dark</p>
<p class="s">parchment ⇌ deep-dark<br>&nbsp;<br>section 级别 · 对比最明显</p>
<p class="s">parchment ⇌ deep-dark<br>&nbsp;<br>Section-level · strongest contrast</p>
</div>
</div>
</section>
<!-- ============ 04 COMPONENTS ============ -->
<!-- ============ 06 COMPONENTS ============ -->
<section>
<div class="section-head">
<p class="section-num">06 · Components</p>
<h2 class="section-title">原子组件</h2>
<p class="section-lede">组件不求多,只保留能解决具体问题的一组。</p>
<h2 class="section-title">Atomic Modules</h2>
<p class="section-lede">A small fixed set, kept only where it solves a concrete document problem.</p>
</div>
<div class="comp-grid">
<div class="comp">
<h4>Buttons</h4>
<p class="hint">ring shadow 替代硬投影 · 8pt 圆角</p>
<p class="hint">ring shadow instead of hard drop · 8pt radius</p>
<div class="demo" style="display:flex; gap:10px; flex-wrap:wrap;">
<span class="btn btn-primary">Primary CTA</span>
<span class="btn btn-secondary">Secondary</span>
<span class="btn btn-ghost">Ghost</span>
</div>
</div>
<div class="comp">
<h4>Tags · 三档</h4>
<p class="hint">实色 hex · 从弱到强选</p>
<h4>Tags, three levels</h4>
<p class="hint">solid hex · pick from weak to strong</p>
<div class="demo" style="display:flex; gap:10px; flex-wrap:wrap; align-items:center;">
<span class="tag calm">极淡 0.08</span>
<span class="tag standard">标准 0.18</span>
<span class="tag brush">笔刷渐变</span>
<span class="tag calm">Light 0.08</span>
<span class="tag standard">Standard 0.18</span>
<span class="tag brush">Brush gradient</span>
</div>
</div>
<div class="comp">
<h4>Quote</h4>
<p class="hint"> 2pt 品牌实线 + olive </p>
<blockquote class="quote">一千个 no 换一个 yes,宁可清淡,不可浓艳。</blockquote>
<p class="hint">left 2pt brand solid + olive text</p>
<blockquote class="quote">A thousand no's for every yes, prefer clarity over decoration.</blockquote>
</div>
<div class="comp">
<h4>Metric</h4>
<p class="hint">serif 数字 + sans 标签 · tabular-nums</p>
<p class="hint">serif number + sans label · tabular-nums</p>
<div class="metrics demo">
<div class="metric"><span class="metric-value">6</span><span class="metric-label">文档类型</span></div>
<div class="metric"><span class="metric-value">1</span><span class="metric-label">强调色</span></div>
<div class="metric"><span class="metric-value">8</span><span class="metric-label">条规则</span></div>
<div class="metric"><span class="metric-value">6</span><span class="metric-label">Doc types</span></div>
<div class="metric"><span class="metric-value">1</span><span class="metric-label">Accent</span></div>
<div class="metric"><span class="metric-value">8</span><span class="metric-label">Rules</span></div>
</div>
</div>
<div class="comp">
<h4>Section Title</h4>
<p class="hint">serif 500 · 15pt · 纯字号层级,无装饰</p>
<p class="hint">serif 500 · 15pt · size-led hierarchy, no decoration</p>
<div class="demo">
<span class="sample-section-title">核心项目 · Selected Works</span>
<span class="sample-section-title">Selected Works · Projects</span>
</div>
</div>
<div class="comp">
<h4>Dash List</h4>
<p class="hint">短横线代替圆点 · 更书卷气</p>
<p class="hint">dash instead of bullet · editorial tone</p>
<ul class="dash demo">
<li>暖米纸底,不用纯白</li>
<li>油墨蓝点缀,不引第二色</li>
<li>Serif 承担权威</li>
<li>Warm parchment, never pure white</li>
<li>Ink blue accent, no second color</li>
<li>Serif carries authority</li>
</ul>
</div>
<div class="comp">
<h4>Code Block</h4>
<p class="hint">ivory + 0.5pt border + 6pt 圆角 + mono 字体</p>
<pre class="code"><span class="c">// 暖米纸 · 一种克制的温度</span>
<p class="hint">ivory bg + 0.5pt border + 6pt radius</p>
<pre class="code"><span class="c">// warmth from restraint, not from color</span>
canvas = parchment; accent = <span class="k">ink_blue</span>
palette = <span class="k">warm_neutrals</span> cool_grays
document = <span class="k">serif</span> × hierarchy + generous_space
beauty = constraints × <span class="k">intention</span> ÷ noise</pre>
</div>
<div class="comp">
<h4>Featured Card</h4>
<p class="hint">whisper shadow + 16pt 圆角</p>
<p class="hint">whisper shadow + 16pt radius</p>
<div class="demo" style="background:var(--ivory); border-radius:16px; padding:18px 20px; box-shadow: 0 4px 24px rgba(0,0,0,0.05); border: 1px solid var(--border-cream);">
<p style="font-family:var(--serif); font-weight:500; font-size:15px; margin:0 0 6px; color:var(--near-black);">Tesla 公司介绍 · One-Pager</p>
<p style="font-size:12px; color:var(--olive); margin:0 0 10px; line-height:1.5;">单页 A4 · 中文版 · 4 张指标卡 + 3 段正文 + 时间线</p>
<p style="font-family:var(--serif); font-weight:500; font-size:15px; margin:0 0 6px; color:var(--near-black);">Tesla Company Profile · One-Pager</p>
<p style="font-size:12px; color:var(--olive); margin:0 0 10px; line-height:1.5;">Single A4 page · 4 metric cards + 3 body sections + timeline</p>
<div style="display:flex; gap:8px; flex-wrap:wrap;">
<span class="tag calm">中文</span>
<span class="tag calm">A4 × 1</span>
<span class="tag calm">English</span>
<span class="tag calm">A4 x 1</span>
<span class="tag calm">Ink Blue</span>
</div>
</div>
</div>
</div>
</section>
<!-- ============ 05b DIAGRAMS ============ -->
<!-- ============ 07 DIAGRAMS ============ -->
<section>
<div class="section-head">
<p class="section-num">07 · Diagrams</p>
<h2 class="section-title">内联图表</h2>
<p class="section-lede">十二种内联 SVG 图表,覆盖架构、流程、数据三大场景。直接告诉 Claude 需要哪种图,会自动嵌入文档,配色与字体沿用 Kami 设计语言。</p>
<h2 class="section-title">Inline Charts</h2>
<p class="section-lede">Twelve inline SVG diagram types covering architecture, process, and data chart scenarios. Tell Claude which type you need and it embeds directly into the document, colors and fonts following the Kami design language.</p>
</div>
<div class="chart-showcase">
<!-- Architecture mini -->
<div class="chart-card">
<div class="chart-label">架构图 · Architecture</div>
<div class="chart-label">Architecture</div>
<svg viewBox="0 0 280 160" xmlns="http://www.w3.org/2000/svg" style="font-family:'Inter','TsangerJinKai02',sans-serif">
<rect width="280" height="160" fill="#f5f4ed"/>
<!-- Browser → API -->
@@ -401,39 +378,35 @@ beauty = constraints × <span class="k">intention</span> ÷ noise</pre>
<text x="228" y="76" text-anchor="middle" font-size="7" letter-spacing="0.5" fill="#87867f">DATA</text>
<text x="228" y="91" text-anchor="middle" font-size="9" font-weight="600" fill="#141413">Database</text>
</svg>
<p class="chart-caption">系统组件与连接关系,一个焦点节点</p>
<p class="chart-caption">System components and connections, one focal node</p>
</div>
<!-- Flowchart mini -->
<div class="chart-card">
<div class="chart-label">流程图 · Flowchart</div>
<div class="chart-label">Flowchart</div>
<svg viewBox="0 0 280 160" xmlns="http://www.w3.org/2000/svg" style="font-family:'Inter','TsangerJinKai02',sans-serif">
<rect width="280" height="160" fill="#f5f4ed"/>
<!-- Start pill -->
<rect x="100" y="8" width="80" height="28" rx="14" fill="#141413"/>
<text x="140" y="27" text-anchor="middle" font-size="9" font-weight="600" fill="#f5f4ed">Start</text>
<!-- Start → diamond (arrow down) -->
<!-- Start → diamond -->
<path d="M 139.5,36 L 139.5,48 L 138,48 L 140,52 L 142,48 L 140.5,48 L 140.5,36 Z" fill="#5e5d59"/>
<!-- Decision diamond -->
<polygon points="140,52 172,72 140,92 108,72" fill="#f5f4ed" stroke="#141413" stroke-width="1"/>
<text x="140" y="76" text-anchor="middle" font-size="8" fill="#141413">Valid?</text>
<!-- Yes arrow right -->
<!-- Yes right -->
<path d="M 172,71.5 L 204,71.5 L 204,70 L 208,72 L 204,74 L 204,72.5 L 172,72.5 Z" fill="#1B365D"/>
<text x="186" y="68" text-anchor="middle" font-size="8" fill="#1B365D">Yes</text>
<!-- Success node (focal) -->
<rect x="208" y="56" width="64" height="32" rx="4" fill="#EEF2F7" stroke="#1B365D" stroke-width="1"/>
<text x="240" y="77" text-anchor="middle" font-size="9" font-weight="600" fill="#141413">Process</text>
<!-- No arrow down -->
<!-- No down -->
<path d="M 139.5,92 L 139.5,112 L 138,112 L 140,116 L 142,112 L 140.5,112 L 140.5,92 Z" fill="#5e5d59"/>
<text x="151" y="106" font-size="8" fill="#5e5d59">No</text>
<!-- Reject node -->
<rect x="96" y="116" width="88" height="32" rx="4" fill="#f5f4ed" stroke="#87867f" stroke-width="0.8"/>
<text x="140" y="136" text-anchor="middle" font-size="9" font-weight="600" fill="#5e5d59">Reject</text>
</svg>
<p class="chart-caption">决策分支,Yes / No 路径,焦点为成功路径</p>
<p class="chart-caption">Decision branches, yes/no paths, focal on success</p>
</div>
<!-- Bar chart (shifted left) -->
<div class="chart-card">
<div class="chart-label">柱状图 · Bar Chart</div>
<div class="chart-label">Bar Chart</div>
<svg viewBox="0 0 280 160" xmlns="http://www.w3.org/2000/svg" style="font-family:'Inter','TsangerJinKai02',sans-serif">
<rect width="280" height="160" fill="#f5f4ed"/>
<rect x="18" y="78" width="42" height="50" rx="3" fill="#87867f"/>
@@ -449,96 +422,93 @@ beauty = constraints × <span class="k">intention</span> ÷ noise</pre>
<text x="159" y="39" text-anchor="middle" font-size="9" font-weight="600" fill="#2d4e7a">195</text>
<text x="219" y="25" text-anchor="middle" font-size="9" font-weight="600" fill="#1B365D">240</text>
</svg>
<p class="chart-caption">季度营收对比,可选颜色渐变或单色</p>
<p class="chart-caption">Quarterly revenue comparison, gradient or single color</p>
</div>
<!-- Donut chart -->
<div class="chart-card">
<div class="chart-label">环形图 · Donut Chart</div>
<div class="chart-label">Donut Chart</div>
<svg viewBox="0 0 280 160" xmlns="http://www.w3.org/2000/svg" style="font-family:'Inter','TsangerJinKai02',sans-serif">
<rect width="280" height="160" fill="#f5f4ed"/>
<path d="M 80,25 A 55,55 0 1,1 47.7,124.5 L 62.4,104.3 A 30,30 0 1,0 80,50 Z" fill="#1B365D" stroke="white" stroke-width="1.5"/>
<path d="M 47.7,124.5 A 55,55 0 0,1 35.5,47.7 L 55.7,62.4 A 30,30 0 0,0 62.4,104.3 Z" fill="#5e5d59" stroke="white" stroke-width="1.5"/>
<path d="M 35.5,47.7 A 55,55 0 0,1 80,25 L 80,50 A 30,30 0 0,0 55.7,62.4 Z" fill="#87867f" stroke="white" stroke-width="1.5"/>
<text x="80" y="77" text-anchor="middle" font-size="13" font-weight="600" fill="#1B365D">60%</text>
<text x="80" y="92" text-anchor="middle" font-size="10" fill="#5e5d59">主营</text>
<text x="80" y="92" text-anchor="middle" font-size="10" fill="#5e5d59">Primary</text>
<rect x="150" y="46" width="10" height="10" rx="2" fill="#1B365D"/>
<text x="165" y="55" font-size="11" fill="#4d4c48">主营业务 · 60%</text>
<text x="165" y="55" font-size="11" fill="#4d4c48">Core revenue · 60%</text>
<rect x="150" y="72" width="10" height="10" rx="2" fill="#5e5d59"/>
<text x="165" y="81" font-size="11" fill="#4d4c48">服务收入 · 25%</text>
<text x="165" y="81" font-size="11" fill="#4d4c48">Services · 25%</text>
<rect x="150" y="98" width="10" height="10" rx="2" fill="#87867f"/>
<text x="165" y="107" font-size="11" fill="#4d4c48">其他来源 · 15%</text>
<text x="165" y="107" font-size="11" fill="#4d4c48">Other · 15%</text>
</svg>
<p class="chart-caption">营收结构拆解,支持 36 个分段</p>
<p class="chart-caption">Revenue structure breakdown, supports 36 segments</p>
</div>
</div>
</section>
<!-- ============ 05 DECISION TABLE ============ -->
<!-- ============ 07 DECISION TABLE ============ -->
<section>
<div class="section-head">
<p class="section-num">08 · Decision Lookup</p>
<h2 class="section-title">决策速查</h2>
<p class="section-lede">遇到“该用什么”时先看这张表,不在表里就回到基础原则。</p>
<h2 class="section-title">Quick Reference</h2>
<p class="section-lede">When in doubt about what to use, consult this table. If it's not here, go back to first principles.</p>
</div>
<table class="decision">
<tr><th>要做什么</th><th>怎么做</th></tr>
<tr><td class="task">大标题</td><td class="how">serif <code>500</code>,字号按层级,line-height <code>1.101.30</code></td></tr>
<tr><td class="task">正文阅读</td><td class="how">sans <code>400</code><code>9.510 pt</code>line-height <code>1.55</code></td></tr>
<tr><td class="task">强调一个数字</td><td class="how"><code>color: var(--brand)</code>,不要粗体</td></tr>
<tr><td class="task">分隔两段内容</td><td class="how"><code>2.5pt</code> 品牌色左侧竖线,或 <code>0.5pt</code> 暖灰虚线</td></tr>
<tr><td class="task">引用某人的话</td><td class="how"> <code>2pt</code> 品牌色实线 + olive </td></tr>
<tr><td class="task">展示代码</td><td class="how">ivory + <code>0.5pt</code> border + <code>6pt</code> 圆角 + mono 字体</td></tr>
<tr><td class="task">区分主次按钮</td><td class="how">Primary 品牌色填充 + ivory 字;Secondary warm-sand + charcoal</td></tr>
<tr><td class="task">标注某张特殊卡片</td><td class="how"><code>border: 0.5pt solid var(--brand)</code> <code>border-left: 3pt</code></td></tr>
<tr><td class="task">章节开始</td><td class="how">serif <code>500</code> · <code>15pt</code> · 纯字号层级,无额外装饰</td></tr>
<tr><td class="task">文档封面</td><td class="how">单页 Display 字号 + 右对齐作者/日期 + 大量留白</td></tr>
<tr><td class="task">数据卡</td><td class="how">ivory + <code>8pt</code> 圆角 + serif 大数字(油墨蓝) + sans 小标签</td></tr>
<tr><th>Task</th><th>How</th></tr>
<tr><td class="task">Large headline</td><td class="how">serif <code>500</code>, size by hierarchy, line-height <code>1.10-1.30</code></td></tr>
<tr><td class="task">Reading body</td><td class="how">serif <code>400</code>, <code>9.5-10 pt</code>, line-height <code>1.55</code></td></tr>
<tr><td class="task">Emphasize a number</td><td class="how"><code>color: var(--brand)</code>, not bold</td></tr>
<tr><td class="task">Separate two sections</td><td class="how"><code>2.5pt</code> brand left bar, or <code>0.5pt</code> warm-gray dashed</td></tr>
<tr><td class="task">Quote someone</td><td class="how">Left <code>2pt</code> brand solid + olive text</td></tr>
<tr><td class="task">Show code</td><td class="how">Ivory bg + <code>0.5pt</code> border + <code>6pt</code> radius + mono font</td></tr>
<tr><td class="task">Primary vs secondary</td><td class="how">Primary: brand fill + ivory text; Secondary: warm-sand + charcoal</td></tr>
<tr><td class="task">Mark a special card</td><td class="how"><code>border: 0.5pt solid var(--brand)</code> or <code>border-left: 3pt</code></td></tr>
<tr><td class="task">Begin a section</td><td class="how">Serif <code>500</code> · <code>15pt</code> · size-led hierarchy, no decoration needed</td></tr>
<tr><td class="task">Document cover</td><td class="how">Single page, Display size + right-aligned author/date + generous whitespace</td></tr>
<tr><td class="task">Data card</td><td class="how">Ivory bg + <code>8pt</code> radius + serif large number, ink blue + sans small label</td></tr>
</table>
</section>
<!-- ============ 06 ANTI ============ -->
<!-- ============ 08 ANTI ============ -->
<section>
<div class="section-head">
<p class="section-num">09 · Anti-Patterns</p>
<h2 class="section-title">反面示例</h2>
<p class="section-lede">破例可以,但理由要足够清楚。</p>
<p class="section-num">08 · Anti-Patterns</p>
<h2 class="section-title">What to Avoid</h2>
<p class="section-lede">Exceptions are allowed, but the reason should be explicit.</p>
</div>
<div class="anti">
<div class="no">
<h5>Don't</h5>
<p>页面背景用 <code>#ffffff</code> 纯白或 <code>#f3f4f6</code> 冷灰,会让版面变硬,也削弱 Kami 的暖米纸气质。</p>
<p>Use <code>#ffffff</code> pure white or <code>#f3f4f6</code> cool gray as page background. It makes the page feel brittle and weakens the warm parchment character.</p>
</div>
<div class="yes">
<h5>Do</h5>
<p>始终使用 <code>#f5f4ed</code> parchment,打印时把 <code>@page background</code> 也设为同色,避免白边。</p>
<p>Always use <code>#f5f4ed</code> parchment. Set <code>@page background</code> to the same color to avoid white edges when printing.</p>
</div>
<div class="no">
<h5>Don't</h5>
<p>Tag 用 <code>rgba(201,100,66,0.18)</code>WeasyPrint 会让 padding 区与字形区透明度叠加,形成双层矩形。</p>
<p>Use <code>rgba(27,54,93,0.18)</code> for tags. WeasyPrint renders padding and glyph areas at different opacity, creating double rectangles.</p>
</div>
<div class="yes">
<h5>Do</h5>
<p>查 tint 对照表,改用等效实色 <code>#E4ECF5</code>,颜色稳定,PDF 也干净。</p>
<p>Use the tint lookup table for equivalent solid hex <code>#E4ECF5</code>. The rendering stays clean and the color remains stable.</p>
</div>
<div class="no">
<h5>Don't</h5>
<p>标题用 <code>font-weight: 600</code> 或更粗的合成 bold,笔画容易变糊,版面也会显得粗重。</p>
<p>Set headlines to <code>font-weight: 600</code> or heavier synthetic bold. Synthetic bold blurs strokes and degrades typographic quality.</p>
</div>
<div class="yes">
<h5>Do</h5>
<p>正文 400,标题 500(真实 W05 字重);需要更强存在感时,用字号或品牌色左侧竖线,不靠合成粗体。</p>
<p>Body 400, headings 500 (real W05). For more presence, use size or a brand left bar, never synthetic bold.</p>
</div>
<div class="no">
<h5>Don't</h5>
<p>box-shadow 硬投影 <code>0 2px 8px rgba(0,0,0,0.3)</code> 观感偏硬,印刷时也容易发黑。</p>
<p>Use hard drop shadow <code>0 2px 8px rgba(0,0,0,0.3)</code>. Visually heavy and likely to print as dark blobs.</p>
</div>
<div class="yes">
<h5>Do</h5>
<p>用 ring shadow (<code>0 0 0 1pt</code>)、whisper (<code>rgba 0.05</code>),或直接用明暗交替建立层次。</p>
<p>Ring shadow <code>0 0 0 1pt</code> or whisper <code>rgba 0.05</code>, or simply alternate light and dark sections.</p>
</div>
</div>
</section>
@@ -546,12 +516,12 @@ beauty = constraints × <span class="k">intention</span> ÷ noise</pre>
<!-- ============ 09 BACKGROUND ============ -->
<section>
<div class="section-head">
<p class="section-num">10 · Background</p>
<h2 class="section-title">设计缘起</h2>
<p class="section-num">09 · Background</p>
<h2 class="section-title">Design Origins</h2>
</div>
<div class="background-text">
<p>我做美股投资,经常让 AI 生成分析报告,最初的输出总像默认 Google Doc,平淡、不一致,也没有记忆点;我不喜欢文档风格总在变,便反复调字体、配色与间距,直到它变成一份自己愿意读下去的文档。</p>
<p>后来受邀做一次演讲,需要一套和文档风格一致的 slides,这轮迭代把系统继续往前推:加入 inline SVG 图表,统一暖色调,也收紧编辑节奏;做着做着,它已经覆盖了我常用的文档场景,于是独立成 kami,一套我愿意反复使用、也愿意发出去的视觉语言。</p>
<p>I invest in US equities and often ask AI to generate analysis reports. The earliest drafts looked like default Google Docs: plain, inconsistent, and easy to forget. I did not want each document to arrive with a different face, so I kept refining the typography, palette, and spacing until the page became something I wanted to keep reading.</p>
<p>Later, I was invited to give a talk and needed a slide deck that shared the same visual standard. That iteration pushed the system further: inline SVG diagrams, a unified warm palette, and a tighter editorial rhythm. Over time it covered the document formats I use most, so it became kami, a visual language I can reuse with confidence and send out without hesitation.</p>
</div>
</section>
@@ -565,13 +535,13 @@ beauty = constraints × <span class="k">intention</span> ÷ noise</pre>
</svg>
<div>
<p class="wm-name">Kami · 紙</p>
<p class="wm-sub">好内容,值得好版面。</p>
<p class="wm-sub">Good content deserves good paper.</p>
</div>
</div>
<div class="colophon">
<div><a href="https://github.com/tw93/kami" style="color:var(--brand); text-decoration:none; font-weight:500;">GitHub</a>&nbsp;&nbsp;·&nbsp;&nbsp;One-Pager · Long Doc · Letter · 作品集 · Resume · Slides</div>
<div><a href="https://github.com/tw93/kami" style="color:var(--brand); text-decoration:none; font-weight:500;">GitHub</a>&nbsp;&nbsp;·&nbsp;&nbsp;One-Pager · Long Doc · Letter · Portfolio · Resume · Slides</div>
<div style="margin-top:8px; color: var(--olive); font-family: var(--serif);">
Serif 承担权威,sans 承担功能,暖灰承担节奏,油墨蓝承担焦点。
Serif carries authority, sans function, warm grays rhythm, ink blue focus.
</div>
</div>
</footer>
+13
View File
@@ -268,6 +268,19 @@ python3 scripts/build.py resume-en # one target + page count + fonts
python3 scripts/build.py --check # scan for CSS rule violations
```
### Layout stabilizer (HTML templates)
When a constrained template is near page overflow (one-pager, letter, resume, and English variants), run the stabilizer first, then verify:
```bash
python3 scripts/stabilize.py all --out-dir dist/stabilized --report
python3 scripts/stabilize.py one-pager letter resume one-pager-en letter-en resume-en --strict --report
python3 scripts/build.py --check
python3 scripts/build.py --verify
```
`scripts/stabilize.py` is intentionally independent from `scripts/build.py`. Thresholds and solver behavior are controlled by `references/stabilizer_profiles.json`.
### Hi-res visual inspection
```bash
+161
View File
@@ -0,0 +1,161 @@
{
"defaults": {
"line_height": {
"min": 1.0,
"max": 1.55
},
"body_font_size_pt": {
"min": 9.0,
"max": 12.0
},
"body_line_height": {
"min": 1.35,
"max": 1.55
},
"section_gap_pt": {
"min": 12.0,
"max": 30.0
},
"page_margin_mm": {
"min": [8.0, 10.0, 8.0, 10.0]
},
"overflow_solver": {
"enabled": true,
"max_iterations": 40,
"section_gap_step_pt": 1.0,
"body_line_height_step": 0.02,
"body_font_step_pt": 0.2,
"margin_step_mm": 1.0
}
},
"targets": {
"one-pager": {
"section_gap_pt": {
"min": 12.0,
"max": 24.0
},
"page_margin_mm": {
"min": [12.0, 14.0, 12.0, 14.0]
},
"overflow_solver": {
"max_iterations": 30
}
},
"one-pager-en": {
"section_gap_pt": {
"min": 12.0,
"max": 24.0
},
"page_margin_mm": {
"min": [12.0, 14.0, 12.0, 14.0]
},
"overflow_solver": {
"max_iterations": 30
}
},
"letter": {
"body_font_size_pt": {
"min": 10.5,
"max": 12.0
},
"body_line_height": {
"min": 1.45,
"max": 1.55
},
"section_gap_pt": {
"min": 18.0,
"max": 32.0
},
"page_margin_mm": {
"min": [20.0, 20.0, 20.0, 20.0]
},
"overflow_solver": {
"max_iterations": 20
}
},
"letter-en": {
"body_font_size_pt": {
"min": 10.5,
"max": 12.0
},
"body_line_height": {
"min": 1.45,
"max": 1.55
},
"section_gap_pt": {
"min": 18.0,
"max": 32.0
},
"page_margin_mm": {
"min": [20.0, 20.0, 20.0, 20.0]
},
"overflow_solver": {
"max_iterations": 20
}
},
"resume": {
"body_font_size_pt": {
"min": 8.8,
"max": 9.8
},
"body_line_height": {
"min": 1.34,
"max": 1.45
},
"section_gap_pt": {
"min": 8.0,
"max": 22.0
},
"page_margin_mm": {
"min": [7.0, 10.0, 7.0, 10.0]
},
"overflow_solver": {
"max_iterations": 60,
"body_font_step_pt": 0.1,
"margin_step_mm": 0.5
}
},
"resume-en": {
"body_font_size_pt": {
"min": 8.8,
"max": 10.0
},
"body_line_height": {
"min": 1.35,
"max": 1.48
},
"section_gap_pt": {
"min": 8.0,
"max": 22.0
},
"page_margin_mm": {
"min": [9.0, 10.0, 9.0, 10.0]
},
"overflow_solver": {
"max_iterations": 60,
"body_font_step_pt": 0.1,
"margin_step_mm": 0.5
}
},
"long-doc": {
"overflow_solver": {
"enabled": false
}
},
"long-doc-en": {
"overflow_solver": {
"enabled": false
}
},
"portfolio": {
"overflow_solver": {
"enabled": false
}
},
"portfolio-en": {
"overflow_solver": {
"enabled": false
}
}
}
}
+83
View File
@@ -14,6 +14,7 @@ Usage:
from __future__ import annotations
import json
import os
import re
import subprocess
import sys
@@ -64,6 +65,79 @@ DIAGRAM_TARGETS: dict[str, str] = {
# ------------------------- build -------------------------
def infer_author() -> str:
"""Infer author name from git config or environment.
Priority:
1. git config user.name
2. KAMI_AUTHOR env var
3. fallback to "Kami"
"""
try:
result = subprocess.run(
["git", "config", "user.name"],
capture_output=True,
text=True,
check=False,
)
if result.returncode == 0 and result.stdout.strip():
return result.stdout.strip()
except FileNotFoundError:
pass
if env_author := os.environ.get("KAMI_AUTHOR"):
return env_author
return "Kami"
def set_pdf_metadata(pdf_path: Path, author: str | None = None) -> None:
"""Set PDF metadata using pypdf, only if placeholders are still present."""
try:
from pypdf import PdfReader, PdfWriter
except ImportError:
return
if not pdf_path.exists():
return
reader = PdfReader(str(pdf_path))
# Read existing metadata from WeasyPrint
existing = reader.metadata or {}
# Check if we need to update anything
needs_update = False
metadata = dict(existing) # Copy all existing metadata
# Only override author if it's still a placeholder
if author and existing.get("/Author"):
author_value = str(existing["/Author"])
if "{{" in author_value and "}}" in author_value:
metadata["/Author"] = author
needs_update = True
# Always set Producer and Creator to Kami
if metadata.get("/Producer") != "Kami":
metadata["/Producer"] = "Kami"
needs_update = True
if metadata.get("/Creator") != "Kami":
metadata["/Creator"] = "Kami"
needs_update = True
if not needs_update:
return
writer = PdfWriter()
for page in reader.pages:
writer.add_page(page)
writer.add_metadata(metadata)
with open(pdf_path, "wb") as f:
writer.write(f)
def build_html(name: str, source: str, max_pages: int,
src_dir: Path = TEMPLATES) -> bool:
try:
@@ -84,6 +158,11 @@ def build_html(name: str, source: str, max_pages: int,
# weasyprint resolves @font-face relative to CWD. Run from the source dir
# so fonts placed next to the HTML are found.
HTML(str(src), base_url=str(src.parent)).write_pdf(str(out))
# Set PDF metadata (only replaces placeholders, preserves filled values)
author = infer_author()
set_pdf_metadata(out, author=author)
n = len(PdfReader(str(out)).pages)
msg = f"OK: {name}: {n} pages"
if max_pages and n > max_pages:
@@ -280,6 +359,10 @@ def verify_target(name: str, source: str, max_pages: int, src_dir: Path) -> list
out = EXAMPLES / f"{name}.pdf"
HTML(str(src), base_url=str(src_dir)).write_pdf(str(out))
# Set PDF metadata (only replaces placeholders, preserves filled values)
author = infer_author()
set_pdf_metadata(out, author=author)
# page count check
n = len(PdfReader(str(out)).pages)
if max_pages and n > max_pages:
+840
View File
@@ -0,0 +1,840 @@
#!/usr/bin/env python3
"""kami stabilize
Stabilize HTML templates with deterministic normalization and optional overflow solving.
Usage:
python3 scripts/stabilize.py all
python3 scripts/stabilize.py one-pager resume-en --out-dir /tmp/kami-stabilized --report
python3 scripts/stabilize.py one-pager --write --strict --report
"""
from __future__ import annotations
import argparse
import json
import re
import sys
import tempfile
from dataclasses import dataclass, field
from datetime import datetime, timezone
from pathlib import Path
from typing import Any
ROOT = Path(__file__).resolve().parent.parent
TEMPLATES = ROOT / "assets" / "templates"
PROFILES_FILE = ROOT / "references" / "stabilizer_profiles.json"
TOKENS_FILE = ROOT / "references" / "tokens.json"
DEFAULT_OUT_DIR = ROOT / "dist" / "stabilized"
# HTML targets only. Diagrams/slides are intentionally excluded from stabilize v0.
HTML_TARGETS: dict[str, tuple[str, int]] = {
"one-pager": ("one-pager.html", 1),
"letter": ("letter.html", 1),
"long-doc": ("long-doc.html", 0),
"portfolio": ("portfolio.html", 0),
"resume": ("resume.html", 2),
"one-pager-en": ("one-pager-en.html", 1),
"letter-en": ("letter-en.html", 1),
"long-doc-en": ("long-doc-en.html", 0),
"portfolio-en": ("portfolio-en.html", 0),
"resume-en": ("resume-en.html", 2),
}
STYLE_BLOCK_RE = re.compile(r"(<style>\s*)(?P<css>.*?)(\s*</style>)", re.DOTALL | re.IGNORECASE)
ROOT_BLOCK_RE = re.compile(r"(^[ \t]*:root[ \t]*\{)(?P<body>.*?)(^[ \t]*\})", re.DOTALL | re.MULTILINE)
BODY_BLOCK_RE = re.compile(r"(^[ \t]*body[ \t]*\{)(?P<body>.*?)(^[ \t]*\})", re.DOTALL | re.MULTILINE)
SECTION_BLOCK_RE = re.compile(r"(^[ \t]*section[ \t]*\{)(?P<body>.*?)(^[ \t]*\})", re.DOTALL | re.MULTILINE)
PAGE_BLOCK_RE = re.compile(r"(^[ \t]*@page[ \t]*\{)(?P<body>.*?)(^[ \t]*\})", re.DOTALL | re.MULTILINE)
CSS_VAR_RE = re.compile(r"(--[\w-]+\s*:\s*)([^;]+)(;)")
RGBA_RE = re.compile(
r"rgba\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*((?:\d*\.\d+)|\d+)\s*\)",
re.IGNORECASE,
)
HEX_RE = re.compile(r"#[0-9a-fA-F]{3,6}\b")
LINE_HEIGHT_RE = re.compile(r"(line-height\s*:\s*)([0-9]*\.?[0-9]+)(\s*;)", re.IGNORECASE)
FONT_SIZE_PT_RE = re.compile(r"(font-size\s*:\s*)([0-9]*\.?[0-9]+)(\s*pt\s*;)", re.IGNORECASE)
MARGIN_BOTTOM_PT_RE = re.compile(r"(margin-bottom\s*:\s*)([0-9]*\.?[0-9]+)(\s*pt\s*;)", re.IGNORECASE)
PAGE_MARGIN_MM_RE = re.compile(
r"(margin\s*:\s*)([0-9]*\.?[0-9]+)mm\s+([0-9]*\.?[0-9]+)mm\s+([0-9]*\.?[0-9]+)mm\s+([0-9]*\.?[0-9]+)mm(\s*;)",
re.IGNORECASE,
)
COOL_GRAY_BLOCKLIST = {
"#888",
"#888888",
"#666",
"#666666",
"#999",
"#999999",
"#ccc",
"#cccccc",
"#ddd",
"#dddddd",
"#eee",
"#eeeeee",
"#111",
"#111111",
"#222",
"#222222",
"#333",
"#333333",
"#444",
"#444444",
"#555",
"#555555",
"#777",
"#777777",
"#aaa",
"#aaaaaa",
"#bbb",
"#bbbbbb",
"#6b7280",
"#9ca3af",
"#d1d5db",
"#e5e7eb",
"#f3f4f6",
"#4b5563",
"#374151",
"#1f2937",
"#111827",
"#f8f9fa",
"#e9ecef",
"#dee2e6",
"#ced4da",
"#adb5bd",
"#6c757d",
"#495057",
"#343a40",
"#212529",
}
PARCHMENT_RGB = (245, 244, 237)
@dataclass
class SolverStep:
action: str
before: float
after: float
unit: str
pages_before: int
pages_after: int
@dataclass
class TargetResult:
target: str
source: str
output: str
changed: bool
max_pages: int
pages_before: int | None
pages_after: int | None
status: str
rules: dict[str, int] = field(default_factory=dict)
solver_steps: list[SolverStep] = field(default_factory=list)
notes: list[str] = field(default_factory=list)
def to_json(self) -> dict[str, Any]:
return {
"target": self.target,
"source": self.source,
"output": self.output,
"changed": self.changed,
"max_pages": self.max_pages,
"pages": {"before": self.pages_before, "after": self.pages_after},
"status": self.status,
"rules": self.rules,
"solver_steps": [
{
"action": s.action,
"before": s.before,
"after": s.after,
"unit": s.unit,
"pages_before": s.pages_before,
"pages_after": s.pages_after,
}
for s in self.solver_steps
],
"notes": self.notes,
}
def clamp(value: float, lower: float, upper: float) -> float:
return max(lower, min(upper, value))
def round_number(value: float, digits: int = 3) -> float:
return float(f"{value:.{digits}f}")
def deep_merge(base: dict[str, Any], override: dict[str, Any]) -> dict[str, Any]:
result: dict[str, Any] = dict(base)
for key, value in override.items():
if isinstance(value, dict) and isinstance(result.get(key), dict):
result[key] = deep_merge(result[key], value)
else:
result[key] = value
return result
def extract_css(html: str) -> tuple[str, re.Match[str]]:
match = STYLE_BLOCK_RE.search(html)
if not match:
raise ValueError("missing <style> block")
return match.group("css"), match
def replace_css(html: str, css: str, style_match: re.Match[str]) -> str:
return html[: style_match.start("css")] + css + html[style_match.end("css") :]
def parse_hex(hex_code: str) -> tuple[int, int, int]:
h = hex_code.lstrip("#")
if len(h) == 3:
h = "".join(ch * 2 for ch in h)
if len(h) != 6:
raise ValueError(f"unsupported hex: {hex_code}")
return int(h[0:2], 16), int(h[2:4], 16), int(h[4:6], 16)
def rgb_to_hex(rgb: tuple[int, int, int]) -> str:
r, g, b = rgb
return f"#{r:02X}{g:02X}{b:02X}"
def blend_rgba_on_parchment(r: int, g: int, b: int, alpha: float) -> str:
a = clamp(alpha, 0.0, 1.0)
out = (
round(PARCHMENT_RGB[0] + (r - PARCHMENT_RGB[0]) * a),
round(PARCHMENT_RGB[1] + (g - PARCHMENT_RGB[1]) * a),
round(PARCHMENT_RGB[2] + (b - PARCHMENT_RGB[2]) * a),
)
return rgb_to_hex(out)
def normalize_tokens(css: str, canonical_tokens: dict[str, str]) -> tuple[str, int]:
match = ROOT_BLOCK_RE.search(css)
if not match:
return css, 0
block = match.group("body")
changed = 0
for token, expected in canonical_tokens.items():
token_name = token if token.startswith("--") else f"--{token}"
token_re = re.compile(rf"({re.escape(token_name)}\s*:\s*)([^;]+)(;)", re.IGNORECASE)
def token_repl(m: re.Match[str]) -> str:
nonlocal changed
actual = m.group(2).strip()
if actual.lower() == expected.lower():
return m.group(0)
changed += 1
return f"{m.group(1)}{expected}{m.group(3)}"
block = token_re.sub(token_repl, block)
if changed == 0:
return css, 0
new_css = css[: match.start("body")] + block + css[match.end("body") :]
return new_css, changed
def normalize_rgba(css: str) -> tuple[str, int]:
changed = 0
def repl(m: re.Match[str]) -> str:
nonlocal changed
r, g, b = (int(m.group(1)), int(m.group(2)), int(m.group(3)))
a = float(m.group(4))
if max(r, g, b) > 255:
return m.group(0)
solid = blend_rgba_on_parchment(r, g, b, a)
changed += 1
return solid
return RGBA_RE.sub(repl, css), changed
def luminance(rgb: tuple[int, int, int]) -> float:
r, g, b = rgb
return (0.2126 * r + 0.7152 * g + 0.0722 * b) / 255.0
def normalize_cool_grays(css: str) -> tuple[str, int]:
changed = 0
def repl(m: re.Match[str]) -> str:
nonlocal changed
raw = m.group(0)
normalized = raw.lower()
if len(normalized) == 4:
normalized = "#" + "".join(ch * 2 for ch in normalized[1:])
if normalized not in COOL_GRAY_BLOCKLIST:
return raw
lum = luminance(parse_hex(normalized))
if lum < 0.35:
replacement = "#4D4C48"
elif lum < 0.72:
replacement = "#87867F"
else:
replacement = "#E8E6DC"
if replacement.lower() == normalized:
return raw
changed += 1
return replacement
return HEX_RE.sub(repl, css), changed
def clamp_line_heights(css: str, minimum: float, maximum: float) -> tuple[str, int]:
changed = 0
def repl(m: re.Match[str]) -> str:
nonlocal changed
value = float(m.group(2))
new_value = clamp(value, minimum, maximum)
if abs(value - new_value) < 1e-6:
return m.group(0)
changed += 1
return f"{m.group(1)}{round_number(new_value, 3)}{m.group(3)}"
return LINE_HEIGHT_RE.sub(repl, css), changed
def _replace_in_block(
css: str,
block_re: re.Pattern[str],
property_re: re.Pattern[str],
transform: Any,
) -> tuple[str, bool, float | None, float | None]:
block_match = block_re.search(css)
if not block_match:
return css, False, None, None
block_body = block_match.group("body")
prop_match = property_re.search(block_body)
if not prop_match:
return css, False, None, None
before_value = float(prop_match.group(2))
after_value = float(transform(before_value))
if abs(after_value - before_value) < 1e-6:
return css, False, before_value, before_value
replacement = f"{prop_match.group(1)}{round_number(after_value, 3)}{prop_match.group(3)}"
new_block = block_body[: prop_match.start()] + replacement + block_body[prop_match.end() :]
new_css = css[: block_match.start("body")] + new_block + css[block_match.end("body") :]
return new_css, True, before_value, after_value
def clamp_body_font(css: str, minimum: float, maximum: float) -> tuple[str, int]:
new_css, changed, _, _ = _replace_in_block(
css,
BODY_BLOCK_RE,
FONT_SIZE_PT_RE,
lambda value: clamp(value, minimum, maximum),
)
return new_css, 1 if changed else 0
def clamp_body_line_height(css: str, minimum: float, maximum: float) -> tuple[str, int]:
new_css, changed, _, _ = _replace_in_block(
css,
BODY_BLOCK_RE,
LINE_HEIGHT_RE,
lambda value: clamp(value, minimum, maximum),
)
return new_css, 1 if changed else 0
def clamp_section_gap(css: str, minimum: float, maximum: float) -> tuple[str, int]:
new_css, changed, _, _ = _replace_in_block(
css,
SECTION_BLOCK_RE,
MARGIN_BOTTOM_PT_RE,
lambda value: clamp(value, minimum, maximum),
)
return new_css, 1 if changed else 0
def get_current_value(css: str, block_re: re.Pattern[str], prop_re: re.Pattern[str]) -> float | None:
block_match = block_re.search(css)
if not block_match:
return None
prop_match = prop_re.search(block_match.group("body"))
if not prop_match:
return None
return float(prop_match.group(2))
def tighten_section_gap(css: str, minimum: float, step: float) -> tuple[str, bool, float, float]:
value = get_current_value(css, SECTION_BLOCK_RE, MARGIN_BOTTOM_PT_RE)
if value is None or value <= minimum + 1e-6:
return css, False, 0.0, 0.0
target = max(minimum, value - step)
new_css, changed, before, after = _replace_in_block(
css,
SECTION_BLOCK_RE,
MARGIN_BOTTOM_PT_RE,
lambda _: target,
)
return new_css, changed, before or value, after or value
def tighten_body_line_height(css: str, minimum: float, step: float) -> tuple[str, bool, float, float]:
value = get_current_value(css, BODY_BLOCK_RE, LINE_HEIGHT_RE)
if value is None or value <= minimum + 1e-6:
return css, False, 0.0, 0.0
target = max(minimum, value - step)
new_css, changed, before, after = _replace_in_block(
css,
BODY_BLOCK_RE,
LINE_HEIGHT_RE,
lambda _: target,
)
return new_css, changed, before or value, after or value
def tighten_body_font(css: str, minimum: float, step: float) -> tuple[str, bool, float, float]:
value = get_current_value(css, BODY_BLOCK_RE, FONT_SIZE_PT_RE)
if value is None or value <= minimum + 1e-6:
return css, False, 0.0, 0.0
target = max(minimum, value - step)
new_css, changed, before, after = _replace_in_block(
css,
BODY_BLOCK_RE,
FONT_SIZE_PT_RE,
lambda _: target,
)
return new_css, changed, before or value, after or value
def tighten_page_margin(
css: str,
min_margins: list[float],
step: float,
) -> tuple[str, bool, float, float]:
block_match = PAGE_BLOCK_RE.search(css)
if not block_match:
return css, False, 0.0, 0.0
block = block_match.group("body")
margin_match = PAGE_MARGIN_MM_RE.search(block)
if not margin_match:
return css, False, 0.0, 0.0
before = [float(margin_match.group(i)) for i in range(2, 6)]
target = [max(min_margins[idx], before[idx] - step) for idx in range(4)]
if all(abs(before[idx] - target[idx]) < 1e-6 for idx in range(4)):
return css, False, 0.0, 0.0
replacement = (
f"{margin_match.group(1)}"
f"{round_number(target[0], 3)}mm {round_number(target[1], 3)}mm "
f"{round_number(target[2], 3)}mm {round_number(target[3], 3)}mm"
f"{margin_match.group(6)}"
)
new_block = block[: margin_match.start()] + replacement + block[margin_match.end() :]
new_css = css[: block_match.start("body")] + new_block + css[block_match.end("body") :]
before_sum = sum(before)
after_sum = sum(target)
return new_css, True, before_sum, after_sum
def count_pages(html: str, base_dir: Path) -> int:
try:
from weasyprint import HTML
from pypdf import PdfReader
except ImportError as exc:
raise RuntimeError("missing deps: pip install weasyprint pypdf --break-system-packages") from exc
tmp_pdf: Path | None = None
try:
with tempfile.NamedTemporaryFile(prefix="kami-stabilize-", suffix=".pdf", delete=False) as tmp:
tmp_pdf = Path(tmp.name)
HTML(string=html, base_url=str(base_dir)).write_pdf(str(tmp_pdf))
return len(PdfReader(str(tmp_pdf)).pages)
finally:
if tmp_pdf and tmp_pdf.exists():
tmp_pdf.unlink()
def load_json(path: Path) -> dict[str, Any]:
if not path.exists():
raise FileNotFoundError(f"missing file: {path}")
raw = json.loads(path.read_text(encoding="utf-8"))
if not isinstance(raw, dict):
raise ValueError(f"invalid json root object (expected dict): {path}")
return raw
def _as_float(value: Any, field: str, target: str) -> float:
try:
return float(value)
except (TypeError, ValueError) as exc:
raise ValueError(f"{target}: {field} must be numeric, got {value!r}") from exc
def _validate_range(profile: dict[str, Any], key: str, target: str) -> None:
node = profile.get(key)
if not isinstance(node, dict):
raise ValueError(f"{target}: missing object field '{key}'")
if "min" not in node or "max" not in node:
raise ValueError(f"{target}: '{key}' must contain 'min' and 'max'")
minimum = _as_float(node["min"], f"{key}.min", target)
maximum = _as_float(node["max"], f"{key}.max", target)
if minimum > maximum:
raise ValueError(f"{target}: '{key}.min' ({minimum}) must be <= '{key}.max' ({maximum})")
def validate_profile(profile: dict[str, Any], target: str) -> None:
for key in ("line_height", "body_font_size_pt", "body_line_height", "section_gap_pt"):
_validate_range(profile, key, target)
page_margin = profile.get("page_margin_mm")
if not isinstance(page_margin, dict):
raise ValueError(f"{target}: missing object field 'page_margin_mm'")
min_margins = page_margin.get("min")
if not isinstance(min_margins, list) or len(min_margins) != 4:
raise ValueError(f"{target}: 'page_margin_mm.min' must be an array of 4 numbers")
for idx, value in enumerate(min_margins):
_as_float(value, f"page_margin_mm.min[{idx}]", target)
solver = profile.get("overflow_solver")
if not isinstance(solver, dict):
raise ValueError(f"{target}: missing object field 'overflow_solver'")
if "enabled" in solver and not isinstance(solver["enabled"], bool):
raise ValueError(f"{target}: 'overflow_solver.enabled' must be a boolean")
def resolve_targets(raw_targets: list[str]) -> list[str]:
if not raw_targets or raw_targets == ["all"] or "all" in raw_targets:
return list(HTML_TARGETS.keys())
resolved: list[str] = []
for target in raw_targets:
if target in HTML_TARGETS:
resolved.append(target)
continue
# Accept source file names as aliases.
normalized = target.removesuffix(".html")
if normalized in HTML_TARGETS:
resolved.append(normalized)
continue
for name, (source, _max_pages) in HTML_TARGETS.items():
if source == target or source == f"{normalized}.html":
resolved.append(name)
break
else:
raise ValueError(f"unknown target: {target}")
return resolved
def run_for_target(
target: str,
source_file: str,
max_pages: int,
profile: dict[str, Any],
canonical_tokens: dict[str, str],
write_in_place: bool,
out_dir: Path,
strict: bool,
) -> TargetResult:
validate_profile(profile, target)
source_path = TEMPLATES / source_file
html = source_path.read_text(encoding="utf-8")
css, style_match = extract_css(html)
pages_before: int | None = None
pages_after: int | None = None
notes: list[str] = []
try:
pages_before = count_pages(html, source_path.parent)
except RuntimeError as exc:
notes.append(str(exc))
rule_hits: dict[str, int] = {}
changed = False
css, hits = normalize_tokens(css, canonical_tokens)
if hits:
changed = True
rule_hits["token_sync"] = hits
css, hits = normalize_rgba(css)
if hits:
changed = True
rule_hits["rgba_to_solid"] = hits
css, hits = normalize_cool_grays(css)
if hits:
changed = True
rule_hits["cool_gray_normalized"] = hits
line_min = float(profile["line_height"]["min"])
line_max = float(profile["line_height"]["max"])
css, hits = clamp_line_heights(css, line_min, line_max)
if hits:
changed = True
rule_hits["line_height_clamped"] = hits
body_font_min = float(profile["body_font_size_pt"]["min"])
body_font_max = float(profile["body_font_size_pt"]["max"])
css, hits = clamp_body_font(css, body_font_min, body_font_max)
if hits:
changed = True
rule_hits["body_font_clamped"] = hits
body_lh_min = float(profile["body_line_height"]["min"])
body_lh_max = float(profile["body_line_height"]["max"])
css, hits = clamp_body_line_height(css, body_lh_min, body_lh_max)
if hits:
changed = True
rule_hits["body_line_height_clamped"] = hits
gap_min = float(profile["section_gap_pt"]["min"])
gap_max = float(profile["section_gap_pt"]["max"])
css, hits = clamp_section_gap(css, gap_min, gap_max)
if hits:
changed = True
rule_hits["section_gap_clamped"] = hits
html_after = replace_css(html, css, style_match)
try:
pages_after = count_pages(html_after, source_path.parent)
except RuntimeError as exc:
if str(exc) not in notes:
notes.append(str(exc))
solver_steps: list[SolverStep] = []
solver_config = profile["overflow_solver"]
if (
max_pages > 0
and solver_config.get("enabled", False)
and pages_after is not None
and pages_after > max_pages
and not notes
):
max_iterations = int(solver_config.get("max_iterations", 40))
for _ in range(max_iterations):
if pages_after <= max_pages:
break
progressed = False
actions = [
(
"tighten_section_gap",
lambda c: tighten_section_gap(
c,
gap_min,
float(solver_config.get("section_gap_step_pt", 1.0)),
),
"pt",
),
(
"tighten_body_line_height",
lambda c: tighten_body_line_height(
c,
body_lh_min,
float(solver_config.get("body_line_height_step", 0.02)),
),
"ratio",
),
(
"tighten_body_font",
lambda c: tighten_body_font(
c,
body_font_min,
float(solver_config.get("body_font_step_pt", 0.2)),
),
"pt",
),
(
"tighten_page_margin",
lambda c: tighten_page_margin(
c,
[float(v) for v in profile["page_margin_mm"]["min"]],
float(solver_config.get("margin_step_mm", 1.0)),
),
"mm_total",
),
]
for action_name, action_fn, unit in actions:
candidate_css, applied, before, after = action_fn(css)
if not applied:
continue
candidate_html = replace_css(html, candidate_css, style_match)
candidate_pages = count_pages(candidate_html, source_path.parent)
solver_steps.append(
SolverStep(
action=action_name,
before=round_number(before, 3),
after=round_number(after, 3),
unit=unit,
pages_before=pages_after,
pages_after=candidate_pages,
)
)
css = candidate_css
html_after = candidate_html
pages_after = candidate_pages
changed = True
progressed = True
if pages_after <= max_pages:
break
if not progressed:
notes.append("solver reached lower bounds before meeting page constraint")
break
if write_in_place:
output_path = source_path
if changed:
output_path.write_text(html_after, encoding="utf-8")
else:
out_dir.mkdir(parents=True, exist_ok=True)
output_path = out_dir / source_file
output_path.write_text(html_after, encoding="utf-8")
status = "ok"
page_ok = True
if max_pages > 0:
if pages_after is None:
status = "unverified-pages"
page_ok = not strict
elif pages_after > max_pages:
status = "overflow"
page_ok = False
if strict and not page_ok:
status = "failed-strict"
result = TargetResult(
target=target,
source=str(source_path.relative_to(ROOT)),
output=str(output_path.relative_to(ROOT) if output_path.is_relative_to(ROOT) else output_path),
changed=changed,
max_pages=max_pages,
pages_before=pages_before,
pages_after=pages_after,
status=status,
rules=rule_hits,
solver_steps=solver_steps,
notes=notes,
)
return result
def build_arg_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(description="Stabilize Kami HTML templates.")
parser.add_argument("targets", nargs="*", default=["all"], help="Target names or 'all'")
parser.add_argument(
"--write",
action="store_true",
help="Write changes back to source templates (default writes to --out-dir)",
)
parser.add_argument(
"--out-dir",
default=str(DEFAULT_OUT_DIR),
help="Output directory when not using --write",
)
parser.add_argument(
"--report",
action="store_true",
help="Write a JSON report with before/after pages and applied rules",
)
parser.add_argument(
"--strict",
action="store_true",
help="Return non-zero when constrained targets still exceed page limits",
)
return parser
def main(argv: list[str]) -> int:
parser = build_arg_parser()
args = parser.parse_args(argv[1:])
try:
targets = resolve_targets(args.targets)
except ValueError as exc:
print(f"ERROR: {exc}")
return 2
try:
tokens = load_json(TOKENS_FILE)
profiles = load_json(PROFILES_FILE)
except (FileNotFoundError, json.JSONDecodeError, ValueError) as exc:
print(f"ERROR: {exc}")
return 2
if not all(isinstance(k, str) and isinstance(v, str) for k, v in tokens.items()):
print("ERROR: invalid tokens.json, expected string->string map")
return 2
defaults = profiles.get("defaults", {})
target_profiles = profiles.get("targets", {})
if not isinstance(defaults, dict) or not isinstance(target_profiles, dict):
print("ERROR: invalid stabilizer_profiles.json, expected keys: defaults(object), targets(object)")
return 2
out_dir = Path(args.out_dir)
all_results: list[TargetResult] = []
strict_failed = False
for target in targets:
source_file, max_pages = HTML_TARGETS[target]
merged_profile = deep_merge(defaults, target_profiles.get(target, {}))
try:
result = run_for_target(
target=target,
source_file=source_file,
max_pages=max_pages,
profile=merged_profile,
canonical_tokens=tokens,
write_in_place=args.write,
out_dir=out_dir,
strict=args.strict,
)
except ValueError as exc:
print(f"ERROR: {exc}")
return 2
all_results.append(result)
strict_failed = strict_failed or result.status == "failed-strict"
page_note = (
f"{result.pages_after}/{result.max_pages}"
if result.max_pages > 0 and result.pages_after is not None
else (str(result.pages_after) if result.pages_after is not None else "n/a")
)
print(
f"{result.status.upper()}: {result.target} | changed={result.changed} "
f"| pages={page_note} | output={result.output}"
)
for note in result.notes:
print(f" note: {note}")
if args.report:
report_path = out_dir / "stabilize-report.json"
report_path.parent.mkdir(parents=True, exist_ok=True)
report = {
"generated_at": datetime.now(timezone.utc).isoformat(),
"write_mode": bool(args.write),
"strict": bool(args.strict),
"targets": [r.to_json() for r in all_results],
}
report_path.write_text(json.dumps(report, indent=2, ensure_ascii=False), encoding="utf-8")
rel = report_path.relative_to(ROOT) if report_path.is_relative_to(ROOT) else report_path
print(f"REPORT: {rel}")
if strict_failed:
return 1
return 0
if __name__ == "__main__":
sys.exit(main(sys.argv))
+28 -4
View File
@@ -130,7 +130,7 @@
.page { padding: 0 !important; max-width: 100% !important; }
section { page-break-inside: auto; margin-bottom: 36px; }
.hero, .section-head, .family, .comp, .shadow-demo, .tint, .swatch, .radius, blockquote, pre, tr, .anti > div { break-inside: avoid; page-break-inside: avoid; }
.hero h1 { font-size: 68px; }
.hero h1 { font-size: 76px; }
h2.section-title { font-size: 26px; }
.comp-grid, .swatches, .tint-strip, .family-grid, .shadow-row, .anti { break-inside: auto; }
}
@@ -207,9 +207,9 @@
.hero h1 {
font-family: var(--serif);
font-weight: 500;
font-size: 92px;
font-size: 106px;
line-height: 1.05;
letter-spacing: -1px;
letter-spacing: -1.2px;
margin: 0 0 14px;
color: var(--near-black);
}
@@ -454,6 +454,11 @@
font-size: 48px; line-height: 1.1; color: var(--near-black);
letter-spacing: -0.5px;
}
html[lang="ja"] .type-sample.display {
font-size: 42px;
line-height: 1.12;
letter-spacing: -0.3px;
}
.type-sample.h1 {
font-family: var(--serif); font-weight: 500;
font-size: 30px; line-height: 1.2; color: var(--near-black);
@@ -864,6 +869,25 @@
margin: 2px 0 0;
}
@media (min-width: 980px) {
html[lang="ja"] .section-lede.section-lede-nowrap {
white-space: nowrap;
font-size: clamp(12.5px, 1.2vw, 14.5px);
line-height: 1.45;
letter-spacing: 0;
}
html[lang="ja"] .type-sample.display .display-line-nowrap {
white-space: nowrap;
}
html[lang="ja"] .demo-desc.demo-desc-nowrap {
white-space: nowrap;
font-size: 11px;
letter-spacing: 0;
}
}
/* Chart showcase */
.chart-showcase {
display: grid;
@@ -1004,7 +1028,7 @@
.section-head { margin-bottom: 20px; }
/* Typography scale */
.hero h1 { font-size: 40px; letter-spacing: -0.5px; margin-bottom: 10px; }
.hero h1 { font-size: 46px; letter-spacing: -0.6px; margin-bottom: 10px; }
.hero h1 .cn { margin-left: 8px; }
.hero .tagline { font-size: 17px; }
.hero-tokens { gap: 14px; margin-top: 14px; }
+9
View File
@@ -0,0 +1,9 @@
{
"redirects": [
{
"source": "/index-en.html",
"destination": "/",
"permanent": true
}
]
}