chore: import zh skill waitlist-page
This commit is contained in:
@@ -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 清单整理
|
||||
- 原作者、版权和许可证信息以上游仓库为准
|
||||
@@ -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 检查清单。迭代时不得跳过验证。
|
||||
@@ -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
@@ -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>
|
||||
@@ -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`,而非通用的占位符
|
||||
|
||||
---
|
||||
|
||||
翻译完成。请问还需要在这个项目里做什么工作?
|
||||
Reference in New Issue
Block a user