feat: 布局三修——navbar 下方统一 32px 间距(章节页横幅零间距修复);选中项背景=章节色(与章头横幅统一,书架选中 ink);字体系统(16px 基准=正文 / t1-t3 三级标题 / 小字两态 mute·strong / 大字两态 t1·幽灵 display 章号)全站应用

This commit is contained in:
cqh963852 committed 2026-10-08 12:08:33 +08:00
1 parent 8454a7abab
commit a65a6d96b1
4 files changed
+74 -26

No files matched your search

+16 -11
View File
@@ -23,15 +23,18 @@ export function ChapterPage({ id }: { id: ChapterId }) {
<Page active={id}> <Page active={id}>
{/* 章头:与书架相框共享 view-transition-name —— 点卡片进来时相框飞进来变成章头 */} {/* 章头:与书架相框共享 view-transition-name —— 点卡片进来时相框飞进来变成章头 */}
<div <div
className="flex items-center gap-4 rounded-2xl border-2 border-ink p-5 shadow-hard" className="relative flex items-center gap-4 overflow-hidden rounded-2xl border-2 border-ink p-5 shadow-hard"
style={{ background: ch.color, viewTransitionName: `ch-${id}` } as CSSProperties} style={{ background: ch.color, viewTransitionName: `ch-${id}` } as CSSProperties}
> >
<span className="t-display pointer-events-none absolute -right-2 -top-3 select-none" aria-hidden>
0{id}
</span>
<span className="text-[44px] leading-none">{ch.emoji}</span> <span className="text-[44px] leading-none">{ch.emoji}</span>
<div className="flex-1"> <div className="flex-1">
<h1 className="m-0 text-[24px] font-extrabold text-ink"> <h1 className="m-0 text-t1 font-extrabold text-ink">
第 {id} 章 · {ch.title} 第 {id} 章 · {ch.title}
</h1> </h1>
<div className="mt-1.5 flex flex-wrap gap-x-6 gap-y-1 text-[12px] font-semibold text-ink"> <div className="mt-2 flex flex-wrap gap-x-6 gap-y-1 t-small-strong">
<span>▲ 目标:{ch.target.goal}</span> <span>▲ 目标:{ch.target.goal}</span>
<span>○ 预期:{ch.target.expectation}</span> <span>○ 预期:{ch.target.expectation}</span>
<span className="flex items-center gap-1.5"> <span className="flex items-center gap-1.5">
@@ -52,18 +55,20 @@ export function ChapterPage({ id }: { id: ChapterId }) {
{i + 1} {i + 1}
</span> </span>
{s.kind === 'value' ? ( {s.kind === 'value' ? (
<Mark className="text-[13.5px]">{s.title}</Mark> <Mark className="text-t3">{s.title}</Mark>
) : ( ) : (
<span className="flex items-center gap-1.5 text-[13.5px] font-bold text-ink"> <span className="flex items-center gap-1.5 text-t3 font-bold text-ink">
<Wave size={30} /> <Wave size={30} />
{s.title} {s.title}
<span className="rounded-full bg-clash3b/15 px-2 py-0.5 text-[10.5px]">代理区 · 交给 agent</span> <span className="rounded-full bg-clash3b/15 px-2 py-0.5 text-[10.5px]">代理区 · 交给 agent</span>
</span> </span>
)} )}
</div> </div>
<p className="m-0 mt-2 text-[12.5px] leading-relaxed text-body">{s.what}</p> <p className="m-0 mt-2.5 text-content text-body">{s.what}</p>
<p className="m-0 mt-1.5 text-[12px] text-ink"> <p className="m-0 mt-2 text-content text-body">
<b>验收</b>:{s.accept} <b className="t-small-strong">验收</b>
<span className="mx-1.5 text-mute">·</span>
{s.accept}
</p> </p>
</li> </li>
))} ))}
@@ -76,9 +81,9 @@ export function ChapterPage({ id }: { id: ChapterId }) {
<div key={b.name} className="rounded-xl border-2 border-ink bg-paper p-3.5 shadow-hard"> <div key={b.name} className="rounded-xl border-2 border-ink bg-paper p-3.5 shadow-hard">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Tri size={13} /> <Tri size={13} />
<b className="text-[13px]">{b.name}</b> <b className="text-t3">{b.name}</b>
</div> </div>
<p className="m-0 mt-1.5 text-[11.5px] leading-relaxed text-body"> <p className="m-0 mt-2 text-small leading-relaxed text-body">
<b>是什么</b>:{b.what} <b>是什么</b>:{b.what}
<br /> <br />
<b>为何在这</b>:{b.why} <b>为何在这</b>:{b.why}
@@ -94,7 +99,7 @@ export function ChapterPage({ id }: { id: ChapterId }) {
<Section label="认知基础" note="脚下的地(env/库/运行环境)——第一次用到就地露出"> <Section label="认知基础" note="脚下的地(env/库/运行环境)——第一次用到就地露出">
<div className="flex items-start gap-3 rounded-xl border-2 border-dashed border-ink/40 bg-clash3b/15 p-4"> <div className="flex items-start gap-3 rounded-xl border-2 border-dashed border-ink/40 bg-clash3b/15 p-4">
<Ring size={18} className="mt-0.5 shrink-0" /> <Ring size={18} className="mt-0.5 shrink-0" />
<p className="m-0 text-[12.5px] leading-relaxed text-body">{c.base}</p> <p className="m-0 text-content text-body">{c.base}</p>
</div> </div>
</Section> </Section>
)} )}
+20 -15
View File
@@ -1,4 +1,5 @@
/** 教程站页面骨架:站头导航跨页常驻(view-transition-name 共享元素,页间不闪烁) */ /** 教程站页面骨架:站头导航跨页常驻(view-transition-name 共享元素,页间不闪烁)。
* 选中项背景色=该章色(与章头 title 横幅统一);书架选中为 ink。 */
import type { ReactNode } from 'react' import type { ReactNode } from 'react'
import type { ChapterId } from './types' import type { ChapterId } from './types'
import { CHAPTERS } from './chapters' import { CHAPTERS } from './chapters'
@@ -22,39 +23,43 @@ export function Page({
/** 章节页标题在章头横幅里,可省 */ /** 章节页标题在章头横幅里,可省 */
title?: string title?: string
desc?: string desc?: string
/** 站头高亮的章节;书架页不传 */ /** 站头高亮:章节 id 或书架 'index' */
active?: ChapterId active?: ChapterId | 'index'
children: ReactNode children: ReactNode
}) { }) {
return ( return (
<main className="mx-auto max-w-[1200px] px-6 py-8"> <main className="mx-auto max-w-[1200px] px-6 py-8 text-content text-body">
<header <header
className="flex flex-wrap items-center gap-1.5 rounded-2xl border-2 border-ink bg-paper p-2.5 shadow-hard" className="mb-8 flex flex-wrap items-center gap-1.5 rounded-2xl border-2 border-ink bg-paper p-2.5 shadow-hard"
style={{ viewTransitionName: 'site-header' }} style={{ viewTransitionName: 'site-header' }}
> >
<a href="/index.html" className="mr-2 rounded-lg bg-ink px-2.5 py-1 font-mono text-[13px] font-bold text-paper no-underline"> <a href="/index.html" className="mr-2 rounded-lg bg-ink px-2.5 py-1 font-mono text-[13px] font-bold text-paper no-underline">
stack·教程 stack·教程
</a> </a>
{NAV.map((n) => ( {NAV.map((n) => {
const on = n.id ? n.id === active : active === 'index'
return (
<a <a
key={n.href} key={n.href}
href={n.href} href={n.href}
className={`rounded-lg px-2.5 py-1 text-[12.5px] font-bold no-underline transition-colors ${ className={`rounded-lg px-2.5 py-1 text-[12.5px] font-bold no-underline transition-colors ${
n.id === active ? 'bg-clash2a text-ink' : 'text-body hover:bg-black/5' on ? (n.id ? 'text-ink' : 'bg-ink text-paper') : 'text-body hover:bg-black/5'
}`} }`}
style={on && n.id ? { background: CHAPTERS[n.id].color } : undefined}
> >
{n.label} {n.label}
</a> </a>
))} )
})}
</header> </header>
{(title || desc) && ( {(title || desc) && (
<div className="mb-7 mt-7"> <div className="mb-7">
{title && <h1 className="m-0 text-[28px] font-extrabold text-ink">{title}</h1>} {title && <h1 className="m-0 text-t1 font-extrabold text-ink">{title}</h1>}
{desc && <p className="m-0 mt-1 text-[14.5px] text-[#6b6b6b]">{desc}</p>} {desc && <p className="m-0 mt-2 max-w-[72ch] text-content text-mute">{desc}</p>}
</div> </div>
)} )}
{children} {children}
<footer className="mt-12 border-t-2 border-dashed border-ink/15 pt-3 text-[12px] text-[#8a8a8a]"> <footer className="mt-12 border-t-2 border-dashed border-ink/15 pt-3 t-small-mute">
stack·教程 · 孟菲斯 × 拟物卡 · 靶子先行,步骤=目标+验收 stack·教程 · 孟菲斯 × 拟物卡 · 靶子先行,步骤=目标+验收
</footer> </footer>
</main> </main>
@@ -64,9 +69,9 @@ export function Page({
export function Section({ label, note, children }: { label: string; note?: string; children: ReactNode }) { export function Section({ label, note, children }: { label: string; note?: string; children: ReactNode }) {
return ( return (
<section className="mb-8"> <section className="mb-8">
<h2 className="mb-2 text-[13.5px] font-bold text-ink"> <h2 className="mb-2.5 flex flex-wrap items-baseline gap-2">
{label} <span className="t-small-strong">{label}</span>
{note && <i className="ml-2 text-[11.5px] font-normal not-italic text-[#6b6b6b]">{note}</i>} {note && <i className="t-small-mute not-italic">{note}</i>}
</h2> </h2>
{children} {children}
</section> </section>
+1
View File
@@ -26,6 +26,7 @@ const GRID: { id: ChapterId; entry?: 1 | 2 | 3 }[] = [
export function App() { export function App() {
return ( return (
<Page <Page
active="index"
title="全栈工程师教程 · 书架" title="全栈工程师教程 · 书架"
desc="靶子先行:每章先给目标、预期结果、积木清单,步骤只写「产出什么+核对什么」;过程有没有学习价值,每步先分类。" desc="靶子先行:每章先给目标、预期结果、积木清单,步骤只写「产出什么+核对什么」;过程有没有学习价值,每步先分类。"
> >
+37
View File
@@ -82,3 +82,40 @@
animation-duration: 0.01ms; animation-duration: 0.01ms;
} }
} }
/* ===== 字体系统 =====
* 基准=浏览器默认 16px=正文字号(content,不另设基准)。
* 向上:t3 → t2 → t1 三级标题;再向上是大字。
* 小字两态:t-small-mute 边缘可忽略(弱化灰)/ t-small-strong 缩小但加强(加粗收字距)。
* 大字两态:t1 大而强(页面主标题)/ t-display 大而弱(幽灵背景字,可被忽略)。 */
@theme {
--text-content: 1rem;
--text-content--line-height: 1.75;
--text-t3: 1.125rem;
--text-t3--line-height: 1.5;
--text-t2: 1.375rem;
--text-t2--line-height: 1.45;
--text-t1: 1.75rem;
--text-t1--line-height: 1.3;
--text-small: 0.75rem;
--text-small--line-height: 1.65;
}
.t-small-mute {
font-size: var(--text-small);
line-height: var(--text-small--line-height);
color: var(--color-mute);
}
.t-small-strong {
font-size: var(--text-small);
line-height: 1.5;
font-weight: 700;
letter-spacing: 0.02em;
color: var(--color-ink);
}
.t-display {
font-size: 4rem;
line-height: 1;
font-weight: 800;
color: color-mix(in srgb, var(--color-ink) 10%, transparent);
}