feat(gitea): 新增 HTML 匯出與範本風格設定

What:新增 html-export 與 html-style 兩支技能、tools/html-render.sh 與 tools/html-style.sh 兩支工具,以及六種版型乘五種風格的 HTML 範本。

Why:wiki 頁與議題要拿給 Gitea 以外的人看時,只能複製 markdown;不同類型的文件也該有各自的版面,不是每份都長一樣。

How:版型(report、slide、dashboard、spec、timeline、onepager)決定內容怎麼排,風格(minimal、corporate、dark、print、vivid)決定看起來長怎樣,兩者自由搭配。哪一種頁面套哪一組由設定決定:專案的 .jsc/html-styles 優先,其次 $JSC_HOME/html-styles.conf,對不到退 DEFAULT,再對不到才用內建的 report/minimal。產出是單一 HTML 檔,CSS 與腳本全部內嵌。

Who:需要把 wiki 頁或議題寄給客戶、主管或跨團隊同事的人。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-26 09:19:25 +08:00
co-authored by Claude Opus 5
parent f8123e829b
commit a0c13f807a
17 changed files with 1021 additions and 0 deletions
+131
View File
@@ -0,0 +1,131 @@
/* 共用排版:所有版型與風格都吃這一份,顏色與字型一律走 CSS 變數,由風格檔決定。 */
*, *::before, *::after { box-sizing: border-box; }
body {
margin: 0;
background: var(--bg);
color: var(--fg);
font-family: var(--font);
font-size: 16px;
line-height: 1.75;
-webkit-text-size-adjust: 100%;
}
.page-head {
padding: 2.5rem 0 1.5rem;
border-bottom: 2px solid var(--accent);
}
.page-head h1 {
margin: 0 0 .35rem;
font-family: var(--font-head);
font-size: 2rem;
line-height: 1.3;
color: var(--head-fg);
}
.subtitle { margin: 0 0 .5rem; color: var(--muted); font-size: 1.05rem; }
.subtitle:empty { display: none; }
.meta {
margin: 0;
color: var(--muted);
font-size: .85rem;
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
.meta a.source { color: var(--accent); text-decoration: none; word-break: break-all; }
.meta a.source:hover { text-decoration: underline; }
.content { padding: 1.5rem 0 3rem; }
.content h1, .content h2, .content h3, .content h4 {
font-family: var(--font-head);
color: var(--head-fg);
line-height: 1.35;
margin: 2rem 0 .75rem;
}
.content h1 { font-size: 1.7rem; }
.content h2 { font-size: 1.4rem; }
.content h3 { font-size: 1.15rem; }
.content h4 { font-size: 1rem; }
.content p { margin: 0 0 1rem; }
.content ul, .content ol { margin: 0 0 1rem; padding-left: 1.5rem; }
.content li { margin: .25rem 0; }
.content li input[type="checkbox"] { margin-right: .4rem; }
.content a { color: var(--accent); text-decoration: none; }
.content a:hover { text-decoration: underline; }
.content blockquote {
margin: 1rem 0;
padding: .6rem 1rem;
border-left: 4px solid var(--accent);
background: var(--card);
color: var(--muted);
}
.content code {
font-family: var(--font-mono);
font-size: .9em;
background: var(--code-bg);
padding: .15em .4em;
border-radius: 4px;
}
.content pre {
margin: 0 0 1rem;
padding: 1rem;
overflow-x: auto;
background: var(--code-bg);
border: 1px solid var(--border);
border-radius: var(--radius);
}
.content pre code { background: none; padding: 0; }
/* 表格一律可橫向捲動,寬表格不會把整頁撐開 */
.table-wrap { overflow-x: auto; margin: 0 0 1.25rem; }
.content table {
border-collapse: collapse;
width: 100%;
font-size: .95rem;
}
.content th, .content td {
border: 1px solid var(--border);
padding: .5rem .7rem;
text-align: left;
vertical-align: top;
}
.content th { background: var(--table-head-bg); color: var(--head-fg); font-weight: 600; }
.content tbody tr:nth-child(even) { background: var(--stripe); }
.content img { max-width: 100%; height: auto; }
.content hr { border: 0; border-top: 1px solid var(--border); margin: 2rem 0; }
.page-foot {
padding: 1.25rem 0 2rem;
border-top: 1px solid var(--border);
color: var(--muted);
font-size: .8rem;
display: flex;
flex-wrap: wrap;
gap: .75rem;
justify-content: space-between;
}
.badge {
display: inline-block;
padding: .1rem .5rem;
border: 1px solid var(--border);
border-radius: 999px;
font-size: .75rem;
color: var(--muted);
}
+63
View File
@@ -0,0 +1,63 @@
// 共用行為:表格加捲動外框、依 h2 切段、產生目錄。各版型自己決定要用哪幾個。
(function (w) {
'use strict';
// 寬表格包一層可橫捲的外框,整頁就不會被撐開。
function wrapTables(root) {
root.querySelectorAll('table').forEach(function (t) {
if (t.parentElement && t.parentElement.classList.contains('table-wrap')) return;
var box = document.createElement('div');
box.className = 'table-wrap';
t.parentNode.insertBefore(box, t);
box.appendChild(t);
});
}
// 依 h2 把內容切成一段一段。h2 之前的內容自成第一段(前言)。
function splitBySection(root) {
var nodes = Array.prototype.slice.call(root.childNodes);
var sections = [];
var current = null;
function open(headingText) {
current = document.createElement('section');
current.className = 'jsc-section';
current.dataset.title = headingText || '';
sections.push(current);
}
nodes.forEach(function (node) {
if (node.nodeType === 1 && node.tagName === 'H2') {
open(node.textContent.trim());
} else if (!current) {
if (node.nodeType === 3 && !node.textContent.trim()) return;
open('');
}
current.appendChild(node);
});
root.innerHTML = '';
sections.forEach(function (s) { root.appendChild(s); });
return sections;
}
// 依 h2、h3 產生目錄,塞進指定容器。標題沒有 id 就補一個。
function buildToc(root, target) {
var heads = root.querySelectorAll('h2, h3');
if (!heads.length) { target.remove(); return; }
var list = document.createElement('ul');
heads.forEach(function (h, i) {
if (!h.id) h.id = 'sec-' + (i + 1);
var li = document.createElement('li');
li.className = 'toc-' + h.tagName.toLowerCase();
var a = document.createElement('a');
a.href = '#' + h.id;
a.textContent = h.textContent.trim();
li.appendChild(a);
list.appendChild(li);
});
target.appendChild(list);
}
w.jsc = { wrapTables: wrapTables, splitBySection: splitBySection, buildToc: buildToc };
})(window);
+52
View File
@@ -0,0 +1,52 @@
<!-- 看板:每個 h2 一張卡片並排,適合進度、狀態、維護清單 -->
<!doctype html>
<html lang="zh-Hant" data-layout="{{LAYOUT}}" data-style="{{STYLE_NAME}}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{TITLE}}</title>
<style>{{BASE}}</style>
<style>{{STYLE}}</style>
<style>
.shell { max-width: 1280px; margin: 0 auto; padding: 0 1.25rem; }
.content { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 1.25rem; align-items: start; }
.jsc-section {
background: var(--card);
border: 1px solid var(--border);
border-radius: var(--radius);
box-shadow: var(--shadow);
padding: 1.1rem 1.25rem;
overflow: hidden;
}
.jsc-section h2 { margin-top: 0; font-size: 1.15rem; }
.jsc-section table { font-size: .88rem; background: var(--bg); }
.jsc-section pre { background: var(--bg); }
/* 只有一段內容時不要孤零零一張窄卡片 */
.content.single { grid-template-columns: minmax(0, 1fr); }
@media print { .content { display: block; } .jsc-section { page-break-inside: avoid; margin-bottom: 1rem; } }
</style>
</head>
<body>
<div class="shell">
<header class="page-head">
<h1>{{TITLE}}</h1>
<p class="subtitle">{{SUBTITLE}}</p>
<p class="meta">{{SOURCE}}<span class="generated">產生時間:{{GENERATED}}</span></p>
</header>
<main class="content" id="content">{{CONTENT}}</main>
<footer class="page-foot">
<span class="badge">{{LAYOUT}}/{{STYLE_NAME}}</span>
<span>本頁由 jsc-gitea:html-export 產生</span>
</footer>
</div>
<script>{{BASE_JS}}</script>
<script>
(function () {
var content = document.getElementById('content');
jsc.wrapTables(content);
var cards = jsc.splitBySection(content);
if (cards.length < 2) content.classList.add('single');
})();
</script>
</body>
</html>
+46
View File
@@ -0,0 +1,46 @@
<!-- 單頁摘要:窄欄、重點框,適合議題摘要與會議結論,印出來剛好一頁 -->
<!doctype html>
<html lang="zh-Hant" data-layout="{{LAYOUT}}" data-style="{{STYLE_NAME}}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{TITLE}}</title>
<style>{{BASE}}</style>
<style>{{STYLE}}</style>
<style>
.shell { max-width: 760px; margin: 0 auto; padding: 0 1.25rem; }
.page-head { padding-top: 2rem; }
.content { font-size: .97rem; }
.content h2 { font-size: 1.15rem; margin-top: 1.4rem; }
.content h3 { font-size: 1rem; }
.content ul { padding-left: 1.2rem; }
/* 待辦清單獨立成框,一眼看得到還有什麼沒做 */
.content li:has(input[type="checkbox"]) { list-style: none; margin-left: -1.2rem; }
.content blockquote { font-size: .95rem; }
.jsc-section + .jsc-section { border-top: 1px dashed var(--border); padding-top: .75rem; }
@media print { body { font-size: 13px; } .page-foot { display: none; } }
</style>
</head>
<body>
<div class="shell">
<header class="page-head">
<h1>{{TITLE}}</h1>
<p class="subtitle">{{SUBTITLE}}</p>
<p class="meta">{{SOURCE}}<span class="generated">產生時間:{{GENERATED}}</span></p>
</header>
<main class="content" id="content">{{CONTENT}}</main>
<footer class="page-foot">
<span class="badge">{{LAYOUT}}/{{STYLE_NAME}}</span>
<span>本頁由 jsc-gitea:html-export 產生</span>
</footer>
</div>
<script>{{BASE_JS}}</script>
<script>
(function () {
var content = document.getElementById('content');
jsc.wrapTables(content);
jsc.splitBySection(content);
})();
</script>
</body>
</html>
+60
View File
@@ -0,0 +1,60 @@
<!-- 報告書:左側目錄加章節內文,適合分析頁、交付頁這類長文件 -->
<!doctype html>
<html lang="zh-Hant" data-layout="{{LAYOUT}}" data-style="{{STYLE_NAME}}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{TITLE}}</title>
<style>{{BASE}}</style>
<style>{{STYLE}}</style>
<style>
.shell { max-width: 1080px; margin: 0 auto; padding: 0 1.25rem; }
.body-grid { display: grid; grid-template-columns: 232px minmax(0, 1fr); gap: 2.5rem; }
.toc {
position: sticky;
top: 1.5rem;
align-self: start;
max-height: calc(100vh - 3rem);
overflow-y: auto;
padding: 1.25rem 0;
font-size: .9rem;
}
.toc h2 { margin: 0 0 .5rem; font-size: .8rem; letter-spacing: .12em; color: var(--muted); text-transform: uppercase; }
.toc ul { list-style: none; margin: 0; padding: 0; }
.toc li { margin: .2rem 0; }
.toc a { color: var(--fg); text-decoration: none; display: block; padding: .15rem .5rem; border-left: 2px solid var(--border); }
.toc a:hover { color: var(--accent); border-left-color: var(--accent); }
.toc .toc-h3 a { padding-left: 1.25rem; color: var(--muted); }
@media (max-width: 860px) {
.body-grid { grid-template-columns: minmax(0, 1fr); gap: 1rem; }
.toc { position: static; max-height: none; border-bottom: 1px solid var(--border); }
}
@media print { .toc { display: none; } .body-grid { display: block; } }
</style>
</head>
<body>
<div class="shell">
<header class="page-head">
<h1>{{TITLE}}</h1>
<p class="subtitle">{{SUBTITLE}}</p>
<p class="meta">{{SOURCE}}<span class="generated">產生時間:{{GENERATED}}</span></p>
</header>
<div class="body-grid">
<nav class="toc" id="toc"><h2>目錄</h2></nav>
<main class="content" id="content">{{CONTENT}}</main>
</div>
<footer class="page-foot">
<span class="badge">{{LAYOUT}}/{{STYLE_NAME}}</span>
<span>本頁由 jsc-gitea:html-export 產生</span>
</footer>
</div>
<script>{{BASE_JS}}</script>
<script>
(function () {
var content = document.getElementById('content');
jsc.wrapTables(content);
jsc.buildToc(content, document.getElementById('toc'));
})();
</script>
</body>
</html>
+90
View File
@@ -0,0 +1,90 @@
<!-- 投影片:一個 h2 一張,鍵盤左右鍵翻頁,適合簡報與匯報 -->
<!doctype html>
<html lang="zh-Hant" data-layout="{{LAYOUT}}" data-style="{{STYLE_NAME}}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{TITLE}}</title>
<style>{{BASE}}</style>
<style>{{STYLE}}</style>
<style>
body { display: flex; flex-direction: column; min-height: 100vh; }
.shell { flex: 1; display: flex; flex-direction: column; max-width: 1200px; width: 100%; margin: 0 auto; padding: 0 1.5rem; }
.page-head { padding: 1.5rem 0 1rem; }
.page-head h1 { font-size: 1.5rem; }
.content { flex: 1; padding: 0; }
.jsc-section { display: none; padding: 1.5rem 0; }
.jsc-section.is-active { display: block; animation: fade .18s ease-out; }
@keyframes fade { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
.jsc-section h2 { margin-top: 0; font-size: 2rem; }
.jsc-section p, .jsc-section li { font-size: 1.15rem; }
.deck-bar {
position: sticky;
bottom: 0;
display: flex;
align-items: center;
gap: .75rem;
padding: .75rem 0 1.25rem;
background: var(--bg);
border-top: 1px solid var(--border);
}
.deck-bar button {
font: inherit;
color: var(--fg);
background: var(--card);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: .35rem .9rem;
cursor: pointer;
}
.deck-bar button:hover { border-color: var(--accent); color: var(--accent); }
.deck-pos { color: var(--muted); font-size: .9rem; margin-left: auto; }
@media print {
.jsc-section, .jsc-section.is-active { display: block; page-break-after: always; }
.deck-bar { display: none; }
}
</style>
</head>
<body>
<div class="shell">
<header class="page-head">
<h1>{{TITLE}}</h1>
<p class="subtitle">{{SUBTITLE}}</p>
<p class="meta">{{SOURCE}}<span class="generated">產生時間:{{GENERATED}}</span></p>
</header>
<main class="content" id="content">{{CONTENT}}</main>
<div class="deck-bar">
<button type="button" id="prev">← 上一頁</button>
<button type="button" id="next">下一頁 →</button>
<span class="badge">{{LAYOUT}}/{{STYLE_NAME}}</span>
<span class="deck-pos" id="pos"></span>
</div>
</div>
<script>{{BASE_JS}}</script>
<script>
(function () {
var content = document.getElementById('content');
jsc.wrapTables(content);
var slides = jsc.splitBySection(content);
var at = 0;
var pos = document.getElementById('pos');
function show(i) {
if (!slides.length) return;
at = Math.max(0, Math.min(slides.length - 1, i));
slides.forEach(function (s, n) { s.classList.toggle('is-active', n === at); });
pos.textContent = (at + 1) + ' / ' + slides.length;
window.scrollTo({ top: 0 });
}
document.getElementById('prev').addEventListener('click', function () { show(at - 1); });
document.getElementById('next').addEventListener('click', function () { show(at + 1); });
document.addEventListener('keydown', function (e) {
if (e.key === 'ArrowLeft' || e.key === 'PageUp') show(at - 1);
if (e.key === 'ArrowRight' || e.key === 'PageDown' || e.key === ' ') show(at + 1);
});
show(0);
})();
</script>
</body>
</html>
+58
View File
@@ -0,0 +1,58 @@
<!-- 規格書:表格與程式碼區塊放大,表頭固定,適合 API 文件與交付規格 -->
<!doctype html>
<html lang="zh-Hant" data-layout="{{LAYOUT}}" data-style="{{STYLE_NAME}}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{TITLE}}</title>
<style>{{BASE}}</style>
<style>{{STYLE}}</style>
<style>
.shell { max-width: 1120px; margin: 0 auto; padding: 0 1.25rem; }
.body-grid { display: grid; grid-template-columns: 210px minmax(0, 1fr); gap: 2rem; }
.toc { position: sticky; top: 1.5rem; align-self: start; padding: 1.25rem 0; font-size: .88rem; }
.toc h2 { margin: 0 0 .5rem; font-size: .78rem; letter-spacing: .12em; color: var(--muted); text-transform: uppercase; }
.toc ul { list-style: none; margin: 0; padding: 0; }
.toc a { color: var(--fg); text-decoration: none; display: block; padding: .15rem .4rem; }
.toc a:hover { color: var(--accent); }
.toc .toc-h3 a { padding-left: 1.1rem; color: var(--muted); }
.jsc-section { border-bottom: 1px solid var(--border); padding-bottom: 1.25rem; margin-bottom: 1.5rem; }
.jsc-section:last-child { border-bottom: 0; }
.content .table-wrap { max-height: 70vh; overflow: auto; border: 1px solid var(--border); border-radius: var(--radius); }
.content .table-wrap table { border: 0; }
.content .table-wrap th { position: sticky; top: 0; z-index: 1; }
.content code { font-size: .92em; }
.content pre { font-size: .95rem; line-height: 1.6; }
/* 欄位表第一欄是欄位名,等寬字比較好對 */
.content td:first-child code { white-space: nowrap; }
@media (max-width: 860px) { .body-grid { grid-template-columns: minmax(0, 1fr); } .toc { position: static; } }
@media print { .toc { display: none; } .body-grid { display: block; } .content .table-wrap { max-height: none; } }
</style>
</head>
<body>
<div class="shell">
<header class="page-head">
<h1>{{TITLE}}</h1>
<p class="subtitle">{{SUBTITLE}}</p>
<p class="meta">{{SOURCE}}<span class="generated">產生時間:{{GENERATED}}</span></p>
</header>
<div class="body-grid">
<nav class="toc" id="toc"><h2>章節</h2></nav>
<main class="content" id="content">{{CONTENT}}</main>
</div>
<footer class="page-foot">
<span class="badge">{{LAYOUT}}/{{STYLE_NAME}}</span>
<span>本頁由 jsc-gitea:html-export 產生</span>
</footer>
</div>
<script>{{BASE_JS}}</script>
<script>
(function () {
var content = document.getElementById('content');
jsc.wrapTables(content);
jsc.buildToc(content, document.getElementById('toc'));
jsc.splitBySection(content);
})();
</script>
</body>
</html>
+61
View File
@@ -0,0 +1,61 @@
<!-- 時間軸:每個 h2 一個節點串成一條線,適合工作日誌與里程碑 -->
<!doctype html>
<html lang="zh-Hant" data-layout="{{LAYOUT}}" data-style="{{STYLE_NAME}}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{TITLE}}</title>
<style>{{BASE}}</style>
<style>{{STYLE}}</style>
<style>
.shell { max-width: 900px; margin: 0 auto; padding: 0 1.25rem; }
.content { position: relative; padding-left: 2rem; }
.content::before {
content: "";
position: absolute;
left: .45rem;
top: .75rem;
bottom: .75rem;
width: 2px;
background: var(--border);
}
.jsc-section { position: relative; padding: 0 0 1.75rem; }
.jsc-section::before {
content: "";
position: absolute;
left: -1.9rem;
top: .95rem;
width: .85rem;
height: .85rem;
border-radius: 50%;
background: var(--accent);
border: 3px solid var(--bg);
}
.jsc-section h2 { margin-top: .5rem; font-size: 1.2rem; }
.jsc-section:last-child { padding-bottom: .5rem; }
@media print { .jsc-section { page-break-inside: avoid; } }
</style>
</head>
<body>
<div class="shell">
<header class="page-head">
<h1>{{TITLE}}</h1>
<p class="subtitle">{{SUBTITLE}}</p>
<p class="meta">{{SOURCE}}<span class="generated">產生時間:{{GENERATED}}</span></p>
</header>
<main class="content" id="content">{{CONTENT}}</main>
<footer class="page-foot">
<span class="badge">{{LAYOUT}}/{{STYLE_NAME}}</span>
<span>本頁由 jsc-gitea:html-export 產生</span>
</footer>
</div>
<script>{{BASE_JS}}</script>
<script>
(function () {
var content = document.getElementById('content');
jsc.wrapTables(content);
jsc.splitBySection(content);
})();
</script>
</body>
</html>
+23
View File
@@ -0,0 +1,23 @@
/* 商務:深藍主色、表頭反白、正式對外用 */
:root {
--bg: #ffffff;
--fg: #22272e;
--head-fg: #0b3358;
--muted: #5c6b7a;
--accent: #0b5fa5;
--border: #c9d6e2;
--card: #eef4fa;
--code-bg: #eef2f6;
--table-head-bg: #0b3358;
--stripe: #f4f8fc;
--radius: 6px;
--shadow: 0 1px 2px rgba(11, 51, 88, .12);
--font: "Noto Sans TC", "PingFang TC", "Microsoft JhengHei", system-ui, sans-serif;
--font-head: var(--font);
--font-mono: "JetBrains Mono", "Cascadia Mono", Consolas, monospace;
}
.page-head { border-bottom-width: 3px; }
.content th { color: #ffffff; letter-spacing: .03em; }
.content h2 { border-left: 5px solid var(--accent); padding-left: .6rem; }
.content table { box-shadow: var(--shadow); }
+21
View File
@@ -0,0 +1,21 @@
/* 深色:深底亮字,長時間閱讀與投影機環境 */
:root {
--bg: #11161c;
--fg: #d7dee6;
--head-fg: #f2f6fa;
--muted: #8b98a6;
--accent: #56a8f5;
--border: #2b3540;
--card: #1a2129;
--code-bg: #1c242d;
--table-head-bg: #1f2932;
--stripe: #161d24;
--radius: 6px;
--shadow: 0 1px 3px rgba(0, 0, 0, .5);
--font: "Noto Sans TC", "PingFang TC", "Microsoft JhengHei", system-ui, sans-serif;
--font-head: var(--font);
--font-mono: "JetBrains Mono", "Cascadia Mono", Consolas, monospace;
}
.content pre { border-color: #2b3540; }
.content h2 { border-bottom: 1px solid var(--border); padding-bottom: .3rem; }
+21
View File
@@ -0,0 +1,21 @@
/* 極簡:白底、細線、無襯線,資訊密度優先 */
:root {
--bg: #ffffff;
--fg: #1f2328;
--head-fg: #0d1117;
--muted: #6a737d;
--accent: #2f6f9f;
--border: #d8dee4;
--card: #f6f8fa;
--code-bg: #f2f4f7;
--table-head-bg: #f6f8fa;
--stripe: #fbfcfd;
--radius: 4px;
--shadow: none;
--font: "Noto Sans TC", "PingFang TC", "Microsoft JhengHei", system-ui, sans-serif;
--font-head: var(--font);
--font-mono: "JetBrains Mono", "Cascadia Mono", Consolas, monospace;
}
.page-head { border-bottom-width: 1px; }
.content h2 { border-bottom: 1px solid var(--border); padding-bottom: .3rem; }
+30
View File
@@ -0,0 +1,30 @@
/* 印刷:襯線字、A4 邊界、去掉陰影,列印或轉 PDF 用 */
:root {
--bg: #ffffff;
--fg: #1b1b1b;
--head-fg: #000000;
--muted: #55555f;
--accent: #4a4a4a;
--border: #b8b8b8;
--card: #f4f4f2;
--code-bg: #f2f2f0;
--table-head-bg: #ececeb;
--stripe: #f9f9f8;
--radius: 0;
--shadow: none;
--font: "Noto Serif TC", "Songti TC", "PMingLiU", Georgia, serif;
--font-head: var(--font);
--font-mono: "JetBrains Mono", Consolas, monospace;
}
@page { size: A4; margin: 20mm 18mm; }
body { font-size: 15px; line-height: 1.85; }
.page-head { border-bottom: 1px solid var(--border); }
.content h2 { page-break-after: avoid; }
.content table, .content pre, .content blockquote { page-break-inside: avoid; }
@media print {
.meta a.source { color: var(--fg); }
.page-foot { border-top: 1px solid var(--border); }
}
+34
View File
@@ -0,0 +1,34 @@
/* 明亮:高彩度、圓角卡片、漸層標題,簡報與對內宣達用 */
:root {
--bg: #fdfbff;
--fg: #241f2e;
--head-fg: #4c1d95;
--muted: #6d6480;
--accent: #7c3aed;
--border: #e2d9f5;
--card: #f6f1ff;
--code-bg: #f1ecfd;
--table-head-bg: #ede4ff;
--stripe: #faf7ff;
--radius: 12px;
--shadow: 0 2px 10px rgba(124, 58, 237, .12);
--font: "Noto Sans TC", "PingFang TC", "Microsoft JhengHei", system-ui, sans-serif;
--font-head: var(--font);
--font-mono: "JetBrains Mono", "Cascadia Mono", Consolas, monospace;
}
.page-head {
border-bottom: 0;
background: linear-gradient(135deg, #7c3aed 0%, #e0378f 100%);
color: #ffffff;
border-radius: var(--radius);
padding: 2rem 1.5rem;
box-shadow: var(--shadow);
}
.page-head h1, .page-head .subtitle, .page-head .meta { color: #ffffff; }
.page-head .meta a.source { color: #ffffff; text-decoration: underline; }
.content h2 { border-left: 6px solid var(--accent); padding-left: .6rem; }
.content table { border-radius: var(--radius); overflow: hidden; box-shadow: var(--shadow); }
.content blockquote { border-radius: var(--radius); }