chore: import zh skill waitlist-page

This commit is contained in:
wehub-skill-sync
2026-07-13 21:35:40 +08:00
commit 574cb6e45c
5 changed files with 1140 additions and 0 deletions
+9
View File
@@ -0,0 +1,9 @@
# WeHub 来源说明
- Skill 名称:`waitlist-page`
- 中文类目:转化页(定价 / waitlist)
- 上游仓库:`nexu-io__open-design`
- 上游路径:`design-templates/waitlist-page/SKILL.md`
- 上游链接:https://github.com/nexu-io/open-design/blob/HEAD/design-templates/waitlist-page/SKILL.md
- 本仓库为 WeHub 中文 Skill 汉化包,基于 skill 市场筛选 Top200 清单整理
- 原作者、版权和许可证信息以上游仓库为准
+228
View File
@@ -0,0 +1,228 @@
---
name: waitlist-page
description: |
简洁的预发布着陆页,支持邮件捕获、品牌标志和可选的装饰图层。
读取 DESIGN.md 以获取颜色、字体和布局规则。
适用于:产品发布、Beta 版注册、早期访问计划、独立项目。
triggers:
- "waitlist page"
- "coming soon page"
- "pre-launch landing page"
- "email capture page"
- "launch page"
od:
mode: prototype
platform: desktop
scenario: marketing
preview:
type: html
entry: example.html
design_system:
requires: true
sections: [color, typography, layout, components]
inputs:
- name: product_name
type: string
required: true
- name: tagline
type: string
required: true
- name: bg_expression
type: string
description: "背景的 CSS 颜色表达式(例如 '#FDE8DF' 或 'rgba(...)')。"
required: true
- name: fg_expression
type: string
description: "文字和主要形状的 CSS 颜色表达式(例如 '#1A1410' 或 'rgba(...)')。"
required: true
- name: accent_expression
type: string
description: "品牌徽章和高亮的 CSS 颜色表达式。"
required: true
- name: deco_expression
type: string
description: "主要装饰元素的 CSS 颜色表达式。"
required: true
- name: stripe_expression
type: string
description: "强调条纹的 CSS 颜色表达式。"
required: true
- name: border_expression
type: string
description: "输入框边框的 CSS 颜色表达式(例如 'rgba(100,50,30,0.38)' 或 'color-mix(in srgb, var(--fg) 38%, transparent)')。必须是有效的 CSS。"
required: true
- name: success_expression
type: string
description: "成功消息的 CSS 颜色表达式。如果 DESIGN.md 中未定义,默认为 #2D6A4F。"
required: true
- name: btn_label_expression
type: string
description: "按钮标签文字的 CSS 颜色表达式(例如 'rgba(255,255,255,1)' 或 '#fff')。确保满足 WCAG AA 对比度要求。"
required: true
- name: ticker_bg_expression
type: string
description: "滚动条背景的 CSS 颜色表达式(例如 'rgba(0,0,0,0.9)')。必须是有效的 CSS。"
required: true
- name: ticker_fg_expression
type: string
description: "滚动条文字的 CSS 颜色表达式(例如 'rgba(255,255,255,0.9)')。确保满足对比度要求。"
required: true
- name: deco_stroke_expression
type: string
description: "SVG 描边的 CSS 颜色表达式(例如 'rgba(0,0,0,0.12)')。通常为柔化后的前景色或中性色。"
required: true
- name: logo_shadow_expression
type: string
description: "Logo 容器阴影的 CSS 颜色表达式(例如 'rgba(0,0,0,0.08)')。通常为微妙的前景色调。"
required: true
- name: logo_fg_expression
type: string
description: "Logo 文字/缩写的 CSS 颜色表达式(例如 'rgba(255,255,255,1)' 或 '#fff')。确保与强调色之间满足对比度要求。"
required: true
- name: logo_mark
type: string
description: "文字缩写在默认情况下会进行 HTML 转义。仅在经过严格的允许列表过滤(移除脚本、事件处理器、foreignObject 和外部引用)后,才允许内联 SVG;不安全的输入会回退为转义后的文本。"
required: false
- name: display_font_url
type: string
description: "展示字体的名称,空格编码为 '+'(例如 'Syne'、'DM+Sans')。用于 Google Fonts URL。"
required: true
- name: display_font_css
type: string
description: "展示字体在 CSS 中显示的名称(例如 'Syne'、'DM Sans')。如需引号已在值中自带;模板中不额外添加引号。"
required: true
- name: body_font_url
type: string
description: "正文字体的名称,空格编码为 '+'(例如 'DM+Sans'、'IBM+Plex+Serif')。用于 Google Fonts URL。"
required: true
- name: body_font_css
type: string
description: "正文字体在 CSS 中显示的名称(例如 'DM Sans'、'IBM Plex Serif')。如需引号已在值中自带;模板中不额外添加引号。"
required: true
outputs:
primary: index.html
capabilities_required:
- file_write
example_prompt: "为某个设计工具制作一个等待名单页面——简洁、极简,带自定义 Logo 和一个行动号召按钮。"
---
# 等待名单页面技能
预发布页面是你与未来用户的首次握手。该技能打造一个聚焦、真诚的入口:你的品牌标识、你的产品介绍、一条加入早期名单的清晰路径。没有人为制造的稀缺感,没有虚假倒计时,没有诱导式营销——只是一个干净、移动端优先的容器,承载真实的兴趣。
## 工作流程
### 前置准备:加载加固模板和品牌基础
0. **加载品牌标识**——读取 `DESIGN.md`,获取颜色系统、字体搭配和空间规则。这是你的基础。等待名单页面的成败取决于与其所代表品牌的一致性。如果 `DESIGN.md` 不存在,请要求用户提供后再继续。
1. **读取并复制可复用模板**——读取 `assets/template.html`。此模板是经过加固的种子文件,适用于所有输出。将其复制到 `index.html` 作为基础文件。不要从头编写 HTML,也不要偏离此结构。模板已包含所有必需的布局、表单结构、装饰、焦点样式和无障碍访问支架。
### 步骤:带验证和转义的 Token 替换
2. **根据输入映射 Token**——对于模板中的每个占位符(例如 `{{PRODUCT_NAME}}``{{BG_EXPRESSION}}``{{BORDER_EXPRESSION}}``{{LOGO_MARK}}`),按照以下替换规则处理:
- **文字 Token**`{{PRODUCT_NAME}}``{{TAGLINE}}`):在插入 HTML 文本节点或属性值之前,对 `<``>``&``"``'` 进行 HTML 转义。
- **HTML Token**`{{LOGO_MARK}}`):如果使用文字缩写,默认进行 HTML 转义。如果使用内联 SVG,必须使用允许列表严格进行过滤:移除 `<script>` 标签、事件处理器(`on*`)、`<foreignObject>`、外部引用(`href``xlink:href``url()`)以及任何不允许的属性/元素后再插入。如果 SVG 无法安全过滤,回退为转义后的文字缩写。切勿直接输出未经过滤的任意 HTML。确保 SVG 能在其容器内干净缩放。
- **颜色表达式 Token**`{{BG_EXPRESSION}}``{{FG_EXPRESSION}}``{{ACCENT_EXPRESSION}}``{{DECO_EXPRESSION}}``{{STRIPE_EXPRESSION}}``{{SUCCESS_EXPRESSION}}``{{BORDER_EXPRESSION}}``{{BTN_LABEL_EXPRESSION}}``{{TICKER_BG_EXPRESSION}}``{{TICKER_FG_EXPRESSION}}``{{DECO_STROKE_EXPRESSION}}``{{LOGO_SHADOW_EXPRESSION}}``{{LOGO_FG_EXPRESSION}}`):必须严格遵循明确的颜色语法(`#hex``rgb`/`rgba``hsl`/`hsla``oklch` 或仅使用局部变量的 `color-mix()`)。坚决拒绝任何包含 `;``{}``<``>`、注释(`/*`)、`@``url(` 或外部引用的输入,以防止 CSS 注入。不要包裹 `#` 或添加额外引号。示例:`rgba(196, 169, 154, 0.38)``color-mix(in srgb, var(--fg) 38%, transparent)``#FDE8DF`。按原样插入 `:root` CSS 变量。如果 DESIGN.md 中有 `--success` 则从中派生;否则仅使用允许的回退值 `#2D6A4F`
- **字体名称 Token**`{{DISPLAY_FONT_CSS}}``{{BODY_FONT_CSS}}`):这些是 CSS font-family 的值,如果包含空格则已自带引号(例如 `'DM Sans'``Syne`)。按原样插入 `--font-display``--font-body` 声明中;不要额外添加引号。
- **字体 URL Token**`{{DISPLAY_FONT_URL}}``{{BODY_FONT_URL}}`):空格必须编码为 `+`,用于 Google Fonts URL(例如 `DM+Sans``IBM+Plex+Serif`)。在插入前验证 URL 格式是否正确。
3. **验证 Token 映射规则**——所有颜色 Token 现在已放入 CSS 变量:
- `--bg` = `{{BG_EXPRESSION}}`(例如 `#FDE8DF`
- `--fg` = `{{FG_EXPRESSION}}`(例如 `#1A1410`
- `--accent` = `{{ACCENT_EXPRESSION}}`(品牌徽章)
- `--deco` = `{{DECO_EXPRESSION}}`(主要装饰)
- `--deco-stripe` = `{{STRIPE_EXPRESSION}}`(强调条纹)
- `--input-border` = `{{BORDER_EXPRESSION}}`(含透明度的完整 CSS 表达式)
- `--success` = `{{SUCCESS_EXPRESSION}}` 或回退值 `#2D6A4F`
- `--btn-label` = `{{BTN_LABEL_EXPRESSION}}`(按钮文字颜色,用于确保对比度)
- `--ticker-bg` = `{{TICKER_BG_EXPRESSION}}`(动画滚动条背景)
- `--ticker-fg` = `{{TICKER_FG_EXPRESSION}}`(动画滚动条文字)
- `--deco-stroke` = `{{DECO_STROKE_EXPRESSION}}`(SVG 描边,通常为 12–15% 透明度的柔化色)
- `--logo-shadow` = `{{LOGO_SHADOW_EXPRESSION}}`(Logo 容器阴影,微妙的前景色调)
- `--logo-fg` = `{{LOGO_FG_EXPRESSION}}`Logo 缩写的对比色文字)
4. **响应式布局**——模板包含移动端优先的缩放:
- 375px:表单堆叠为单列,Logo 缩小至 40px,装饰元素压缩,无水平滚动。
- 768px:舒适的两栏呼吸空间。
- 1440px+:居中布局,留白充裕。
- 验证所有文字在 375×667 和 390×844 下仍然可读,且邮箱输入框和提交按钮完全可见(无裁剪)。
### 验证:输出前运行加固质量门禁
5. **运行质量检查清单**——读取 `references/checklist.md`。在输出前验证每一项 **P0 门禁**
- 仅有一个行动号召(邮件表单)
- 无倒计时、无虚假社交证明、无 emoji
- 375px 下无水平滚动
- 邮箱输入框有 `type="email"``required`;名字没有 `required`
- 表单未使用 `novalidate`JS 使用 `checkValidity()` 防护
- 成功消息有 `role="status"``aria-live="polite"`
- 所有颜色来自 DESIGN.md 或允许的回退值;**不使用任何自创的 hex 值**
- 所有用户文本已进行 HTML 转义;颜色 Token 遵循严格语法;字体已进行 URL 编码
- **必须通过 P0,否则不输出。** 如果任何 P0 门禁失败,向用户询问或修复 Token 映射后重试。
6. **验证 P1 门禁**,以确保质量提交(推荐):
- 提交按钮有悬停和激活状态
- 表单验证带有清晰的反馈
- 滚动条动画遵循 `prefers-reduced-motion: reduce`
- 所有交互元素有可见的焦点样式(input:focus 包含 `outline`
- Tab/Enter 键盘支持
- 正文文字的 WCAG AA 对比度(≥4.5:1)和按钮标签(≥4.5:1)
- Logo 的 alt/aria-label 存在;滚动条有 `aria-hidden="true"`
- 已设置 `<html lang="">`
### 输出
7. **输出干净的 HTML**——单文件,CSS 内联,图形使用 SVG。用 `data-od-id` 标记交互元素(标题、表单、Logo、滚动条、网格等),以便代理无需解析即可进行自定义。
## 质量门禁
该技能采用加固的、基于模板的工作流程,确保合规。**你必须遵循此执行路径;偏离模板将使输出不合格。**
**强制性的基于模板的执行:**
-`assets/template.html` 为基础;不要从头编写 HTML。
- 将其复制到 `index.html`,仅将 `{{PLACEHOLDER}}` Token 替换为经过验证/转义的值。
- 在输出前运行 `references/checklist.md` 中的每一项 P0 门禁;如果有任何失败,修复后重新验证。
**P0 门禁(必须通过):**
- 单一行动号召:邮件表单是唯一的交互元素。无导航、无辅助按钮、无社交链接。
- Logo 位置:固定在左上角,与 DESIGN.md 的强调色一致,移动端缩放(50px → 40px)。
- 颜色一致性:每种颜色均来自 DESIGN.md 调色板。唯一允许硬编码的例外:`--success``#2D6A4F`
- 无反模式:无倒计时、无虚假社交证明、无 emoji 图标、无 lorem ipsum。
- 内容完整性:标题和文案直接关联到 `product_name``tagline` 输入——无填充文案。
- 移动端适配:375px 下无水平滚动。邮箱输入框和提交按钮在 375×667 和 390×844 下完全可见(无裁剪)。垂直溢出可滚动,不隐藏。
- 排版纪律:仅使用展示字体 + 正文字体(两种字体规则)。各章节字号一致。
- 表单结构:两个字段(名字可选,邮箱必填),`checkValidity()` 防护,成功消息带 `role="status"`
- Token 转义:所有用户提供的文本已进行 HTML 转义;颜色 Token 遵循严格语法且不含不安全字符(`;``{}``<``>``/*``@``url(`);字体名称已进行 URL 编码;`{{LOGO_MARK}}` 为转义后的文字缩写或经过严格过滤的内联 SVG(无脚本、无事件处理器、无 foreignObject、无外部 href)。
**P1 门禁(质量提交建议通过):**
- 首屏区域视觉上独立且位于折叠线以上。
- 邮箱提交按钮有悬停和激活状态。
- 表单验证提供清晰的反馈。
- 页面在所有移动端视口下可滚动(不被裁剪);行动号召无需滚动即可见。
- 滚动条动画在 `prefers-reduced-motion: reduce` 下暂停或移除。
- 所有交互元素有可见的焦点样式(outline,而非仅 outline:none)。
- 键盘:Tab 键可到达每个表单字段;Enter 键可提交。
- WCAG AA 对比度:正文文字 ≥4.5:1,按钮标签 ≥4.5:1。
- Logo 的 alt/aria-label 存在;滚动条有 `aria-hidden="true"`
- `<html lang="">` 设置为正确的语言代码。
**装饰克制原则:**
- 下半区增强体验但不分散注意力。使用透明度、柔和描边、弱化动画。
- 渐变高度不超过视口高度的 20%。
- 螺旋线、条纹、网格或滚动条均仅使用 DESIGN.md 中的颜色。
## 输出
**仅在所有 P0 门禁在 `references/checklist.md` 中通过后方可输出。**
在标签之间输出工件:
```
<artifact identifier="waitlist-id" type="text/html" title="即将上线 — {{PRODUCT_NAME}}">
<!doctype html>
<html lang="en">
...
</html>
</artifact>
```
工件上方一行描述文字,下方不输出任何内容。
**输出后:** 如果用户要求更改,就地更新 index.html,并在输出下一版本前重新运行 P0 检查清单。迭代时不得跳过验证。
+457
View File
@@ -0,0 +1,457 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{PRODUCT_NAME}} — 即将推出</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={{DISPLAY_FONT_URL}}:wght@700;800&family={{BODY_FONT_URL}}:wght@400;500;600&display=swap" rel="stylesheet">
<style>
:root {
--bg: {{BG_EXPRESSION}};
--fg: {{FG_EXPRESSION}};
--accent: {{ACCENT_EXPRESSION}};
--deco: {{DECO_EXPRESSION}};
--deco-stripe: {{STRIPE_EXPRESSION}};
--input-border: {{BORDER_EXPRESSION}};
--success: {{SUCCESS_EXPRESSION}};
--btn-label: {{BTN_LABEL_EXPRESSION}};
--ticker-bg: {{TICKER_BG_EXPRESSION}};
--ticker-fg: {{TICKER_FG_EXPRESSION}};
--deco-stroke: {{DECO_STROKE_EXPRESSION}};
--logo-shadow: {{LOGO_SHADOW_EXPRESSION}};
--logo-fg: {{LOGO_FG_EXPRESSION}};
--font-body: {{BODY_FONT_CSS}}, sans-serif;
--font-display: {{DISPLAY_FONT_CSS}}, serif;
}
*, *::before, *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html, body {
height: 100%;
overflow-x: hidden;
}
body {
font-family: var(--font-body);
background-color: var(--bg);
color: var(--fg);
display: flex;
flex-direction: column;
min-height: 100svh;
overflow-x: hidden;
overflow-y: auto;
}
/* Logo — reusable component */
.logo {
position: absolute;
top: 20px;
left: 24px;
display: flex;
align-items: center;
gap: 12px;
z-index: 10;
}
.logo-container {
width: 48px;
height: 48px;
flex-shrink: 0;
border-radius: 50%;
background: var(--accent);
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
color: var(--logo-fg);
font-family: var(--font-display);
font-size: 20px;
font-weight: 700;
letter-spacing: -0.02em;
}
.logo-container svg {
width: 100%;
height: 100%;
object-fit: contain;
}
.logo-text {
display: flex;
align-items: center;
gap: 8px;
flex-shrink: 0;
}
.logo-name {
font-family: var(--font-display);
font-weight: 700;
font-size: 16px;
color: var(--fg);
}
.logo-dot {
font-size: 8px;
color: var(--accent);
opacity: 0.6;
}
/* Upper section — hero */
.upper {
flex: 0 0 auto;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
padding: 60px 24px 40px;
position: relative;
z-index: 2;
}
.headline {
font-family: var(--font-display);
font-weight: 800;
font-size: clamp(48px, 7vw, 80px);
letter-spacing: -0.02em;
line-height: 1.1;
color: var(--fg);
max-width: 900px;
margin: 0 auto 18px;
}
.subtext {
font-size: clamp(14px, 1.3vw, 16px);
font-weight: 400;
line-height: 1.5;
color: var(--fg);
opacity: 0.75;
max-width: 480px;
margin-bottom: 40px;
}
/* Form */
.form-row {
display: flex;
gap: 8px;
width: 100%;
max-width: 560px;
justify-content: center;
}
.form-row input {
flex: 1;
min-width: 0;
padding: 14px 16px;
font-family: var(--font-body);
font-size: 14px;
color: var(--fg);
background: transparent;
border: 1.5px solid var(--input-border);
border-radius: 3px;
outline: none;
transition: border-color 0.2s;
}
.form-row input::placeholder {
color: var(--input-border);
opacity: 0.7;
}
.form-row input:focus {
border-color: var(--fg);
outline: 2px solid var(--fg);
outline-offset: 2px;
}
.form-row button {
flex-shrink: 0;
padding: 14px 24px;
font-family: var(--font-body);
font-size: 14px;
font-weight: 600;
color: var(--btn-label);
background: var(--fg);
border: none;
border-radius: 2px;
cursor: pointer;
white-space: nowrap;
transition: opacity 0.15s;
}
.form-row button:hover {
opacity: 0.85;
}
.success-msg {
display: none;
margin-top: 16px;
font-size: 14px;
font-weight: 500;
color: var(--success);
}
.success-msg.visible {
display: block;
}
/* Decoration section */
.deco-section {
flex: 1;
position: relative;
overflow: hidden;
min-height: 25vh;
display: flex;
flex-direction: column;
}
/* Coil pattern */
.coil-row {
display: block;
width: 100%;
height: 60px;
flex-shrink: 0;
background: var(--bg);
}
/* Accent stripe */
.stripe {
width: 100%;
height: 12px;
background: var(--deco-stripe);
flex-shrink: 0;
}
/* Floor with grid */
.grid-floor {
flex: 1;
position: relative;
background-color: var(--deco);
min-height: 80px;
}
.grid-svg {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
}
/* Ticker ribbon */
.ticker-wrap {
width: 100%;
overflow: hidden;
background: var(--ticker-bg);
padding: 10px 0;
flex-shrink: 0;
}
.ticker-track {
display: flex;
gap: 24px;
white-space: nowrap;
animation: ticker-scroll 20s linear infinite;
}
.ticker-unit {
font-family: var(--font-body);
font-size: 11px;
font-weight: 600;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--ticker-fg);
flex-shrink: 0;
display: flex;
align-items: center;
gap: 12px;
margin-right: -12px;
}
.ticker-star {
color: var(--deco-stripe);
margin-right: 4px;
}
@keyframes ticker-scroll {
from { transform: translateX(0); }
to { transform: translateX(-50%); }
}
@media (prefers-reduced-motion: reduce) {
.ticker-track {
animation: none;
}
}
/* Responsive */
@media (max-width: 600px) {
.logo {
left: 16px;
top: 16px;
}
.logo-container {
width: 40px;
height: 40px;
}
.logo-name {
font-size: 13px;
}
.upper {
padding: 50px 20px 30px;
}
.headline {
margin: 0 auto 12px;
}
.subtext {
margin-bottom: 30px;
}
.form-row {
flex-direction: column;
align-items: stretch;
}
.form-row input,
.form-row button {
width: 100%;
}
}
</style>
</head>
<body>
<!-- Logo placeholder — replace or customize -->
<div class="logo">
<div class="logo-container" data-od-id="logo-mark">
{{LOGO_MARK}}
</div>
<div class="logo-text">
<span class="logo-name" data-od-id="logo-name">{{PRODUCT_NAME}}</span>
<span class="logo-dot">·</span>
</div>
</div>
<!-- Hero section -->
<section class="upper">
<h1 class="headline" data-od-id="headline">{{PRODUCT_NAME}}<br>即将上线</h1>
<p class="subtext" data-od-id="tagline">{{TAGLINE}}</p>
<form class="form-row" id="waitlist-form" data-od-id="form">
<input type="text" name="firstname" placeholder="名字">
<input type="email" name="email" placeholder="工作邮箱" required>
<button type="submit">加入候补名单</button>
</form>
<p class="success-msg" id="success-msg" role="status">您已加入名单,我们会与您联系。</p>
</section>
<!-- Decorative section -->
<div class="deco-section">
<!-- Coil pattern (optional) -->
<svg class="coil-row" viewBox="0 0 1440 60" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg" data-od-id="coil">
<g fill="none" style="stroke: var(--deco-stroke);" stroke-width="1.5">
<path d="M 9,60 A 41,30 0 0 1 91,60"/>
<path d="M 94,60 A 41,30 0 0 1 176,60"/>
<path d="M 179,60 A 41,30 0 0 1 261,60"/>
<path d="M 264,60 A 41,30 0 0 1 346,60"/>
<path d="M 349,60 A 41,30 0 0 1 431,60"/>
<path d="M 434,60 A 41,30 0 0 1 516,60"/>
<path d="M 519,60 A 41,30 0 0 1 601,60"/>
<path d="M 604,60 A 41,30 0 0 1 686,60"/>
<path d="M 689,60 A 41,30 0 0 1 771,60"/>
<path d="M 774,60 A 41,30 0 0 1 856,60"/>
<path d="M 859,60 A 41,30 0 0 1 941,60"/>
<path d="M 944,60 A 41,30 0 0 1 1026,60"/>
<path d="M 1029,60 A 41,30 0 0 1 1111,60"/>
<path d="M 1114,60 A 41,30 0 0 1 1196,60"/>
<path d="M 1199,60 A 41,30 0 0 1 1281,60"/>
<path d="M 1284,60 A 41,30 0 0 1 1366,60"/>
<path d="M 1369,60 A 41,30 0 0 1 1451,60"/>
</g>
</svg>
<!-- Accent stripe -->
<div class="stripe" data-od-id="stripe"></div>
<!-- Floor with perspective grid -->
<div class="grid-floor">
<svg class="grid-svg" viewBox="0 0 1280 400" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg" data-od-id="grid">
<g style="stroke: var(--deco-stroke);" stroke-width="1" fill="none">
<!-- Radiating lines from center top -->
<line x1="640" y1="0" x2="0" y2="400"/>
<line x1="640" y1="0" x2="128" y2="400"/>
<line x1="640" y1="0" x2="256" y2="400"/>
<line x1="640" y1="0" x2="384" y2="400"/>
<line x1="640" y1="0" x2="512" y2="400"/>
<line x1="640" y1="0" x2="640" y2="400"/>
<line x1="640" y1="0" x2="768" y2="400"/>
<line x1="640" y1="0" x2="896" y2="400"/>
<line x1="640" y1="0" x2="1024" y2="400"/>
<line x1="640" y1="0" x2="1152" y2="400"/>
<line x1="640" y1="0" x2="1280" y2="400"/>
<!-- Horizontal grid lines -->
<line x1="0" y1="80" x2="1280" y2="80"/>
<line x1="0" y1="160" x2="1280" y2="160"/>
<line x1="0" y1="240" x2="1280" y2="240"/>
<line x1="0" y1="320" x2="1280" y2="320"/>
</g>
</svg>
</div>
</div>
<!-- Ticker ribbon -->
<div class="ticker-wrap" aria-hidden="true" data-od-id="ticker">
<div class="ticker-track">
<span class="ticker-unit">即将推出 <span class="ticker-star"></span></span>
<span class="ticker-unit">即将推出 <span class="ticker-star"></span></span>
<span class="ticker-unit">即将推出 <span class="ticker-star"></span></span>
<span class="ticker-unit">即将推出 <span class="ticker-star"></span></span>
<span class="ticker-unit">即将推出 <span class="ticker-star"></span></span>
<span class="ticker-unit">即将推出 <span class="ticker-star"></span></span>
<span class="ticker-unit">即将推出 <span class="ticker-star"></span></span>
<span class="ticker-unit">即将推出 <span class="ticker-star"></span></span>
<span class="ticker-unit">即将推出 <span class="ticker-star"></span></span>
<span class="ticker-unit">即将推出 <span class="ticker-star"></span></span>
<span class="ticker-unit">即将推出 <span class="ticker-star"></span></span>
<span class="ticker-unit">即将推出 <span class="ticker-star"></span></span>
<!-- Duplicate for seamless loop -->
<span class="ticker-unit">即将推出 <span class="ticker-star"></span></span>
<span class="ticker-unit">即将推出 <span class="ticker-star"></span></span>
<span class="ticker-unit">即将推出 <span class="ticker-star"></span></span>
<span class="ticker-unit">即将推出 <span class="ticker-star"></span></span>
<span class="ticker-unit">即将推出 <span class="ticker-star"></span></span>
<span class="ticker-unit">即将推出 <span class="ticker-star"></span></span>
<span class="ticker-unit">即将推出 <span class="ticker-star"></span></span>
<span class="ticker-unit">即将推出 <span class="ticker-star"></span></span>
<span class="ticker-unit">即将推出 <span class="ticker-star"></span></span>
<span class="ticker-unit">即将推出 <span class="ticker-star"></span></span>
<span class="ticker-unit">即将推出 <span class="ticker-star"></span></span>
<span class="ticker-unit">即将推出 <span class="ticker-star"></span></span>
</div>
</div>
<script>
document.getElementById('waitlist-form').addEventListener('submit', function(e) {
e.preventDefault();
if (!this.checkValidity()) {
this.reportValidity();
return;
}
this.style.display = 'none';
document.getElementById('success-msg').classList.add('visible');
});
</script>
</body>
</html>
+400
View File
@@ -0,0 +1,400 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Meridian — 即将上线</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=Syne:wght@700;800&family=DM+Sans:wght@400;500;600&display=swap" rel="stylesheet">
<style>
:root {
--bg: #FDE8DF;
--fg: #1A1410;
--color-ink: #1A1410;
--accent: #1A1410;
--deco: #E8835A;
--deco-logo: #E8522A;
--deco-stripe: #F5A623;
--input-border: rgba(196, 169, 154, 0.38);
--success: #2D6A4F;
--btn-label: #FDE8DF;
--ticker-bg: rgba(0, 0, 0, 0.9);
--ticker-fg: rgba(255, 255, 255, 0.9);
--deco-stroke: rgba(0, 0, 0, 0.12);
--logo-shadow: rgba(0, 0, 0, 0.08);
--font-body: 'DM Sans', sans-serif;
--font-display: 'Syne', sans-serif;
}
*, *::before, *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html, body {
height: 100%;
overflow-x: hidden;
}
.logo {
position: absolute;
top: 20px;
left: 24px;
display: flex;
align-items: center;
gap: 14px;
z-index: 10;
}
.logo-container {
width: 48px;
height: 48px;
flex-shrink: 0;
border-radius: 50%;
background: linear-gradient(135deg, var(--deco-logo) 0%, var(--deco) 100%);
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
box-shadow: 0 2px 8px var(--logo-shadow);
}
.logo-svg {
width: 100%;
height: 100%;
object-fit: contain;
padding: 2px;
}
.logo-text {
display: flex;
align-items: center;
gap: 10px;
flex-shrink: 0;
}
.logo-name {
font-family: var(--font-display);
font-weight: 700;
font-size: 18px;
letter-spacing: -0.02em;
color: var(--color-ink);
}
.logo-dot {
font-size: 10px;
color: var(--deco-logo);
opacity: 0.6;
}
body {
font-family: var(--font-body);
background-color: var(--bg);
color: var(--fg);
display: flex;
flex-direction: column;
min-height: 100svh;
overflow-x: hidden;
overflow-y: auto;
}
.upper {
flex: 0 0 auto;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
padding: 60px 24px 40px;
position: relative;
z-index: 2;
}
.headline {
font-family: var(--font-display);
font-weight: 800;
font-size: clamp(56px, 8vw, 96px);
letter-spacing: -0.03em;
line-height: 1.0;
color: var(--fg);
text-align: center;
max-width: 900px;
margin: 0 auto 22px;
}
.subtext {
font-size: clamp(15px, 1.5vw, 18px);
font-weight: 400;
line-height: 1.6;
color: var(--fg);
max-width: 420px;
margin-bottom: 44px;
}
.form-row {
display: flex;
gap: 8px;
width: 100%;
max-width: 560px;
justify-content: center;
}
.form-row input {
flex: 1;
min-width: 0;
padding: 14px 18px;
font-family: var(--font-body);
font-size: 15px;
color: var(--color-ink);
background: transparent;
border: 1.5px solid var(--input-border);
border-radius: 4px;
outline: none;
transition: border-color 0.2s;
}
.form-row input::placeholder {
color: var(--input-border);
}
.form-row input:focus {
border-color: var(--fg);
outline: 2px solid var(--fg);
outline-offset: 2px;
}
.form-row button {
flex-shrink: 0;
padding: 14px 22px;
font-family: var(--font-body);
font-size: 15px;
font-weight: 500;
color: var(--btn-label);
background: var(--accent);
border: none;
border-radius: 2px;
cursor: pointer;
white-space: nowrap;
transition: opacity 0.15s, transform 0.1s;
}
.form-row button:hover {
opacity: 0.85;
}
.form-row button:active {
transform: scale(0.98);
}
.success-msg {
display: none;
margin-top: 18px;
font-size: 15px;
font-weight: 500;
color: var(--success);
}
.success-msg.visible {
display: block;
}
.deco-section {
flex: 1;
position: relative;
overflow: hidden;
min-height: 25vh;
display: flex;
flex-direction: column;
}
.coil-row {
display: block;
width: 100%;
height: 60px;
flex-shrink: 0;
background: var(--bg);
}
.stripe {
width: 100%;
height: 14px;
background: var(--deco-stripe);
flex-shrink: 0;
}
.grid-floor {
flex: 1;
position: relative;
background-color: var(--deco);
min-height: 80px;
}
.grid-svg {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
}
.ticker-wrap {
width: 100%;
overflow: hidden;
background: var(--ticker-bg);
padding: 10px 0;
flex-shrink: 0;
}
.ticker-track {
display: flex;
gap: 24px;
white-space: nowrap;
animation: ticker-scroll 20s linear infinite;
}
.ticker-unit {
font-family: var(--font-body);
font-size: 11px;
font-weight: 600;
letter-spacing: 0.15em;
text-transform: uppercase;
color: var(--ticker-fg);
flex-shrink: 0;
display: flex;
align-items: center;
gap: 12px;
margin-right: -12px;
}
.ticker-star {
color: var(--deco-stripe);
margin-right: 4px;
}
@keyframes ticker-scroll {
from { transform: translateX(0); }
to { transform: translateX(-50%); }
}
@media (prefers-reduced-motion: reduce) {
.ticker-track {
animation: none;
}
}
@media (max-width: 600px) {
.logo {
left: 16px;
top: 16px;
gap: 10px;
}
.logo-container {
width: 40px;
height: 40px;
}
.logo-name {
font-size: 14px;
}
.upper {
padding: 30px 16px;
flex: 0 0 50vh;
}
.form-row {
flex-direction: column;
align-items: stretch;
}
.form-row input,
.form-row button {
width: 100%;
}
}
</style>
</head>
<body>
<div class="logo">
<div class="logo-container">
<svg class="logo-svg" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<circle cx="50" cy="50" r="48" fill="var(--deco-logo)"/>
<text x="50" y="60" font-family="'DM Sans', sans-serif" font-size="42" font-weight="700" text-anchor="middle" fill="var(--btn-label)" letter-spacing="-1">Md</text>
</svg>
</div>
<div class="logo-text">
<span class="logo-name">Meridian</span>
<span class="logo-dot">·</span>
</div>
</div>
<section class="upper">
<h1 class="headline">Meridian 即将<br>上线</h1>
<p class="subtext">在你还没想完的时候,这个工作空间就已经把你的想法串联起来了。</p>
<form class="form-row" id="waitlist-form">
<input type="text" name="firstname" placeholder="名字" autocomplete="given-name">
<input type="email" name="email" placeholder="工作邮箱" autocomplete="email" required>
<button type="submit">加入候补名单</button>
</form>
<p class="success-msg" id="success-msg" role="status">你已在名单上!我们会与你联系。</p>
</section>
<div class="deco-section">
<svg class="coil-row" viewBox="0 0 1440 60" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
<g fill="none" style="stroke: var(--deco-stroke);" stroke-width="1.5">
<path d="M 9,60 A 41,30 0 0 1 91,60"/>
<path d="M 94,60 A 41,30 0 0 1 176,60"/>
<path d="M 179,60 A 41,30 0 0 1 261,60"/>
<path d="M 264,60 A 41,30 0 0 1 346,60"/>
<path d="M 349,60 A 41,30 0 0 1 431,60"/>
<path d="M 434,60 A 41,30 0 0 1 516,60"/>
<path d="M 519,60 A 41,30 0 0 1 601,60"/>
<path d="M 604,60 A 41,30 0 0 1 686,60"/>
<path d="M 689,60 A 41,30 0 0 1 771,60"/>
<path d="M 774,60 A 41,30 0 0 1 856,60"/>
<path d="M 859,60 A 41,30 0 0 1 941,60"/>
<path d="M 944,60 A 41,30 0 0 1 1026,60"/>
<path d="M 1029,60 A 41,30 0 0 1 1111,60"/>
<path d="M 1114,60 A 41,30 0 0 1 1196,60"/>
<path d="M 1199,60 A 41,30 0 0 1 1281,60"/>
<path d="M 1284,60 A 41,30 0 0 1 1366,60"/>
<path d="M 1369,60 A 41,30 0 0 1 1451,60"/>
</g>
</svg>
<div class="stripe"></div>
<div class="grid-floor">
<svg class="grid-svg" viewBox="0 0 1280 400" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
<g style="stroke: var(--deco-stroke);" stroke-width="1" fill="none">
<line x1="640" y1="0" x2="0" y2="400"/>
<line x1="640" y1="0" x2="128" y2="400"/>
<line x1="640" y1="0" x2="256" y2="400"/>
<line x1="640" y1="0" x2="384" y2="400"/>
<line x1="640" y1="0" x2="512" y2="400"/>
<line x1="640" y1="0" x2="640" y2="400"/>
<line x1="640" y1="0" x2="768" y2="400"/>
<line x1="640" y1="0" x2="896" y2="400"/>
<line x1="640" y1="0" x2="1024" y2="400"/>
<line x1="640" y1="0" x2="1152" y2="400"/>
<line x1="640" y1="0" x2="1280" y2="400"/>
<line x1="0" y1="80" x2="1280" y2="80"/>
<line x1="0" y1="160" x2="1280" y2="160"/>
<line x1="0" y1="240" x2="1280" y2="240"/>
<line x1="0" y1="320" x2="1280" y2="320"/>
<line x1="0" y1="390" x2="1280" y2="390"/>
</g>
</svg>
</div>
</div>
<div class="ticker-wrap" aria-hidden="true">
<div class="ticker-track">
<span class="ticker-unit">即将上线 <span class="ticker-star"></span></span>
<span class="ticker-unit">即将上线 <span class="ticker-star"></span></span>
<span class="ticker-unit">即将上线 <span class="ticker-star"></span></span>
<span class="ticker-unit">即将上线 <span class="ticker-star"></span></span>
<span class="ticker-unit">即将上线 <span class="ticker-star"></span></span>
<span class="ticker-unit">即将上线 <span class="ticker-star"></span></span>
<span class="ticker-unit">即将上线 <span class="ticker-star"></span></span>
<span class="ticker-unit">即将上线 <span class="ticker-star"></span></span>
<span class="ticker-unit">即将上线 <span class="ticker-star"></span></span>
<span class="ticker-unit">即将上线 <span class="ticker-star"></span></span>
<span class="ticker-unit">即将上线 <span class="ticker-star"></span></span>
<span class="ticker-unit">即将上线 <span class="ticker-star"></span></span>
<span class="ticker-unit">即将上线 <span class="ticker-star"></span></span>
<span class="ticker-unit">即将上线 <span class="ticker-star"></span></span>
<span class="ticker-unit">即将上线 <span class="ticker-star"></span></span>
<span class="ticker-unit">即将上线 <span class="ticker-star"></span></span>
<span class="ticker-unit">即将上线 <span class="ticker-star"></span></span>
<span class="ticker-unit">即将上线 <span class="ticker-star"></span></span>
<span class="ticker-unit">即将上线 <span class="ticker-star"></span></span>
<span class="ticker-unit">即将上线 <span class="ticker-star"></span></span>
<span class="ticker-unit">即将上线 <span class="ticker-star"></span></span>
<span class="ticker-unit">即将上线 <span class="ticker-star"></span></span>
<span class="ticker-unit">即将上线 <span class="ticker-star"></span></span>
<span class="ticker-unit">即将上线 <span class="ticker-star"></span></span>
</div>
</div>
<script>
document.getElementById('waitlist-form').addEventListener('submit', function(e) {
e.preventDefault();
if (!this.checkValidity()) {
this.reportValidity();
return;
}
this.style.display = 'none';
document.getElementById('success-msg').classList.add('visible');
});
</script>
</body>
</html>
+46
View File
@@ -0,0 +1,46 @@
# 检查清单 — Waitlist 页面
## P0 — 在输出 `<artifact>` 之前必须通过
- [ ] 页面有且只有一个主要 CTA(邮箱输入框 + 提交按钮)
- [ ] 首屏渐变区高度不超过视口高度的 20%
- [ ] 邮箱输入框有可见的标签或有意义的占位符——不只是"Email"
- [ ] 没有编造的社会证明数字(例如"10,000 人正在等待"
- [ ] **不存在倒计时器**——倒计时器是本 skill 的明确反模式(见 SKILL.md §Quality gates
- [ ] 在 375px 视口宽度下无水平滚动
- [ ] 没有使用通用 emoji 图标作为装饰元素
- [ ] 排版最多使用两种字体家族(展示字体 + 正文字体)
- [ ] 名字(First name)输入框没有 `required` 属性
- [ ] 工作邮箱输入框有 `type="email"``required`
- [ ] 表单**不**使用 `novalidate`JS 守卫在展示成功状态前调用 `checkValidity()`
- [ ] 成功消息使用 `role="status"``aria-live="polite"`,以便屏幕阅读器播报
- [ ] 所有颜色值源自 DESIGN.md;唯一允许硬编码的例外是用于 `--success``#2D6A4F`
- [ ] 所有用户提供的文本令牌均已进行 HTML 转义;颜色令牌遵循严格语法(`#hex``rgb/hsl/oklch``color-mix()`),且不包含不安全字符(`;``{}``<``>``/*``@``url(`);字体名称令牌在 Google Fonts URL 中进行 URL 编码;`{{LOGO_MARK}}` 为转义后的文本首字母或经过严格清理的内联 SVG(无脚本/事件)
## P1 — 高质量提交通常应满足
- [ ] Hero 区域视觉上清晰可辨,且在首屏可见
- [ ] 邮箱提交按钮有悬停和激活状态
- [ ] 表单验证在出错时提供清晰的内联反馈(原生 `reportValidity()` 亦可接受)
- [ ] 页面在 375×667 和 390×844 尺寸下可滚动(未被裁剪);在这些尺寸下 CTA 无需滚动即可见
- [ ]`prefers-reduced-motion: reduce` 下,滚动字幕动画已暂停或移除
- [ ] 所有可交互元素有可见的焦点样式(不仅仅是 `outline: none`
- [ ] 键盘:Tab 键可到达每个表单字段和提交按钮;Enter 键可提交表单
- [ ] 正文文本在背景上的颜色对比度满足 WCAG AA(≥ 4.5:1
- [ ] 按钮标签在按钮背景上的颜色对比度满足 WCAG AA(≥ 4.5:1
- [ ] Logo 文本/首字母在 Logo 容器背景上的颜色对比度满足 WCAG AA(≥ 4.5:1
- [ ] 如果 Logo 是 `<img>` 或有意义的 SVG,则存在 Logo 的 `alt` 文本或 `aria-label`
- [ ] 滚动字幕条带有 `aria-hidden="true"`(装饰性,非有意义内容)
- [ ] `<html lang="">` 属性已设置为正确的语言代码
## P2 — 加分打磨项
- [ ] Google Fonts 使用 `display=swap` 加载,避免 FOIT
- [ ] 表单输入框有匹配的 `autocomplete` 属性(`given-name``email`
- [ ] 按钮悬停时有细微的微动效(仅缩放或透明度——不引起布局偏移)
- [ ] 装饰区域在任何视口宽度下均不覆盖或重叠表单
- [ ] 页面标题为 `{{PRODUCT_NAME}} — Coming Soon`,而非通用的占位符
---
翻译完成。请问还需要在这个项目里做什么工作?