报纸版 HTML 设计口味
给「审核大纲、项目复盘、内部简报」等需要严肃感的页面做的中文报纸观感模板: 暖米纸面、粗黑分割线、宋体大标题、强对比、可打印。 靠排版建立层级,不用装饰图片、渐变、阴影、圆角。
效果演示
下面是用这套设计系统拼的一个迷你「文章索引」页,把报头、栏目条、按钮、徽标、信息卡、社论摘句、空状态、三个 JS 组件(自定义下拉、复制链接、加载遮罩)串成一个活例:
试这几下:
- 顶部下拉切换类目 —— 「改值即整页提交」,加载遮罩短暂出现,筛选状态写进 URL 的
?cat=;列表按类目过滤,切到没有文章的类目会看到空状态卡 - 点复制本页链接 —— 复制的是当前筛选后的完整 URL,toast 提示「链接已复制」
- 对比按钮(默认 / 实心 / 禁用)、徽标、信息卡、摘句(普通 / 反色)几种排版语言
真身在 assets/newspaper-demo.html,样式与交互逻辑复用同级 newspaper.css / newspaper.js(同一份源文件,不重复定义,跟下面「接入方式」一致)。
何时用 / 何时不用
- 用:网页文档、审核稿、长页、报告列表 / 门户、需要打印或导出 PDF 的严肃页面
- 不用:演示页 / 滚动动画 / 投屏页面(那是另一套 16:9 舞台式风格)
接入方式
把本 skill assets/ 下三个文件(css / js / favicon)拷到你的项目 static/:
-
<head>引入: -
</body>前(仅当用到 JS 组件时): -
多页项目建议做一个 Jinja
base.html放公共头/尾,各页{% extends %}+{% block content %}。
设计系统在 newspaper.css 里
设计令牌、字体、字重、分割线规则、现成类(.page / .masthead / .kicker / .section-rule / .part / .core-left / .btn / .badge / .card / .no-data 等)的完整定义和注释,都在 newspaper.css 里,按章节注释组织(---- 设计令牌 ---- / ---- 报头 ---- / ---- 区块 ---- 等)。直接读 CSS 比读 markdown 描述更准。
下面是核心骨架示例,更多见 CSS:
<div class="page">
<header class="masthead">
<div class="meta"><span>栏目 · KICKER</span><span>右侧元信息</span></div>
<h1>大标题</h1>
<p class="subtitle">副标题</p>
</header>
<div class="kicker">栏目条</div>
<!-- 内容 -->
</div>
三个 JS 组件(需引 newspaper.js)
全部 class 钩子驱动、无硬编码 id、有无 JS 兜底。
1. 自定义下拉
给原生 <select> 加 class="np-select":JS 隐藏它、生成 .custom-select 列表(原生 select 仍作数据源 + 无 JS 兜底)。
「改值即整页提交」:把 select 放进 <form data-np-autosubmit>,加无 JS 兜底按钮 <noscript><button type="submit" class="btn">应用</button></noscript>。
<form method="get" data-np-autosubmit>
<select name="dept" class="np-select"><option value="">全部</option>…</select>
<noscript><button type="submit" class="btn">应用</button></noscript>
</form>
2. 复制链接 + toast
data-link 为相对路径时自动补 window.location.origin;绝对 URL 原样复制。
页面有 .toast 则复用,否则自动创建。
3. 加载遮罩
<div class="np-loading-region">
<div class="loading-overlay">
<div class="loading-box"><div class="label">正在加载…</div><div class="loading-bar"></div></div>
</div>
<!-- 结果内容 -->
</div>
任意表单 submit 时自动 .show,pageshow(后退)时自动清除。
favicon
assets/favicon.svg 是极简圆框 + 两字母大字(默认 BI)+ 蓝图辅助网格的模板标记。
要换团队标识,改 <text> 里的字母即可(文件头注释里有示例)。
配套资产源码(折叠)
源文件全部在同级 assets/,下面通过 snippets 引用展示(源文件只一份,引用不算重复;真身也可按文件 URL 直接下载):
newspaper.css(含完整章节注释 —— 详细设计令牌、组件类、注释说明都在这)
/* ============================================================
报纸版设计系统 (newspaper-style)
暖米纸面 · 粗黑分割线 · 宋体大标题 · 强对比 · 可打印
用法:<link rel="stylesheet" href=".../newspaper.css">
配合 newspaper.js 可启用自定义下拉 / 复制链接 / 加载遮罩组件。
============================================================ */
/* ---- 设计令牌 ---- */
:root {
--paper: #f3efe4; /* 暖米色纸张背景 */
--ink: #151515; /* 主文字与粗线 */
--muted: #595247; /* 报头元信息、栏目 kicker */
--body: #2e2a24; /* 正文色,比纯黑柔和 */
--accent: #8a2f1b; /* 强调色(章节编号 / 少量重点) */
}
/* ---- 基础 ---- */
* { box-sizing: border-box; }
html,
body {
min-height: 100vh;
margin: 0;
background: var(--paper);
color: var(--ink);
font-family: "Songti SC", "Noto Serif CJK SC", "STSong", Georgia, serif;
}
/* 列向 flex,配合 .page 的四向 auto margin 实现版心上下居中 */
body {
display: flex;
flex-direction: column;
}
/* ---- 版心 ---- */
.page {
width: min(1120px, calc(100% - 40px));
/* 四向 auto:内容比视口短时上下左右居中,超高时也不裁切顶部 */
margin: auto;
padding: 24px 0 36px;
}
/* ---- 报头 ---- */
.masthead {
border-top: 4px solid var(--ink);
border-bottom: 4px solid var(--ink);
padding: 22px 0 24px;
}
.masthead .meta {
display: flex;
justify-content: space-between;
align-items: baseline;
color: var(--muted);
font-size: 14px;
font-weight: 800;
letter-spacing: 0.18em;
}
.masthead h1 {
margin: 24px 0 0;
max-width: 900px;
font-size: clamp(40px, 8vw, 88px);
line-height: 0.95;
font-weight: 900;
letter-spacing: 0;
}
.masthead .subtitle {
margin: 16px 0 0;
font-size: 18px;
font-weight: 800;
color: var(--body);
}
/* ---- 栏目条 ---- */
.kicker {
margin: 28px 0 20px;
border-top: 2px solid var(--ink);
border-bottom: 2px solid var(--ink);
padding: 8px 0;
color: var(--muted);
text-align: center;
font-size: 14px;
font-weight: 800;
letter-spacing: 0.26em;
}
/* ---- 区块分割线 ---- */
.section-rule { border-bottom: 4px solid var(--ink); padding: 26px 0; }
.part { border-bottom: 4px solid var(--ink); padding-bottom: 24px; }
.part-number { color: var(--accent); font-size: 14px; font-weight: 900; letter-spacing: 0.24em; }
/* ---- 社论摘句 ---- */
.core-left {
margin-top: 22px;
border-left: 8px solid var(--ink);
padding-left: 16px;
font-size: 22px;
line-height: 1.55;
font-weight: 900;
}
.core-invert {
margin-top: 22px;
border: 2px solid var(--ink);
background: var(--ink);
color: var(--paper);
padding: 16px;
font-size: 22px;
line-height: 1.55;
font-weight: 900;
}
/* ---- 按钮(padding/font-size 可在使用处覆盖)---- */
.btn {
border: 2px solid var(--ink);
background: var(--paper);
color: var(--ink);
text-decoration: none;
padding: 9px 18px;
font-size: 15px;
font-weight: 900;
font-family: inherit;
letter-spacing: 0.06em;
white-space: nowrap;
cursor: pointer;
}
.btn:hover { background: var(--ink); color: var(--paper); }
.btn-solid { background: var(--ink); color: var(--paper); }
.btn-solid:hover { background: var(--paper); color: var(--ink); }
.btn.disabled {
background: var(--paper);
color: var(--muted);
border-color: var(--muted);
cursor: not-allowed;
}
/* ---- 徽标 ---- */
.badge {
display: inline-block;
border: 2px solid var(--ink);
background: var(--ink);
color: var(--paper);
padding: 2px 10px;
font-size: 13px;
font-weight: 800;
letter-spacing: 0.06em;
margin-left: 8px;
}
/* ---- 信息框(不是现代卡片:方角、无阴影)---- */
.card { border: 2px solid var(--ink); padding: 16px 18px; }
/* ---- 空状态 ---- */
.no-data {
border: 2px solid var(--ink);
text-align: center;
padding: 40px 20px;
font-size: 18px;
font-weight: 800;
}
.no-data .hint { font-size: 15px; font-weight: 800; color: var(--muted); margin-top: 10px; }
/* ============================================================
组件 1:复制提示 toast(配合 newspaper.js)
============================================================ */
.toast {
position: fixed;
left: 50%;
bottom: 32px;
transform: translateX(-50%);
border: 2px solid var(--ink);
background: var(--ink);
color: var(--paper);
padding: 12px 24px;
font-size: 15px;
font-weight: 900;
letter-spacing: 0.1em;
opacity: 0;
pointer-events: none;
transition: opacity 0.2s ease;
z-index: 1000;
}
.toast.show { opacity: 1; }
/* ============================================================
组件 2:自定义下拉(配合 newspaper.js)
原生 <select class="np-select"> 作为数据源与无 JS 兜底;
JS 启用后隐藏它、生成 .custom-select 列表。
需在 <head> 尽早执行 document.documentElement.className += ' js'。
============================================================ */
.js select.np-select { display: none; }
.custom-select { position: relative; width: 100%; }
.custom-select .cs-trigger {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
width: 100%;
padding: 10px 12px;
border: 2px solid var(--ink);
border-radius: 0;
background: var(--paper);
color: var(--ink);
font-family: inherit;
font-size: 16px;
font-weight: 800;
text-align: left;
cursor: pointer;
}
.custom-select.open .cs-trigger { border-color: var(--accent); }
.custom-select .cs-arrow {
flex: none;
width: 0;
height: 0;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-top: 7px solid var(--ink);
}
.custom-select.open .cs-arrow { transform: rotate(180deg); }
.custom-select .cs-menu {
position: absolute;
left: 0;
right: 0;
top: calc(100% - 2px);
z-index: 30;
margin: 0;
padding: 0;
list-style: none;
border: 2px solid var(--ink);
background: var(--paper);
max-height: 280px;
overflow-y: auto;
display: none;
scrollbar-width: thin;
scrollbar-color: var(--ink) var(--paper);
}
.custom-select.open .cs-menu { display: block; }
.custom-select .cs-menu::-webkit-scrollbar { width: 12px; }
.custom-select .cs-menu::-webkit-scrollbar-track { background: var(--paper); border-left: 2px solid var(--ink); }
.custom-select .cs-menu::-webkit-scrollbar-thumb { background: var(--ink); border: 2px solid var(--paper); }
.custom-select .cs-menu::-webkit-scrollbar-thumb:hover { background: var(--accent); }
.custom-select .cs-option {
padding: 10px 12px;
font-size: 15px;
font-weight: 800;
color: var(--body);
cursor: pointer;
border-bottom: 1px solid rgba(21, 21, 21, 0.15);
}
.custom-select .cs-option:last-child { border-bottom: none; }
.custom-select .cs-option.selected { color: var(--accent); }
.custom-select .cs-option:hover,
.custom-select .cs-option.active { background: var(--ink); color: var(--paper); }
/* ============================================================
组件 3:加载遮罩(配合 newspaper.js)
把内容放进 .np-loading-region(相对定位容器),
内置一个 .loading-overlay,切换/提交时加 .show。
============================================================ */
.np-loading-region { position: relative; min-height: 160px; }
.loading-overlay {
position: absolute;
inset: 0;
background: rgba(243, 239, 228, 0.88);
display: none;
align-items: center;
justify-content: center;
z-index: 10;
}
.loading-overlay.show { display: flex; }
.loading-box { border: 2px solid var(--ink); background: var(--paper); padding: 24px 32px; text-align: center; }
.loading-box .label { font-size: 18px; font-weight: 900; letter-spacing: 0.2em; color: var(--ink); margin-bottom: 14px; }
.loading-bar { width: 220px; height: 10px; border: 2px solid var(--ink); overflow: hidden; }
.loading-bar::before {
content: "";
display: block;
width: 40%;
height: 100%;
background: var(--ink);
animation: loading-slide 1s linear infinite;
}
@keyframes loading-slide {
0% { transform: translateX(-110%); }
100% { transform: translateX(275%); }
}
/* ---- 响应式:860px 断点,多列变单列、保留粗线 ---- */
@media (max-width: 860px) {
.page { width: calc(100% - 28px); }
}
newspaper.js(三个组件交互逻辑)
/* ============================================================
报纸版交互组件 (newspaper-style)
全部基于 class/属性钩子,自初始化,无硬编码 id。
放在 </body> 前: <script src=".../newspaper.js"></script>
(另需在 <head> 尽早放:
<script>document.documentElement.className += ' js';</script>
以避免原生下拉闪现。本文件也会兜底补上 .js 类。)
钩子约定:
- 自定义下拉: <select class="np-select"> … </select>
- 改值即提交: 把 select 放进 <form data-np-autosubmit> (可选)
- 加载遮罩: 表单提交时,自动给页面里的 .loading-overlay 加 .show
- 复制链接: <button class="np-copy" data-link="/foo">复制链接</button>
- 提示 toast: 页面有 <div class="toast"></div> 则复用,否则自动创建
============================================================ */
(function () {
document.documentElement.className += ' js';
/* ---- toast ---- */
var toastEl = document.querySelector('.toast');
var toastTimer = null;
function ensureToast() {
if (!toastEl) {
toastEl = document.createElement('div');
toastEl.className = 'toast';
document.body.appendChild(toastEl);
}
return toastEl;
}
function showToast(msg) {
var t = ensureToast();
t.textContent = msg;
t.classList.add('show');
if (toastTimer) clearTimeout(toastTimer);
toastTimer = setTimeout(function () { t.classList.remove('show'); }, 1600);
}
/* ---- 加载遮罩 ---- */
function showLoading() {
document.querySelectorAll('.loading-overlay').forEach(function (o) { o.classList.add('show'); });
}
function hideLoading() {
document.querySelectorAll('.loading-overlay').forEach(function (o) { o.classList.remove('show'); });
}
// 后退/bfcache 恢复时清除残留遮罩
window.addEventListener('pageshow', hideLoading);
// 任意表单提交(含原生按钮)都显示遮罩
document.querySelectorAll('form').forEach(function (f) {
f.addEventListener('submit', showLoading);
});
/* ---- 复制链接 ---- */
function fallbackCopy(text) {
var ta = document.createElement('textarea');
ta.value = text;
ta.style.position = 'fixed';
ta.style.opacity = '0';
document.body.appendChild(ta);
ta.select();
try { document.execCommand('copy'); showToast('链接已复制'); }
catch (e) { showToast('复制失败,请手动复制'); }
document.body.removeChild(ta);
}
document.querySelectorAll('.np-copy').forEach(function (btn) {
btn.addEventListener('click', function () {
var link = btn.getAttribute('data-link') || '';
var full = /^https?:/i.test(link) ? link : (window.location.origin + link);
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(full).then(
function () { showToast('链接已复制'); },
function () { fallbackCopy(full); }
);
} else {
fallbackCopy(full);
}
});
});
/* ---- 自定义下拉 ---- */
function closeAll(except) {
document.querySelectorAll('.custom-select.open').forEach(function (w) {
if (w !== except && w._close) w._close();
});
}
function buildSelect(sel) {
var autosubmit = !!sel.closest('form[data-np-autosubmit]');
var wrap = document.createElement('div');
wrap.className = 'custom-select';
var trigger = document.createElement('button');
trigger.type = 'button';
trigger.className = 'cs-trigger';
trigger.setAttribute('aria-haspopup', 'listbox');
trigger.setAttribute('aria-expanded', 'false');
var label = document.createElement('span');
label.className = 'cs-label';
var arrow = document.createElement('span');
arrow.className = 'cs-arrow';
trigger.appendChild(label);
trigger.appendChild(arrow);
var menu = document.createElement('ul');
menu.className = 'cs-menu';
menu.setAttribute('role', 'listbox');
var activeIdx = -1;
function setActive(i) {
var items = menu.children;
for (var k = 0; k < items.length; k++) items[k].classList.toggle('active', k === i);
activeIdx = i;
if (i >= 0 && items[i]) items[i].scrollIntoView({ block: 'nearest' });
}
function syncLabel() { label.textContent = sel.options[sel.selectedIndex].textContent.trim(); }
function choose(i) {
sel.selectedIndex = i;
syncLabel();
close();
sel.dispatchEvent(new Event('change', { bubbles: true }));
}
function open() {
closeAll(wrap);
wrap.classList.add('open');
trigger.setAttribute('aria-expanded', 'true');
setActive(sel.selectedIndex);
}
function close() {
wrap.classList.remove('open');
trigger.setAttribute('aria-expanded', 'false');
setActive(-1);
}
wrap._close = close;
Array.prototype.forEach.call(sel.options, function (opt, i) {
var li = document.createElement('li');
li.className = 'cs-option' + (opt.selected ? ' selected' : '');
li.setAttribute('role', 'option');
li.textContent = opt.textContent.trim();
li.addEventListener('click', function (e) { e.stopPropagation(); choose(i); });
menu.appendChild(li);
});
syncLabel();
trigger.addEventListener('click', function (e) {
e.stopPropagation();
if (wrap.classList.contains('open')) close(); else open();
});
trigger.addEventListener('keydown', function (e) {
var n = menu.children.length;
if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
e.preventDefault();
if (!wrap.classList.contains('open')) { open(); return; }
var ni = activeIdx + (e.key === 'ArrowDown' ? 1 : -1);
if (ni < 0) ni = 0;
if (ni >= n) ni = n - 1;
setActive(ni);
} else if (e.key === 'Enter' || e.key === ' ') {
if (wrap.classList.contains('open')) {
e.preventDefault();
if (activeIdx >= 0) choose(activeIdx);
}
} else if (e.key === 'Escape') {
close();
}
});
// 改值即提交(可选):先显示遮罩再提交
if (autosubmit) {
sel.addEventListener('change', function () {
showLoading();
sel.form.submit();
});
}
wrap.appendChild(trigger);
wrap.appendChild(menu);
sel.parentNode.appendChild(wrap);
}
document.querySelectorAll('select.np-select').forEach(buildSelect);
document.addEventListener('click', function () { closeAll(null); });
})();
favicon.svg(模板图标)
<!-- 模板 favicon:极简圆框 + 两字母大字 + 蓝图辅助网格。
换成团队标识时,改 <text> 里的字母即可(如 BI / NS / XX)。 -->
<svg id="bi-svg-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="100%" height="100%" style="background:#FFFFFF;">
<!-- 极简画布背景 -->
<rect width="512" height="512" fill="#FFFFFF"/>
<!-- 基础圆框容器 -->
<circle cx="256" cy="256" r="232" fill="none" stroke="#000000" stroke-width="8"/>
<!-- 绝对中心对齐核心大字 -->
<text x="254" y="257" text-anchor="middle" dominant-baseline="central" style="font-family: 'Inter', sans-serif; font-weight: 900; letter-spacing: -15px;" font-size="330" fill="#000000">BI</text>
<!-- 辅助设计线 -->
<g opacity="0.2">
<pattern id="icon-grid" width="32" height="32" patternUnits="userSpaceOnUse">
<path d="M 32 0 L 0 0 0 32" fill="none" stroke="#000000" stroke-width="0.5"/>
</pattern>
<rect width="512" height="512" fill="url(#icon-grid)"/>
<line x1="256" y1="0" x2="256" y2="512" stroke="#FF0000" stroke-width="1" stroke-dasharray="2 2"/>
<line x1="0" y1="256" x2="512" y2="256" stroke="#FF0000" stroke-width="1" stroke-dasharray="2 2"/>
<circle cx="256" cy="256" r="236" fill="none" stroke="#000000" stroke-width="0.5" stroke-dasharray="4 4"/>
<circle cx="256" cy="256" r="128" fill="none" stroke="#000000" stroke-width="0.5" stroke-dasharray="4 4"/>
</g>
</svg>
newspaper-demo.html(上面「效果演示」的 demo 源码,自包含、未压缩)
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>报纸版设计系统 demo:文章索引</title>
<!--
newspaper-demo.html —— newspaper-style 设计系统的交互全家福。
复现的不变量(对应 newspaper-style.md 的组件文档):
- 类目下拉是「改值即整页提交」:np-select + data-np-autosubmit,筛选状态进 URL(?cat=xxx)
- 复制链接按钮复制的是「当前筛选后」的 URL,而不是固定地址
- 无匹配类目时显示 .no-data 空状态
样式/交互逻辑均复用同级 newspaper.css / newspaper.js(同一份源文件,不重复定义)。
-->
<script>document.documentElement.className += ' js';</script>
<link rel="stylesheet" href="newspaper.css">
</head>
<body>
<div class="page">
<header class="masthead">
<div class="meta"><span>版式实验室 · DEMO</span><span>NO. 07</span></div>
<h1>报纸版设计系统</h1>
<p class="subtitle">暖米纸面 · 粗黑分割线 · 强对比排版 —— 交互组件全家福</p>
</header>
<div class="kicker">目录 · 共六篇 · 演示用虚构内容</div>
<div class="section-rule">
<form method="get" data-np-autosubmit style="display:flex; gap:14px; align-items:flex-end; flex-wrap:wrap;">
<div style="min-width:220px; flex:1;">
<div style="font-size:13px; font-weight:800; color:var(--muted); letter-spacing:0.12em; margin-bottom:6px;">按类目筛选</div>
<select name="cat" class="np-select">
<option value="">全部类目</option>
<option value="shizheng">时政</option>
<option value="caijing">财经</option>
<option value="wenhua">文化</option>
</select>
<noscript><button type="submit" class="btn" style="margin-top:8px;">应用</button></noscript>
</div>
<button type="button" class="btn np-copy" data-link="">复制本页链接</button>
</form>
</div>
<div class="np-loading-region">
<div class="loading-overlay">
<div class="loading-box"><div class="label">正在加载…</div><div class="loading-bar"></div></div>
</div>
<div id="list" style="display:grid; gap:14px; margin-top:22px;">
<article class="card" data-cat="shizheng">
<div style="display:flex; align-items:baseline; justify-content:space-between; gap:12px;">
<h3 style="margin:0; font-size:19px;">总编按:版式即态度<span class="badge">本期头条</span></h3>
<span style="font-size:13px; color:var(--muted); white-space:nowrap;">时政</span>
</div>
<p style="margin:10px 0 0; color:var(--body); line-height:1.6;">粗线分割、无阴影无圆角,靠排版本身建立层级——这是一种克制,也是一种立场。</p>
</article>
<article class="card" data-cat="caijing">
<div style="display:flex; align-items:baseline; justify-content:space-between; gap:12px;">
<h3 style="margin:0; font-size:19px;">数字纸媒的印刷术乡愁</h3>
<span style="font-size:13px; color:var(--muted); white-space:nowrap;">财经</span>
</div>
<p style="margin:10px 0 0; color:var(--body); line-height:1.6;">屏幕想模仿纸张的重量感,最后学会的却是纸张的规矩:留白、对齐、粗细分明的边界。</p>
</article>
<article class="card" data-cat="wenhua">
<div style="display:flex; align-items:baseline; justify-content:space-between; gap:12px;">
<h3 style="margin:0; font-size:19px;">宋体重生记:从铅字到网页</h3>
<span style="font-size:13px; color:var(--muted); white-space:nowrap;">文化</span>
</div>
<p style="margin:10px 0 0; color:var(--body); line-height:1.6;">字重与字号的对比,替代了插画和渐变,成为页面上唯一的视觉重音。</p>
</article>
<article class="card" data-cat="shizheng">
<div style="display:flex; align-items:baseline; justify-content:space-between; gap:12px;">
<h3 style="margin:0; font-size:19px;">会议室里的排版政治<span class="badge">已归档</span></h3>
<span style="font-size:13px; color:var(--muted); white-space:nowrap;">时政</span>
</div>
<p style="margin:10px 0 0; color:var(--body); line-height:1.6;">谁的标题占据头版、谁的引言被摘句放大,从来不只是设计问题。</p>
</article>
<article class="card" data-cat="caijing">
<div style="display:flex; align-items:baseline; justify-content:space-between; gap:12px;">
<h3 style="margin:0; font-size:19px;">广告位与叙事权的百年拉锯</h3>
<span style="font-size:13px; color:var(--muted); white-space:nowrap;">财经</span>
</div>
<p style="margin:10px 0 0; color:var(--body); line-height:1.6;">版面越干净,越衬得出「这一段是重点」——强对比本身就是一种编辑判断。</p>
</article>
<article class="card" data-cat="wenhua">
<div style="display:flex; align-items:baseline; justify-content:space-between; gap:12px;">
<h3 style="margin:0; font-size:19px;">留白是最贵的版面</h3>
<span style="font-size:13px; color:var(--muted); white-space:nowrap;">文化</span>
</div>
<p style="margin:10px 0 0; color:var(--body); line-height:1.6;">不装饰,恰恰是最费判断力的装饰选择。</p>
</article>
</div>
<div id="empty" class="no-data" style="display:none; margin-top:22px;">
本类目暂无文章
<div class="hint">换个类目试试,或选「全部类目」</div>
</div>
</div>
<div class="core-left">“靠排版建立层级,不用装饰图片、渐变、阴影、圆角。”</div>
<div class="core-invert">下拉筛选、复制链接、加载遮罩——这三个组件的源码都在 newspaper.js 里,全部基于 class 钩子,无硬编码 id。</div>
<div class="section-rule" style="display:flex; gap:12px; flex-wrap:wrap; align-items:center;">
<button class="btn">默认按钮</button>
<button class="btn btn-solid">实心按钮</button>
<button class="btn disabled" disabled>禁用按钮</button>
<span class="badge">徽标示例</span>
</div>
<div class="toast"></div>
</div>
<script>
(function () {
var params = new URLSearchParams(location.search);
var cat = params.get('cat') || '';
var select = document.querySelector('select[name="cat"]');
if (select) select.value = cat;
var cards = document.querySelectorAll('#list [data-cat]');
var visible = 0;
cards.forEach(function (card) {
var match = !cat || card.getAttribute('data-cat') === cat;
card.style.display = match ? '' : 'none';
if (match) visible++;
});
document.getElementById('empty').style.display = visible ? 'none' : '';
var copyBtn = document.querySelector('.np-copy');
if (copyBtn) copyBtn.setAttribute('data-link', location.pathname + location.search);
})();
</script>
<script src="newspaper.js"></script>
</body>
</html>