
Readme Generator
- 16 installs
- 40 repo stars
- Updated June 3, 2026
- geekjourneyx/readme-generator
Generate a portfolio-grade README.md for a GitHub project with restrained narrative, up to two compressed visual assets, MIT license, and recommended Description and Topics.
About
Produces a clear, well-designed README that answers what a project is, why it matters, and how to start, plus optional images, license, and GitHub metadata suggestions. A developer uses it to write, generate, or optimize a project README and its first-impression page.
- Judges visual intensity by project type to avoid template-stuffing
- Outputs README plus recommended GitHub Description, Topics, and optional gh CLI updates
Readme Generator by the numbers
- 16 all-time installs (skills.sh)
- Ranked #1,053 of 1,879 Documentation skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/geekjourneyx/readme-generator --skill readme-generatorAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 16 |
|---|---|
| repo stars | ★ 40 |
| Last updated | June 3, 2026 |
| Repository | geekjourneyx/readme-generator ↗ |
What it does
Generate a portfolio-grade README.md for a GitHub project with restrained narrative, up to two compressed visual assets, MIT license, and recommended Description and Topics.
Files
GitHub README Generator
README 是项目的第一张作品集页面。它不是说明书的目录,也不是功能清单的容器。它要在很短时间内回答三件事:
1. 这是什么。 2. 为什么值得看。 3. 我怎么开始使用。
本 Skill 的目标是生成 100 分 README 作品:清楚、有审美、克制、可信,能让项目像一个完整作品一样被理解。
---
第一性原理
README 是信任入口和路径入口,不是完整文档。它应该帮助第一次打开仓库的人做出快速判断:
1. 这个项目解决什么问题。 2. 它适不适合我。 3. 我是否能马上运行、安装或继续了解。
1w star 以上开源项目通常不是靠信息量取胜,而是靠清晰的首屏、直接的上手路径、可信的文档入口和克制的社区信息取胜。图片、徽章、作者信息和设计理念都只是辅助;一旦它们拖慢理解,就是噪音。
本 Skill 的核心取舍:README 先讲清项目,再做美化;视觉服务理解,不替代理解。
---
作品标准
用下面的评分表约束所有输出:
| 维度 | 分值 | 判断标准 |
|---|---|---|
| 15 秒理解 | 25 | 首屏能看懂项目名、价值、适用对象 |
| 项目故事 | 20 | 不是堆功能,而是讲清背景、动机和结果 |
| 视觉表达 | 20 | 图片像作品,不像小字流程截图 |
| 快速开始 | 15 | 安装和使用路径短、明确、可复制 |
| 可信产物 | 10 | 展示真实输出、能力边界或结果 |
| 克制降噪 | 10 | 去掉重复、口号、过度解释和装饰 |
低于 90 分的 README 不交付;先删噪音、放大重点、重排叙事。
高星项目基线
默认向高星开源项目学习这些结构:
- 项目名 + 一句话价值主张。
- 少量必要 badge,不堆状态装饰。
- 快速开始或文档入口靠前。
- 示例只在能降低上手成本时出现。
- 贡献、社区、安全、许可证简洁清楚。
- UI / 产品项目可放截图;库、SDK、基础设施项目少图或无图。
不要把 README 写成设计宣言、完整说明书、功能墙、社交名片或内部工作流报告。
---
设计原则
必须坚持
- H1 必须是项目正式名称,紧跟一句价值主张。
- README 开头先讲项目价值,再放安装细节。
- 图片只表达一个重点,但应该承担项目名片功能:让读者一眼看到项目名和定位。
- 默认最多两张图片:一张封面,一张核心能力或结果图。
- GitHub 会缩小图片显示,图片里的主文案必须按海报字号设计。
- 对功能的描述要具体,但不夸张;能用结果说明就不要自夸。
- 对作者和许可证保持简洁,不做社交名片堆砌。
必须避免
- ASCII 艺术标题。
- emoji 装饰标题或作者表格。
- 大段“我们很专业”的空话。
- 6 个以上小卡片堆在一张图里。
- 流程图里塞满阶段、命令和小字说明。
- 把第三张流程图当作默认产物;工作方式通常用正文讲更清楚。
- 把 Image Gen 当作长文排版工具。
- 把 README 写成完整产品手册;详细文档应放到
docs/。
---
总体流程
Phase 0 项目阅读和模式识别
Phase 1 项目故事提炼
Phase 2 视觉生成方式选择
Phase 3 作品级视觉资产生成
Phase 4 README 组装
Phase 5 GitHub 元信息建议
Phase 6 验证和交付---
Phase 0: 项目阅读和模式识别
先读取项目,而不是直接写模板。
检查:
ls
find . -maxdepth 2 -type f | sed 's#^\./##' | sort | head -80优先读取:
README.mdpackage.json/pyproject.toml/go.mod/Cargo.tomldocs/- 主要入口文件
- 示例、截图、演示文件
判断场景和视觉预算:
| 场景 | 判断方式 | 策略 | 图片上限 |
|---|---|---|---|
| 新建 README | 没有 README,或 README 很短 | 完整生成,但保持短路径 | 1-2 |
| 升级 README | 已有 README,有有效内容 | 保留独特内容,重写结构和首屏 | 0-2 |
| 作品集强化 | 用户强调审美、故事、展示 | 优先做叙事和封面表现 | 2 |
| 纯文档模式 | SDK、库、后端工具、基础设施 | 少图,重安装、API、文档入口 | 0-1 |
| UI / 产品展示 | 有界面、截图、demo、视觉结果 | 用真实结果或封面辅助理解 | 1-2 |
升级现有 README 时,不要删除用户已有的关键内容。先提取可保留内容,再重排。
降噪审查
写 README 前先标记哪些内容应外移或删除:
| 内容 | 默认处理 |
|---|---|
| 设计评分表、工作原则、内部方法论 | 放在 SKILL.md 或 docs/,不进 README |
| 生成文件树、模板变量、脚本细节 | 只在用户需要开发文档时保留 |
| 第三张流程图 | 删除,改成 2-4 行正文或简短列表 |
| 过多社交链接 | 只留 GitHub / 主页等核心入口 |
| 太泛的功能列表 | 合并成 3 个结果导向能力 |
| 安装前的长背景 | 缩短,快速开始提前 |
---
Phase 1: 项目故事提炼
采集或推断 7 个字段:
| 字段 | 说明 |
|---|---|
project_name | 项目正式名称 |
tagline | 一句话价值主张,短、有判断 |
origin | 项目出现的背景:为什么需要它 |
audience | 谁会用它 |
promise | 它帮用户得到什么结果 |
proof | 真实能力、截图、输出、示例、指标 |
start | 最短上手路径 |
不要问太多问题。能从项目里推断就直接推断;只有影响叙事准确性时才问用户。
推荐叙事结构
项目名
一句话价值主张
视觉封面(按项目类型决定是否需要)
这是什么
为什么需要它
你会得到什么
快速开始
示例或输出
工作方式(正文,不默认配图)
安装
许可证
作者如果项目偏工具、库或基础设施,把“快速开始”提前到“为什么需要它”之后。README 的顺序要服务读者行动,不服务模板完整性。
---
Phase 2: 视觉生成方式选择
README 图片有两类:
1. 作品封面:传达气质、主题、记忆点。 2. 结构说明图:传达步骤、能力、对比、流程。
根据用户意图选择模式:
| 模式 | 适用场景 | 图片策略 |
|---|---|---|
portfolio | 默认推荐,适合需要展示完整作品感的项目 | 1 张封面 + 1 张核心能力图 |
clean-doc | SDK、库、后端工具、严肃基础设施 | 0-1 张图,优先快速开始和示例 |
visual-story | AI 工具、设计工具、独立产品、作品展示 | 最多 2 张图,Codex Image Gen 负责记忆点 |
structured | 用户明确要信息图、对比图、流程图 | 1-2 张 HTML/CSS 海报,保证文字准确 |
默认先判断项目类型,不要强行套 portfolio。视觉资产生成优先级固定为:
1. Codex Image Gen Skill:优先用 Codex 内置图片生成能力生成视觉资产。 2. 轻量化:README 展示图优先转成 WebP,再写入 README 引用路径。 3. HTML to PNG fallback:只有当 Image Gen 不可用、输出不符合要求、或用户明确要求结构化精确文字时,才退化到 HTML/CSS 模板截图。
不要把 HTML 截图当默认路径。它是可靠兜底,不是首选视觉方案。
---
Phase 3: 作品级视觉资产生成
默认输出使用两个稳定文件名:
assets/banner.webp
assets/features.webp这样 README 引用路径稳定,不管图片来自 Codex Image Gen 还是 HTML 截图兜底。
图片职责
| 图片 | 目标 | 推荐方式 |
|---|---|---|
banner.webp | 项目名片,建立项目名、定位和记忆点 | Codex Image Gen 优先 |
features.webp | 核心能力、结果或必要流程的传播图 | Codex Image Gen 优先;文字精确时 HTML 兜底 |
不要默认生成 workflow.png 或 workflow.webp。如果用户明确要求流程图,把流程内容合并进 features.webp 或放到正文,不新增第三张图。
默认视觉风格
所有 README 视觉资产默认采用同一套风格:
黑底、极简、电影打光、高对比、大留白、低亮度、白/灰/暖金三色、高级杂志封面感。
画面质感:极深黑背景 #050505,纸张颗粒,浅景深,体积雾,细窄轮廓光,局部金属质感。
质量目标:出自 1w star 设计师水准作品。设计约束:
- 背景以
#050505深黑为主。 - 色彩只使用白、灰、暖金;不要引入彩虹渐变、紫蓝霓虹或高饱和装饰色。
- 使用大留白和局部光,而不是堆元素。
- 图片应该有少量高价值文字,承担传播和定位,不做长说明。
- 文字越少越强:每张图只保留读者离开 README 后仍该记住的信息。
- 不放段落、命令、表格、版本号堆叠和密集说明。
项目名片文字策略
默认使用「项目名片型图片」,不是纯氛围图。
| 图片 | 推荐文字 | 上限 |
|---|---|---|
banner.webp | 项目名 + 一句话定位 + 1-3 个短标签 | 18 个英文词或 28 个中文字 |
features.webp | 2-3 个结果短语,必要时加一个短标题 | 每个短语 2-5 个词 |
好文字应该像封面标题,不像说明书:
- 项目名必须清楚,优先放在
banner.webp。 - 定位句说结果,不说口号,例如 “Portfolio-grade README design for open source projects”。
- 标签只放搜索和记忆价值最高的词,例如
Story、Visual、Signal。 - 中文可以用,但要少;英文项目名、短英文标签通常更稳。
- 如果 Image Gen 把文字写错,重试一次;仍不准确时,保留 Image Gen 视觉底图,用 HTML/CSS fallback 承载精确文字。
- 不为了“全程 AI 生成”牺牲项目名和定位的准确性。
字号底线
按 1920×1080 设计时:
| 元素 | 最小字号 |
|---|---|
| 主标题 | 92px |
| 中文主标题 | 80px |
| 大卡标题 | 48px |
| 正文说明 | 28px |
| 辅助标签 | 22px |
| 页脚 | 20px |
不要使用 18px 以下文字。GitHub 缩放后会不可读。
Codex Image Gen 产物只要求保持 16:9 和足够清晰,不要为了凑 1920×1080 而把好图强行重采样。README 展示图优先保存为 WebP;1920×1080 是 HTML to PNG fallback 的模板尺寸。
HTML to PNG fallback
仅在 Image Gen 不可用、用户要求精确结构化文字、或 Image Gen 输出无法通过检查时使用:
node scripts/gen_infographic.mjs /tmp/readme-banner.html assets/banner.png 1920 1080
node scripts/gen_infographic.mjs /tmp/readme-features.html assets/features.png 1920 1080
node scripts/convert_webp_assets.mjs assets/banner.png assets/banner.webp assets/features.png assets/features.webp模板来自:
templates/banner.html
templates/features.html模板变量:
{{PROJECT_NAME}}
{{TAGLINE}}
{{PRIMARY_COLOR}}
{{CATEGORY}}
{{PLATFORM}}
{{LANGUAGE}}
{{VERSION_INFO}}
{{TECH_CARDS}}
{{FEATURE_CARDS}}
{{FEATURE_COUNT}}卡片结构:
{{TECH_CARDS}}用在封面右侧,建议 2-3 条,只放短标签和一句说明。{{FEATURE_CARDS}}只放 2-3 张大卡,第一张可加featured。- 如果需要表达工作流程,用
features.webp的 3 个结果阶段承载,不新增第三张图。
Codex Image Gen / gpt-image-2 方式
默认优先调用 Codex 自带图片生成能力,不要在项目里临时硬编码 API 脚本。
适合 Image Gen 的内容:
- README 封面。
- 产品氛围图。
- 作品集视觉。
- 抽象概念图。
- 用现有图片做风格延展。
不适合 Image Gen 的内容:
- 精确流程图。
- 大量中文文字或多段说明。
- 命令、版本、表格。
- 必须逐字准确的长文或 UI 图。
推荐 prompt 结构:
Use case: productivity-visual
Asset type: GitHub README visual asset, 16:9
Project: <project_name>
Story: <origin + promise>
Visual direction: black background, minimalist, cinematic lighting, high contrast, large negative space, low brightness, premium magazine cover
Texture and lighting: #050505 deep black background, subtle paper grain, shallow depth of field, volumetric haze, thin rim light, selective metallic highlights
Palette: white, gray, warm gold only
Composition: one strong visual idea, restrained, spacious, no dense UI
Quality bar: 10k-star designer portfolio quality
Text policy: include only high-value name-card text. Use the exact project name, one short positioning line, and optional 1-3 short labels. No paragraphs, commands, tables, tiny captions, or decorative text.
Avoid: emoji, clutter, fake interface text, tiny labels, generic gradients, bright neon, overdesigned dashboards如果 Image Gen 不支持、无法调用、或输出出现错误文字/风格偏差,先用一个更短的文字 prompt 重试一次;如果仍不准,再退回 HTML to PNG 兜底流程承载精确文字。
图片轻量化
README 展示图片默认使用 WebP。WebP 通常比 PNG 更适合 README 加载;PNG 只作为 HTML 截图兜底的中间产物、透明图或需要无损保存时使用。
默认命令:
npm run webp如果项目没有 WebP 转换脚本,优先使用 cwebp、sips 或可用的轻量工具。不要为了转换引入重型依赖。无法转换时再保留 PNG,并说明原因。
视觉检查
每张图生成后检查:
- 缩小到 GitHub README 显示宽度后仍能读。
- 一张图只讲一个重点。
- 没有小字堆叠。
- 项目名和定位句一眼可见。
- 图中文字没有拼写错误、乱码或伪文字。
- 没有过度装饰。
- 视觉风格和项目故事一致。
- Image Gen 图没有错误文字或多余水印。
- 压缩后图片仍然清晰,没有明显色带、噪点破坏或文字糊边。
---
Phase 4: README 组装
默认结构:
<div align="center">
# 项目名
**一句话价值主张**
<img src="assets/banner.webp" alt="[项目名] — [价值主张]" width="100%">
[](./LICENSE)
</div>
---
## 这是什么
[2-3 句话讲清项目、背景、结果。]
## 为什么需要它
[讲真实问题,不列反模式清单。]
## 你会得到什么
<img src="assets/features.webp" alt="[3 个核心能力]" width="100%">
## 工作方式
[2-4 行讲清工作方式;不要默认再放第三张图。]
## 快速开始
[最短可执行路径。]
## 安装
[依赖和安装命令。]
## 许可证
[MIT](./LICENSE)
## 关于作者
[简洁作者信息。]规则:
- 不要超过 3 个 badge,除非项目确实需要状态标识。
- 不要在图片后重复同样的功能列表。
- 不要把“设计原则”“项目结构”“生成内容”都塞进 README;只保留对读者有用的部分。
- 不要默认放第三张 workflow 图;两张图已经足够承载作品感和核心信息。
- 如果有详细说明,放到
docs/,README 只做入口。
---
Phase 5: GitHub 元信息推荐
Repository Description 和 Topics 是 GitHub 搜索、Trending 关联、AI 搜索引用和人工快速判断的入口。它们不是 README 内容,不要写进 README.md;只在最终回复里作为独立建议输出。
First-principles 目标
- Description 回答:这个项目为谁创造什么结果。
- Topics 回答:这个项目应该被放进哪些搜索桶。
- 推荐数量 由信号质量决定,不追求多;高质量 7-9 个通常优于铺满 20 个。
- Trending / SEO / GEO 三者平衡:既要覆盖热门搜索词,也要保持项目事实准确,方便 AI 引用。
Description 生成规则
生成 3 个候选,并逐个打分:
[Verb] [specific outcome] for [audience] — [1-3 precise keywords]硬约束:
- 英文优先,≤ 160 字符。
- 动词开头:Design / Generate / Build / Create / Convert / Automate。
- 包含项目最核心的 2-3 个关键词。
- 不用 emoji、感叹号、营销形容词。
- 不写无法从项目事实支撑的能力。
评分:
| 维度 | 分值 | 判断 |
|---|---|---|
| 准确性 | 40 | 是否忠于项目真实能力 |
| 搜索价值 | 25 | 是否覆盖 GitHub / Google 常搜词 |
| AI 可引用性 | 20 | 是否能被 ChatGPT / Claude / Perplexity 直接理解 |
| 克制程度 | 15 | 是否没有夸张和废话 |
星级:
| 分数 | 星级 |
|---|---|
| 90-100 | 5 星,推荐使用 |
| 80-89 | 4 星,可用但可再精简 |
| 70-79 | 3 星,只适合备选 |
| < 70 | 不推荐 |
Topics 生成规则
先建立候选池,再筛选最终推荐。
候选来源:
- 项目类型:
agent-skill,cli-tool,developer-tools,documentation - 核心技术:
nodejs,playwright,codex,image-generation - 应用领域:
readme,github-readme,open-source,portfolio - 当前趋势:AI agent、image generation、developer tooling、documentation automation
- 项目实际文件和 README 叙事中出现的关键词
筛选规则:
- 推荐 7-9 个,不超过 10 个,除非项目确实横跨多个明确领域。
- Topic 必须小写,只用字母、数字和 hyphen。
- 删除太泛的词:
software,tool,app,github,project,ai。 - 删除重复词:
readme和github-readme可以共存;open-source和opensource只留一个。 - 不为追热门添加不真实 topic。
每个 topic 给出星级和理由:
| 星级 | 含义 |
|---|---|
| 5 星 | 强相关、高搜索价值、应加入 |
| 4 星 | 相关且有发现价值,可加入 |
| 3 星 | 有一定关系,但不够核心 |
| 2 星 | 相关性弱,通常不推荐 |
| 1 星 | 噪音,不加入 |
输出格式:
GitHub Description 推荐:
1. ★★★★★ <description>
理由:...
2. ★★★★☆ <description>
理由:...
最终推荐:<description>
GitHub Topics 推荐:
加入:
- ★★★★★ github-readme — 精准描述项目用途
- ★★★★★ agent-skill — 符合项目形态
- ★★★★☆ image-generation — 当项目支持 Image Gen 时加入
不建议加入:
- ★★☆☆☆ ai — 太泛,搜索噪音大
- ★★☆☆☆ github — 太泛,不能帮助分类可选 gh CLI 更新
如果用户安装了 gh,当前目录已经是 Git 仓库,并且能解析 GitHub remote,可以建议用户让 Agent 用 gh repo edit 更新。不要擅自更新;必须先给出将执行的内容并等待用户确认。
检查:
gh --version
git remote -v建议命令格式:
gh repo edit OWNER/REPO \
--description "<final-description>" \
--add-topic topic-one \
--add-topic topic-two \
--add-topic topic-three如果需要替换旧 topics,先读取当前 topics,再只移除明确不推荐的项:
gh repo view OWNER/REPO --json description,repositoryTopics
gh repo edit OWNER/REPO --remove-topic old-topic --add-topic new-topic说明:git 不能更新仓库元信息
git 命令不能修改 GitHub 仓库的 Description 或 Topics。原因:Description 和 Topics 是 GitHub 平台元信息,不是 Git 仓库里的 commit、branch、tag 或 remote 配置。
git remote -v 只能用来识别 OWNER/REPO,不能用来更新元信息。
如果没有 gh,只给出推荐值,让用户到 GitHub 页面手动更新;不要提供复杂备选命令。
不要用 README 文件保存这些推荐;它们属于交付时对用户的操作建议。
---
Phase 6: 验证和交付
交付前必须验证:
node --version
npm run showcase
file assets/banner.webp assets/features.webp
git status --short如果改了模板或实际图片,必须重新生成 WebP 并打开检查。
最终汇报只说清楚:
- 改了什么。
- 结果怎样。
- 哪些验证跑过。
- 是否还有需要用户决定的点。
不要把实现细节和冗长过程写给用户。
node_modules/
package-lock.json
*.html
!templates/*.html
results.tsv
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project follows semantic versioning.
[Unreleased]
Added
- Added Codex Image Gen-first visual asset rules with compressed image output before README embedding.
- Added dependency-free PNG recompression script and wired it into the showcase asset generation flow.
- Added first-principles README guidance based on high-star open source project patterns.
- Added visual-budget and noise-audit rules so the Skill can choose between portfolio, clean documentation, and structured README modes.
- Added project name-card image guidance so README visuals carry the project name, positioning line, and a few high-signal labels.
- Added WebP conversion for README showcase assets.
Changed
- Updated HTML-to-PNG fallback templates to match the black, cinematic, high-contrast magazine-cover visual direction.
- Reduced the default README visual set from three images to two:
banner.pngandfeatures.png. - Replaced the separate workflow image with concise README prose.
- Regenerated
assets/banner.pngandassets/features.pngwith Codex Image Gen instead of HTML-to-PNG screenshots. - Switched README showcase assets from PNG to WebP for lighter loading.
Removed
- Removed
assets/workflow.pngandtemplates/workflow.htmlfrom the default showcase flow.
[1.1.0] - 2026-06-02
Added
- Added portfolio-grade README generation guidance focused on project story, visual quality, and reduced noise.
- Added Codex Image Gen / gpt-image-2 guidance for README cover assets, with clear boundaries for when HTML screenshots remain the better choice.
- Added GitHub Description and Topics recommendation workflow with scoring, star ratings, and optional
gh repo editguidance. - Added
npm run showcaseto regenerate this repository's README visual assets from the templates.
Changed
- Reworked the README from a feature-heavy explanation into a portfolio-style project introduction.
- Redesigned the banner, features, and workflow templates as large-type editorial posters.
- Regenerated
assets/banner.png,assets/features.png, andassets/workflow.pngusing the new visual system. - Updated the Skill workflow around story-first README design, visual mode selection, and metadata recommendations.
[1.0.0] - 2026-04-20
Added
- Initial README generator Skill.
- Added HTML templates for banner, features, and workflow infographics.
- Added Playwright-based screenshot script for generating 16:9 PNG assets.
- Added README structure guidance, MIT license handling, and GitHub metadata suggestions.
MIT License
Copyright (c) 2026 geekjourneyx
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
{
"name": "readme-generator",
"version": "1.1.0",
"description": "AI Agent Skill for creating portfolio-grade GitHub READMEs with story-driven writing, visual assets, and metadata recommendations",
"scripts": {
"screenshot": "node scripts/gen_infographic.mjs",
"showcase": "node scripts/render_showcase_assets.mjs",
"compress": "node scripts/compress_png_assets.mjs",
"webp": "node scripts/convert_webp_assets.mjs"
},
"keywords": [
"readme",
"github-readme",
"agent-skill",
"codex",
"image-generation",
"portfolio",
"infographic",
"seo"
],
"author": "geekjourneyx",
"license": "MIT",
"type": "module",
"dependencies": {
"playwright": "^1.59.1"
}
}
<div align="center">
GitHub README Generator
把项目 README 做成一份可以展示的作品。
<img src="assets/banner.webp" alt="GitHub README Generator — portfolio-grade README design for open source projects" width="100%">
  
</div>
---
这是什么
GitHub README Generator 是一个 Agent Skill,用来把普通项目 README 升级成作品集级项目首页。
它不会只替你填模板,而是先阅读项目,提炼背景、价值和使用路径,再生成克制的文案、作品级视觉资产和 GitHub 元信息建议。
---
为什么需要它
GitHub 项目就是开发者的作品集。README 是读者、招聘者、用户和 AI 搜索首先看到的页面。
很多项目明明有价值,却被冗长说明、小字截图、过度装饰和模板化功能列表削弱了第一印象。这个 Skill 的目标是反过来做:少说废话,放大重点,让项目自己的故事站出来。
---
你会得到什么
<img src="assets/features.webp" alt="Three outcomes: story, visual identity, and clean delivery" width="100%">
---
工作方式
它先阅读项目,判断 README 应该是偏作品展示还是偏清晰文档;再提炼项目故事,生成正文、两张以内的视觉资产和 GitHub 元信息建议。
---
快速开始
安装后,在 Codex 中打开你的项目目录,然后直接说:
帮我把这个项目的 README 做成作品集级项目首页也可以指定视觉方式:
用 Codex Image Gen 生成 README 封面,其余保持清晰信息图生成一个极简文档型 README,不要重视觉---
安装
npx skills add https://github.com/geekjourneyx/readme-generator如果需要使用 HTML 模板截图生成图片,请确认本地有 Node.js 和 Playwright:
node -v
npm install
npx playwright install chromium---
视觉模式
| 模式 | 适合项目 | 图片策略 |
|---|---|---|
portfolio | 默认推荐,多数开源项目 | 封面可用 Codex Image Gen,说明图用大字海报 |
clean-doc | SDK、库、后端工具 | 少图,优先快速开始和示例 |
visual-story | AI 工具、设计工具、独立产品 | 更强视觉叙事和封面表现 |
structured | 需要流程、能力、对比图 | HTML/CSS 模板截图,保证文字准确 |
---
生成内容
project/
├── README.md
├── LICENSE
└── assets/
├── banner.webp
└── features.webp图片可以来自两条路线:
- Codex Image Gen:适合封面、氛围图、作品集视觉。
- HTML 截图模板:适合功能、流程、对比等需要文字准确的图。
---
设计标准
这个 Skill 按 100 分 README 标准工作:
| 维度 | 分值 |
|---|---|
| 15 秒理解项目价值 | 25 |
| 讲清项目故事 | 20 |
| 视觉像作品而不是说明书 | 20 |
| 快速开始足够直接 | 15 |
| 有可信产物展示 | 10 |
| 克制、少噪音、不过度设计 | 10 |
---
许可证
MIT
---
关于作者
| 个人主页 | geekjourney.dev |
| GitHub | geekjourneyx |
| @seekjourney | |
| 公众号 | 微信搜「极客杰尼」 |
README 设计系统
色彩主题
默认采用黑底、极简、电影打光、高对比、大留白的杂志封面方向。项目本身没有强品牌色时,不再套多套主题。
--bg-primary: #050505 /* 极深黑背景 */
--bg-secondary: #0c0b09 /* 低亮度面板 */
--border: #2a251b /* 暖暗边界 */
--text-primary: #f4f0e8 /* 暖白主文字 */
--text-muted: #9a9488 /* 灰色辅助文字 */
--accent: #c8a15a /* 暖金强调 */只使用白、灰、暖金三色。不要引入彩虹渐变、紫蓝霓虹或高饱和装饰色。
字体层级
H1 项目名:92px+ / 800 weight / 0 letter-spacing
中文主标题:80px+ / 800 weight / 0 letter-spacing
H2 章节标题:48px+ / 800 weight / 0 letter-spacing
卡片标题:48px+ / 700 weight
正文:28px+ / 400-600 weight
标签/角标:22px+ / 600 weight / uppercase不要使用 18px 以下文字。GitHub 会缩小图片显示,小字会直接失效。
图片文字策略
图片是项目名片,不是无字氛围图,也不是长文信息图。
| 图片 | 必须承担 | 推荐文字 |
|---|---|---|
| banner.webp | 项目名和定位 | 项目名 + 一句话定位 + 1-3 个短标签 |
| features.webp | 核心结果和记忆点 | 2-3 个结果短语,必要时加一个短标题 |
文字上限:
banner.webp不超过 18 个英文词或 28 个中文字。features.webp每个短语 2-5 个词。- 不放段落、命令、表格、密集标签和小字脚注。
文字应该服务传播和定位。读者只看图,也应该知道项目叫什么、解决什么问题、为什么值得继续看。
16:9 视觉资产规格
| 图片 | 尺寸 | 用途 |
|---|---|---|
| banner.webp | 16:9 | README 顶部项目名片 |
| features.webp | 16:9 | 核心能力、结果或必要流程 |
默认最多两张图,全部存放在 assets/ 目录,在 README 中以 width="100%" 嵌入。
Codex Image Gen 产物只要求保持 16:9 和足够清晰。README 展示图优先使用 WebP;1920×1080 是 HTML to PNG fallback 的模板尺寸,不是 Image Gen 必须尺寸。
不要默认生成单独的流程图。工作方式优先用正文说明;如果必须可视化流程,把它合并进 features.webp。
Badge 规范
[](./LICENSE)
[]()
[](https://nodejs.org)颜色建议:
- License:
#3b82f6(蓝) - Version:
#10b981(绿) - Platform/Runtime:
#6b7280(灰) - Status:
#f59e0b(橙,beta)/#10b981(绿,stable)
最多 3 个 badge,保持视觉整洁。
作者区块模板
## 关于作者
| | |
|:---|:---|
| 个人主页 | [domain.dev](https://domain.dev) |
| GitHub | [username](https://github.com/username) |作者区块只保留最重要入口。纯文字标签,不使用 emoji shortcode(与零 Emoji 原则一致)。
SEO / GEO 最佳实践
GitHub Repository Description(≤ 160 字符)
这是 Google 搜索结果里显示的摘要,也是 AI(ChatGPT/Perplexity/Gemini)引用项目时的描述来源。
格式模板:
[动词短语:功能描述] — [技术关键词1], [技术关键词2]. [可选:一个差异化说明]示例(好):
Generate beautifully typeset travel guidebook PDFs with parallel AI research agents and Playwright export
AI agent skill for creating professional travel itinerary books from natural language trip descriptions示例(差):
🚀 Amazing travel tool that makes your trips AWESOME!!
一个很强大的旅行工具规则: 1. 英文优先(国际受众更广) 2. 动词开头(Generate/Build/Create/Convert/Automate) 3. 包含核心技术名词(作为 SEO 关键词) 4. 不用 emoji,不用感叹号,不说「very good」「amazing」 5. 描述「做什么」而不是「有多好」
---
GitHub Topics(7-10 个)
Topics 直接影响 GitHub 搜索排名和 Explore 推荐。
分类策略:
| 类别 | 数量 | 示例 |
|---|---|---|
| 技术栈 | 2-3 | nodejs, python, typescript |
| 框架/工具 | 2-3 | playwright, claude, react |
| 应用领域 | 2-3 | travel, pdf-generation, automation |
| 项目类型 | 1-2 | agent-skill, cli-tool, library |
避免:
- 太宽泛:
software,tool,project,github - 重复信息:如果 description 里有
nodejs,topics 里可以省略 - 超过 10 个:质量比数量重要
---
README H1 标题 SEO
H1 是爬虫权重最高的信号。
# Travel Guidebook规则:
- 必须是项目的正式名称(和仓库名一致或接近)
- 不在 H1 里塞关键词(Google 会降权)
- H1 之后的 blockquote 或 bold 标语可以包含自然关键词
---
GEO(生成式引擎优化)
AI 搜索引擎(ChatGPT/Perplexity/Claude)引用 README 时,优先选择:
1. 结构清晰:有明确的 H2 章节标题 2. 数据具体:有具体数字(「5 分钟内生成」「支持 30+ 页」) 3. 代码示例:有可执行的命令示例 4. 对比说明:说明与其他方案的区别(「不是模板填充,而是...」)
在 README 开头的「这是什么」章节,用 1-2 句话精确描述项目,这段话很可能被 AI 直接引用。
#!/usr/bin/env node
/**
* Losslessly recompress PNG IDAT chunks with Node's built-in zlib.
*
* This is intentionally dependency-free so README asset generation can always
* run after Playwright screenshots or Codex Image Gen output is copied in.
*/
import { readFileSync, statSync, writeFileSync } from 'node:fs';
import { resolve } from 'node:path';
import { deflateSync, inflateSync } from 'node:zlib';
const PNG_SIGNATURE = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]);
const DEFAULT_FILES = [
'assets/banner.png',
'assets/features.png',
];
const files = process.argv.slice(2);
const targets = files.length ? files : DEFAULT_FILES;
const crcTable = new Uint32Array(256);
for (let n = 0; n < 256; n += 1) {
let c = n;
for (let k = 0; k < 8; k += 1) {
c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
}
crcTable[n] = c >>> 0;
}
function crc32(buffer) {
let c = 0xffffffff;
for (const byte of buffer) {
c = crcTable[(c ^ byte) & 0xff] ^ (c >>> 8);
}
return (c ^ 0xffffffff) >>> 0;
}
function makeChunk(type, data) {
const typeBuffer = Buffer.from(type, 'ascii');
const length = Buffer.alloc(4);
const crc = Buffer.alloc(4);
length.writeUInt32BE(data.length, 0);
crc.writeUInt32BE(crc32(Buffer.concat([typeBuffer, data])), 0);
return Buffer.concat([length, typeBuffer, data, crc]);
}
function optimizePng(path) {
const input = readFileSync(path);
if (!input.subarray(0, 8).equals(PNG_SIGNATURE)) {
throw new Error(`${path} is not a PNG file`);
}
const passthroughChunks = [];
const idatChunks = [];
let offset = 8;
while (offset < input.length) {
const length = input.readUInt32BE(offset);
const type = input.subarray(offset + 4, offset + 8).toString('ascii');
const data = input.subarray(offset + 8, offset + 8 + length);
const rawChunk = input.subarray(offset, offset + 12 + length);
offset += 12 + length;
if (type === 'IDAT') {
idatChunks.push(data);
continue;
}
if (type === 'IEND') {
const inflated = inflateSync(Buffer.concat(idatChunks));
const recompressed = deflateSync(inflated, { level: 9 });
const output = Buffer.concat([
PNG_SIGNATURE,
...passthroughChunks,
makeChunk('IDAT', recompressed),
rawChunk,
]);
writeFileSync(path, output);
return;
}
passthroughChunks.push(rawChunk);
}
throw new Error(`${path} has no IEND chunk`);
}
for (const file of targets) {
const path = resolve(file);
const before = statSync(path).size;
optimizePng(path);
const after = statSync(path).size;
const saved = Math.max(0, before - after);
console.log(`${file}: ${before} -> ${after} bytes (${saved} saved)`);
}
#!/usr/bin/env node
/**
* Convert README visual assets to WebP using cwebp.
*
* WebP keeps README image loading lightweight. This script intentionally uses
* the local cwebp binary instead of adding a heavy image dependency.
*/
import { execFileSync } from 'node:child_process';
import { existsSync, statSync } from 'node:fs';
import { resolve } from 'node:path';
const DEFAULT_JOBS = [
['assets/banner.png', 'assets/banner.webp'],
['assets/features.png', 'assets/features.webp'],
];
const args = process.argv.slice(2);
const quality = '86';
function parseJobs() {
if (!args.length) return DEFAULT_JOBS;
if (args.length % 2 !== 0) {
throw new Error('Usage: node scripts/convert_webp_assets.mjs [input.png output.webp]...');
}
const jobs = [];
for (let i = 0; i < args.length; i += 2) {
jobs.push([args[i], args[i + 1]]);
}
return jobs;
}
function ensureCwebp() {
try {
execFileSync('cwebp', ['-version'], { stdio: 'ignore' });
} catch {
throw new Error('cwebp is required to convert README assets to WebP.');
}
}
ensureCwebp();
for (const [inputFile, outputFile] of parseJobs()) {
const input = resolve(inputFile);
const output = resolve(outputFile);
if (!existsSync(input) && existsSync(output)) {
console.log(`${outputFile}: already converted`);
continue;
}
if (!existsSync(input)) {
throw new Error(`${inputFile} does not exist`);
}
execFileSync('cwebp', ['-q', quality, input, '-o', output], { stdio: 'inherit' });
const before = statSync(input).size;
const after = statSync(output).size;
const saved = Math.max(0, before - after);
console.log(`${outputFile}: ${before} -> ${after} bytes (${saved} saved)`);
}
#!/usr/bin/env node
/**
* gen_infographic.mjs
* Playwright screenshot utility for README infographic generation.
*
* Usage:
* node scripts/gen_infographic.mjs <input.html> <output.png> [width] [height]
*
* Examples:
* node scripts/gen_infographic.mjs /tmp/banner.html assets/banner.png 1920 1080
* node scripts/gen_infographic.mjs /tmp/features.html assets/features.png
*/
import { chromium } from 'playwright';
import { resolve } from 'path';
import { existsSync } from 'fs';
const [,, inputHtml, outputPng, width = '1920', height = '1080'] = process.argv;
if (!inputHtml || !outputPng) {
console.error('Usage: node gen_infographic.mjs <input.html> <output.png> [width] [height]');
process.exit(1);
}
const inputPath = resolve(inputHtml);
if (!existsSync(inputPath)) {
console.error(`Input file not found: ${inputPath}`);
process.exit(1);
}
const w = parseInt(width, 10);
const h = parseInt(height, 10);
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.setViewportSize({ width: w, height: h });
await page.goto(`file://${inputPath}`);
// Wait for fonts (Google Fonts CDN) and any CSS transitions
await page.waitForTimeout(2500);
await page.screenshot({
path: resolve(outputPng),
clip: { x: 0, y: 0, width: w, height: h },
});
await browser.close();
console.log(`✅ ${outputPng} (${w}×${h})`);
})();
#!/usr/bin/env node
/**
* Render this skill's own README showcase assets from the HTML templates.
*
* The generic screenshot utility stays in gen_infographic.mjs. This script only
* fills the templates with this repository's current story and calls the utility.
*/
import { execFileSync } from 'node:child_process';
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
const root = resolve(dirname(fileURLToPath(import.meta.url)), '..');
const tmpDir = '/private/tmp/readme-generator-showcase';
mkdirSync(tmpDir, { recursive: true });
const common = {
PROJECT_NAME: 'GitHub README Generator',
TAGLINE: '把项目 README 做成一份可以展示的作品。',
PRIMARY_COLOR: '#c8a15a',
CATEGORY: 'Portfolio README Design',
PLATFORM: 'Codex',
LANGUAGE: 'Markdown',
VERSION_INFO: 'Story-driven · Image Gen ready',
};
const techCards = `
<div class="tech-card">
<div class="tech-icon">01</div>
<div>
<div class="tech-name">Story</div>
<div class="tech-desc">先提炼项目为什么存在,而不是先堆功能。</div>
</div>
</div>
<div class="tech-card">
<div class="tech-icon">02</div>
<div>
<div class="tech-name">Visual</div>
<div class="tech-desc">封面可用 Codex Image Gen,说明图保持清晰准确。</div>
</div>
</div>
<div class="tech-card">
<div class="tech-icon">03</div>
<div>
<div class="tech-name">Delivery</div>
<div class="tech-desc">输出 README、视觉资产和 GitHub 元信息建议。</div>
</div>
</div>`;
const featureCards = `
<article class="card featured">
<div>
<div class="card-tag">Story</div>
<h3 class="card-title">讲清项目为什么存在</h3>
<p class="card-desc">把背景、对象和结果压缩成读者能快速判断的叙事。</p>
</div>
<div>
<div class="rule"></div>
<div class="card-icon">01</div>
</div>
</article>
<article class="card">
<div>
<div class="card-tag">Visual</div>
<h3 class="card-title">让 README 像作品被看见</h3>
<p class="card-desc">用封面、海报和留白建立第一印象,避免小字信息堆叠。</p>
</div>
<div>
<div class="rule"></div>
<div class="card-icon">02</div>
</div>
</article>
<article class="card">
<div>
<div class="card-tag">Signal</div>
<h3 class="card-title">保留真正有用的信息</h3>
<p class="card-desc">快速开始、可信结果、作者和许可证,其他噪音交给 docs。</p>
</div>
<div>
<div class="rule"></div>
<div class="card-icon">03</div>
</div>
</article>`;
const jobs = [
{
template: 'banner.html',
html: `${tmpDir}/banner.html`,
png: `${tmpDir}/banner.png`,
webp: `${root}/assets/banner.webp`,
vars: { ...common, TECH_CARDS: techCards },
},
{
template: 'features.html',
html: `${tmpDir}/features.html`,
png: `${tmpDir}/features.png`,
webp: `${root}/assets/features.webp`,
vars: { ...common, FEATURE_CARDS: featureCards, FEATURE_COUNT: '3' },
},
];
for (const job of jobs) {
let html = readFileSync(`${root}/templates/${job.template}`, 'utf8');
for (const [key, value] of Object.entries(job.vars)) {
html = html.replaceAll(`{{${key}}}`, value);
}
writeFileSync(job.html, html);
execFileSync(
process.execPath,
[`${root}/scripts/gen_infographic.mjs`, job.html, job.png, '1920', '1080'],
{ stdio: 'inherit' },
);
}
execFileSync(
process.execPath,
[
`${root}/scripts/convert_webp_assets.mjs`,
`${tmpDir}/banner.png`,
`${root}/assets/banner.webp`,
`${tmpDir}/features.png`,
`${root}/assets/features.webp`,
],
{ stdio: 'inherit' },
);
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=1920, height=1080">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
:root {
--accent: {{PRIMARY_COLOR}};
--paper: #050505;
--ink: #f3efe7;
--muted: rgba(243, 239, 231, 0.58);
--line: rgba(243, 239, 231, 0.14);
}
body {
width: 1920px;
height: 1080px;
overflow: hidden;
background: var(--paper);
font-family: Georgia, "Times New Roman", "Noto Serif SC", "Songti SC", serif;
color: var(--ink);
}
.page {
width: 100%;
height: 100%;
padding: 92px 120px 78px;
display: grid;
grid-template-columns: 1fr 540px;
gap: 96px;
position: relative;
}
.page::before {
content: "";
position: absolute;
inset: 42px;
border: 1px solid var(--line);
background-image: linear-gradient(115deg, rgba(255,255,255,0.035), transparent 34%, rgba(200,161,90,0.04));
pointer-events: none;
}
.page::after {
content: "";
position: absolute;
right: 0;
top: 0;
width: 42%;
height: 100%;
background: linear-gradient(180deg, #050505 0%, #090806 100%);
z-index: 0;
}
.hero,
.side {
position: relative;
z-index: 1;
}
.kicker {
display: flex;
align-items: center;
gap: 24px;
margin-bottom: 88px;
font-family: "Avenir Next", "Helvetica Neue", Arial, sans-serif;
font-size: 24px;
font-weight: 700;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--accent);
}
.kicker::before {
content: "";
width: 96px;
height: 3px;
background: var(--accent);
}
h1 {
max-width: 940px;
font-size: 104px;
line-height: 1;
letter-spacing: -0.035em;
font-weight: 700;
}
.statement {
max-width: 870px;
margin-top: 56px;
font-family: "Avenir Next", "Helvetica Neue", Arial, "PingFang SC", sans-serif;
font-size: 29px;
line-height: 1.42;
color: var(--muted);
font-weight: 600;
}
.meta {
display: flex;
gap: 18px;
margin-top: 76px;
font-family: "Avenir Next", "Helvetica Neue", Arial, sans-serif;
}
.pill {
min-width: 132px;
border: 1px solid rgba(243,239,231,0.16);
padding: 18px 22px;
font-size: 22px;
font-weight: 700;
color: rgba(243,239,231,0.62);
background: rgba(255,255,255,0.02);
text-align: center;
}
.side {
height: 100%;
padding: 86px 0 58px;
display: flex;
flex-direction: column;
justify-content: space-between;
color: #f2efe8;
}
.side-title {
font-family: "Avenir Next", "Helvetica Neue", Arial, sans-serif;
font-size: 24px;
line-height: 1.35;
color: rgba(242,239,232,0.62);
max-width: 440px;
}
.side-title strong {
color: #fffaf0;
font-weight: 800;
}
.tech-list {
display: grid;
gap: 18px;
}
.tech-card {
border-top: 1px solid rgba(242,239,232,0.18);
padding-top: 22px;
display: grid;
grid-template-columns: 78px 1fr;
gap: 22px;
align-items: start;
}
.tech-icon {
font-family: Georgia, "Times New Roman", serif;
font-size: 36px;
line-height: 1;
color: var(--accent);
}
.tech-name {
font-family: "Avenir Next", "Helvetica Neue", Arial, sans-serif;
font-size: 25px;
font-weight: 800;
color: #fffaf0;
}
.tech-desc {
margin-top: 8px;
font-family: "Avenir Next", "Helvetica Neue", Arial, "PingFang SC", sans-serif;
font-size: 20px;
line-height: 1.35;
color: rgba(242,239,232,0.62);
}
.version-tag {
font-family: "Avenir Next", "Helvetica Neue", Arial, sans-serif;
font-size: 22px;
font-weight: 700;
color: rgba(242,239,232,0.48);
}
</style>
</head>
<body>
<main class="page">
<section class="hero">
<div class="kicker">{{CATEGORY}}</div>
<h1>{{PROJECT_NAME}}</h1>
<p class="statement">{{TAGLINE}}</p>
<div class="meta">
<div class="pill">MIT</div>
<div class="pill">{{PLATFORM}}</div>
<div class="pill">{{LANGUAGE}}</div>
</div>
</section>
<aside class="side">
<div class="side-title"><strong>README is the first gallery wall.</strong><br>Make the project understandable before making it exhaustive.</div>
<div class="tech-list">
{{TECH_CARDS}}
</div>
<div class="version-tag">{{VERSION_INFO}}</div>
</aside>
</main>
</body>
</html>
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=1920, height=1080">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
:root {
--accent: {{PRIMARY_COLOR}};
--paper: #050505;
--ink: #f3efe7;
--muted: rgba(243, 239, 231, 0.58);
--line: rgba(243, 239, 231, 0.14);
}
body {
width: 1920px;
height: 1080px;
overflow: hidden;
background: var(--paper);
color: var(--ink);
font-family: "Avenir Next", "Helvetica Neue", Arial, "PingFang SC", sans-serif;
}
.page {
width: 100%;
height: 100%;
padding: 86px 120px 94px;
display: flex;
flex-direction: column;
}
.header {
display: grid;
grid-template-columns: 1fr auto;
align-items: end;
padding-bottom: 42px;
border-bottom: 1px solid var(--line);
}
.eyebrow {
font-size: 23px;
font-weight: 800;
color: var(--accent);
letter-spacing: 0.12em;
text-transform: uppercase;
margin-bottom: 20px;
}
h2 {
font-family: Georgia, "Times New Roman", "Noto Serif SC", "Songti SC", serif;
font-size: 68px;
line-height: 1.02;
letter-spacing: -0.035em;
}
.count {
font-size: 25px;
color: var(--muted);
font-weight: 700;
}
.grid {
flex: 1;
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1px;
margin-top: 74px;
background: var(--line);
border: 1px solid var(--line);
}
.card {
background: #090909;
min-height: 600px;
padding: 58px 54px 52px;
display: flex;
flex-direction: column;
justify-content: space-between;
position: relative;
}
.card.featured {
background: #0f0d08;
color: #fffaf0;
outline: 2px solid var(--accent);
outline-offset: -2px;
}
.card-icon {
font-family: Georgia, "Times New Roman", serif;
font-size: 64px;
line-height: 1;
color: rgba(243,239,231,0.16);
}
.featured .card-icon {
color: rgba(255,250,240,0.32);
}
.card-tag {
font-size: 22px;
font-weight: 800;
letter-spacing: 0.08em;
color: var(--accent);
text-transform: uppercase;
}
.featured .card-tag {
color: rgba(255,250,240,0.78);
}
.card-title {
margin-top: 34px;
font-family: Georgia, "Times New Roman", "Noto Serif SC", "Songti SC", serif;
font-size: 44px;
line-height: 1.05;
letter-spacing: -0.025em;
color: var(--ink);
}
.featured .card-title {
color: #fffaf0;
}
.card-desc {
margin-top: 30px;
font-size: 25px;
line-height: 1.45;
font-weight: 600;
color: var(--muted);
max-width: 430px;
}
.featured .card-desc {
color: rgba(255,250,240,0.84);
}
.rule {
width: 100%;
height: 2px;
background: var(--line);
margin-top: 48px;
}
.featured .rule {
background: rgba(255,250,240,0.34);
}
</style>
</head>
<body>
<main class="page">
<header class="header">
<div>
<div class="eyebrow">What the reader remembers</div>
<h2>三个结果,足够了</h2>
</div>
<div class="count">{{FEATURE_COUNT}} core outcomes</div>
</header>
<section class="grid">
{{FEATURE_CARDS}}
</section>
</main>
</body>
</html>