<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>手作纸感</title>
<script>
// Restore a theme picked with the button before first paint; otherwise follow the system.
try {
const saved = localStorage.getItem("paper-theme");
if (saved) document.documentElement.dataset.theme = saved;
} catch {}
</script>
<style>
:root {
--paper: #fbf8f1;
--paper-2: #f3eee2;
--card: #fffdf8;
--ink: #1c2230;
--ink-2: #4a5163;
--ink-3: #636877;
--line: #e6dfd0;
--dot: #d8d0bf;
--blue: #2f5bd3;
--blue-soft: #e8eefc;
--blue-line: rgba(47, 91, 211, 0.35);
--seal: #c0391f;
--seal-soft: #fbe7e2;
--stamp: #c0391f; /* seal background behind cream text, the same in both themes */
--hi: #ffe066;
--hi-strong: rgba(255, 224, 102, 0.55);
--note: #ffe88a;
--term: #161a22;
--term-green: #5fd38a;
--yellow: #ffe066;
--green: #277a4b;
--callout: #fff6f3;
--code-ink: #9b2f1c;
--on-blue: #fff;
--topbar: rgba(251, 248, 241, 0.86);
--shadow: 0 1px 0 rgba(28, 34, 48, 0.04), 0 10px 28px -14px rgba(28, 34, 48, 0.28);
--ease: cubic-bezier(0.16, 1, 0.3, 1);
--spring: linear(
0,
0.062,
0.215,
0.411,
0.615,
0.799,
0.95,
1.059,
1.126,
1.158,
1.161,
1.145,
1.116,
1.083,
1.051,
1.023,
1.001,
0.986,
0.977,
0.974,
0.974,
0.978,
0.983,
0.988,
0.994,
0.998,
1.001,
1.003,
1.004,
1.004,
1
);
/* System fonts only. Four voices: serif headings, sans body, mono labels, hand-written accents. */
--f-serif:
"Songti SC", STSong, "Noto Serif CJK SC", "Source Han Serif SC", SimSun, "AR PL UMing CN", Georgia, serif;
--f-sans: system-ui, -apple-system, "PingFang SC", "Microsoft YaHei", "Noto Sans CJK SC", sans-serif;
--f-mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
--f-hand: "Kaiti SC", STKaiti, KaiTi, "AR PL UKai CN", "Segoe Print", "Bradley Hand", cursive;
color-scheme: light;
}
/* Night desk: an explicit choice (data-theme) wins; otherwise the system setting decides. */
:root[data-theme="dark"] {
--paper: #14161b;
--paper-2: #1c1f26;
--card: #1e2129;
--ink: #ece7dc;
--ink-2: #bab6ac;
--ink-3: #959aa8;
--line: #31353f;
--dot: #2c3039;
--blue: #8eaaff;
--blue-soft: #1f2a47;
--blue-line: rgba(142, 170, 255, 0.45);
--seal: #f0694e;
--seal-soft: #3b211b;
--hi: #7d6416;
--hi-strong: rgba(255, 214, 90, 0.26);
--note: #ecd77e;
--term: #0d0f13;
--green: #5cc189;
--callout: #2a1915;
--code-ink: #f4957f;
--on-blue: #14161b;
--topbar: rgba(20, 22, 27, 0.86);
--shadow: 0 1px 0 rgba(0, 0, 0, 0.25), 0 10px 28px -14px rgba(0, 0, 0, 0.8);
color-scheme: dark;
}
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
--paper: #14161b;
--paper-2: #1c1f26;
--card: #1e2129;
--ink: #ece7dc;
--ink-2: #bab6ac;
--ink-3: #959aa8;
--line: #31353f;
--dot: #2c3039;
--blue: #8eaaff;
--blue-soft: #1f2a47;
--blue-line: rgba(142, 170, 255, 0.45);
--seal: #f0694e;
--seal-soft: #3b211b;
--hi: #7d6416;
--hi-strong: rgba(255, 214, 90, 0.26);
--note: #ecd77e;
--term: #0d0f13;
--green: #5cc189;
--callout: #2a1915;
--code-ink: #f4957f;
--on-blue: #14161b;
--topbar: rgba(20, 22, 27, 0.86);
--shadow: 0 1px 0 rgba(0, 0, 0, 0.25), 0 10px 28px -14px rgba(0, 0, 0, 0.8);
color-scheme: dark;
}
}
* {
box-sizing: border-box;
}
body {
margin: 0;
/* Dotted paper: one radial-gradient dot per 22px cell. */
background: var(--paper) radial-gradient(var(--dot) 1px, transparent 1.2px) 0 0 / 22px 22px;
color: var(--ink);
font: 400 16px/1.75 var(--f-sans);
-webkit-font-smoothing: antialiased;
}
a {
color: inherit;
}
:focus-visible {
outline: 2px solid var(--blue);
outline-offset: 2px;
}
/* ---------- top bar ---------- */
.topbar {
position: sticky;
top: 0;
z-index: 10;
display: flex;
align-items: center;
gap: 12px;
height: 60px;
padding: 0 16px;
background: var(--topbar);
-webkit-backdrop-filter: blur(8px);
backdrop-filter: blur(8px);
border-bottom: 2px dashed var(--blue-line);
}
.brand {
display: flex;
align-items: center;
gap: 12px;
text-decoration: none;
}
.brand b {
display: block;
font: 700 17px/1.1 var(--f-serif);
}
.brand small {
font: 400 11px/1.2 var(--f-mono);
color: var(--ink-3);
letter-spacing: 0.04em;
}
.spacer {
flex: 1;
}
/* ---------- seals and stamps ---------- */
.seal {
display: grid;
place-items: center;
flex: none;
width: 36px;
height: 36px;
background: var(--stamp);
color: #fff8ef;
border-radius: 6px;
transform: rotate(-4deg);
font: 900 13px/1.05 var(--f-serif);
letter-spacing: 1px;
writing-mode: vertical-rl;
box-shadow: inset 0 0 0 2px rgba(255, 248, 239, 0.55);
}
.seal.big {
width: 64px;
height: 64px;
font-size: 22px;
border-radius: 8px;
box-shadow: inset 0 0 0 3px rgba(255, 248, 239, 0.55);
}
.stamp-round {
display: grid;
place-items: center;
flex: none;
width: 64px;
height: 64px;
border-radius: 50%;
border: 2.5px solid var(--seal);
color: var(--seal);
font: 800 14px/1 var(--f-mono);
letter-spacing: 0.04em;
transform: rotate(-8deg);
/* the inner ring: a card-coloured gap, then a thin seal-coloured line */
box-shadow:
inset 0 0 0 4px var(--card),
inset 0 0 0 5.5px var(--seal);
}
.stamp-round:nth-child(3) {
transform: rotate(6deg);
}
.stamp-round:nth-child(4) {
transform: rotate(-3deg);
}
/* ---------- buttons, tags, marks ---------- */
.chip-btn {
font: 500 13px/1 var(--f-sans);
color: var(--ink-2);
padding: 8px 12px;
border-radius: 10px;
border: 1px dashed var(--ink-3);
background: transparent;
cursor: pointer;
text-decoration: none;
white-space: nowrap;
transition:
scale 0.45s var(--spring),
color 0.15s,
background-color 0.15s;
}
.chip-btn:hover {
border-style: solid;
color: var(--ink);
background: var(--card);
}
.chip-btn:active {
scale: 0.93;
}
.chip-btn.solid {
border: 1px solid var(--blue);
background: var(--blue);
color: var(--on-blue);
}
.chip-btn.ink {
border: 1px solid var(--ink);
background: var(--ink);
color: var(--paper);
}
.tag {
display: inline-block;
padding: 3px 8px;
border-radius: 6px;
font: 500 11px/1.4 var(--f-mono);
color: var(--blue);
background: var(--blue-soft);
}
.tag.red {
color: var(--seal);
background: var(--seal-soft);
}
/* Highlighter: a band across the lower part of the text, not the whole line box. */
.mark {
padding: 0 4px;
background: linear-gradient(transparent 58%, var(--hi) 58%, var(--hi) 92%, transparent 92%);
}
.kicker {
font: 500 11px/1 var(--f-mono);
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--ink-3);
}
/* ---------- layout ---------- */
main {
max-width: 1140px;
margin: 0 auto;
padding: 26px 16px 60px;
}
.hero-row {
display: grid;
grid-template-columns: minmax(0, 1fr) 260px;
gap: 32px;
align-items: center;
margin-bottom: 26px;
}
.hero h1 {
margin: 0;
font: 400 52px/1.15 var(--f-hand);
}
.hero p {
margin: 6px 0 0;
font: 400 22px/1.3 var(--f-hand);
color: var(--blue);
}
.hero .sub {
margin-top: 12px;
font: 400 12px/1.6 var(--f-mono);
color: var(--ink-3);
letter-spacing: 0.06em;
}
.grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 22px;
align-items: start;
}
.span {
grid-column: 1 / -1;
}
/* ---------- cards ---------- */
.card {
position: relative;
padding: 20px 22px;
background: var(--card);
border: 2px dashed var(--blue-line);
border-radius: 14px;
box-shadow: var(--shadow);
}
.card > .kicker {
display: block;
margin-bottom: 12px;
}
a.card {
display: block;
text-decoration: none;
transition:
transform 0.6s var(--spring),
border-color 0.2s;
}
a.card:hover {
border-style: solid;
border-color: var(--blue);
transform: translateY(-3px) rotate(-0.4deg);
}
.tape {
--t: 255, 214, 61;
position: absolute;
top: -9px;
left: 50%;
width: 78px;
height: 18px;
transform: translateX(-50%) rotate(-3deg);
background: repeating-linear-gradient(90deg, rgba(var(--t), 0.9) 0 5px, rgba(var(--t), 0.45) 5px 10px);
border-radius: 1px;
}
.tape.blue {
--t: 47, 91, 211;
opacity: 0.55;
}
/* ---------- palette ---------- */
.swatches {
display: grid;
grid-template-columns: repeat(10, minmax(0, 1fr));
gap: 10px;
}
.swatches + .kicker {
display: block;
margin: 16px 0 12px;
}
.sw {
margin: 0;
font: 400 11px/1.35 var(--f-mono);
color: var(--ink-2);
}
.sw i {
display: block;
height: 44px;
margin-bottom: 6px;
border-radius: 8px;
background: var(--c);
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1);
}
.sw b {
display: block;
font-weight: 600;
color: var(--ink);
}
/* ---------- sticky note ---------- */
.sticky {
position: relative;
margin: 0;
padding: 24px 22px 18px;
background: var(--note);
border-radius: 4px;
transform: rotate(3deg);
box-shadow: 0 16px 24px -16px rgba(120, 90, 0, 0.45);
}
.sticky blockquote {
margin: 0;
font: 400 24px/1.35 var(--f-hand);
color: #3b2f05; /* paper-coloured note: dark ink in both themes */
}
.sticky figcaption {
margin-top: 12px;
font: 400 11px/1.5 var(--f-mono);
color: #7a6510;
}
/* ---------- type ---------- */
.t-display {
margin: 0 0 6px;
font: 900 36px/1.25 var(--f-serif);
}
.t-h2 {
margin: 18px 0 8px;
font: 900 24px/1.4 var(--f-serif);
/* A highlighter stroke that follows the text across line breaks. */
text-decoration: underline;
text-decoration-color: var(--hi);
text-decoration-thickness: 0.34em;
text-underline-offset: -0.2em;
text-decoration-skip-ink: none;
}
.t-h2::before {
content: "§01";
/* inline-block: a block child would inherit the heading's highlighter underline */
display: inline-block;
width: 100%;
margin-bottom: 8px;
font: 500 12px/1 var(--f-mono);
color: var(--seal);
letter-spacing: 0.12em;
}
.t-h3 {
margin: 14px 0 4px;
font: 700 18px/1.5 var(--f-serif);
}
.t-h3::before {
content: "▸ ";
color: var(--blue);
}
.prose {
margin: 0 0 10px;
font-size: 15.5px;
line-height: 1.9;
color: var(--ink-2);
}
.prose strong {
color: var(--ink);
background: linear-gradient(transparent 70%, var(--hi-strong) 70%);
}
.prose a {
color: var(--blue);
text-decoration-color: var(--blue-line);
text-underline-offset: 3px;
}
.prose em {
font-style: normal;
font-weight: 500;
color: var(--ink);
/* Chinese emphasis dots under each character instead of italics. */
text-emphasis: dot var(--seal);
text-emphasis-position: under right;
}
.prose code {
font: 400 0.86em/1 var(--f-mono);
padding: 2px 6px;
border-radius: 5px;
background: var(--paper-2);
border: 1px solid var(--line);
color: var(--code-ink);
}
.type-row {
display: flex;
flex-wrap: wrap;
gap: 8px 18px;
align-items: baseline;
padding-top: 12px;
border-top: 1px dashed var(--line);
}
.margin-note {
display: inline-flex;
align-items: flex-end;
gap: 4px;
font: 400 22px/1.2 var(--f-hand);
color: var(--seal);
transform: rotate(-4deg);
}
/* ---------- components ---------- */
.memo h3 {
margin: 6px 0 6px;
font: 700 17px/1.45 var(--f-serif);
}
.memo p {
margin: 0 0 12px;
font-size: 13.5px;
line-height: 1.7;
color: var(--ink-2);
}
.tags {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.row {
display: flex;
flex-wrap: wrap;
gap: 12px;
align-items: center;
}
.row + .row {
margin-top: 16px;
}
.callout {
display: flex;
gap: 14px;
align-items: flex-start;
margin-top: 18px;
padding: 14px 16px;
border: 1.5px solid var(--seal);
border-radius: 12px;
background: var(--callout);
font-size: 14px;
line-height: 1.75;
color: var(--ink-2);
}
.callout .stamp {
flex: none;
display: grid;
place-items: center;
width: 42px;
height: 42px;
border-radius: 50%;
border: 2px solid var(--seal);
color: var(--seal);
font: 900 13px/1 var(--f-serif);
transform: rotate(-10deg);
}
.term {
margin-top: 18px;
padding: 12px 16px;
border-radius: 12px;
background: var(--term);
color: #d7dce6;
font: 400 12.5px/1.7 var(--f-mono);
}
.term .p {
color: var(--term-green);
}
.term .y {
background: var(--yellow);
color: #161a22;
padding: 1px 6px;
border-radius: 3px;
}
@media (max-width: 860px) {
.swatches {
grid-template-columns: repeat(5, minmax(0, 1fr));
gap: 10px 8px;
}
.sw {
font-size: 10px;
}
.hero-row,
.grid {
grid-template-columns: minmax(0, 1fr);
}
.sticky {
max-width: 300px;
margin: 8px auto 0;
}
}
@media (max-width: 520px) {
.brand small {
display: none;
}
.hero h1 {
font-size: 38px;
}
.hero p {
font-size: 19px;
}
.t-display {
font-size: 28px;
}
.card {
padding: 18px 16px;
}
}
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
transition: none !important;
}
}
</style>
</head>
<body>
<header class="topbar">
<a class="brand" href="#">
<span class="seal" aria-hidden="true">纸本</span>
<span><b>小王的纸本</b><small>notes · 手记</small></span>
</a>
<span class="spacer"></span>
<button class="chip-btn" type="button" id="theme" aria-pressed="false">☾ 夜间</button>
</header>
<main>
<div class="hero-row">
<section class="hero">
<h1>把笔记写成<span class="mark">一张纸</span></h1>
<p>cream paper · pen blue · seal red · highlighter</p>
<div class="sub">奶油纸 + 点阵底 · 印章红 / 钢笔蓝 / 荧光黄 · 深色模式齐全</div>
</section>
<figure class="sticky">
<span class="tape" aria-hidden="true"></span>
<blockquote>手写体只用在标题和批注上,正文仍然好读。</blockquote>
<figcaption>— 便利贴 · .sticky</figcaption>
</figure>
</div>
<div class="grid">
<section class="card span" aria-labelledby="pal-h">
<span class="kicker" id="pal-h">Palette · 日间</span>
<div class="swatches">
<figure class="sw" style="--c: #fbf8f1"><i></i><b>--paper</b>#fbf8f1</figure>
<figure class="sw" style="--c: #fffdf8"><i></i><b>--card</b>#fffdf8</figure>
<figure class="sw" style="--c: #e6dfd0"><i></i><b>--line</b>#e6dfd0</figure>
<figure class="sw" style="--c: #1c2230"><i></i><b>--ink</b>#1c2230</figure>
<figure class="sw" style="--c: #4a5163"><i></i><b>--ink-2</b>#4a5163</figure>
<figure class="sw" style="--c: #2f5bd3"><i></i><b>--blue</b>#2f5bd3</figure>
<figure class="sw" style="--c: #c0391f"><i></i><b>--seal</b>#c0391f</figure>
<figure class="sw" style="--c: #ffe066"><i></i><b>--hi</b>#ffe066</figure>
<figure class="sw" style="--c: #ffe88a"><i></i><b>--note</b>#ffe88a</figure>
<figure class="sw" style="--c: #161a22"><i></i><b>--term</b>#161a22</figure>
</div>
<span class="kicker">Palette · 夜间</span>
<div class="swatches">
<figure class="sw" style="--c: #14161b"><i></i><b>--paper</b>#14161b</figure>
<figure class="sw" style="--c: #1e2129"><i></i><b>--card</b>#1e2129</figure>
<figure class="sw" style="--c: #31353f"><i></i><b>--line</b>#31353f</figure>
<figure class="sw" style="--c: #ece7dc"><i></i><b>--ink</b>#ece7dc</figure>
<figure class="sw" style="--c: #bab6ac"><i></i><b>--ink-2</b>#bab6ac</figure>
<figure class="sw" style="--c: #8eaaff"><i></i><b>--blue</b>#8eaaff</figure>
<figure class="sw" style="--c: #f0694e"><i></i><b>--seal</b>#f0694e</figure>
<figure class="sw" style="--c: #7d6416"><i></i><b>--hi</b>#7d6416</figure>
<figure class="sw" style="--c: #ecd77e"><i></i><b>--note</b>#ecd77e</figure>
<figure class="sw" style="--c: #0d0f13"><i></i><b>--term</b>#0d0f13</figure>
</div>
</section>
<section class="card" aria-labelledby="type-h">
<span class="kicker" id="type-h">Type · 四种声音</span>
<p class="t-display">标题用宋体,粗一点</p>
<h2 class="t-h2">二级标题带一道荧光笔</h2>
<h3 class="t-h3">三级标题前面一个蓝色小三角</h3>
<p class="prose">
正文用系统无衬线,行高放到 1.9。<strong>重点</strong>用半截荧光黄,<a href="#">链接</a>是钢笔蓝,
中文强调不用斜体而是<em>着重号</em>,行内代码是 <code>--seal</code> 这样的小纸条。
</p>
<div class="type-row">
<span class="kicker">mono · 标签 / 日期</span>
<span class="margin-note"
>手写批注
<svg width="34" height="22" viewBox="0 0 34 22" aria-hidden="true">
<path
d="M2 4 C 12 16, 22 18, 31 12 M24 8 L31 12 L25 19"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/></svg
></span>
</div>
</section>
<section class="card" aria-labelledby="comp-h">
<span class="kicker" id="comp-h">Pieces · 小部件</span>
<a class="card memo" href="#">
<span class="tape blue" aria-hidden="true"></span>
<span class="kicker">笔记 · 10-12</span>
<h3>虚线卡片,悬停变实线</h3>
<p>边框是半透明的钢笔蓝虚线,鼠标放上来变成实线并轻轻抬起。</p>
<span class="tags"><span class="tag">css</span><span class="tag red">手作感</span></span>
</a>
<div class="row" style="margin-top: 22px">
<span class="seal big" aria-hidden="true">小王</span>
<span class="stamp-round">MIT</span>
<span class="stamp-round">2026</span>
<span class="stamp-round">OK</span>
</div>
<div class="row">
<button class="chip-btn" type="button">虚线按钮</button>
<button class="chip-btn solid" type="button">主要按钮</button>
<button class="chip-btn ink" type="button">墨色按钮</button>
</div>
<div class="callout">
<span class="stamp" aria-hidden="true">注意</span>
<div>提示框是一枚歪着的圆章加一圈印章红的细框,比图标更有纸上的感觉。</div>
</div>
<div class="term"><span class="p">$</span> echo "see you" <span class="y">[done]</span></div>
</section>
</div>
</main>
<script>
// Theme button: flips data-theme and remembers it. Without JS the system setting still applies.
const root = document.documentElement;
const btn = document.getElementById("theme");
const systemDark = matchMedia("(prefers-color-scheme: dark)");
const current = () => root.dataset.theme ?? (systemDark.matches ? "dark" : "light");
function paint() {
const dark = current() === "dark";
btn.setAttribute("aria-pressed", String(dark));
btn.textContent = dark ? "☀ 日间" : "☾ 夜间";
}
btn.addEventListener("click", () => {
root.dataset.theme = current() === "dark" ? "light" : "dark";
try {
localStorage.setItem("paper-theme", root.dataset.theme);
} catch {}
paint();
});
systemDark.addEventListener("change", paint);
paint();
</script>
</body>
</html>