Files
skillhub-177-tweaks/example.html
T
2026-07-13 21:37:04 +08:00

751 lines
24 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="zh-CN" data-mode="light">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Filebase · Tweaks 演示</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Source+Serif+4:opsz,wght@8..60,400;8..60,600;8..60,700&family=IBM+Plex+Mono:wght@400;500;600&family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<style>
:root {
--accent: #c96442;
--scale: 1;
--density: 1;
--motion-mult: 1;
--bg: #f6f4ef;
--paper: #ffffff;
--ink: #1a1a1c;
--muted: #6b6964;
--rule: #e2dfd7;
--serif: 'Source Serif 4', Georgia, serif;
--sans: 'Inter', -apple-system, system-ui, sans-serif;
--mono: 'IBM Plex Mono', ui-monospace, monospace;
}
[data-mode="dark"] {
--bg: #0e0d0c;
--paper: #181715;
--ink: #f4f1ea;
--muted: #8a857a;
--rule: #2a2723;
}
@media (prefers-reduced-motion: reduce) {
:root { --motion-mult: 0; }
}
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; min-height: 100%; }
body {
background: var(--bg);
color: var(--ink);
font-family: var(--sans);
font-size: calc(16px * var(--scale));
line-height: 1.55;
transition: background calc(220ms * var(--motion-mult)) ease,
color calc(220ms * var(--motion-mult)) ease;
}
/* ============ 布局 ============ */
.wrap {
max-width: 1200px;
margin: 0 auto;
padding: calc(28px * var(--density)) calc(40px * var(--density));
}
/* ============ 头部 / 导航 ============ */
.nav {
display: flex;
justify-content: space-between;
align-items: center;
padding: calc(20px * var(--density)) 0;
gap: calc(32px * var(--density));
border-bottom: 1px solid var(--rule);
}
.brand {
display: flex;
align-items: center;
gap: 10px;
font-family: var(--serif);
font-weight: 700;
font-size: calc(20px * var(--scale));
letter-spacing: -0.01em;
}
.brand-mark {
width: 28px; height: 28px;
border-radius: 6px;
background: var(--accent);
transition: background calc(220ms * var(--motion-mult)) ease;
}
.nav-links {
display: flex;
gap: calc(28px * var(--density));
font-size: calc(14px * var(--scale));
color: var(--muted);
}
.nav-links a {
color: inherit;
text-decoration: none;
transition: color calc(180ms * var(--motion-mult)) ease;
}
.nav-links a:hover { color: var(--ink); }
.cta {
display: inline-block;
padding: calc(10px * var(--density)) calc(18px * var(--density));
background: var(--accent);
color: #fff;
border-radius: 6px;
font-size: calc(13px * var(--scale));
font-weight: 600;
letter-spacing: 0.02em;
text-decoration: none;
transition: background calc(220ms * var(--motion-mult)) ease,
transform calc(220ms * var(--motion-mult)) ease;
}
.cta:hover { transform: translateY(-1px); }
/* ============ 英雄区 ============ */
.hero {
padding: calc(96px * var(--density)) 0 calc(80px * var(--density));
display: grid;
grid-template-columns: 1.4fr 1fr;
gap: calc(64px * var(--density));
align-items: center;
}
@media (max-width: 880px) {
.hero { grid-template-columns: 1fr; }
}
.eyebrow {
font-family: var(--mono);
font-size: calc(11px * var(--scale));
letter-spacing: 0.22em;
text-transform: uppercase;
color: var(--accent);
margin-bottom: calc(22px * var(--density));
transition: color calc(220ms * var(--motion-mult)) ease;
}
.h1 {
font-family: var(--serif);
font-weight: 700;
font-size: calc(58px * var(--scale));
line-height: 1.04;
letter-spacing: -0.02em;
margin: 0 0 calc(22px * var(--density));
}
.h1 em {
font-style: italic;
color: var(--accent);
font-weight: 600;
transition: color calc(220ms * var(--motion-mult)) ease;
}
.lede {
font-size: calc(19px * var(--scale));
color: var(--muted);
max-width: 38ch;
margin: 0 0 calc(36px * var(--density));
line-height: 1.5;
}
.row { display: flex; gap: calc(14px * var(--density)); align-items: center; flex-wrap: wrap; }
.secondary {
font-size: calc(13px * var(--scale));
color: var(--muted);
font-family: var(--mono);
letter-spacing: 0.18em;
text-transform: uppercase;
}
/* 英雄区卡片预览 */
.hero-card {
background: var(--paper);
border: 1px solid var(--rule);
border-radius: 10px;
padding: calc(20px * var(--density));
box-shadow: 0 12px 40px rgba(0,0,0,0.06);
font-family: var(--mono);
font-size: calc(12px * var(--scale));
transition: background calc(220ms * var(--motion-mult)) ease,
border-color calc(220ms * var(--motion-mult)) ease;
}
[data-mode="dark"] .hero-card { box-shadow: 0 12px 40px rgba(0,0,0,0.4); }
.hero-card .label {
color: var(--muted);
letter-spacing: 0.2em;
text-transform: uppercase;
margin-bottom: calc(12px * var(--density));
font-size: calc(10px * var(--scale));
}
.hero-card pre {
margin: 0;
padding: calc(14px * var(--density));
background: var(--bg);
border-radius: 6px;
color: var(--ink);
font-family: var(--mono);
font-size: calc(12px * var(--scale));
line-height: 1.55;
overflow-x: auto;
}
.hero-card .k { color: var(--accent); }
.hero-card .c { color: var(--muted); }
.hero-card .ok { color: #4a7a3f; }
[data-mode="dark"] .hero-card .ok { color: #8db876; }
/* ============ 统计数据条 ============ */
.stats {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: calc(32px * var(--density));
padding: calc(48px * var(--density)) 0;
border-top: 1px solid var(--rule);
border-bottom: 1px solid var(--rule);
}
@media (max-width: 720px) {
.stats { grid-template-columns: repeat(2, 1fr); }
}
.stat {
display: flex;
flex-direction: column;
gap: calc(6px * var(--density));
}
.stat .num {
font-family: var(--serif);
font-weight: 700;
font-size: calc(40px * var(--scale));
line-height: 1;
letter-spacing: -0.02em;
color: var(--accent);
transition: color calc(220ms * var(--motion-mult)) ease;
}
.stat .lbl {
font-size: calc(13px * var(--scale));
color: var(--muted);
max-width: 22ch;
line-height: 1.45;
}
/* ============ 功能网格 ============ */
.features {
padding: calc(80px * var(--density)) 0 calc(40px * var(--density));
}
.section-title {
font-family: var(--serif);
font-weight: 600;
font-size: calc(34px * var(--scale));
letter-spacing: -0.015em;
margin: 0 0 calc(48px * var(--density));
max-width: 22ch;
}
.feat-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: calc(28px * var(--density));
}
@media (max-width: 880px) {
.feat-grid { grid-template-columns: 1fr; }
}
.feat {
padding: calc(28px * var(--density));
background: var(--paper);
border: 1px solid var(--rule);
border-radius: 10px;
transition: background calc(220ms * var(--motion-mult)) ease,
border-color calc(220ms * var(--motion-mult)) ease,
transform calc(220ms * var(--motion-mult)) ease;
}
.feat:hover { transform: translateY(-2px); }
.feat .ico {
width: 36px; height: 36px;
border-radius: 8px;
background: color-mix(in oklch, var(--accent) 18%, transparent);
color: var(--accent);
display: grid;
place-items: center;
font-family: var(--serif);
font-weight: 700;
font-size: calc(15px * var(--scale));
margin-bottom: calc(20px * var(--density));
}
.feat h3 {
font-family: var(--serif);
font-weight: 600;
font-size: calc(20px * var(--scale));
margin: 0 0 calc(8px * var(--density));
letter-spacing: -0.01em;
}
.feat p {
color: var(--muted);
font-size: calc(15px * var(--scale));
line-height: 1.55;
margin: 0;
}
/* ============ CTA 横幅 ============ */
.banner {
margin: calc(60px * var(--density)) 0 calc(40px * var(--density));
padding: calc(56px * var(--density)) calc(48px * var(--density));
background: var(--ink);
color: var(--bg);
border-radius: 14px;
display: grid;
grid-template-columns: 1fr auto;
gap: calc(40px * var(--density));
align-items: center;
transition: background calc(220ms * var(--motion-mult)) ease,
color calc(220ms * var(--motion-mult)) ease;
}
@media (max-width: 720px) {
.banner { grid-template-columns: 1fr; }
}
.banner h2 {
font-family: var(--serif);
font-weight: 600;
font-size: calc(32px * var(--scale));
letter-spacing: -0.015em;
line-height: 1.15;
margin: 0 0 calc(8px * var(--density));
max-width: 22ch;
}
.banner p {
color: rgba(244,241,234,0.68);
font-size: calc(15px * var(--scale));
margin: 0;
}
[data-mode="dark"] .banner p { color: rgba(26,26,28,0.68); }
.ft {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
gap: 16px;
padding: calc(28px * var(--density)) 0;
border-top: 1px solid var(--rule);
font-family: var(--mono);
font-size: calc(11px * var(--scale));
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--muted);
}
/* =========================================================
面板 · 与 assets/wrap.html 使用相同的基本元素
========================================================= */
.tw-panel {
position: fixed;
top: 16px;
right: 16px;
z-index: 100;
width: 280px;
max-width: calc(100vw - 32px);
background: var(--paper);
border: 1px solid var(--rule);
border-radius: 8px;
box-shadow: 0 8px 32px rgba(0,0,0,0.08);
font-family: var(--sans);
transition: transform calc(220ms * var(--motion-mult)) cubic-bezier(.2,.8,.2,1),
opacity calc(220ms * var(--motion-mult)) ease,
background calc(220ms * var(--motion-mult)) ease;
}
[data-mode="dark"] .tw-panel { box-shadow: 0 8px 32px rgba(0,0,0,0.4); }
.tw-panel.tw-hidden {
transform: translateX(calc(100% + 32px));
opacity: 0;
pointer-events: none;
}
.tw-head {
display: flex;
justify-content: space-between;
align-items: center;
padding: 14px 18px;
border-bottom: 1px solid var(--rule);
}
.tw-head .ttl {
font-family: var(--mono);
font-size: 10px;
letter-spacing: 0.24em;
text-transform: uppercase;
color: var(--muted);
}
.tw-head .toggle {
background: transparent;
border: 1px solid var(--rule);
color: var(--muted);
width: 24px; height: 24px;
border-radius: 4px;
cursor: pointer;
font-family: var(--mono);
font-size: 11px;
padding: 0;
}
.tw-head .toggle:hover { color: var(--ink); }
.tw-body { padding: 14px 18px 18px; }
.tw-row { display: flex; flex-direction: column; gap: 6px; margin-bottom: 14px; }
.tw-row:last-child { margin-bottom: 0; }
.tw-row .lbl {
font-family: var(--mono);
font-size: 10px;
letter-spacing: 0.22em;
text-transform: uppercase;
color: var(--muted);
}
.tw-seg {
display: flex;
border: 1px solid var(--rule);
border-radius: 5px;
overflow: hidden;
background: var(--bg);
}
.tw-seg button {
flex: 1;
padding: 7px 8px;
background: transparent;
border: 0;
border-left: 1px solid var(--rule);
cursor: pointer;
font-family: var(--sans);
font-size: 12px;
font-weight: 500;
color: var(--muted);
transition: color calc(180ms * var(--motion-mult)) ease,
background calc(180ms * var(--motion-mult)) ease;
}
.tw-seg button:first-child { border-left: 0; }
.tw-seg button:hover { color: var(--ink); }
.tw-seg button[aria-pressed='true'] {
background: var(--paper);
color: var(--ink);
box-shadow: inset 0 -2px 0 var(--accent);
}
.tw-swatches { display: grid; grid-template-columns: repeat(5, 1fr); gap: 6px; }
.tw-swatch {
width: 100%; aspect-ratio: 1;
border: 2px solid transparent;
border-radius: 5px;
cursor: pointer;
padding: 0;
transition: transform calc(160ms * var(--motion-mult)) ease,
border-color calc(160ms * var(--motion-mult)) ease;
}
.tw-swatch:hover { transform: scale(1.06); }
.tw-swatch[aria-pressed='true'] { border-color: var(--ink); }
.tw-foot {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 18px;
border-top: 1px solid var(--rule);
font-family: var(--mono);
font-size: 10px;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--muted);
}
.tw-foot button {
background: transparent;
border: 0;
color: var(--muted);
cursor: pointer;
padding: 0;
font: inherit;
letter-spacing: inherit;
text-transform: inherit;
}
.tw-foot button:hover { color: var(--ink); }
kbd {
font-family: var(--mono);
font-size: 9px;
padding: 2px 5px;
border: 1px solid var(--rule);
border-radius: 3px;
color: var(--ink);
}
.tw-restore {
position: fixed;
top: 16px; right: 16px;
z-index: 100;
width: 36px; height: 36px;
border-radius: 50%;
border: 1px solid var(--rule);
background: var(--paper);
color: var(--ink);
font-family: var(--mono);
font-size: 13px;
font-weight: 500;
cursor: pointer;
display: none;
align-items: center;
justify-content: center;
transition: transform calc(180ms * var(--motion-mult)) ease;
}
.tw-restore:hover { transform: scale(1.06); }
.tw-restore.tw-show { display: flex; }
@media (max-width: 720px) {
.tw-panel { left: 16px; right: 16px; width: auto; }
}
</style>
</head>
<body>
<div class="wrap">
<!-- 导航 -->
<nav class="nav">
<div class="brand">
<span class="brand-mark" aria-hidden="true"></span>
<span>Filebase</span>
</div>
<div class="nav-links">
<a href="#">产品</a>
<a href="#">定价</a>
<a href="#">文档</a>
<a href="#">更新日志</a>
<a href="#">客户案例</a>
</div>
<a class="cta" href="#">免费开始</a>
</nav>
<!-- 英雄区 -->
<header class="hero">
<div>
<div class="eyebrow">B 轮 · 2026</div>
<h1 class="h1">带宽账单才是真正的 <em>漏洞</em></h1>
<p class="lede">一款只传输变更内容的同步引擎。在实际客户负载下,网络传输数据量减少 38 倍。</p>
<div class="row">
<a class="cta" href="#">免费开始</a>
<span class="secondary">无需信用卡</span>
</div>
</div>
<div class="hero-card">
<div class="label">Filebase 同步 — 典型运行</div>
<pre><span class="c">// 每小时定时任务 · feature/render-pass 分支</span>
$ filebase sync --watch
<span class="k"></span> 差异: <span class="ok">12.4 MB</span> <span class="c">(共 4.7 GB</span>
<span class="k"></span> 上传: <span class="ok">8.2 MB</span> <span class="c">(去重后)</span>
<span class="k"></span> 延迟: <span class="ok">340 ms</span> <span class="c">p99</span>
<span class="ok">✓ 耗时 2.1 秒完成</span></pre>
</div>
</header>
<!-- 统计数据 -->
<section class="stats">
<div class="stat">
<div class="num">38×</div>
<div class="lbl">相比普通同步,网络传输数据量减少</div>
</div>
<div class="stat">
<div class="num">3,184</div>
<div class="lbl">来自后期制作、设计、机器学习领域的付费团队</div>
</div>
<div class="stat">
<div class="num">99.99%</div>
<div class="lbl">过去 12 个月滚动运行时长可用率</div>
</div>
<div class="stat">
<div class="num">$2.1M</div>
<div class="lbl">Q1 用户群体报告的累计出站流量节省</div>
</div>
</section>
<!-- 功能 -->
<section class="features">
<h2 class="section-title">团队在第一个月就选择切换的三个理由。</h2>
<div class="feat-grid">
<article class="feat">
<div class="ico" aria-hidden="true"></div>
<h3>块级差异</h3>
<p>编辑一个 4 GB Final Cut 项目中的一帧,只需同步 12 MB。差异比较不在乎文件有多大——它只关心什么发生了变化。</p>
</article>
<article class="feat">
<div class="ico" aria-hidden="true"></div>
<h3>跨区域去重</h3>
<p>柏林的团队上传了一个东京团队已经推送过的检查点。我们能识别出来。不会有任何数据传输。</p>
</article>
<article class="feat">
<div class="ico" aria-hidden="true"></div>
<h3>即插即用,兼容 S3 / GCS</h3>
<p>配置一个环境变量,零应用代码改动。使用现有存储桶、现有 IAM,收获全新的带宽账单。</p>
</article>
</div>
</section>
<!-- CTA 横幅 -->
<section class="banner">
<div>
<h2>为存储付费。不再为传输买单。</h2>
<p>免费开始,无需信用卡。生产团队最快 14 天内完成部署。</p>
</div>
<a class="cta" href="#" style="background: var(--accent); color: #fff;">预约演示</a>
</section>
<footer class="ft">
<span>© 2026 Filebase, Inc.</span>
<span>隐私 · 服务条款 · 状态</span>
<span>使用 OD tweaks 技能构建</span>
</footer>
</div>
<!-- ============ 调整面板 ============ -->
<aside class="tw-panel" id="tw-panel" aria-label="调整面板">
<header class="tw-head">
<span class="ttl">调整 · Filebase</span>
<button class="toggle" id="tw-close" aria-label="隐藏面板" title="隐藏 (T)">×</button>
</header>
<div class="tw-body">
<div class="tw-row">
<span class="lbl">强调色</span>
<div class="tw-swatches" id="tw-accent" role="radiogroup" aria-label="强调色"></div>
</div>
<div class="tw-row">
<span class="lbl">模式</span>
<div class="tw-seg" id="tw-mode" role="radiogroup" aria-label="颜色模式">
<button data-val="light" aria-pressed="true">浅色</button>
<button data-val="dark" aria-pressed="false">深色</button>
</div>
</div>
<div class="tw-row">
<span class="lbl">字号缩放</span>
<div class="tw-seg" id="tw-scale" role="radiogroup" aria-label="字号缩放">
<button data-val="0.85" aria-pressed="false">紧凑</button>
<button data-val="1" aria-pressed="true">标准</button>
<button data-val="1.15" aria-pressed="false">宽松</button>
</div>
</div>
<div class="tw-row">
<span class="lbl">密度</span>
<div class="tw-seg" id="tw-density" role="radiogroup" aria-label="密度">
<button data-val="0.75" aria-pressed="false">紧凑</button>
<button data-val="1" aria-pressed="true">标准</button>
<button data-val="1.4" aria-pressed="false">宽敞</button>
</div>
</div>
<div class="tw-row">
<span class="lbl">动效</span>
<div class="tw-seg" id="tw-motion" role="radiogroup" aria-label="动效">
<button data-val="0" aria-pressed="false">关闭</button>
<button data-val="1" aria-pressed="true">柔和</button>
<button data-val="1.6" aria-pressed="false">活泼</button>
</div>
</div>
</div>
<footer class="tw-foot">
<span><kbd>T</kbd> 隐藏 · <kbd>R</kbd> 重置</span>
<button id="tw-reset" type="button">重置</button>
</footer>
</aside>
<button class="tw-restore" id="tw-restore" aria-label="显示面板" title="显示面板 (T)">T</button>
<script>
const STORAGE_KEY = 'tweaks-filebase-example';
const ACCENT_PRESETS = [
{ id: 'rust', val: '#c96442' },
{ id: 'cobalt', val: '#2c4d8e' },
{ id: 'sage', val: '#4a7a3f' },
{ id: 'plum', val: '#7a3f6a' },
{ id: 'graphite', val: '#3a3a3a' },
];
const DEFAULTS = {
accent: ACCENT_PRESETS[0].val,
mode: 'light',
scale: 1,
density: 1,
motion: matchMedia('(prefers-reduced-motion: reduce)').matches ? 0 : 1,
};
function load() {
try {
const raw = localStorage.getItem(STORAGE_KEY);
if (!raw) return { ...DEFAULTS };
return { ...DEFAULTS, ...JSON.parse(raw) };
} catch { return { ...DEFAULTS }; }
}
function save(s) {
try { localStorage.setItem(STORAGE_KEY, JSON.stringify(s)); } catch {}
}
function applyState(s) {
const r = document.documentElement;
r.style.setProperty('--accent', s.accent);
r.style.setProperty('--scale', s.scale);
r.style.setProperty('--density', s.density);
r.style.setProperty('--motion-mult', s.motion);
r.setAttribute('data-mode', s.mode);
paintAccent(s.accent);
paintSeg('tw-mode', s.mode);
paintSeg('tw-scale', String(s.scale));
paintSeg('tw-density', String(s.density));
paintSeg('tw-motion', String(s.motion));
}
function paintAccent(val) {
document.querySelectorAll('#tw-accent button').forEach((b) =>
b.setAttribute('aria-pressed', b.dataset.val === val ? 'true' : 'false'),
);
}
function paintSeg(id, val) {
document.querySelectorAll('#' + id + ' button').forEach((b) =>
b.setAttribute('aria-pressed', b.dataset.val === val ? 'true' : 'false'),
);
}
function buildAccent(state) {
const host = document.getElementById('tw-accent');
host.innerHTML = '';
for (const p of ACCENT_PRESETS) {
const b = document.createElement('button');
b.type = 'button';
b.className = 'tw-swatch';
b.dataset.val = p.val;
b.setAttribute('aria-label', p.id);
b.style.background = p.val;
b.addEventListener('click', () => {
state.accent = p.val;
save(state); applyState(state);
});
host.appendChild(b);
}
}
function bindSeg(id, key, parser) {
document.getElementById(id).addEventListener('click', (e) => {
const btn = e.target.closest('button[data-val]');
if (!btn) return;
state[key] = parser ? parser(btn.dataset.val) : btn.dataset.val;
save(state); applyState(state);
});
}
const state = load();
buildAccent(state);
bindSeg('tw-mode', 'mode');
bindSeg('tw-scale', 'scale', parseFloat);
bindSeg('tw-density', 'density', parseFloat);
bindSeg('tw-motion', 'motion', parseFloat);
applyState(state);
const panel = document.getElementById('tw-panel');
const restore = document.getElementById('tw-restore');
function setPanelVisible(v) {
panel.classList.toggle('tw-hidden', !v);
restore.classList.toggle('tw-show', !v);
}
document.getElementById('tw-close').addEventListener('click', () => setPanelVisible(false));
restore.addEventListener('click', () => setPanelVisible(true));
document.getElementById('tw-reset').addEventListener('click', () => {
Object.assign(state, DEFAULTS);
save(state); applyState(state);
});
addEventListener('keydown', (e) => {
if (e.metaKey || e.ctrlKey || e.altKey) return;
if (e.target.matches('input, textarea, select, [contenteditable]')) return;
if (e.key === 't' || e.key === 'T') {
e.preventDefault();
setPanelVisible(panel.classList.contains('tw-hidden'));
} else if (e.key === 'r' || e.key === 'R') {
e.preventDefault();
Object.assign(state, DEFAULTS);
save(state); applyState(state);
}
});
</script>
</body>
</html>