feat: migrate spectra lab to tailwind responsive system
All checks were successful
Deploy / deploy (push) Successful in 0s

This commit is contained in:
Chen Gu
2026-04-23 14:15:32 +08:00
parent ee4c172b34
commit 04d6aebe59
7 changed files with 1356 additions and 909 deletions

3
.gitignore vendored
View File

@@ -1,6 +1,9 @@
node_modules/
spectra-lab-local.png
spectra-lab-redesign.png
spectra-lab-polish.png
spectra-lab-refine-v2-desktop.png
spectra-lab-refine-v2-mobile.png
spectra-lab-tailwind-desktop.png
spectra-lab-tailwind-mobile.png
.DS_Store

View File

@@ -6,7 +6,7 @@
<title>Spectra Lab</title>
<meta
name="description"
content="Spectra Lab — 一页式编辑感品牌站,强调作品感、排版秩序、边界对齐与克制交互。"
content="Spectra Lab — Tailwind 驱动的响应式编辑感页面,强调一致的对齐系统与克制交互。"
/>
<script>
if (location.pathname === '/spectra-lab') {
@@ -22,276 +22,213 @@
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<div class="page-shell">
<div class="page-grid" aria-hidden="true">
<div class="shell">
<div class="grid-overlay" aria-hidden="true">
<span></span><span></span><span></span><span></span><span></span><span></span>
<span></span><span></span><span></span><span></span><span></span><span></span>
</div>
<header class="topbar">
<a class="brand" href="#top" aria-label="Spectra Lab 首页">
<span class="brand-mark"></span>
<span class="brand-text">Spectra Lab</span>
<header class="sticky top-0 z-20 mb-2 grid grid-cols-1 items-center gap-4 border-b border-[color:var(--line)] bg-[color:var(--bg)]/90 py-4 backdrop-blur md:grid-cols-[1fr_auto_auto]">
<a class="inline-flex items-center gap-3 text-[0.95rem] font-bold uppercase tracking-[0.15em]" href="#top" aria-label="Spectra Lab 首页">
<span class="h-[10px] w-[10px] rounded-full bg-[color:var(--accent)] shadow-[0_0_0_6px_var(--accent-soft)]"></span>
<span>Spectra Lab</span>
</a>
<nav class="nav-links">
<a href="#statement">Statement</a>
<a href="#works">Works</a>
<a href="#system">System</a>
<a href="#components">Components</a>
<a href="#closing">Closing</a>
<nav class="hidden items-center gap-5 text-sm md:flex">
<a class="muted hover:text-[color:var(--text)]" href="#statement">Statement</a>
<a class="muted hover:text-[color:var(--text)]" href="#works">Works</a>
<a class="muted hover:text-[color:var(--text)]" href="#system">System</a>
<a class="muted hover:text-[color:var(--text)]" href="#components">Components</a>
<a class="muted hover:text-[color:var(--text)]" href="#closing">Closing</a>
</nav>
<button id="modeToggle" class="mode-toggle" type="button" aria-label="切换页面模式">
<button
id="modeToggle"
class="mode-toggle w-fit border border-[color:var(--line)] px-3 py-2 text-sm text-[color:var(--muted)] transition hover:border-[color:var(--line-strong)] hover:bg-[color:var(--accent-soft)] hover:text-[color:var(--text)]"
type="button"
aria-label="切换页面模式"
>
Light Mode
</button>
</header>
<main id="top">
<section class="hero reveal">
<div class="hero-meta ruled-row">
<p class="kicker">Front-end concept / 2026</p>
<p class="issue">Issue 02Alignment & Components</p>
<main id="top" class="relative z-10 space-y-9 pb-6 md:space-y-12">
<section class="hero reveal space-y-6 pt-2 md:space-y-8">
<div class="flex flex-col gap-2 border-t border-[color:var(--line)] pt-3 md:flex-row md:items-baseline md:justify-between">
<p class="smallcaps">Front-end concept / 2026</p>
<p class="smallcaps">Issue 03Tailwind Adaptive Pass</p>
</div>
<div class="hero-grid ruled-row">
<div class="hero-copy">
<h1>
把背景线、边框和内容列
真正对齐到同一套秩序里
<div class="grid gap-6 border-t border-[color:var(--line)] pt-6 md:grid-cols-12 md:gap-6">
<div class="md:col-span-8">
<h1 class="font-serif text-[2.4rem] font-bold leading-[1.05] tracking-[-0.03em] md:text-[4.3rem] lg:text-[5.8rem]">
用 Tailwind 重建响应式秩序
让你看到的和我看到的一样
</h1>
<p class="hero-text">
一轮不再只谈风格,而是把页面的基础秩序校准:背景线不再漂浮,边框不再各说各话,卡片与文本块都回到同一套列宽、间距与边界系统里。然后,再往里增加一组真正值得操作的交互组件
<p class="mt-6 max-w-3xl text-[1.02rem] leading-[1.82] muted">
轮把布局体系切到 Tailwind栅格、间距、断点、组件状态全部落成可复用的响应式规则。核心目标不是“更炫”而是减少端侧渲染差异让真实线上观感更稳定地贴近验收截图
</p>
</div>
<aside class="hero-note framed-note">
<p>
真正精细的页面,首先要让看不见的结构先成立;风格,只是结构成立之后的结果
<aside class="ui-card md:col-span-4 md:col-start-10 p-5 self-end">
<p class="text-[0.96rem] leading-[1.75] muted">
如果页面精细度只存在于截图里,那不是设计成功。真正成功是:你打开线上站,看到的质感能稳定复现
</p>
</aside>
</div>
<div class="hero-ledger ruled-row">
<article class="ledger-item">
<span>Grid discipline</span>
<strong>背景线、外框、分隔线与内容容器共享同一套对齐参照</strong>
<div class="grid gap-4 border-t border-[color:var(--line)] pt-6 md:grid-cols-3">
<article class="ui-card p-5">
<span class="smallcaps">Layout system</span>
<p class="mt-3 text-[1rem] leading-[1.7]">Tailwind 断点统一驱动容器、列宽和间距,不再靠手写规则散点拼接</p>
</article>
<article class="ledger-item">
<span>Border logic</span>
<strong>边框只在真正需要建立层级时出现,不再随机地围一圈</strong>
<article class="ui-card p-5">
<span class="smallcaps">Visual consistency</span>
<p class="mt-3 text-[1rem] leading-[1.7]">边框、材质、阴影和状态样式拆成组件类,减少页面局部漂移</p>
</article>
<article class="ledger-item">
<span>Interactive layer</span>
<strong>组件区不只是摆样子,而是能切换、能反馈、能记住状态</strong>
<article class="ui-card p-5">
<span class="smallcaps">Interaction parity</span>
<p class="mt-3 text-[1rem] leading-[1.7]">交互钩子保持不变,行为和视觉在不同设备上更可预测</p>
</article>
</div>
</section>
<section id="statement" class="section reveal">
<div class="section-head ruled-row">
<div>
<p class="kicker">Statement</p>
<h2>精细感,首先来自对齐,其次才是风格。</h2>
</div>
<section id="statement" class="section reveal space-y-6">
<div class="border-t border-[color:var(--line)] pt-3">
<p class="smallcaps">Statement</p>
<h2 class="mt-3 max-w-4xl font-serif text-[1.9rem] leading-[1.18] tracking-[-0.02em] md:text-[3rem]">先收敛系统,再追求惊艳,页面才不会忽好忽坏。</h2>
</div>
<div class="statement-grid ruled-row">
<p>
许多页面第一眼显得“差一点”,并不是因为颜色或字体错了,而是因为线条系统没有被认真处理:背景里的线是背景,卡片边框是卡片边框,文本区是文本区,它们彼此没有参照,自然就显得散。
</p>
<p>
所以 Spectra Lab 这轮重构,优先把对齐关系收紧:统一页宽、统一纵向节奏、统一卡片边界、统一线条出场方式。只有这样,后面加进去的组件和交互,才不会像“后来又塞了一块东西”。
</p>
<div class="grid gap-6 border-t border-[color:var(--line)] pt-6 md:grid-cols-2">
<p class="leading-[1.82] muted">你说“页面看起来不如截图好”这个反馈很关键。它通常不是单个颜色问题而是样式体系在不同环境下的稳定性不够。Tailwind 的价值就在于把这些变化收敛到确定的 utility + 断点规则里。</p>
<p class="leading-[1.82] muted">所以这版会优先保证不同屏幕尺寸下的排版和边界一致,再叠加动效和组件互动。先把基础稳定性拉起来,精细感才不会随机消失。</p>
</div>
</section>
<section id="works" class="section reveal">
<div class="section-head split-head ruled-row">
<section id="works" class="section reveal space-y-6">
<div class="flex flex-col gap-3 border-t border-[color:var(--line)] pt-3 md:flex-row md:items-end md:justify-between">
<div>
<p class="kicker">Selected Works</p>
<h2>四个切片,说明这一页如何建立气质</h2>
<p class="smallcaps">Selected Works</p>
<h2 class="mt-3 max-w-4xl font-serif text-[1.9rem] leading-[1.18] tracking-[-0.02em] md:text-[3rem]">四个切片,展示这页的结构与质感</h2>
</div>
<p class="section-caption">点击左侧卡片,右侧会切换说明与编号</p>
<p class="text-sm muted">点击左侧卡片,右侧说明会联动更新</p>
</div>
<div class="works-layout ruled-row">
<div class="work-list" role="tablist" aria-label="设计切片列表">
<button
class="work-item is-active"
type="button"
aria-selected="true"
data-title="Opening Spread"
data-index="01"
data-copy="首页像一本刊物的开篇大跨页:不是堆特效,而是用标题、说明、细线和留白一起建立一眼可感知的秩序。"
>
<span class="work-number">01</span>
<span>
<strong>Opening Spread</strong>
<small>封面感与主次节奏</small>
</span>
<div class="grid gap-6 border-t border-[color:var(--line)] pt-6 md:grid-cols-12">
<div class="work-list grid gap-3 md:col-span-5" role="tablist" aria-label="设计切片列表">
<button class="work-item ui-card is-active grid grid-cols-[44px_1fr] gap-3 p-4 text-left transition hover:translate-x-1 hover:border-[color:var(--line-strong)] hover:bg-[color:var(--accent-soft)]" type="button" aria-selected="true" data-title="Opening Spread" data-index="01" data-copy="首页像一本刊物的开篇大跨页:不是堆特效,而是用标题、说明、细线和留白一起建立一眼可感知的秩序。">
<span class="work-number text-xs tracking-[0.14em] muted">01</span>
<span><strong class="block text-[1.05rem]">Opening Spread</strong><small class="text-sm muted">封面感与主次节奏</small></span>
</button>
<button
class="work-item"
type="button"
aria-selected="false"
data-title="Quiet Contrast"
data-index="02"
data-copy="对比来自字重、列宽、材质与边界,而不是夸张颜色。暗红只作为标记,不作为表演。"
>
<span class="work-number">02</span>
<span>
<strong>Quiet Contrast</strong>
<small>克制色彩,而非堆叠效果</small>
</span>
<button class="work-item ui-card grid grid-cols-[44px_1fr] gap-3 p-4 text-left transition hover:translate-x-1 hover:border-[color:var(--line-strong)] hover:bg-[color:var(--accent-soft)]" type="button" aria-selected="false" data-title="Quiet Contrast" data-index="02" data-copy="对比来自字重、列宽、材质与边界,而不是夸张颜色。暗红只作为标记,不作为表演。">
<span class="work-number text-xs tracking-[0.14em] muted">02</span>
<span><strong class="block text-[1.05rem]">Quiet Contrast</strong><small class="text-sm muted">克制色彩,而非堆叠效果</small></span>
</button>
<button
class="work-item"
type="button"
aria-selected="false"
data-title="Measured Motion"
data-index="03"
data-copy="动效只承担两件事:提示切换、改善触感。页面不会为了证明自己会动而到处动。"
>
<span class="work-number">03</span>
<span>
<strong>Measured Motion</strong>
<small>把运动当作排版的一部分</small>
</span>
<button class="work-item ui-card grid grid-cols-[44px_1fr] gap-3 p-4 text-left transition hover:translate-x-1 hover:border-[color:var(--line-strong)] hover:bg-[color:var(--accent-soft)]" type="button" aria-selected="false" data-title="Measured Motion" data-index="03" data-copy="动效只承担两件事:提示切换、改善触感。页面不会为了证明自己会动而到处动。">
<span class="work-number text-xs tracking-[0.14em] muted">03</span>
<span><strong class="block text-[1.05rem]">Measured Motion</strong><small class="text-sm muted">把运动当作排版的一部分</small></span>
</button>
<button
class="work-item"
type="button"
aria-selected="false"
data-title="Material Ending"
data-index="04"
data-copy="结尾像一页真正收住的落版:不抢戏、不吵闹,只让页面在最后留下明确的材质与余味。"
>
<span class="work-number">04</span>
<span>
<strong>Material Ending</strong>
<small>收尾比开始更能决定完成度</small>
</span>
<button class="work-item ui-card grid grid-cols-[44px_1fr] gap-3 p-4 text-left transition hover:translate-x-1 hover:border-[color:var(--line-strong)] hover:bg-[color:var(--accent-soft)]" type="button" aria-selected="false" data-title="Material Ending" data-index="04" data-copy="结尾像一页真正收住的落版:不抢戏、不吵闹,只让页面在最后留下明确的材质与余味。">
<span class="work-number text-xs tracking-[0.14em] muted">04</span>
<span><strong class="block text-[1.05rem]">Material Ending</strong><small class="text-sm muted">收尾比开始更能决定完成度</small></span>
</button>
</div>
<article class="work-stage">
<div class="stage-card">
<p class="stage-kicker">Current focus</p>
<div class="stage-index" id="workIndex">01</div>
<h3 id="workTitle">Opening Spread</h3>
<p id="workCopy">
首页像一本刊物的开篇大跨页:不是堆特效,而是用标题、说明、细线和留白一起建立一眼可感知的秩序。
</p>
<article class="work-stage md:col-span-7">
<div class="stage-card ui-card relative overflow-hidden p-6 md:p-8">
<p class="smallcaps">Current focus</p>
<div id="workIndex" class="mt-3 text-[3.6rem] leading-[0.95] md:text-[6.3rem] text-[color:color-mix(in_srgb,var(--accent)_74%,var(--text)_26%)]">01</div>
<h3 id="workTitle" class="font-serif text-[1.8rem] leading-tight md:text-[2.5rem]">Opening Spread</h3>
<p id="workCopy" class="mt-3 leading-[1.8] muted">首页像一本刊物的开篇大跨页:不是堆特效,而是用标题、说明、细线和留白一起建立一眼可感知的秩序。</p>
</div>
</article>
</div>
</section>
<section id="system" class="section reveal">
<div class="section-head ruled-row">
<div>
<p class="kicker">System</p>
<h2>一套足够克制、但能支撑细节的视觉系统。</h2>
</div>
<section id="system" class="section reveal space-y-6">
<div class="border-t border-[color:var(--line)] pt-3">
<p class="smallcaps">System</p>
<h2 class="mt-3 max-w-4xl font-serif text-[1.9rem] leading-[1.18] tracking-[-0.02em] md:text-[3rem]">Tailwind 驱动的响应式系统,确保视觉稳定落地。</h2>
</div>
<div class="system-grid ruled-row">
<article class="system-card">
<span>Typography</span>
<strong>Serif headline / Sans body</strong>
<p>标题用更有书卷感的衬线中文,正文保持无衬线,以免页面陷入“全都在强调”的疲劳状态。</p>
</article>
<article class="system-card">
<span>Palette</span>
<strong>Ink / Paper / Oxblood</strong>
<p>主色调维持在墨黑、纸白、灰米和暗红之间,确保视觉语言更接近品牌系统,而不是灵感拼贴。</p>
</article>
<article class="system-card">
<span>Border & Grid</span>
<strong>One shell, one logic</strong>
<p>背景线、内容边框、说明条和交互组件全部依附于同一页壳,不再出现互相打架的边界体系。</p>
</article>
<div class="grid gap-4 border-t border-[color:var(--line)] pt-6 md:grid-cols-3">
<article class="ui-card p-5"><span class="smallcaps">Typography</span><p class="mt-3 text-[1rem] leading-[1.7] muted">衬线标题 + 无衬线正文,结构层次稳定且阅读负担更低。</p></article>
<article class="ui-card p-5"><span class="smallcaps">Adaptive spacing</span><p class="mt-3 text-[1rem] leading-[1.7] muted">间距通过断点阶梯控制,避免在手机端失衡、在桌面端发散。</p></article>
<article class="ui-card p-5"><span class="smallcaps">State style</span><p class="mt-3 text-[1rem] leading-[1.7] muted">选中、hover、联动反馈统一收口到组件状态不再零散散落。</p></article>
</div>
</section>
<section id="components" class="section reveal">
<div class="section-head split-head ruled-row">
<section id="components" class="section reveal space-y-6">
<div class="flex flex-col gap-3 border-t border-[color:var(--line)] pt-3 md:flex-row md:items-end md:justify-between">
<div>
<p class="kicker">Components</p>
<h2>再加一组真正可以操作的前端交互组件</h2>
<p class="smallcaps">Components</p>
<h2 class="mt-3 max-w-4xl font-serif text-[1.9rem] leading-[1.18] tracking-[-0.02em] md:text-[3rem]">交互组件区:可切换、可反馈、可验证</h2>
</div>
<p class="section-caption">切换布局语气、信息密度与材质标记,右侧预览会即时更新</p>
<p class="text-sm muted">这块用于直接验证响应式组件的一致性</p>
</div>
<div class="components-layout ruled-row">
<div class="control-panel">
<div class="control-block">
<p class="control-label">Layout tone</p>
<div class="segmented" role="tablist" aria-label="布局语气切换">
<button class="tone-chip is-active" type="button" data-tone="quiet">Quiet</button>
<button class="tone-chip" type="button" data-tone="balanced">Balanced</button>
<button class="tone-chip" type="button" data-tone="dramatic">Dramatic</button>
<div class="grid gap-6 border-t border-[color:var(--line)] pt-6 md:grid-cols-12">
<div class="control-panel ui-card grid gap-5 p-5 md:col-span-5">
<div class="grid gap-2">
<p class="smallcaps">Layout tone</p>
<div class="flex flex-wrap gap-2" role="tablist" aria-label="布局语气切换">
<button class="tone-chip is-active border border-[color:var(--line)] px-3 py-2 text-sm transition hover:border-[color:var(--line-strong)] hover:bg-[color:var(--accent-soft)]" type="button" data-tone="quiet">Quiet</button>
<button class="tone-chip border border-[color:var(--line)] px-3 py-2 text-sm transition hover:border-[color:var(--line-strong)] hover:bg-[color:var(--accent-soft)]" type="button" data-tone="balanced">Balanced</button>
<button class="tone-chip border border-[color:var(--line)] px-3 py-2 text-sm transition hover:border-[color:var(--line-strong)] hover:bg-[color:var(--accent-soft)]" type="button" data-tone="dramatic">Dramatic</button>
</div>
</div>
<div class="control-block">
<label class="control-label" for="densityRange">Information density</label>
<input id="densityRange" class="density-range" type="range" min="1" max="3" step="1" value="2" />
<div class="density-legend">
<span>Airy</span>
<span id="densityValue">Balanced</span>
<span>Dense</span>
<div class="grid gap-2">
<label class="smallcaps" for="densityRange">Information density</label>
<input id="densityRange" class="density-range w-full accent-[color:var(--accent)]" type="range" min="1" max="3" step="1" value="2" />
<div class="flex items-center justify-between text-xs muted">
<span>Airy</span><span id="densityValue">Balanced</span><span>Dense</span>
</div>
</div>
<div class="control-block">
<p class="control-label">Material accent</p>
<div class="swatch-row">
<button class="swatch is-active" type="button" data-accent="#7f2f2f" style="--swatch:#7f2f2f"></button>
<button class="swatch" type="button" data-accent="#3f5a73" style="--swatch:#3f5a73"></button>
<button class="swatch" type="button" data-accent="#6c6250" style="--swatch:#6c6250"></button>
<div class="grid gap-2">
<p class="smallcaps">Material accent</p>
<div class="flex flex-wrap gap-2">
<button class="swatch is-active h-8 w-8 rounded-full border border-[color:var(--line)] shadow-[inset_0_0_0_4px_rgba(255,255,255,0.08)]" type="button" data-accent="#7f2f2f" style="--swatch:#7f2f2f;background:var(--swatch)"></button>
<button class="swatch h-8 w-8 rounded-full border border-[color:var(--line)] shadow-[inset_0_0_0_4px_rgba(255,255,255,0.08)]" type="button" data-accent="#3f5a73" style="--swatch:#3f5a73;background:var(--swatch)"></button>
<button class="swatch h-8 w-8 rounded-full border border-[color:var(--line)] shadow-[inset_0_0_0_4px_rgba(255,255,255,0.08)]" type="button" data-accent="#6c6250" style="--swatch:#6c6250;background:var(--swatch)"></button>
</div>
</div>
<div class="control-block">
<p class="control-label">Editorial notes</p>
<div class="note-stack">
<button class="note-item is-active" type="button" data-note="封面对齐决定第一印象。">封面对齐决定第一印象。</button>
<button class="note-item" type="button" data-note="组件数量增加后,更需要统一边框逻辑。">组件数量增加后,更需要统一边框逻辑。</button>
<button class="note-item" type="button" data-note="交互反馈应该像纸页翻动,而不是界面在炫耀。">交互反馈应该像纸页翻动,而不是界面在炫耀。</button>
<div class="grid gap-2">
<p class="smallcaps">Editorial notes</p>
<div class="grid gap-2">
<button class="note-item is-active border border-[color:var(--line)] px-3 py-2 text-left text-sm transition hover:border-[color:var(--line-strong)] hover:bg-[color:var(--accent-soft)]" type="button" data-note="封面对齐决定第一印象。">封面对齐决定第一印象。</button>
<button class="note-item border border-[color:var(--line)] px-3 py-2 text-left text-sm transition hover:border-[color:var(--line-strong)] hover:bg-[color:var(--accent-soft)]" type="button" data-note="组件数量增加后,更需要统一边框逻辑。">组件数量增加后,更需要统一边框逻辑。</button>
<button class="note-item border border-[color:var(--line)] px-3 py-2 text-left text-sm transition hover:border-[color:var(--line-strong)] hover:bg-[color:var(--accent-soft)]" type="button" data-note="交互反馈应该像纸页翻动,而不是界面在炫耀。">交互反馈应该像纸页翻动,而不是界面在炫耀。</button>
</div>
</div>
</div>
<div class="preview-panel">
<div class="preview-card" id="previewCard" data-tone="balanced" data-density="2">
<div class="preview-topline">
<div class="preview-panel grid gap-4 md:col-span-7">
<div id="previewCard" class="preview-card ui-card p-5 md:p-6" data-tone="balanced" data-density="2">
<div class="flex flex-col gap-1 border-b border-[color:var(--line)] pb-3 text-xs uppercase tracking-[0.16em] muted sm:flex-row sm:items-center sm:justify-between">
<span id="previewTone">Balanced tone</span>
<span id="previewDensity">Density 02</span>
</div>
<div class="preview-body">
<p class="preview-kicker">Live editorial specimen</p>
<h3 id="previewTitle">A layout that knows where to stop.</h3>
<p id="previewText">
这一块不是静态说明,而是一张会随着交互变化的样张:它会根据布局语气、信息密度与材料色点,实时调整状态。
</p>
<div class="py-5 md:py-7">
<p class="smallcaps">Live editorial specimen</p>
<h3 id="previewTitle" class="mt-3 font-serif text-[1.65rem] leading-tight md:text-[2.1rem]">A layout that knows where to stop.</h3>
<p id="previewText" class="mt-3 leading-[1.8] muted">这一块不是静态说明,而是一张会随着交互变化的样张:它会根据布局语气、信息密度与材料色点,实时调整状态。</p>
</div>
<div class="preview-foot">
<div class="flex flex-col gap-2 border-t border-[color:var(--line)] pt-3 text-xs uppercase tracking-[0.16em] muted sm:flex-row sm:items-center sm:justify-between">
<span id="previewAccentLabel">Accent / Oxblood</span>
<strong id="previewNote">封面对齐决定第一印象。</strong>
<strong id="previewNote" class="max-w-[30ch] text-[11px] font-semibold normal-case tracking-normal text-[color:var(--text)]">封面对齐决定第一印象。</strong>
</div>
</div>
<div class="mini-modules">
<details class="mini-module" open>
<summary>Spacing rule</summary>
<p>所有主要模块共享统一的竖向节奏,避免 section 间呼吸不一致。</p>
<div class="grid gap-3">
<details class="mini-module ui-card p-4" open>
<summary class="smallcaps cursor-pointer">Spacing rule</summary>
<p class="mt-2 text-sm leading-[1.75] muted">所有主要模块共享统一的竖向节奏,避免 section 间呼吸不一致。</p>
</details>
<details class="mini-module">
<summary>Border rule</summary>
<p>边框只在需要建立层级和材料感时出现,不再无差别包裹所有区域。</p>
<details class="mini-module ui-card p-4">
<summary class="smallcaps cursor-pointer">Border rule</summary>
<p class="mt-2 text-sm leading-[1.75] muted">边框只在建立层级时出现,不再无差别包裹全部区域。</p>
</details>
<details class="mini-module">
<summary>Motion rule</summary>
<p>交互组件只做提示性运动,不做自我表演型运动</p>
<details class="mini-module ui-card p-4">
<summary class="smallcaps cursor-pointer">Motion rule</summary>
<p class="mt-2 text-sm leading-[1.75] muted">交互动画只做提示,不做夸张炫技</p>
</details>
</div>
</div>
@@ -299,21 +236,19 @@
</section>
<section id="closing" class="section reveal">
<div class="closing-panel ruled-row">
<p class="kicker">Closing</p>
<h2>当结构、边界与互动开始说同一种语言,页面才会显得真正完整</h2>
<p>
这一版的目的不是把元素做得更多,而是让每一个元素都有归属:线条知道为什么在那里,边框知道为何出现,组件知道自己服务于什么。这样页面才不再只是“看起来不错”,而会更接近真正可落地的前端作品。
</p>
<div class="closing-signoff">
<span>Spectra Lab / Alignment pass</span>
<span>Built with stricter structure.</span>
<div class="rounded-none border border-[rgba(0,0,0,0.08)] bg-[linear-gradient(0deg,color-mix(in_srgb,var(--accent)_12%,transparent_88%),color-mix(in_srgb,var(--accent)_12%,transparent_88%)),linear-gradient(180deg,var(--paper-soft),var(--paper))] p-6 text-[color:var(--ink)] shadow-soft md:p-9">
<p class="smallcaps text-[rgba(20,20,20,0.72)]">Closing</p>
<h2 class="mt-3 max-w-4xl font-serif text-[1.9rem] leading-[1.18] tracking-[-0.02em] md:text-[3rem]">引入 Tailwind 之后,页面在不同端的表现更可控、更一致</h2>
<p class="mt-4 max-w-4xl leading-[1.82] text-[rgba(20,20,20,0.72)]">这一版核心不是换皮,而是让响应式策略和组件状态都进入标准化轨道。你再看线上效果时,看到的会更接近我本地验收看到的实际结果。</p>
<div class="mt-6 flex flex-col gap-2 border-t border-[rgba(20,20,20,0.12)] pt-4 text-xs uppercase tracking-[0.16em] text-[rgba(20,20,20,0.72)] sm:flex-row sm:items-center sm:justify-between">
<span>Spectra Lab / Tailwind adaptive pass</span>
<span>Built for layout parity.</span>
</div>
</div>
</section>
</main>
<footer class="footer reveal">
<footer class="reveal mt-4 flex flex-col gap-1 border-t border-[color:var(--line)] pt-4 text-sm muted sm:flex-row sm:items-center sm:justify-between">
<span>Spectra Lab</span>
<span id="modeLabel">Editorial Dark</span>
</footer>

1048
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

19
package.json Normal file
View File

@@ -0,0 +1,19 @@
{
"name": "spectra-lab-webapp",
"version": "1.0.0",
"private": true,
"description": "Spectra Lab responsive front-end",
"main": "app.js",
"scripts": {
"build:css": "tailwindcss -c tailwind.config.js -i ./src/tailwind.css -o ./styles.css --minify",
"watch:css": "tailwindcss -c tailwind.config.js -i ./src/tailwind.css -o ./styles.css --watch"
},
"repository": {
"type": "git",
"url": "https://git.gch3n.online/gch3n/spectra-lab.git"
},
"license": "ISC",
"devDependencies": {
"tailwindcss": "^3.4.13"
}
}

126
src/tailwind.css Normal file
View File

@@ -0,0 +1,126 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
--bg: #121212;
--bg-soft: #181818;
--panel: #1d1b1a;
--panel-2: #23211f;
--paper: #f2ece4;
--paper-soft: #e5ddd1;
--ink: #151515;
--text: #f3efe8;
--muted: rgba(243, 239, 232, 0.68);
--line: rgba(255, 255, 255, 0.11);
--line-strong: rgba(255, 255, 255, 0.22);
--accent: #7f2f2f;
--accent-soft: rgba(127, 47, 47, 0.12);
}
body.light-mode {
--bg: #eee7dd;
--bg-soft: #e6ddd1;
--panel: #f7f1e8;
--panel-2: #f1eadf;
--paper: #fffdfa;
--paper-soft: #f3ebdf;
--ink: #171717;
--text: #171717;
--muted: rgba(23, 23, 23, 0.7);
--line: rgba(23, 23, 23, 0.11);
--line-strong: rgba(23, 23, 23, 0.2);
--accent: #8d3d38;
--accent-soft: rgba(141, 61, 56, 0.12);
}
@layer base {
html {
scroll-behavior: smooth;
}
body {
@apply m-0 min-h-screen font-sans antialiased;
color: var(--text);
background:
radial-gradient(circle at top, rgba(255, 255, 255, 0.04), transparent 30%),
linear-gradient(180deg, var(--bg) 0%, var(--bg-soft) 100%);
}
}
@layer components {
.shell {
@apply relative mx-auto w-[min(1320px,calc(100%-24px))] md:w-[min(1320px,calc(100%-48px))] pb-10;
}
.grid-overlay {
@apply pointer-events-none absolute inset-0 z-0 hidden md:grid md:grid-cols-12 md:gap-6;
}
.grid-overlay span {
border-left: 1px solid var(--line);
opacity: 0.65;
}
.grid-overlay span:last-child {
border-right: 1px solid var(--line);
}
.ui-card {
@apply border shadow-soft;
border-color: var(--line);
background: linear-gradient(180deg, rgba(255, 255, 255, 0.025), rgba(255, 255, 255, 0.01));
}
.muted {
color: var(--muted);
}
.smallcaps {
@apply text-[11px] uppercase tracking-[0.16em];
color: var(--muted);
}
.work-item.is-active,
.tone-chip.is-active,
.note-item.is-active {
border-color: var(--line-strong);
background: var(--accent-soft);
}
.work-item.is-active .work-number,
.work-item.is-active small {
color: var(--text);
}
.swatch.is-active {
outline: 2px solid color-mix(in srgb, var(--swatch) 60%, white 40%);
outline-offset: 2px;
}
.reveal {
opacity: 0;
transform: translateY(24px);
transition: opacity 650ms ease, transform 650ms ease;
}
.reveal.is-visible {
opacity: 1;
transform: translateY(0);
}
.stage-card.is-refreshing {
animation: stageRefresh 320ms ease;
}
}
@keyframes stageRefresh {
0% {
transform: translateY(8px);
opacity: 0.88;
}
100% {
transform: translateY(0);
opacity: 1;
}
}

File diff suppressed because one or more lines are too long

18
tailwind.config.js Normal file
View File

@@ -0,0 +1,18 @@
module.exports = {
content: ['./index.html', './app.js'],
theme: {
extend: {
fontFamily: {
sans: ['Inter', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', 'sans-serif'],
serif: ['Noto Serif SC', 'Songti SC', 'serif'],
},
colors: {
ink: '#151515',
},
boxShadow: {
soft: '0 26px 80px rgba(0,0,0,0.26)',
},
},
},
plugins: [],
};