feat: 布局三修——navbar 下方统一 32px 间距(章节页横幅零间距修复);选中项背景=章节色(与章头横幅统一,书架选中 ink);字体系统(16px 基准=正文 / t1-t3 三级标题 / 小字两态 mute·strong / 大字两态 t1·幽灵 display 章号)全站应用
This commit is contained in:
1 parent
8454a7abab
commit
a65a6d96b1
4 files changed
+82
-34
No files matched your search
@@ -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>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -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) => {
|
||||||
<a
|
const on = n.id ? n.id === active : active === 'index'
|
||||||
key={n.href}
|
return (
|
||||||
href={n.href}
|
<a
|
||||||
className={`rounded-lg px-2.5 py-1 text-[12.5px] font-bold no-underline transition-colors ${
|
key={n.href}
|
||||||
n.id === active ? 'bg-clash2a text-ink' : 'text-body hover:bg-black/5'
|
href={n.href}
|
||||||
}`}
|
className={`rounded-lg px-2.5 py-1 text-[12.5px] font-bold no-underline transition-colors ${
|
||||||
>
|
on ? (n.id ? 'text-ink' : 'bg-ink text-paper') : 'text-body hover:bg-black/5'
|
||||||
{n.label}
|
}`}
|
||||||
</a>
|
style={on && n.id ? { background: CHAPTERS[n.id].color } : undefined}
|
||||||
))}
|
>
|
||||||
|
{n.label}
|
||||||
|
</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>
|
||||||
|
|||||||
@@ -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="靶子先行:每章先给目标、预期结果、积木清单,步骤只写「产出什么+核对什么」;过程有没有学习价值,每步先分类。"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
Reference in new issue
Block a user