Skill: cellinlab/cell-cc-design (version 0.1.0)

--- name: cc-design description: "High-fidelity HTML design and prototype skill for landing pages, slide decks, mobile mockups, interactive prototypes, dashboard explorations, design-direction advising, and design review work. Use when Codex needs to design a screen, make an interface look polished, explore multiple visual directions, recommend a style direction before building, adapt an existing brand style, critique a design, or deliver a reviewable HTML-based visual artifact. Also use when the user mentions presentations, design systems, wireframes, UI mockups, motion studies, design philosophy, style selection, or asks to 'make it look good'. Do not trigger for pure content strategy, pure writing, or bitmap image generation alone." metadata: {"openclaw":{"homepage":"https://github.com/cellinlab/cell-skills/tree/main/skills/cc-design"}} --- # CC Design ## Overview 这个 Skill 只处理一类任务： - 把视觉需求做成可预览、可截图、可迭代的 HTML 设计产物 它覆盖的是： - 落地页 - 演示稿 / slide deck - dashboard / UI mockup - 移动端 screen / onboarding - 可点击交互原型 - 视觉方向探索与对比 它不处理： - 纯文案策划 - 纯内容写作 - 纯图片生成 - Figma 文件生产 如果用户真正缺的是内容策略、文章、脚本或图片素材，不要在这里偷偷扩 scope。 ## Quick Start 1. 先判断输出类型：landing page、deck、mobile mockup、prototype，还是视觉探索。 2. 先看项目里有没有现成设计系统、token、组件、品牌文档或现有页面。 3. 再决定加载哪些 references，必要时复制 `assets/templates/` 下的模板。 4. 写 HTML 之前，先明确视觉焦点、情绪基调、层级和留白策略。 5. 完成后默认做结构检查和截图检查；需要导出时再调用脚本。 ## Routing Guide | 任务类型 | 先读什么 | 可复用模板 | 重点检查 | |---|---|---|---| | 任意设计任务 | `references/design-excellence.md` | - | 视觉层级是否成立 | | 任务模糊 / 需要先选风格 | `references/design-direction-advisor.md` | `assets/templates/design_canvas.jsx` 可选 | 3 个方向是否真的不同 | | 需要设计评审 | `references/design-review-guide.md` | - | 反馈是否可执行 | | 没有现成设计系统 | `references/frontend-design.md` | 按场景选择 | 是否落入通用 AI 风 | | 有现成设计系统 / 代码 / 品牌资产 | `references/design-context.md` | 按场景选择 | 是否真的复用了上下文 | | 具体品牌 / 产品任务 | `references/brand-asset-protocol.md` + `references/brand-style-loader.md` | 按场景选择 | 事实和资产是否核实 | | 品牌风格克隆 | `references/brand-style-loader.md` | 按场景选择 | 品牌气质是否对味 | | Landing page / 产品页 | `references/design-patterns.md` | `assets/templates/browser_window.jsx` 可选 | 响应式与首屏层级 | | Slide deck | `references/starter-components.md` | `assets/templates/deck_stage.js` | 固定画布缩放与翻页 | | 多方案探索 | `references/tweaks-system.md` | `assets/templates/design_canvas.jsx` | 方案切换是否清晰 | | Mobile mockup | `references/starter-components.md` + `references/interactive-prototype.md` | `assets/templates/ios_frame.jsx` / `android_frame.jsx` | 安全区、触达区、设备框 | | Interactive prototype | `references/interactive-prototype.md` + `references/react-babel-setup.md` | 按场景选择 | 导航、状态、交互是否成立 | | 动画 / motion study | `references/starter-components.md` + `references/react-babel-setup.md` | `assets/templates/animations.jsx` | 时间轴和播放状态 | | 设计系统 / token 体系 | `references/design-system-creation.md` | - | token 是否完整且被真正使用 | | 需要导出 | `references/platform-tools.md` | - | 文件是否实际生成 | ## Default Contract 默认采用以下约定，除非用户另有说明： - 输出语言：中文说明 - 核心交付：HTML 文件 - 默认优先复用现有设计上下文，而不是凭空发明新风格 - 如果任务提到真实品牌或产品，默认先核实事实与资产，再设计 - 如果没有设计系统，先用可控 token 和字体组合建立视觉基线 - 如果需求模糊，默认先提 3 个方向而不是直接拍板 - 默认至少提供 2-3 个方向中的一个可比较入口，或通过 tweaks 暴露变化 - 交付前默认检查 console、结构和截图 - 需要导出时，优先导出 PDF / PPTX / 单文件 HTML，而不是口头说“可以导出” ## Workflow ### Step 0: Verify Facts for Real-World Entities 如果任务里出现了具体品牌、产品、公司或已命名服务： - 先核实它是否存在、是否已发布、当前形态是什么 - 先找 logo、产品图、UI 截图或官方资产 - 不要直接凭印象写“像某某品牌” 需要时读 [references/brand-asset-protocol.md](references/brand-asset-protocol.md)。 ### Step 1: Scope the Request 至少先弄清： - 最终产物是什么 - 几屏 / 几页 / 几个方向 - 保真度要到什么程度 - 有没有现成品牌或参考对象 - 是否需要导出 - 是先做方向建议，还是直接做成品 如果信息不够，读 [references/question-protocol.md](references/question-protocol.md)。 ### Step 2: Acquire Design Context 开始做之前，先主动搜： - 现有 HTML / CSS / 组件 - token 文件 / 设计系统说明 - 已上线页面 - 品牌文案或视觉参考 已有系统时，优先复用它的颜色、字体、间距、圆角、阴影和组件语言。 如果上下文很少，先读 [references/design-context.md](references/design-context.md)。 ### Step 3: Lock the Visual Intent 写代码之前，先用 [references/design-excellence.md](references/design-excellence.md) 的思路回答这些问题： - 这一屏最重要的视觉焦点是什么 - 目标情绪更偏信任、兴奋、专业还是创意 - 层级如何建立 - 留白和节奏如何组织 - 颜色和字体如何配合 - 是否需要多个方向并列比较 如果没有设计系统，再读 [references/frontend-design.md](references/frontend-design.md)。 如果用户还没能决定方向，不要硬做，先读 [references/design-direction-advisor.md](references/design-direction-advisor.md)。 ### Step 4: Work Like a Junior Designer, Not a Black Box 开始实现时，不要直接闷头做完整稿。 优先做： - 一版 assumptions + placeholder - 一次早期展示 - 用户确认后的主版本推进 - 中途再展示一次 - 最后才做 polish 需要更细流程时，读 [references/junior-designer-workflow.md](references/junior-designer-workflow.md)。 ### Step 5: Build with the Right Level of Structure 按场景选择最省力的方式： - deck 用 `assets/templates/deck_stage.js` - 多方案对比用 `assets/templates/design_canvas.jsx` - 手机稿用 `assets/templates/ios_frame.jsx` 或 `android_frame.jsx` - 浏览器场景用 `assets/templates/browser_window.jsx` - 桌面工具窗口用 `assets/templates/macos_window.jsx` - 动画场景用 `assets/templates/animations.jsx` 如果要写 React 内联原型，读 [references/react-babel-setup.md](references/react-babel-setup.md)。 ### Step 6: Add Variation without File Explosion 如果用户要多个方向，不要默认拆成一堆 HTML。 优先： - 用 `design_canvas` 并列展示 - 用 tweaks 面板切换颜色、排版、布局、文案层级 需要时读 [references/tweaks-system.md](references/tweaks-system.md)。 ### Step 7: Verify Before Delivery 完成后必须做验证，不要“写完即交”。 默认检查： - 有没有 console error / 关键 warning - 主要结构有没有渲染出来 - 截图里层级、留白、对比和对齐是否成立 - slide / prototype 的导航和缩放有没有坏掉 详细流程见 [references/verification-protocol.md](references/verification-protocol.md)。 ### Step 8: Review When Asked, Export When Needed 如果用户要求 review、比较多个方向、或交付前做专家审查： - 从方向匹配、层级、做工、功能性、辨识度 5 个维度看 - 给出可执行问题，不要只说“还差点意思” - 最后附 `Quick Wins` 需要时读 [references/design-review-guide.md](references/design-review-guide.md)。 如果用户需要导出，再读 [references/platform-tools.md](references/platform-tools.md) 并调用脚本： - `node skills/cc-design/scripts/gen_pptx.js` - `node skills/cc-design/scripts/open_for_print.js` - `node skills/cc-design/scripts/super_inline_html.js` ## Hard Rules Do not: - 在已有设计系统时无视上下文另起炉灶 - 在真实品牌 / 产品任务里跳过事实核验和资产采集 - 把视觉任务做成普通模板页 - 在需求模糊时直接默认一个 generic 方向 - 用一堆花哨效果掩盖信息层级问题 - 明明需要多方向，却只给一个含糊方案 - 没截图、没检查就直接交付 - 把图片生成需求硬塞进 HTML 设计流程 Always: - 先明确输出类型和约束 - 先搜上下文，再设计 - 具体品牌任务先核实事实与资产 - 先做视觉判断，再写代码 - 在大任务里先给 assumptions / placeholder，再继续 - 需要模板时优先复用模板 - 交付前至少做一轮结构检查和截图检查 - 诚实说明哪些导出 / 预览环节已验证，哪些还没验证 ## Resource Map - [references/question-protocol.md](references/question-protocol.md) - 范围不清时，用这个收口需求。 - [references/design-context.md](references/design-context.md) - 现有系统、代码、截图和品牌上下文的采集顺序。 - [references/brand-asset-protocol.md](references/brand-asset-protocol.md) - 具体品牌 / 产品任务的事实核验与资产协议。 - [references/junior-designer-workflow.md](references/junior-designer-workflow.md) - 假设、placeholder、中途回看和分阶段推进。 - [references/design-direction-advisor.md](references/design-direction-advisor.md) - 需求模糊时先给 3 个方向。 - [references/design-excellence.md](references/design-excellence.md) - 写代码前先做视觉判断。 - [references/frontend-design.md](references/frontend-design.md) - 没有设计系统时，用这个建立视觉基线。 - [references/design-patterns.md](references/design-patterns.md) - 常见 landing page / 内容区块 / 布局模式。 - [references/design-system-creation.md](references/design-system-creation.md) - 需要从零搭 token 和组件基线时，用这个。 - [references/brand-style-loader.md](references/brand-style-loader.md) - 用户点名品牌风格时，走这个研究路径。 - [references/interactive-prototype.md](references/interactive-prototype.md) - 交互原型的导航、状态与过渡。 - [references/react-babel-setup.md](references/react-babel-setup.md) - React + Babel 内联原型约束。 - [references/starter-components.md](references/starter-components.md) - 所有模板脚手架的用途和接入方式。 - [references/tweaks-system.md](references/tweaks-system.md) - 多方向切换与 tweak 面板。 - [references/platform-tools.md](references/platform-tools.md) - 预览、截图、导出路径。 - [references/verification-protocol.md](references/verification-protocol.md) - 结构 / 视觉验证流程。 - [references/design-review-guide.md](references/design-review-guide.md) - 设计 review 的 5 维度和 quick wins 输出。 - [references/case-studies/README.md](references/case-studies/README.md) - 典型案例入口。 - [assets/templates/deck_stage.js](assets/templates/deck_stage.js) - 演示稿固定画布与翻页容器。 - [assets/templates/design_canvas.jsx](assets/templates/design_canvas.jsx) - 多方案并排画布。 - [assets/templates/ios_frame.jsx](assets/templates/ios_frame.jsx) - iPhone 设备框。 - [assets/templates/android_frame.jsx](assets/templates/android_frame.jsx) - Android 设备框。 - [assets/templates/browser_window.jsx](assets/templates/browser_window.jsx) - 浏览器窗口壳。 - [assets/templates/macos_window.jsx](assets/templates/macos_window.jsx) - macOS 桌面窗口壳。 - [assets/templates/animations.jsx](assets/templates/animations.jsx) - 动画时间轴引擎。 - [scripts/gen_pptx.js](scripts/gen_pptx.js) - HTML -> PPTX。 - [scripts/open_for_print.js](scripts/open_for_print.js) - HTML -> PDF。 - [scripts/super_inline_html.js](scripts/super_inline_html.js) - 打包单文件 HTML。

path: agents/openai.yaml
content: interface:
  display_name: "CC Design"
  short_description: "高保真 HTML 设计与原型工作流"
  default_prompt: "Use $cc-design to first propose 3 distinct HTML-based design directions for this page or feature, grounded in the existing product style if available; if the context is clear, build the strongest direction and verify it before delivery."


path: assets/templates/android_frame.jsx
content: /**
 * Android Frame — React component for Android phone device bezel with status bar.
 * Load via: <script type="text/babel" src="android_frame.jsx"></script>
 *
 * Usage:
 *   <AndroidFrame>
 *     <div>Your app screen content here</div>
 *   </AndroidFrame>
 */

function AndroidFrame({ children, color = '#000' }) {
  // Auto-compute text contrast
  const isLight = color.match(/^#[0-9a-f]{6}$/i)
    ? (() => { const r = parseInt(color.slice(1,3),16), g = parseInt(color.slice(3,5),16), b = parseInt(color.slice(5,7),16); return (r*299+g*587+b*114)/1000 > 128; })()
    : false;
  const textColor = isLight ? '#000' : '#fff';

  const frameStyles = {
    width: '393px',
    height: '851px',
    borderRadius: '28px',
    border: '4px solid #1a1a1a',
    background: color,
    overflow: 'hidden',
    position: 'relative',
    boxShadow: '0 20px 60px rgba(0,0,0,0.3)',
  };

  const statusBarStyles = {
    height: '48px',
    display: 'flex',
    alignItems: 'center',
    justifyContent: 'space-between',
    padding: '0 20px',
    fontFamily: 'system-ui, sans-serif',
    fontSize: '13px',
    fontWeight: 500,
    color: textColor,
  };

  const cameraCutoutStyles = {
    position: 'absolute',
    top: '12px',
    left: '50%',
    transform: 'translateX(-50%)',
    width: '12px',
    height: '12px',
    background: '#111',
    borderRadius: '50%',
    zIndex: 10,
  };

  const contentStyles = {
    height: 'calc(100% - 48px)',
    overflow: 'auto',
    position: 'relative',
  };

  const navBarStyles = {
    position: 'absolute',
    bottom: '0',
    left: '0',
    right: '0',
    height: '48px',
    display: 'flex',
    alignItems: 'center',
    justifyContent: 'center',
    gap: '60px',
    background: 'rgba(0,0,0,0.5)',
  };

  const navBtnStyles = { width: '18px', height: '18px', borderRadius: '50%', border: `2px solid ${isLight ? 'rgba(0,0,0,0.5)' : 'rgba(255,255,255,0.5)'}` };

  const time = new Date().toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', hour12: false });

  return React.createElement('div', { style: frameStyles },
    React.createElement('div', { style: cameraCutoutStyles }),
    React.createElement('div', { style: statusBarStyles },
      React.createElement('span', null, time),
      React.createElement('div', { style: { display: 'flex', gap: '6px', alignItems: 'center' } },
        React.createElement('span', { style: { fontSize: '11px' } }, 'LTE'),
        React.createElement('span', null, '87%')
      )
    ),
    React.createElement('div', { style: contentStyles }, children),
    React.createElement('div', { style: navBarStyles },
      React.createElement('div', { style: { ...navBtnStyles, border: 'none', width: '14px', height: '14px' } }),
      React.createElement('div', { style: navBtnStyles }),
      React.createElement('div', { style: navBtnStyles })
    )
  );
}

Object.assign(window, { AndroidFrame });


path: assets/templates/animations.jsx
content: /**
 * Animations — Timeline-based animation engine for React.
 * Load via: <script type="text/babel" src="animations.jsx"></script>
 *
 * Provides: Stage, Sprite, useTime(), useSprite(), Easing, interpolate()
 *
 * Usage:
 *   <Stage duration={5} width={1920} height={1080}>
 *     <Sprite start={0} end={2}>
 *       <div style={{ background: 'red', width: 100, height: 100 }}>Fade in</div>
 *     </Sprite>
 *   </Stage>
 */

const AnimationsContext = React.createContext({ time: 0, duration: 1, sprite: null });

function useTime() {
  return React.useContext(AnimationsContext).time;
}

function useSprite() {
  return React.useContext(AnimationsContext).sprite ?? { start: 0, end: 1, progress: 0 };
}

const Easing = {
  linear: (t) => t,
  easeIn: (t) => t * t,
  easeOut: (t) => t * (2 - t),
  easeInOut: (t) => (t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t),
  bounce: (t) => {
    const n1 = 7.5625, d1 = 2.75;
    if (t < 1 / d1) return n1 * t * t;
    if (t < 2 / d1) return n1 * (t -= 1.5 / d1) * t + 0.75;
    if (t < 2.5 / d1) return n1 * (t -= 2.25 / d1) * t + 0.9375;
    return n1 * (t -= 2.625 / d1) * t + 0.984375;
  },
  elastic: (t) => {
    if (t === 0 || t === 1) return t;
    return -Math.pow(2, 10 * (t - 1)) * Math.sin((t - 1.1) * 5 * Math.PI);
  },
};

function interpolate(start, end, progress, easing = Easing.linear) {
  const t = easing(Math.max(0, Math.min(1, progress)));
  return start + (end - start) * t;
}

function Sprite({ children, start = 0, end = 1, easing = 'linear', style = {} }) {
  const { time } = React.useContext(AnimationsContext);
  const duration = end - start;
  const easingFn = Easing[easing] || Easing.linear;

  let opacity = 1;
  if (time < start) opacity = 0;
  else if (time > end) opacity = 0;
  else if (time < start + 0.3) opacity = easingFn((time - start) / 0.3);
  else if (time > end - 0.3) opacity = easingFn((time - end + 0.3) / 0.3);

  const progress = duration > 0 ? Math.max(0, Math.min(1, (time - start) / duration)) : 0;
  const sprite = { start, end, progress, easing };

  return React.createElement(AnimationsContext.Provider, { value: { ...React.useContext(AnimationsContext), sprite } },
    React.createElement('div', {
      style: { ...style, opacity, position: 'absolute', inset: 0 },
      'data-sprite-start': start,
      'data-sprite-end': end,
    }, children)
  );
}

function Stage({ children, duration = 5, width = 1920, height = 1080 }) {
  const [playing, setPlaying] = React.useState(false);
  const [time, setTime] = React.useState(0);
  const [scale, setScale] = React.useState(Math.min(window.innerWidth / width, window.innerHeight / height) * 0.9);
  const rafRef = React.useRef(null);
  const startRef = React.useRef(null);

  React.useEffect(() => {
    const onResize = () => setScale(Math.min(window.innerWidth / width, window.innerHeight / height) * 0.9);
    window.addEventListener('resize', onResize);
    return () => window.removeEventListener('resize', onResize);
  }, [width, height]);

  const play = React.useCallback(() => {
    setPlaying(true);
    startRef.current = performance.now() - time * 1000;
  }, [time]);

  const pause = React.useCallback(() => {
    setPlaying(false);
    if (rafRef.current) cancelAnimationFrame(rafRef.current);
  }, []);

  React.useEffect(() => {
    if (!playing) return;
    const tick = (now) => {
      const elapsed = (now - startRef.current) / 1000;
      if (elapsed >= duration) {
        setTime(duration);
        setPlaying(false);
        return;
      }
      setTime(elapsed);
      rafRef.current = requestAnimationFrame(tick);
    };
    rafRef.current = requestAnimationFrame(tick);
    return () => { if (rafRef.current) cancelAnimationFrame(rafRef.current); };
  }, [playing, duration]);

  return React.createElement('div', {
    style: { width: '100vw', height: '100vh', background: '#000', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center' }
  },
    React.createElement(AnimationsContext.Provider, { value: { time, duration } },
      React.createElement('div', {
        style: { width: `${width}px`, height: `${height}px`, transform: `scale(${scale})`, transformOrigin: 'center center', position: 'relative', overflow: 'hidden' }
      }, children)
    ),
    // Controls
    React.createElement('div', {
      style: { marginTop: '16px', display: 'flex', alignItems: 'center', gap: '12px', color: '#fff', fontFamily: 'system-ui, sans-serif', fontSize: '13px' }
    },
      React.createElement('button', {
        onClick: playing ? pause : play,
        style: { padding: '6px 16px', borderRadius: '6px', border: '1px solid rgba(255,255,255,.3)', background: 'rgba(255,255,255,.1)', color: '#fff', cursor: 'pointer' }
      }, playing ? '⏸ Pause' : '▶ Play'),
      React.createElement('input', {
        type: 'range', min: 0, max: duration, step: 0.01, value: time,
        onChange: (e) => setTime(parseFloat(e.target.value)),
        style: { width: '300px', accentColor: '#fff' }
      }),
      React.createElement('span', null, `${time.toFixed(2)}s / ${duration}s`)
    )
  );
}

Object.assign(window, { Stage, Sprite, useTime, useSprite, Easing, interpolate, AnimationsContext });


path: assets/templates/browser_window.jsx
content: /**
 * Browser Window — React component for desktop browser chrome with tab bar.
 * Load via: <script type="text/babel" src="browser_window.jsx"></script>
 *
 * Usage:
 *   <BrowserWindow url="https://example.com" width={1024} height={768}>
 *     <div>Page content</div>
 *   </BrowserWindow>
 */

function BrowserWindow({ children, url = 'https://example.com', width = 1024, height = 768, background = '#fff' }) {
  const wrapperStyles = {
    width: `${width}px`,
    height: `${height}px`,
    borderRadius: '10px',
    overflow: 'hidden',
    boxShadow: '0 20px 60px rgba(0,0,0,0.2), 0 0 0 1px rgba(0,0,0,0.1)',
    display: 'flex',
    flexDirection: 'column',
    background,
  };

  const tabBarStyles = {
    height: '38px',
    background: '#e8e8e8',
    display: 'flex',
    alignItems: 'center',
    padding: '0 10px',
    gap: '8px',
    borderBottom: '1px solid #d0d0d0',
  };

  const dotStyles = (color) => ({
    width: '12px',
    height: '12px',
    borderRadius: '50%',
    background: color,
    border: '0.5px solid rgba(0,0,0,0.12)',
    flexShrink: 0,
  });

  const tabStyles = {
    display: 'flex',
    alignItems: 'center',
    gap: '6px',
    padding: '4px 12px',
    background: '#fff',
    borderRadius: '6px',
    fontFamily: 'system-ui, -apple-system, sans-serif',
    fontSize: '12px',
    color: '#333',
    maxWidth: '200px',
    overflow: 'hidden',
    whiteSpace: 'nowrap',
    textOverflow: 'ellipsis',
  };

  const tabIconStyles = {
    width: '14px',
    height: '14px',
    borderRadius: '3px',
    background: 'linear-gradient(135deg, #4285f4, #34a853, #fbbc05, #ea4335)',
    flexShrink: 0,
  };

  const toolbarStyles = {
    height: '36px',
    background: '#f0f0f0',
    display: 'flex',
    alignItems: 'center',
    padding: '0 12px',
    gap: '8px',
    borderBottom: '1px solid #d0d0d0',
  };

  const urlBarStyles = {
    flex: 1,
    height: '24px',
    borderRadius: '12px',
    background: '#fff',
    border: '1px solid #ccc',
    padding: '0 10px',
    fontFamily: 'system-ui, sans-serif',
    fontSize: '12px',
    color: '#555',
    display: 'flex',
    alignItems: 'center',
    lineHeight: 1,
  };

  const contentStyles = {
    flex: 1,
    overflow: 'auto',
    position: 'relative',
  };

  return React.createElement('div', { style: wrapperStyles },
    React.createElement('div', { style: tabBarStyles },
      React.createElement('div', { style: dotStyles('#FF5F56') }),
      React.createElement('div', { style: dotStyles('#FFBD2E') }),
      React.createElement('div', { style: dotStyles('#27C93F') }),
      React.createElement('div', { style: tabStyles },
        React.createElement('div', { style: tabIconStyles }),
        url.replace(/^https?:\/\//, '').split('/')[0]
      )
    ),
    React.createElement('div', { style: toolbarStyles },
      React.createElement('span', { style: { color: '#999', fontSize: '14px' } }, '←'),
      React.createElement('span', { style: { color: '#999', fontSize: '14px' } }, '→'),
      React.createElement('span', { style: { color: '#999', fontSize: '14px' } }, '↻'),
      React.createElement('div', { style: urlBarStyles }, `🔒 ${url}`)
    ),
    React.createElement('div', { style: contentStyles }, children)
  );
}

Object.assign(window, { BrowserWindow });


path: assets/templates/deck_stage.js
content: /**
 * Deck Stage — Fixed-size slide presentation stage with scaling, keyboard nav,
 * slide-count overlay, localStorage persistence, and print-to-PDF support.
 *
 * Usage: <deck-stage> wraps <section> elements as slides (1920x1080 fixed canvas).
 * Load via: <script src="deck_stage.js"></script>
 */

class DeckStage extends HTMLElement {
  constructor() {
    super();
    this.currentSlide = 0;
    this.slides = [];
  }

  connectedCallback() {
    this.slides = Array.from(this.querySelectorAll(':scope > section'));
    if (this.slides.length === 0) return;

    // Restore position
    const saved = localStorage.getItem('cc-deck-position');
    if (saved !== null) this.currentSlide = Math.min(parseInt(saved), this.slides.length - 1);

    // Tag slides
    this.slides.forEach((s, i) => {
      s.style.display = i === this.currentSlide ? '' : 'none';
      if (!s.hasAttribute('data-screen-label')) {
        s.setAttribute('data-screen-label', `${String(i + 1).padStart(2, '0')}`);
      }
      s.setAttribute('data-om-validate', '');
    });

    this._build();
    this._scale();
    this._postIndex();

    window.addEventListener('resize', () => this._scale());
    document.addEventListener('keydown', (e) => this._onKey(e));
  }

  _build() {
    // Inner canvas
    this.canvas = document.createElement('div');
    this.canvas.style.cssText = 'width:1920px;height:1080px;position:relative;transform-origin:top left;';

    // Move slides into canvas
    this.slides.forEach(s => this.canvas.appendChild(s));
    this.appendChild(this.canvas);

    // Slide counter overlay
    this.counter = document.createElement('div');
    this.counter.style.cssText = 'position:fixed;bottom:12px;left:50%;transform:translateX(-50%);' +
      'font:13px/1 system-ui,sans-serif;color:rgba(255,255,255,.6);z-index:100;pointer-events:none;';
    this._updateCounter();
    this.appendChild(this.counter);

    // Prev/Next buttons (outside scaled element)
    const btnStyle = 'position:fixed;top:50%;transform:translateY(-50%);z-index:100;' +
      'width:44px;height:44px;border-radius:50%;border:1px solid rgba(255,255,255,.2);' +
      'background:rgba(0,0,0,.4);color:#fff;font-size:20px;cursor:pointer;display:flex;' +
      'align-items:center;justify-content:center;';

    this.prevBtn = document.createElement('button');
    this.prevBtn.innerHTML = '‹';
    this.prevBtn.style.cssText = btnStyle + 'left:12px;';
    this.prevBtn.onclick = () => this.go(this.currentSlide - 1);

    this.nextBtn = document.createElement('button');
    this.nextBtn.innerHTML = '›';
    this.nextBtn.style.cssText = btnStyle + 'right:12px;';
    this.nextBtn.onclick = () => this.go(this.currentSlide + 1);

    this.appendChild(this.prevBtn);
    this.appendChild(this.nextBtn);
  }

  _scale() {
    if (!this.canvas) return;
    const sx = window.innerWidth / 1920;
    const sy = window.innerHeight / 1080;
    const scale = Math.min(sx, sy);
    this.canvas.style.transform = `scale(${scale})`;
    this.style.width = '100vw';
    this.style.height = '100vh';
    this.style.background = '#000';
    this.style.overflow = 'hidden';
  }

  _onKey(e) {
    if (e.key === 'ArrowRight' || e.key === ' ') { e.preventDefault(); this.go(this.currentSlide + 1); }
    if (e.key === 'ArrowLeft') { e.preventDefault(); this.go(this.currentSlide - 1); }
    if (e.key === 'Home') { e.preventDefault(); this.go(0); }
    if (e.key === 'End') { e.preventDefault(); this.go(this.slides.length - 1); }
  }

  go(index) {
    if (index < 0 || index >= this.slides.length) return;
    this.slides[this.currentSlide].style.display = 'none';
    this.currentSlide = index;
    this.slides[this.currentSlide].style.display = '';
    this._updateCounter();
    this._postIndex();
    localStorage.setItem('cc-deck-position', String(index));
  }

  _updateCounter() {
    if (this.counter) {
      this.counter.textContent = `${this.currentSlide + 1} / ${this.slides.length}`;
    }
  }

  _postIndex() {
    window.postMessage({ slideIndexChanged: this.currentSlide }, '*');
  }
}

customElements.define('deck-stage', DeckStage);

// Print: one page per slide
if (typeof window !== 'undefined') {
  window.addEventListener('beforeprint', () => {
    const stage = document.querySelector('deck-stage');
    if (!stage) return;
    stage.slides.forEach(s => { s.style.display = ''; s.style.pageBreakAfter = 'always'; });
  });
  window.addEventListener('afterprint', () => {
    const stage = document.querySelector('deck-stage');
    if (!stage) return;
    stage.slides.forEach((s, i) => {
      s.style.display = i === stage.currentSlide ? '' : 'none';
      s.style.pageBreakAfter = '';
    });
  });
}


path: assets/templates/design_canvas.jsx
content: /**
 * Design Canvas — React component for presenting 2+ static options side-by-side.
 * Load via: <script type="text/babel" src="design_canvas.jsx"></script>
 *
 * Usage:
 *   <DesignCanvas labels={["Option A", "Option B", "Option C"]}>
 *     <div>Option A content</div>
 *     <div>Option B content</div>
 *     <div>Option C content</div>
 *   </DesignCanvas>
 */

function DesignCanvas({ labels = [], children, columns, gap = 16 }) {
  const count = React.Children.count(children);
  const cols = columns || Math.min(count, 3);

  const canvasStyles = {
    display: 'grid',
    gridTemplateColumns: `repeat(${cols}, 1fr)`,
    gap: `${gap}px`,
    padding: `${gap}px`,
    width: '100%',
    minHeight: '100vh',
    background: '#f5f5f5',
    boxSizing: 'border-box',
  };

  return React.createElement('div', { style: canvasStyles },
    React.Children.map(children, (child, i) => {
      const cellStyles = {
        background: '#fff',
        borderRadius: '8px',
        overflow: 'hidden',
        boxShadow: '0 1px 3px rgba(0,0,0,0.1)',
        position: 'relative',
      };

      const labelStyles = {
        position: 'absolute',
        top: 0,
        left: 0,
        right: 0,
        padding: '6px 12px',
        background: 'rgba(0,0,0,0.7)',
        color: '#fff',
        fontSize: '12px',
        fontWeight: 600,
        fontFamily: 'system-ui, sans-serif',
        zIndex: 10,
      };

      return React.createElement('div', { key: i, style: cellStyles },
        labels[i] && React.createElement('div', { style: labelStyles }, labels[i]),
        child
      );
    })
  );
}

// Export to global scope
Object.assign(window, { DesignCanvas });


path: assets/templates/ios_frame.jsx
content: /**
 * iOS Frame — React component for iPhone device bezel with status bar.
 * Load via: <script type="text/babel" src="ios_frame.jsx"></script>
 *
 * Usage:
 *   <IOSFrame>
 *     <div>Your app screen content here</div>
 *   </IOSFrame>
 */

function IOSFrame({ children, color = '#000', showNotch = true }) {
  // Auto-compute text contrast
  const isLight = color.match(/^#[0-9a-f]{6}$/i)
    ? (() => { const r = parseInt(color.slice(1,3),16), g = parseInt(color.slice(3,5),16), b = parseInt(color.slice(5,7),16); return (r*299+g*587+b*114)/1000 > 128; })()
    : false;
  const textColor = isLight ? '#000' : '#fff';

  const frameStyles = {
    width: '393px',
    height: '852px',
    borderRadius: '50px',
    border: '4px solid #1a1a1a',
    background: color,
    overflow: 'hidden',
    position: 'relative',
    boxShadow: '0 20px 60px rgba(0,0,0,0.3)',
  };

  const statusBarStyles = {
    height: '54px',
    display: 'flex',
    alignItems: 'flex-end',
    justifyContent: 'space-between',
    padding: '0 32px 8px',
    fontFamily: 'system-ui, -apple-system, sans-serif',
    fontSize: '15px',
    fontWeight: 600,
    color: textColor,
  };

  const contentStyles = {
    height: 'calc(100% - 54px)',
    overflow: 'auto',
    position: 'relative',
  };

  const notchStyles = {
    position: 'absolute',
    top: '0',
    left: '50%',
    transform: 'translateX(-50%)',
    width: '126px',
    height: '34px',
    background: '#000',
    borderRadius: '0 0 20px 20px',
    zIndex: 10,
  };

  const homeIndicatorStyles = {
    position: 'absolute',
    bottom: '8px',
    left: '50%',
    transform: 'translateX(-50%)',
    width: '134px',
    height: '5px',
    background: isLight ? 'rgba(0,0,0,0.3)' : 'rgba(255,255,255,0.3)',
    borderRadius: '3px',
  };

  const time = new Date().toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', hour12: false });

  return React.createElement('div', { style: frameStyles },
    showNotch && React.createElement('div', { style: notchStyles }),
    React.createElement('div', { style: statusBarStyles },
      React.createElement('span', null, time),
      React.createElement('div', { style: { display: 'flex', gap: '4px', alignItems: 'center' } },
        React.createElement('span', { style: { fontSize: '13px' } }, '5G'),
        React.createElement('span', null, '87%')
      )
    ),
    React.createElement('div', { style: contentStyles }, children),
    React.createElement('div', { style: homeIndicatorStyles })
  );
}

Object.assign(window, { IOSFrame });


path: assets/templates/macos_window.jsx
content: /**
 * macOS Window — React component for desktop window chrome with traffic lights.
 * Load via: <script type="text/babel" src="macos_window.jsx"></script>
 *
 * Usage:
 *   <MacOSWindow title="My App" width={800} height={600}>
 *     <div>Window content</div>
 *   </MacOSWindow>
 */

function MacOSWindow({ children, title = 'Untitled', width = 800, height = 600, background = '#fff' }) {
  const wrapperStyles = {
    width: `${width}px`,
    height: `${height}px`,
    borderRadius: '10px',
    overflow: 'hidden',
    boxShadow: '0 20px 60px rgba(0,0,0,0.2), 0 0 0 1px rgba(0,0,0,0.1)',
    display: 'flex',
    flexDirection: 'column',
    background,
  };

  const titleBarStyles = {
    height: '38px',
    background: '#e8e8e8',
    display: 'flex',
    alignItems: 'center',
    padding: '0 14px',
    gap: '8px',
    borderBottom: '1px solid #d0d0d0',
    flexShrink: 0,
  };

  const dotStyles = (color) => ({
    width: '12px',
    height: '12px',
    borderRadius: '50%',
    background: color,
    border: '0.5px solid rgba(0,0,0,0.12)',
  });

  const titleTextStyles = {
    flex: 1,
    textAlign: 'center',
    fontFamily: 'system-ui, -apple-system, sans-serif',
    fontSize: '13px',
    fontWeight: 500,
    color: '#333',
    paddingRight: '56px', // balance traffic lights
  };

  const contentStyles = {
    flex: 1,
    overflow: 'auto',
    position: 'relative',
  };

  return React.createElement('div', { style: wrapperStyles },
    React.createElement('div', { style: titleBarStyles },
      React.createElement('div', { style: dotStyles('#FF5F56') }),
      React.createElement('div', { style: dotStyles('#FFBD2E') }),
      React.createElement('div', { style: dotStyles('#27C93F') }),
      React.createElement('div', { style: titleTextStyles }, title)
    ),
    React.createElement('div', { style: contentStyles }, children)
  );
}

Object.assign(window, { MacOSWindow });


path: references/brand-asset-protocol.md
content: # Brand Asset Protocol

> **Load when:** 任务涉及具体品牌、产品、公司、App、硬件或已命名服务
> **Skip when:** 只是抽象概念页、内部练习稿、完全不依赖真实品牌识别的探索
> **Why it matters:** 品牌任务的识别度首先来自真实资产，不来自“猜一个像它的配色”
> **Typical failure it prevents:** 产品根本没核实就开做；只抽象提色值却没有 logo / 产品图 / UI；做出来“像科技产品”但不像用户提到的那个品牌

## Rule 0: Verify Facts Before Styling

只要任务里出现了明确品牌或产品名，先核实事实，再设计。

至少确认：

- 这个产品 / 服务是否真实存在
- 是否已经发布
- 当前版本或主要形态是什么

如果这些事实不清楚，就先查，不要凭印象写。

## Asset Priority

识别度优先级通常是：

1. `Logo`
2. `产品图 / 官方渲染图`
3. `UI 截图 / 界面素材`
4. `颜色`
5. `字体`

因此，不要只做色板抽取就以为完成了品牌研究。

## Minimum Asset Checklist

遇到品牌任务时，优先向用户或项目索取：

- Logo：SVG 或高清 PNG
- 产品图：实体产品、包装、设备或官方渲染图
- UI 截图：如果是数字产品，这一项几乎是必需的
- 品牌色 / 字体 / design system
- 官方网站、品牌指南或 press kit

## Search Order

如果用户没有提供，按这个顺序补：

1. 官方网站或品牌页
2. 官方 press / media kit
3. 产品详情页
4. App Store / 产品截图
5. 用户给的竞品或参考

## Fallback Rules

如果真实资产不足：

- 明确告诉用户缺了什么
- 优先用诚实 placeholder，而不是编造“看起来像”的品牌物料
- 可以继续做结构和方向，但不要假装已经完成品牌化

## Deliverable Honesty

结果里至少说明：

- 用到了哪些真实资产
- 哪些资产是缺失的
- 当前更像“品牌方向稿”还是“品牌成稿”


path: references/brand-style-loader.md
content: # GetDesign.md Brand Style Loader

## Purpose

This reference enables cc-design to progressively load design systems from https://getdesign.md — a curated collection of DESIGN.md files capturing the visual DNA of popular brands (Stripe, Vercel, Notion, Apple, Tesla, etc.).

## When to Load This Reference

Load this reference when:
- User mentions a specific brand name in their design request (e.g., "Stripe-style payment form", "Notion-like dashboard")
- User asks to "clone" or "match" a well-known product's design
- User wants to explore multiple brand aesthetics before committing

## Brand Detection Pattern

Scan the user's request for these brand categories:

**AI & LLM Platforms:** OpenAI, Anthropic, Claude, Cursor, Perplexity, Midjourney, Replicate, Hugging Face, Cohere, Stability AI

**Developer Tools:** Vercel, GitHub, Linear, Figma, Supabase, Sentry, Raycast, Expo, Postman, Railway, Render

**Fintech & Crypto:** Stripe, Coinbase, Binance, Revolut, Cash App, Robinhood, Plaid

**Enterprise & Cloud:** IBM, Salesforce, MongoDB, Redis, Snowflake, Databricks, Confluent

**Automotive:** Tesla, SpaceX, Lamborghini, Ferrari, Porsche, BMW, Mercedes-Benz, Bugatti

**Consumer Tech:** Apple, Spotify, Airbnb, Uber, Netflix, Nike, Notion

**Media & Social:** X (Twitter), Instagram, YouTube, TikTok, Discord, Slack

## Workflow

### Step 1: Fetch Brand List

Use the host's web-browsing capability to get the current brand catalog:

```
Open `https://getdesign.md` and extract all available brand names plus categories as a structured list.
```

### Step 2: Match User Intent

If user says "Stripe-style checkout":
- Detected brand: **Stripe**
- Category: Fintech
- Design characteristics to expect: Clean gradients, purple accent, trust-building typography

### Step 3: Fetch Specific DESIGN.md

GetDesign.md hosts files at: `https://raw.githubusercontent.com/VoltAgent/awesome-design-md/main/designs/{brand-slug}.md`

Common slug patterns:
- `stripe.md`
- `vercel.md`
- `notion.md`
- `linear.md`
- `apple.md`

Use web access to retrieve:

```
`https://raw.githubusercontent.com/VoltAgent/awesome-design-md/main/designs/stripe.md`

Extract the complete design system specification including colors, typography, spacing, and component patterns.
```

### Step 4: Parse Design Tokens

Extract these key sections from the fetched DESIGN.md:

**Colors:**
- Primary palette (hex codes)
- Semantic colors (success, error, warning)
- Background/surface colors
- Text hierarchy colors

**Typography:**
- Font families (with fallbacks)
- Size scale (headings, body, captions)
- Weight scale
- Line heights

**Spacing:**
- Base unit (usually 4px or 8px)
- Scale multipliers
- Component-specific spacing

**Components:**
- Button styles (primary, secondary, ghost)
- Input field patterns
- Card/container styles
- Navigation patterns

**Motion:**
- Transition durations
- Easing functions
- Animation principles

### Step 5: Merge with Project Context

**If project has existing DESIGN.md:**
- Read current DESIGN.md
- Ask user: "I found a Stripe design system. Should I:
  1. Replace your current design entirely
  2. Merge Stripe's colors but keep your typography
  3. Use Stripe as inspiration but adapt to your brand
  4. Just show me Stripe's system for reference"

**If no DESIGN.md exists:**
- Use fetched design system as the foundation
- Adapt brand-specific elements (replace "Stripe" with user's product name)
- Generate DESIGN.md in project root

### Step 6: Generate Adapted HTML

When writing HTML, inject the design tokens:

```html
<style>
  :root {
    /* Colors from Stripe DESIGN.md */
    --primary: #635BFF;
    --primary-hover: #0A2540;
    --surface: #FFFFFF;
    --text: #0A2540;
    --text-secondary: #425466;

    /* Typography from Stripe DESIGN.md */
    --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
    --font-mono: "SF Mono", Monaco, "Cascadia Code", monospace;

    /* Spacing from Stripe DESIGN.md */
    --space-unit: 8px;
    --space-xs: calc(var(--space-unit) * 1);
    --space-sm: calc(var(--space-unit) * 2);
    --space-md: calc(var(--space-unit) * 3);
    --space-lg: calc(var(--space-unit) * 4);
  }
</style>
```

## Multi-Brand Blending (Advanced)

If user requests multiple brand influences (e.g., "Stripe's colors with Linear's layout"):

1. Fetch both DESIGN.md files
2. Extract non-conflicting tokens:
   - Colors from Brand A
   - Typography from Brand B
   - Spacing from Brand A
   - Component patterns from Brand B
3. Generate a hybrid DESIGN.md with attribution comments
4. Preview both pure versions + hybrid in design_canvas.jsx for comparison

## Fallback Strategy

If brand not found on getdesign.md:
1. Search for `{brand} design system` using the host's web search capability
2. Extract design principles from official brand guidelines
3. Manually construct a minimal DESIGN.md with:
   - Colors from screenshots
   - Typography from web inspector
   - Spacing inferred from visual rhythm
4. Note in DESIGN.md: "// Reconstructed from public sources, not official"

## Anti-Patterns to Avoid

❌ **Don't blindly copy:** Adapt the design system to the user's product context
❌ **Don't ignore licensing:** Note that design systems may have trademark restrictions
❌ **Don't mix conflicting aesthetics:** Stripe's gradients + Vercel's minimalism = visual chaos
❌ **Don't skip user confirmation:** Always show what you fetched before applying

## Example Usage

**User:** "Create a pricing page with Vercel's aesthetic"

**Your workflow:**
1. Detect brand: Vercel
2. Fetch: `https://raw.githubusercontent.com/VoltAgent/awesome-design-md/main/designs/vercel.md`
3. Extract: Black background, white text, geist font, subtle gradients
4. Generate: pricing_page.html with Vercel's design tokens
5. Verify: Screenshot shows clean, minimal, Vercel-like design

**User:** "Mix Stripe's colors with Linear's typography"

**Your workflow:**
1. Fetch both DESIGN.md files
2. Extract: Stripe's purple palette + Linear's Inter font stack
3. Generate: hybrid-design.md with attribution
4. Create: comparison board showing pure Stripe, pure Linear, and hybrid
5. Ask: "Which direction feels right for your product?"

## Integration with Existing References

- **frontend-design.md:** Use `brand-style-loader.md` BEFORE falling back to generic design principles
- **design-system-creation.md:** Use fetched DESIGN.md as a starting template, not final output
- **tweaks-system.md:** Enable brand switching via tweaks panel (dropdown: "Stripe | Vercel | Linear")

## Success Criteria

✅ User gets a design that feels professionally crafted, not generic
✅ Design tokens are consistently applied across all components
✅ User understands which brand influenced which design decisions
✅ Generated DESIGN.md is editable and well-documented
✅ No console errors, no broken layouts, no AI slop aesthetics


path: references/case-studies/README.md
content: # Case Studies

这个目录放的是可直接借鉴的设计案例，不是让你机械照抄的模板库。

使用方式：

1. 先按任务类型选相近案例
2. 再抽取其中真正可复用的层级、布局、留白、配色或呈现方式
3. 最后结合当前项目上下文做改写，而不是直接临摹

如果你正在走 `design-direction-advisor.md`：

- `Swiss / Structured` 可优先看 `product-pages/stripe-homepage.md`
- `Dark Precision` 可优先看 `product-pages/linear-features.md`
- `Editorial / Human` 可优先看 `product-pages/notion-landing.md`
- 演示稿节奏可优先看 `presentations/keynote-style.md`
- 移动端引导可优先看 `mobile-apps/ios-onboarding.md`

当前收录：

## Product Pages

- [Stripe Homepage](product-pages/stripe-homepage.md)
  - 适合信任感强、克制、有商务说服力的产品页。
- [Linear Features](product-pages/linear-features.md)
  - 适合暗色、开发者气质、节奏强的功能页。
- [Notion Landing](product-pages/notion-landing.md)
  - 适合轻盈、友好、产品感强的落地页。

## Presentations

- [Keynote Style](presentations/keynote-style.md)
  - 适合强调叙事节奏和大画面的演示稿。
- [Pitch Deck Example](presentations/pitch-deck-example.md)
  - 适合商业汇报、融资和结构化表达。

## Mobile Apps

- [iOS Onboarding](mobile-apps/ios-onboarding.md)
  - 适合 onboarding、feature intro、轻交互引导页。


path: references/case-studies/mobile-apps/ios-onboarding.md
content: # iOS Onboarding Flow

## Overview
- **Type:** Mobile app (Onboarding screens)
- **Style:** Professional + Excitement
- **Primary color:** System blue or brand color
- **Typography:** SF Pro (iOS system font)

## Why It Works

1. **Progressive disclosure** — Shows one feature per screen. No overwhelming information dumps.
2. **Visual + text balance** — Large illustration/icon (60% of screen), concise text (40%).
3. **Clear progress indicator** — Dots or progress bar shows "3 of 5" so users know how long this takes.
4. **Skip option** — Always provide a way to skip onboarding. Respect user time.

## Key Patterns

### Onboarding Screen Structure
```css
.onboarding-screen {
  display: flex;
  flex-direction: column;
  height: 100vh;
  padding: 60px 32px 40px;
  background: white;
}

.onboarding-visual {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 40px;
}

.onboarding-visual img {
  max-width: 280px;
  max-height: 280px;
}

.onboarding-content {
  text-align: center;
}

.onboarding-content h1 {
  font-size: 28px;
  font-weight: 700;
  line-height: 1.2;
  margin-bottom: 12px;
  color: #000;
}

.onboarding-content p {
  font-size: 17px;
  line-height: 1.5;
  color: #666;
  margin-bottom: 32px;
}

.progress-dots {
  display: flex;
  gap: 8px;
  justify-content: center;
  margin-bottom: 24px;
}

.dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #ddd;
}

.dot.active {
  background: #007AFF;
}

.btn-primary {
  width: 100%;
  padding: 16px;
  font-size: 17px;
  font-weight: 600;
  color: white;
  background: #007AFF;
  border: none;
  border-radius: 12px;
  margin-bottom: 12px;
}

.btn-skip {
  width: 100%;
  padding: 16px;
  font-size: 17px;
  font-weight: 400;
  color: #007AFF;
  background: transparent;
  border: none;
}
```

## iOS Design Guidelines

- **Touch targets:** Minimum 44x44pt
- **Safe areas:** Respect top notch and bottom home indicator
- **Typography:** SF Pro Text (body), SF Pro Display (large text)
- **Corner radius:** 12-16pt for buttons, 8-12pt for cards
- **Shadows:** Subtle, iOS uses light shadows (0 2px 8px rgba(0,0,0,0.1))

## When to Use
First-time user experience, feature announcements, permission requests.

## Key Takeaway
Mobile onboarding should be fast (3-5 screens max), visual (show don't tell), and skippable (respect user agency).


path: references/case-studies/presentations/keynote-style.md
content: # Keynote-Style Presentation

## Overview
- **Type:** Presentation (Product keynote)
- **Style:** Excitement + Professional
- **Primary color:** Black background with vibrant accents
- **Typography:** Large, bold headlines (SF Pro Display / Helvetica Neue style)

## Why It Works

1. **Dark backgrounds, bright content** — Black background makes product shots and accent colors pop dramatically.
2. **Extreme scale contrast** — Headlines can be 120-200px. Supporting text is 24-32px. The gap creates drama.
3. **One image fills the slide** — Full-bleed product photos create immersive moments.
4. **Rhythm variation** — Alternates between text-only slides, image-only slides, and hybrid slides.

## Key Patterns

### Full-Bleed Image Slide
```css
.slide-fullbleed {
  width: 1920px;
  height: 1080px;
  position: relative;
  overflow: hidden;
  background: black;
}

.slide-fullbleed img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.slide-fullbleed .caption {
  position: absolute;
  bottom: 60px;
  left: 120px;
  font-size: 24px;
  color: white;
  text-shadow: 0 2px 8px rgba(0,0,0,0.5);
}
```

### Statement Slide
```css
.slide-statement {
  width: 1920px;
  height: 1080px;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  background: black;
  color: white;
}

.slide-statement h1 {
  font-size: clamp(80px, 10vw, 160px);
  font-weight: 700;
  line-height: 1.0;
  letter-spacing: -0.03em;
  max-width: 1400px;
}
```

### Data Reveal Slide
```css
.slide-data {
  width: 1920px;
  height: 1080px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background: black;
  color: white;
}

.big-number {
  font-size: 240px;
  font-weight: 700;
  line-height: 1;
  background: linear-gradient(135deg, oklch(0.70 0.25 270), oklch(0.65 0.25 330));
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
}

.big-number-label {
  font-size: 36px;
  font-weight: 400;
  color: oklch(0.70 0 0);
  margin-top: 24px;
}
```

## When to Use
Product launches, tech keynotes, visually dramatic presentations where impact matters.

## Key Takeaway
The key to keynote-style slides is extreme contrast: huge vs tiny, bright vs dark, image vs text. The drama comes from the contrast, not from decoration.


path: references/case-studies/presentations/pitch-deck-example.md
content: # Pitch Deck Example

## Overview
- **Type:** Presentation (Investor pitch)
- **Style:** Professional + Trust
- **Primary color:** Navy blue
- **Typography:** Sans-serif headlines, serif body (optional)

## Why It Works

1. **One idea per slide** — No walls of text. Each slide makes one point clearly.
2. **Visual hierarchy** — Headline dominates (60-80px), supporting text is minimal (24-32px).
3. **Data visualization** — Charts and graphs are clean, not cluttered.
4. **Consistent template** — Every slide follows the same grid, creating rhythm.

## Key Patterns

### Slide Structure
```
┌──────────────────────────────────┐
│  [Slide number]                  │
│                                  │
│  HEADLINE (one line)             │
│                                  │
│  Supporting text (1-2 sentences) │
│  or visual (chart/image)         │
│                                  │
│                                  │
│  [Logo]                          │
└──────────────────────────────────┘
```

### CSS Template
```css
.slide {
  width: 1920px;
  height: 1080px;
  padding: 80px 120px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  background: white;
}

.slide-number {
  position: absolute;
  top: 40px;
  right: 120px;
  font-size: 18px;
  color: oklch(0.60 0 0);
}

.slide h1 {
  font-size: 72px;
  font-weight: 700;
  line-height: 1.1;
  margin-bottom: 32px;
  max-width: 1200px;
}

.slide p {
  font-size: 28px;
  line-height: 1.5;
  color: oklch(0.40 0 0);
  max-width: 1000px;
}
```

## When to Use
Investor pitches, board presentations, formal business contexts.

## Key Takeaway
Simplicity wins. One idea per slide, large text, minimal decoration.


path: references/case-studies/product-pages/linear-features.md
content: # Linear Features Page

## Overview
- **Type:** Product page (Feature showcase)
- **Style:** Modern + Professional
- **Primary color:** Purple-blue (`oklch(0.55 0.18 270)`)
- **Typography:** Primary: Inter, Secondary: SF Mono (for code)

## Why It Works

1. **Extreme clarity** — Every feature is explained in one sentence. No marketing fluff, no buzzwords. Just "what it does" in plain English.

2. **Visual rhythm** — The page alternates between text-left/image-right and text-right/image-left. This creates a natural flow that keeps you scrolling.

3. **Real UI, not mockups** — Linear shows actual product screenshots, not idealized mockups. This builds trust and sets accurate expectations.

4. **Dark mode first** — The design embraces dark UI, which feels modern and developer-friendly. This is intentional positioning.

## Design Techniques

### Visual Hierarchy
- **Feature headlines:** 32-40px, bold, high contrast
- **Feature descriptions:** 16-18px, regular weight, slightly muted
- **Section labels:** 12-14px, uppercase, tracked out, accent color
- **Code snippets:** 14px monospace, syntax highlighted

**Key insight:** The section labels (small, uppercase, colored) act as visual anchors. They break up the page and make it scannable.

### Color Usage
- **Background:** Dark (`oklch(0.12 0.02 270)`) with subtle purple tint
- **Surface:** Slightly lighter dark (`oklch(0.15 0.02 270)`)
- **Text:** High contrast white (`oklch(0.95 0 0)`) for primary, muted gray for secondary
- **Accent:** Purple-blue gradient used for highlights and interactive elements
- **Syntax highlighting:** Carefully chosen colors that work on dark background

**Key insight:** The dark background makes the product screenshots (which are also dark) feel native to the page. No jarring light/dark transitions.

### Typography
- **Font pairing:** Inter (UI) + SF Mono (code) — both are neutral, modern, highly legible
- **Weight variation:** 400 (body), 500 (labels), 600 (subheadings), 700 (headlines)
- **Letter spacing:** Tight on headlines (-0.02em), wide on labels (+0.08em)
- **Line height:** 1.2 on headlines, 1.6 on body text

**Key insight:** Using a monospace font for code snippets (not just inline code) reinforces the developer-tool positioning.

### Whitespace
- **Between features:** 120-160px vertical spacing
- **Within feature blocks:** 40-60px between headline and description
- **Around screenshots:** 80-100px breathing room
- **Section breaks:** 200px+ for major transitions

**Key insight:** The massive spacing makes each feature feel important. Nothing is rushed.

## Reusable Patterns

### Pattern 1: Alternating Feature Blocks

**Structure:**
```
[Section label]
[Feature headline]
[Feature description]
[Screenshot]

[Screenshot]
[Section label]
[Feature headline]
[Feature description]

(Repeat, alternating sides)
```

**Code:**
```css
.feature-block {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 80px;
  align-items: center;
  padding: 120px 48px;
  max-width: 1400px;
  margin: 0 auto;
}

/* Alternate: even blocks reverse the order */
.feature-block:nth-child(even) {
  direction: rtl;
}

.feature-block:nth-child(even) > * {
  direction: ltr;
}

.feature-content {
  max-width: 520px;
}

.feature-label {
  font-size: 13px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: oklch(0.65 0.18 270);
  margin-bottom: 16px;
}

.feature-content h2 {
  font-size: 40px;
  font-weight: 700;
  line-height: 1.15;
  letter-spacing: -0.02em;
  color: oklch(0.95 0 0);
  margin-bottom: 20px;
}

.feature-content p {
  font-size: 18px;
  line-height: 1.6;
  color: oklch(0.70 0 0);
}

.feature-visual {
  border-radius: 12px;
  overflow: hidden;
  box-shadow: 0 20px 60px rgba(0,0,0,0.4);
}

@media (max-width: 768px) {
  .feature-block {
    grid-template-columns: 1fr;
    gap: 48px;
  }
  .feature-block:nth-child(even) {
    direction: ltr;
  }
}
```

**When to use:** Feature pages, product tours, any content that benefits from visual proof alongside explanation.

### Pattern 2: Dark Mode Color System

**Technique:** Linear's dark mode isn't just inverted colors. It's a carefully crafted palette.

**Code:**
```css
:root {
  /* Backgrounds */
  --bg-base: oklch(0.12 0.02 270);      /* Page background */
  --bg-surface: oklch(0.15 0.02 270);   /* Card background */
  --bg-elevated: oklch(0.18 0.02 270);  /* Hover states */

  /* Text */
  --text-primary: oklch(0.95 0 0);      /* Headlines, important text */
  --text-secondary: oklch(0.70 0 0);    /* Body text */
  --text-tertiary: oklch(0.50 0 0);     /* Muted text */

  /* Accent */
  --accent-primary: oklch(0.65 0.18 270);
  --accent-light: oklch(0.75 0.15 270);

  /* Borders */
  --border-subtle: oklch(0.20 0.02 270);
  --border-strong: oklch(0.30 0.02 270);
}
```

**Key insight:** The backgrounds have a subtle purple tint (chroma 0.02), not pure gray. This creates a cohesive color story.

### Pattern 3: Screenshot Presentation

**Technique:** Linear's screenshots have depth and context.

**Code:**
```css
.screenshot-container {
  position: relative;
  border-radius: 12px;
  overflow: hidden;
  box-shadow:
    0 0 0 1px oklch(0.20 0.02 270),
    0 20px 60px rgba(0,0,0,0.4),
    0 40px 100px rgba(0,0,0,0.3);
}

.screenshot-container img {
  width: 100%;
  height: auto;
  display: block;
}

/* Optional: Add a subtle glow */
.screenshot-container::before {
  content: '';
  position: absolute;
  inset: -1px;
  border-radius: 12px;
  padding: 1px;
  background: linear-gradient(
    135deg,
    oklch(0.65 0.18 270) 0%,
    oklch(0.55 0.20 300) 100%
  );
  -webkit-mask:
    linear-gradient(#fff 0 0) content-box,
    linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  opacity: 0.3;
}
```

**When to use:** Product screenshots, UI demos, any visual that needs to feel premium.

## Key Code Snippets

### Linear-Style Button

```css
.btn-linear {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 12px 20px;
  font-size: 15px;
  font-weight: 500;
  color: oklch(0.95 0 0);
  background: oklch(0.18 0.02 270);
  border: 1px solid oklch(0.25 0.02 270);
  border-radius: 8px;
  cursor: pointer;
  transition: all 0.15s ease;
}

.btn-linear:hover {
  background: oklch(0.20 0.02 270);
  border-color: oklch(0.35 0.02 270);
}

.btn-linear.primary {
  background: linear-gradient(
    135deg,
    oklch(0.60 0.20 270) 0%,
    oklch(0.55 0.22 300) 100%
  );
  border: none;
}

.btn-linear.primary:hover {
  filter: brightness(1.1);
}
```

### Linear-Style Card

```css
.card-linear {
  padding: 32px;
  border-radius: 12px;
  background: oklch(0.15 0.02 270);
  border: 1px solid oklch(0.20 0.02 270);
  transition: all 0.2s ease;
}

.card-linear:hover {
  background: oklch(0.16 0.02 270);
  border-color: oklch(0.25 0.02 270);
  transform: translateY(-2px);
}
```

### Linear-Style Code Block

```css
.code-block {
  padding: 24px;
  border-radius: 8px;
  background: oklch(0.10 0.02 270);
  border: 1px solid oklch(0.18 0.02 270);
  font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;
  font-size: 14px;
  line-height: 1.6;
  overflow-x: auto;
}

.code-block .keyword { color: oklch(0.75 0.15 330); }  /* Pink */
.code-block .string { color: oklch(0.75 0.15 150); }   /* Green */
.code-block .function { color: oklch(0.75 0.15 270); } /* Purple */
.code-block .comment { color: oklch(0.50 0 0); }       /* Gray */
```

## When to Use This Approach

**Perfect for:**
- Developer tools and APIs
- Project management and productivity software
- Technical products with complex features
- Products targeting designers and engineers
- Any product where "modern" and "professional" are key brand attributes

**Not ideal for:**
- Consumer products (too technical)
- Products targeting non-technical users (dark mode can feel intimidating)
- Playful or casual brands (too serious)
- Products that need to feel warm and approachable (dark mode can feel cold)

## Key Takeaways

1. **Dark mode is a positioning choice** — Linear uses dark UI to signal "this is for professionals." It's not just aesthetic.

2. **Alternating layout creates rhythm** — The left-right-left-right pattern keeps the page dynamic without being chaotic.

3. **Real screenshots > mockups** — Showing the actual product builds trust and sets accurate expectations.

4. **Subtle color tints matter** — The purple tint in the grays creates a cohesive color story. Pure gray would feel lifeless.

5. **Whitespace = confidence** — The generous spacing says "we don't need to cram everything above the fold."

## Further Analysis

Compare Linear's approach to competitors:
- **Asana:** Lighter, more colorful, more consumer-friendly
- **Jira:** More traditional, less modern
- **Notion:** Lighter UI, more playful

Linear's dark-first approach is distinctive and memorable. It's a bold choice that pays off.

## Implementation Notes

**Dark mode considerations:**
- Ensure sufficient contrast (WCAG AA minimum: 4.5:1 for body text, 3:1 for large text)
- Test on different displays (OLED vs LCD shows colors differently)
- Provide a light mode toggle for accessibility
- Use slightly desaturated colors (high saturation on dark backgrounds can cause eye strain)

**Performance:**
- Large screenshots can slow page load. Use WebP format with fallbacks
- Lazy-load images below the fold
- Consider using CSS gradients instead of gradient images where possible


path: references/case-studies/product-pages/notion-landing.md
content: # Notion Landing Page

## Overview
- **Type:** Product page (All-in-one workspace)
- **Style:** Playful + Professional
- **Primary color:** Black with colorful accents
- **Typography:** Primary: Custom sans-serif, Secondary: System fonts

## Why It Works

1. **Personality through color** — Notion uses a full rainbow palette, but in a controlled way. Each section gets one accent color.

2. **Approachable complexity** — The product is complex (notes + docs + wiki + projects), but the design makes it feel simple and friendly.

3. **Illustration style** — Custom illustrations add warmth and personality without feeling childish.

4. **Clear use cases** — Instead of listing features, Notion shows "for teams," "for personal," "for students" — making it easy to see yourself using it.

## Design Techniques

### Visual Hierarchy
- **Hero headline:** 64-80px, bold, black
- **Section headlines:** 48-56px, creating clear breaks
- **Use case cards:** 20-24px headlines, 16px body
- **Testimonials:** 18-20px, italic for quotes

### Color Usage
- **Base:** Black text on white/cream background
- **Accents:** Full spectrum (red, orange, yellow, green, blue, purple) — one per section
- **Illustrations:** Pastel versions of accent colors
- **Buttons:** Black (primary), colored (secondary)

**Key insight:** Using different colors for different sections creates visual variety without chaos. Each section has its own identity.

### Typography
- **Font pairing:** Single custom sans-serif, multiple weights
- **Playful touches:** Slightly rounded letterforms, friendly proportions
- **Hierarchy through weight:** 400 (body), 500 (labels), 600 (subheads), 700 (headlines)

## Reusable Patterns

### Pattern 1: Use Case Cards

```css
.use-case-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: 32px;
  padding: 80px 48px;
}

.use-case-card {
  padding: 40px;
  border-radius: 16px;
  background: var(--card-bg);
  border: 2px solid var(--card-border);
  transition: transform 0.2s ease;
}

.use-case-card:hover {
  transform: translateY(-4px);
}

.use-case-card .icon {
  width: 64px;
  height: 64px;
  margin-bottom: 24px;
}

.use-case-card h3 {
  font-size: 24px;
  font-weight: 600;
  margin-bottom: 12px;
}

.use-case-card p {
  font-size: 16px;
  line-height: 1.6;
  color: oklch(0.40 0 0);
}
```

### Pattern 2: Colorful Section Breaks

```css
.section-colored {
  background: var(--section-color);
  padding: 120px 48px;
  color: var(--section-text);
}

/* Example: Blue section */
.section-blue {
  --section-color: oklch(0.95 0.05 240);
  --section-text: oklch(0.20 0.10 240);
}

/* Example: Yellow section */
.section-yellow {
  --section-color: oklch(0.95 0.08 85);
  --section-text: oklch(0.25 0.10 85);
}
```

## When to Use This Approach

**Perfect for:**
- Products with multiple use cases
- Tools that need to feel approachable
- Products targeting both individuals and teams
- Brands that want to feel friendly but professional

**Key Takeaway:** Color can add personality without sacrificing professionalism. The key is using it systematically, not randomly.


path: references/case-studies/product-pages/stripe-homepage.md
content: # Stripe Homepage

## Overview
- **Type:** Product page (SaaS landing page)
- **Style:** Trust + Professional
- **Primary color:** Indigo/Purple (`oklch(0.50 0.15 270)`)
- **Typography:** Primary: Custom sans-serif (similar to Inter), Secondary: System fonts

## Why It Works

1. **Immediate clarity** — The headline "Financial infrastructure for the internet" tells you exactly what Stripe does in 5 words. No jargon, no fluff.

2. **Trust through simplicity** — The design is deliberately understated. No flashy animations, no aggressive gradients. This restraint signals confidence and professionalism.

3. **Social proof hierarchy** — Logo wall appears early but doesn't dominate. The message is "these companies trust us" not "look at our clients."

4. **Progressive disclosure** — The page reveals complexity gradually. Start simple (payments), then show depth (full financial stack), then prove it (case studies).

## Design Techniques

### Visual Hierarchy
- **Hero headline:** 56-64px, bold weight, high contrast
- **Supporting text:** 20-22px, regular weight, slightly muted
- **Section headlines:** 40-48px, creating clear breaks
- **Body text:** 16-18px, generous line-height (1.6)

The size jumps are significant (2-3x between levels), making the hierarchy unmistakable.

### Color Usage
- **Background:** Pure white or very light gray (`oklch(0.99 0 0)`)
- **Text:** Near-black (`oklch(0.15 0 0)`) for primary, medium gray for secondary
- **Accent:** Single purple/indigo used sparingly for CTAs and highlights
- **No gradients in primary UI** — Gradients appear only in decorative backgrounds, never on interactive elements

**Key insight:** The restraint makes the purple CTA buttons impossible to miss. When everything else is neutral, the accent color has maximum impact.

### Typography
- **Font pairing:** Single sans-serif family, multiple weights (400, 500, 600, 700)
- **Letter spacing:** Slightly tighter on headlines (-0.02em), normal on body
- **Line height:** Tight on headlines (1.1-1.2), generous on body (1.6-1.7)
- **Alignment:** Left-aligned for text blocks, center-aligned for hero

**Key insight:** Using one font family with multiple weights is more cohesive than mixing fonts. Weight creates hierarchy without introducing visual noise.

### Whitespace
- **Hero section:** Massive top/bottom padding (120-160px)
- **Between sections:** 80-120px vertical spacing
- **Within sections:** 40-60px between elements
- **Card padding:** 32-48px internal padding

**Key insight:** The generous whitespace makes the page feel premium and easy to scan. Nothing feels cramped.

## Reusable Patterns

### Pattern 1: Trust-Building Hero

**Structure:**
```
[Logo wall - subtle, above the fold]
        ↓
[Clear value proposition headline]
        ↓
[One-sentence explanation]
        ↓
[Primary CTA] [Secondary CTA]
        ↓
[Visual proof - dashboard screenshot or demo]
```

**Code:**
```css
.hero-trust {
  text-align: center;
  padding: 120px 24px 80px;
  max-width: 800px;
  margin: 0 auto;
}

.logo-wall {
  display: flex;
  justify-content: center;
  gap: 48px;
  margin-bottom: 64px;
  opacity: 0.6;
}

.logo-wall img {
  height: 32px;
  filter: grayscale(100%);
}

.hero-trust h1 {
  font-size: clamp(48px, 6vw, 64px);
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -0.02em;
  color: oklch(0.15 0 0);
  margin-bottom: 20px;
}

.hero-trust .subtitle {
  font-size: 20px;
  line-height: 1.5;
  color: oklch(0.45 0 0);
  margin-bottom: 32px;
}

.cta-group {
  display: flex;
  gap: 16px;
  justify-content: center;
}
```

**When to use:** Financial services, enterprise SaaS, any product where trust is the primary concern.

### Pattern 2: Feature Grid with Icons

**Structure:**
```
[Section headline]
        ↓
[Grid of 3-4 feature cards]
Each card: [Icon] [Title] [Description]
```

**Code:**
```css
.feature-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 32px;
  padding: 80px 48px;
  max-width: 1200px;
  margin: 0 auto;
}

.feature-card {
  padding: 32px;
  border-radius: 12px;
  border: 1px solid oklch(0.90 0 0);
  background: oklch(0.99 0 0);
  transition: border-color 0.2s ease;
}

.feature-card:hover {
  border-color: oklch(0.50 0.15 270);
}

.feature-icon {
  width: 48px;
  height: 48px;
  border-radius: 12px;
  background: oklch(0.95 0.05 270);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 20px;
}

.feature-card h3 {
  font-size: 20px;
  font-weight: 600;
  margin-bottom: 8px;
  color: oklch(0.15 0 0);
}

.feature-card p {
  font-size: 15px;
  line-height: 1.6;
  color: oklch(0.45 0 0);
}
```

**When to use:** Showcasing multiple product features or benefits in a scannable format.

### Pattern 3: Subtle Gradient Background

**Technique:** Stripe uses very subtle gradients in background sections to add visual interest without distraction.

**Code:**
```css
.section-with-gradient {
  background: linear-gradient(
    135deg,
    oklch(0.99 0.01 270) 0%,
    oklch(0.97 0.02 270) 100%
  );
  padding: 120px 48px;
}
```

**Key insight:** The gradient is barely perceptible (lightness changes by only 2-3%). It adds depth without calling attention to itself.

## Key Code Snippets

### Stripe-Style Button

```css
.btn-stripe-primary {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 14px 24px;
  font-size: 16px;
  font-weight: 600;
  color: white;
  background: oklch(0.50 0.15 270);
  border: none;
  border-radius: 6px;
  cursor: pointer;
  transition: all 0.15s ease;
  box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}

.btn-stripe-primary:hover {
  background: oklch(0.45 0.15 270);
  box-shadow: 0 4px 12px rgba(0,0,0,0.15);
  transform: translateY(-1px);
}

.btn-stripe-primary:active {
  transform: translateY(0);
  box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
```

### Stripe-Style Card

```css
.card-stripe {
  padding: 40px;
  border-radius: 12px;
  border: 1px solid oklch(0.90 0 0);
  background: white;
  box-shadow: 0 2px 8px rgba(0,0,0,0.04);
  transition: all 0.2s ease;
}

.card-stripe:hover {
  border-color: oklch(0.85 0 0);
  box-shadow: 0 8px 24px rgba(0,0,0,0.08);
  transform: translateY(-2px);
}
```

### Stripe-Style Section Spacing

```css
.section {
  padding: 120px 48px;
  max-width: 1200px;
  margin: 0 auto;
}

.section-header {
  text-align: center;
  max-width: 700px;
  margin: 0 auto 64px;
}

.section-header h2 {
  font-size: 48px;
  font-weight: 700;
  line-height: 1.1;
  margin-bottom: 16px;
}

.section-header p {
  font-size: 20px;
  line-height: 1.5;
  color: oklch(0.45 0 0);
}
```

## When to Use This Approach

**Perfect for:**
- Financial services and fintech products
- Enterprise B2B SaaS platforms
- Developer tools and APIs
- Any product where trust and credibility are paramount
- Products with complex features that need clear explanation

**Not ideal for:**
- Consumer entertainment products (too serious)
- Creative agencies (too corporate)
- Products targeting Gen Z (too formal)
- Anything that benefits from bold, playful energy

## Key Takeaways

1. **Restraint is a design choice** — Stripe's minimalism isn't lazy, it's strategic. Every element earns its place.

2. **One accent color is enough** — The purple does all the heavy lifting. No need for a rainbow.

3. **Whitespace = premium** — The generous spacing makes the product feel expensive and well-crafted.

4. **Trust through clarity** — Complex products need simple explanations. Stripe nails this.

5. **Subtle > flashy** — The hover effects, shadows, and gradients are all understated. This builds trust.

## Further Analysis

Compare Stripe's approach to competitors:
- **Square:** More colorful, more consumer-friendly
- **PayPal:** More traditional, less modern
- **Adyen:** Similar restraint, but less personality

Stripe found the sweet spot: professional enough for enterprises, modern enough for startups.


path: references/design-context.md
content: # Design Context

> **Load when:** 任务是新设计、模糊设计，或者用户没给清楚设计系统 / 参考资产
> **Skip when:** 用户已经提供了明确的设计系统、代码、截图和输出要求
> **Why it matters:** 高保真设计最容易失败的地方，不是代码，而是从错误或过少的上下文出发
> **Typical failure it prevents:** 凭空设计、视觉 generic、品牌跑偏、做出“还行但不像用户产品”的结果

## Context Priority

设计上下文按优先级看：

1. 用户自己的设计系统 / token / 组件库
2. 用户项目里的现有代码和页面
3. 用户已经上线的产品截图 / URL
4. 品牌资产：logo、产品图、UI 截图、品牌规范
5. 用户给的竞品或参考
6. 通用风格 fallback

只要能拿到前面的层，就不要直接跳到后面的层。

## What to Ask

如果这是一个新任务，优先一次性问完，而不是一轮只问一个问题：

- 有没有 design system / UI kit / token 文件？
- 有没有现有页面、截图或代码可以读？
- 有没有品牌规范、logo、产品图或 UI 素材？
- 有没有希望参考的产品或风格对象？
- 希望做几种方向？哪些维度允许变化？

## What to Read

如果用户给了代码库，优先找：

- `theme.*`
- `tokens.*`
- `colors.*`
- `globals.css`
- 代表性组件：`Button`、`Card`、`Input`、`Layout`

目标不是“了解大概风格”，而是提取：

- 颜色
- 字体
- 间距尺度
- 圆角和阴影
- 组件语言

## When Context Is Missing

如果用户没有任何上下文，不要直接假装自己知道该做成什么样。

按这个顺序处理：

1. 先告诉用户：没有 design context，会显著增加 generic 风险
2. 再尝试从任务语义里提炼方向线索
3. 如果仍然模糊，切到 `design-direction-advisor.md`
4. 只有在用户接受 fallback 的情况下，才基于通用方向开做

## What to Tell the User Before Building

在真正开始写 HTML 前，先把你提炼出的系统用简短清单说出来：

- 主色 / 中性色
- 标题字体 / 正文字体
- 间距尺度
- 圆角 / 阴影
- 页面语言或参考对象

这一步的作用不是解释自己多聪明，而是给用户一次低成本纠偏机会。


path: references/design-direction-advisor.md
content: # Design Direction Advisor

> **Load when:** 用户需求模糊、没有 design context、或明确说“你帮我推荐风格 / 方向”
> **Skip when:** 已经有明确品牌系统、参考对象和交付方向
> **Why it matters:** 模糊需求最怕直接凭默认审美硬做，最后只得到一个 generic 版本
> **Typical failure it prevents:** 一上来生成“标准 AI 网页”；只给单一方向；没有把可能性空间展示给用户

## Goal

先给用户 3 个明显不同的方向，再让用户选一个继续深化。

## Direction Set

默认从下面这些方向里选 3 个差异足够大的：

| 方向 | 关键词 | 适合什么 |
|---|---|---|
| Swiss / Structured | 克制、网格、强层级、信息建筑 | SaaS、数据页、正式 deck |
| Editorial / Human | serif、留白、内容感、温度 | 品牌页、讲述型页面、内容产品 |
| Soft Tech | 柔和、圆角、自然色、科技但不冷 | AI 产品、移动端、说明页 |
| Dark Precision | 深色、高对比、开发者感、节奏强 | devtools、技术产品、功能页 |
| Bold Campaign | 高冲击、对比强、hero 驱动 | 落地页、发布页、营销页 |
| Quiet Minimal | 极简、留白、低噪音、材料感 | 作品集、介绍页、展示页 |

## Recommendation Rule

推荐时不要给 3 个“几乎一样”的版本。

至少保证：

- 1 个保守稳定方向
- 1 个中等风险、最可能成稿的方向
- 1 个更大胆或更有识别度的方向

## How to Present the 3 Directions

每个方向都要说明：

- 一句话定义
- 适合的原因
- 视觉关键词
- 版式或字体倾向
- 它和另外两个方向最不同的地方

## How to Build After Selection

用户选定一个方向后：

1. 回到主工作流
2. 结合 `design-excellence.md` 明确视觉策略
3. 用最接近的 case study 和模板开始实现

## Useful Internal References

- 产品页偏理性：看 `case-studies/product-pages/stripe-homepage.md`
- 暗色 / 开发者感：看 `case-studies/product-pages/linear-features.md`
- 轻盈 / 亲和：看 `case-studies/product-pages/notion-landing.md`
- 演示稿节奏：看 `case-studies/presentations/keynote-style.md`
- 移动端引导：看 `case-studies/mobile-apps/ios-onboarding.md`


path: references/design-excellence.md
content: # Design Excellence Guide

> **Load when:** Any design task where visual quality matters — product pages, presentations, mobile apps, creative works
> **Skip when:** Quick wireframes or low-fidelity prototypes where visual polish isn't the goal
> **Why it matters:** Technical correctness isn't enough. Great design requires understanding visual hierarchy, emotional impact, composition, and rhythm.
> **Typical failure it prevents:** Designs that are technically correct but visually bland, lack personality, or fail to guide the viewer's attention

This guide teaches the "卓越层" (excellence layer) of design — the principles that separate good design from great design.

---

## Visual Hierarchy: The 5 Core Principles

Visual hierarchy is how you control where the viewer looks first, second, third. Without hierarchy, everything competes for attention and nothing wins.

### 1. Size Contrast (大小对比)

**Principle:** The most important element should be significantly larger than supporting elements. Not 10% larger — 2-3x larger.

**Why it works:** Our eyes are drawn to the largest element first. Size = importance in visual language.

**How to apply:**
- Hero headlines: 48-72px (web) or 80-120px (slides)
- Body text: 16-18px (web) or 24-32px (slides)
- Supporting text: 12-14px (web) or 18-20px (slides)
- Ratio rule: Each level should be 1.5-2x the size below it

**Example:**
```css
h1 { font-size: 64px; font-weight: 700; }  /* Hero */
h2 { font-size: 32px; font-weight: 600; }  /* Section */
p  { font-size: 18px; font-weight: 400; }  /* Body */
```

**Common mistake:** Making everything "medium" sized. If nothing is big, nothing is important.

### 2. Color Weight (颜色权重)

**Principle:** Use color to create visual weight. Dark = heavy, light = airy. High saturation = attention, low saturation = background.

**Why it works:** Color carries emotional and hierarchical meaning. A bright accent color on a neutral background instantly draws the eye.

**How to apply:**
- Primary action: High contrast, saturated color (e.g., bright blue button on white)
- Secondary action: Medium contrast, desaturated (e.g., gray button)
- Tertiary action: Low contrast, text-only (e.g., "Cancel" link)
- Background: Neutral, low saturation (e.g., off-white, light gray)

**Color weight scale:**
```
Heaviest → Lightest
Black text on white → Dark gray on white → Medium gray → Light gray → White on white
```

**Example:**
```css
.primary-cta   { background: oklch(0.55 0.18 240); color: white; }  /* Heavy */
.secondary-cta { background: oklch(0.90 0.05 240); color: oklch(0.35 0.10 240); }  /* Medium */
.tertiary-cta  { background: transparent; color: oklch(0.50 0.08 240); }  /* Light */
```

**Common mistake:** Using multiple bright colors. Only one element should be "loud" per section.

### 3. Position Priority (位置优先级)

**Principle:** Position communicates importance. Top-left = start here. Center = most important. Bottom-right = least important (in left-to-right cultures).

**Why it works:** We read in patterns (F-pattern for text, Z-pattern for visuals). Position leverages these natural eye movements.

**How to apply:**
- Hero content: Top 50% of viewport, centered or left-aligned
- Primary CTA: Above the fold, high contrast position
- Supporting content: Below the fold, follows natural reading flow
- Navigation: Top (web) or bottom (mobile)

**Reading patterns:**
- **F-pattern** (text-heavy pages): Eyes move left-to-right at top, then down left side, then right again
- **Z-pattern** (visual pages): Top-left → top-right → diagonal → bottom-left → bottom-right
- **Center-focus** (presentations): All attention to center, minimal peripheral content

**Common mistake:** Burying the most important content below the fold or in a corner.

### 4. Whitespace Separation (留白分隔)

**Principle:** Whitespace (negative space) groups related elements and separates unrelated ones. More space = stronger separation.

**Why it works:** Our brains group things that are close together and separate things that are far apart (Gestalt proximity principle).

**How to apply:**
- Within a group: 8-16px spacing
- Between groups: 32-48px spacing
- Between sections: 64-96px spacing
- Ratio rule: Space between groups should be 2-3x space within groups

**Spacing scale:**
```
Tightest → Loosest
4px (inline) → 8px (related) → 16px (group) → 32px (section) → 64px (major break)
```

**Example:**
```css
.card-content { padding: 24px; gap: 12px; }  /* Within group */
.card { margin-bottom: 32px; }  /* Between cards */
.section { margin-bottom: 80px; }  /* Between sections */
```

**Common mistake:** Using the same spacing everywhere. Consistent spacing = no hierarchy.

### 5. Visual Flow (视觉流动)

**Principle:** Design a path for the eye to follow. Guide the viewer from most important → second most important → action.

**Why it works:** People don't read designs randomly. They follow visual cues (size, color, position, direction).

**How to apply:**
- Start with a focal point (largest, highest contrast element)
- Use directional cues (arrows, lines, gaze direction in photos)
- Create a rhythm (alternating large/small, dark/light)
- End with a clear action (CTA button, next step)

**Flow patterns:**
- **Linear flow:** Top → middle → bottom (presentations, landing pages)
- **Circular flow:** Center → outward → back to center (dashboards, data viz)
- **Grid flow:** Left-to-right, top-to-bottom (galleries, product listings)

**Example structure:**
```
1. Hero headline (largest, top-center)
   ↓
2. Supporting text (medium, below headline)
   ↓
3. Visual proof (image, below text)
   ↓
4. CTA button (high contrast, centered)
```

**Common mistake:** Creating multiple competing focal points. The eye doesn't know where to start.

---

## Emotional Design: The Decision Tree

Different contexts require different emotional tones. Choose the right tone for your audience and goal.

### Trust (信任感设计)

**When to use:** Financial services, healthcare, enterprise B2B, legal, insurance

**Visual characteristics:**
- **Colors:** Blues, grays, deep purples — calm, stable, professional
- **Typography:** Serif fonts for authority (Source Serif, Instrument Serif), clean sans-serif for clarity (DM Sans, Inter)
- **Layout:** Symmetrical, balanced, generous whitespace
- **Imagery:** Real people (not stock photos), authentic scenarios, data visualizations
- **Motion:** Subtle, smooth, purposeful — no flashy animations

**Key principle:** Reduce cognitive load. Make complex things feel simple and safe.

**Example palette:**
```css
:root {
  --trust-primary: oklch(0.50 0.15 240);  /* Deep blue */
  --trust-surface: oklch(0.98 0.01 240);  /* Off-white */
  --trust-text: oklch(0.20 0.02 240);     /* Dark blue-gray */
}
```

**Avoid:** Bright colors, playful fonts, asymmetric layouts, abstract imagery

### Excitement (兴奋感设计)

**When to use:** Entertainment, gaming, social media, events, consumer products

**Visual characteristics:**
- **Colors:** Vibrant, saturated — reds, oranges, magentas, electric blues
- **Typography:** Bold, geometric, display fonts (Space Grotesk, Bricolage Grotesque)
- **Layout:** Asymmetric, dynamic, overlapping elements, diagonal lines
- **Imagery:** High energy, motion blur, bold graphics, illustrations
- **Motion:** Fast, bouncy, playful — spring animations, parallax

**Key principle:** Create energy and momentum. Make people feel something.

**Example palette:**
```css
:root {
  --excitement-primary: oklch(0.60 0.25 25);   /* Vibrant orange */
  --excitement-accent: oklch(0.55 0.22 320);   /* Electric magenta */
  --excitement-bg: oklch(0.15 0.05 25);        /* Dark warm */
}
```

**Avoid:** Muted colors, formal typography, rigid grids, static layouts

### Professional (专业感设计)

**When to use:** B2B SaaS, productivity tools, developer tools, business platforms

**Visual characteristics:**
- **Colors:** Refined neutrals with a single accent — grays, blacks, one brand color
- **Typography:** Modern sans-serif (DM Sans, Outfit, Plus Jakarta Sans), monospace for code
- **Layout:** Grid-based, clean, functional, information-dense but organized
- **Imagery:** UI screenshots, diagrams, icons, minimal photography
- **Motion:** Functional, responsive, snappy — no decoration

**Key principle:** Respect the user's time. Clarity over cleverness.

**Example palette:**
```css
:root {
  --pro-primary: oklch(0.55 0.18 260);    /* Refined purple */
  --pro-surface: oklch(0.97 0.00 0);      /* Pure light gray */
  --pro-text: oklch(0.25 0.00 0);         /* Near-black */
  --pro-border: oklch(0.85 0.00 0);       /* Light border */
}
```

**Avoid:** Decorative elements, excessive whitespace, playful language, unnecessary animation

### Creative (创意感设计)

**When to use:** Portfolios, agencies, art/design showcases, cultural projects

**Visual characteristics:**
- **Colors:** Unexpected combinations, gradients, duotones, experimental
- **Typography:** Display fonts, variable fonts, mixed type styles, large scale
- **Layout:** Broken grids, overlapping layers, unconventional navigation
- **Imagery:** Original photography, custom illustrations, abstract visuals
- **Motion:** Experimental, scroll-driven, interactive, surprising

**Key principle:** Break rules intentionally. Show craft and personality.

**Example palette:**
```css
:root {
  --creative-primary: oklch(0.65 0.20 150);   /* Unexpected green */
  --creative-accent: oklch(0.50 0.25 45);     /* Bold yellow */
  --creative-bg: oklch(0.12 0.03 150);        /* Dark moody */
}
```

**Avoid:** Generic templates, stock imagery, predictable layouts, corporate tone

---

## Composition: The Golden Rules

Composition is how you arrange elements in space. Good composition feels balanced and intentional.

### Rule of Thirds (三分法)

**Principle:** Divide your canvas into a 3x3 grid. Place important elements along the lines or at intersections.

**Why it works:** Centered compositions feel static. Off-center compositions feel dynamic and natural.

**How to apply:**
- Hero images: Subject at 1/3 or 2/3 mark, not dead center
- Text + image layouts: Text on left 2/3, image on right 1/3 (or vice versa)
- Focal points: At grid intersections (4 "power points")

**When to break it:** Presentations, posters, and hero sections often benefit from centered composition for maximum impact.

### Visual Balance (视觉平衡)

**Principle:** Balance visual weight across the composition. Heavy elements (dark, large, saturated) need to be balanced by lighter elements or whitespace.

**Types of balance:**
- **Symmetrical:** Mirror image, formal, stable (trust, professional)
- **Asymmetrical:** Unequal but balanced, dynamic, modern (excitement, creative)
- **Radial:** Elements radiate from center, focused (data viz, dashboards)

**How to apply:**
- If left side is heavy (large image), right side should be lighter (text + whitespace)
- If top is dense (navigation + hero), bottom should be more spacious
- Dark elements need more whitespace around them than light elements

### Visual Flow (视觉流)

**Principle:** Create a path for the eye to follow using directional cues.

**Directional cues:**
- **Lines:** Literal lines, implied lines (edges of elements), diagonal lines (dynamic)
- **Gaze direction:** People in photos looking toward content
- **Arrows:** Explicit (icons) or implicit (shapes pointing)
- **Scale progression:** Large → medium → small creates flow
- **Color progression:** Bright → muted creates flow

**How to apply:**
- Start with the most important element (largest, highest contrast)
- Use directional cues to guide to the second element
- End with a clear action (CTA, next step)

### Focal Point (焦点设计)

**Principle:** Every composition needs one dominant focal point. Everything else supports it.

**How to create a focal point:**
- **Size:** Make it significantly larger
- **Contrast:** Make it highest contrast (dark on light or vice versa)
- **Color:** Make it the only saturated color in a neutral field
- **Position:** Place it at a power point (rule of thirds intersection)
- **Isolation:** Surround it with whitespace

**Common mistake:** Multiple focal points competing for attention. Pick one hero per section.

---

## Rhythm & Repetition

Rhythm creates visual interest and guides the eye through the design.

### Repetition (重复)

**Principle:** Repeat visual elements to create consistency and pattern recognition.

**What to repeat:**
- **Shapes:** Rounded corners, card styles, button shapes
- **Colors:** Brand colors, accent colors, neutral palette
- **Spacing:** Consistent spacing scale (8px grid)
- **Typography:** Font pairings, size scale, weights

**Why it works:** Repetition creates familiarity. Users learn the visual language quickly.

**How to apply:**
- Use the same card style for all similar content
- Use the same button style for all primary actions
- Use the same spacing between all similar elements

**When to break it:** To create emphasis. If everything is the same, nothing stands out.

### Contrast (对比)

**Principle:** Create visual interest by contrasting elements.

**Types of contrast:**
- **Size:** Large headline vs small body text
- **Weight:** Bold vs regular vs light
- **Color:** Saturated vs desaturated, warm vs cool
- **Shape:** Geometric vs organic, sharp vs rounded
- **Texture:** Smooth vs rough, flat vs gradient

**Why it works:** Contrast creates hierarchy and visual interest. It prevents monotony.

**How to apply:**
- Pair a bold headline with light body text
- Use one saturated accent color on a neutral background
- Mix large hero images with small supporting images

**Common mistake:** Not enough contrast. Everything feels "medium" and nothing stands out.

### Progression (渐进)

**Principle:** Create a sense of movement by gradually changing a property.

**What to progress:**
- **Size:** Large → medium → small (creates depth)
- **Opacity:** 100% → 80% → 60% (creates fade)
- **Color:** Saturated → desaturated (creates focus)
- **Spacing:** Tight → loose (creates breathing room)

**Why it works:** Progression guides the eye and creates rhythm.

**Example:**
```css
h1 { font-size: 64px; opacity: 1.0; }
h2 { font-size: 40px; opacity: 0.9; }
h3 { font-size: 28px; opacity: 0.8; }
p  { font-size: 18px; opacity: 0.7; }
```

---

## Context-Specific Strategies

Different output types require different design approaches.

### Product Pages (产品页面)

**Goal:** Build trust → explain value → drive conversion

**Design strategy:**
- **Hero:** Large, clear value proposition + visual proof (screenshot, demo)
- **Social proof:** Logos, testimonials, metrics — early and prominent
- **Features:** Benefit-focused, scannable, visual hierarchy
- **CTA:** High contrast, above the fold, repeated at natural decision points

**Visual tone:** Trust + Professional (unless consumer product, then Trust + Excitement)

**Key metrics:** Conversion rate, time to CTA click, scroll depth

### Presentations (演示文稿)

**Goal:** Maintain attention → convey key points → create memorable moments

**Design strategy:**
- **One idea per slide:** No walls of text
- **Visual hierarchy:** Headline dominates, supporting text is minimal
- **Rhythm:** Vary slide types (text, image, data, quote) every 3-5 slides
- **Memorable moments:** Use full-bleed images, bold statements, surprising data

**Visual tone:** Depends on audience (Professional for business, Excitement for pitch, Creative for design)

**Key metrics:** Audience engagement, recall of key points, time per slide

### Mobile Apps (移动应用)

**Goal:** Immediate clarity → effortless interaction → delightful experience

**Design strategy:**
- **Touch targets:** Minimum 44x44px, generous spacing
- **Thumb zones:** Primary actions in bottom 1/3 of screen
- **Progressive disclosure:** Show only what's needed, hide complexity
- **Feedback:** Immediate visual response to every interaction

**Visual tone:** Professional + Excitement (balance efficiency with delight)

**Key metrics:** Task completion rate, time to complete, error rate

### Creative Works (创意作品)

**Goal:** Capture attention → evoke emotion → showcase craft

**Design strategy:**
- **Bold choices:** Unexpected layouts, experimental typography, unique color
- **Craft details:** Micro-interactions, custom illustrations, thoughtful motion
- **Personality:** Strong point of view, distinctive voice, memorable style
- **Surprise:** Break conventions intentionally, create "wow" moments

**Visual tone:** Creative (obviously) — but grounded in solid fundamentals

**Key metrics:** Time on page, scroll depth, social shares, portfolio inquiries

---

## Before You Build: The Design Checklist

Before writing any HTML, answer these questions:

1. **What's the one thing the viewer should notice first?** (Focal point)
2. **What emotion should this design evoke?** (Trust / Excitement / Professional / Creative)
3. **What's the visual flow?** (Where does the eye start, move, and end?)
4. **What's the spacing strategy?** (Tight groups, loose sections, generous breaks)
5. **What's the color strategy?** (Neutral base + one accent, or bold multi-color?)
6. **What's the typography hierarchy?** (3-4 levels maximum, clear size contrast)

If you can't answer these, you're not ready to build yet. Design decisions should be intentional, not accidental.

---

## Common Mistakes to Avoid

1. **No clear focal point** — Everything competes for attention
2. **Insufficient contrast** — Everything is "medium" weight
3. **Inconsistent spacing** — Random gaps between elements
4. **Too many colors** — More than 3-4 colors creates chaos
5. **Weak typography hierarchy** — Headlines only slightly larger than body
6. **Ignoring whitespace** — Cramming too much into too little space
7. **Generic stock imagery** — Adds no value, looks like every other site
8. **Decorative elements** — Elements that don't serve the content
9. **Inconsistent style** — Mixing multiple design languages
10. **No emotional direction** — Design feels neutral and forgettable

---

## Further Reading

- **Visual hierarchy:** "The Non-Designer's Design Book" by Robin Williams
- **Composition:** "Grid Systems in Graphic Design" by Josef Müller-Brockmann
- **Color theory:** "Interaction of Color" by Josef Albers
- **Emotional design:** "Designing for Emotion" by Aarron Walter
- **Web-specific:** "Refactoring UI" by Adam Wathan & Steve Schoger


path: references/design-patterns.md
content: # Design Patterns Library

> **Load when:** Building product pages, dashboards, mobile apps, or any UI that benefits from proven layout patterns
> **Skip when:** Pure creative/experimental work where breaking conventions is the goal
> **Why it matters:** Proven patterns reduce decision fatigue and produce reliably good results. Deviating from patterns should be intentional, not accidental.
> **Typical failure it prevents:** Reinventing layouts from scratch (poorly), missing standard UI conventions, inconsistent card/grid structures

This library provides battle-tested design patterns with ready-to-use CSS. Each pattern includes structure, rationale, and code.

---

## Hero Section Patterns

The hero is the first thing users see. It must communicate the core value in under 3 seconds.

### Pattern 1: Center-Aligned (中心对称式)

**When to use:** Product announcements, landing pages with a single strong message, presentations

**Structure:**
```
┌──────────────────────────────────┐
│                                  │
│         [Overline text]          │
│        ══════════════            │
│     LARGE HERO HEADLINE         │
│         supporting text          │
│                                  │
│           [ CTA ]                │
│                                  │
│       [Visual / Screenshot]      │
│                                  │
└──────────────────────────────────┘
```

**CSS:**
```css
.hero-center {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 80px 24px 64px;
  max-width: 800px;
  margin: 0 auto;
}
.hero-center h1 {
  font-size: clamp(40px, 6vw, 72px);
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -0.02em;
  margin-bottom: 20px;
}
.hero-center .subtitle {
  font-size: clamp(18px, 2.5vw, 22px);
  line-height: 1.5;
  color: var(--color-muted);
  max-width: 600px;
  margin-bottom: 32px;
}
.hero-center .overline {
  font-size: 14px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--color-primary);
  margin-bottom: 16px;
}
```

**Design tips:**
- Headline should be under 10 words
- Subtitle should be 1-2 sentences max
- Visual/screenshot should have subtle shadow or frame

### Pattern 2: Split-Screen (左右分栏式)

**When to use:** Products with strong visual assets, features with screenshots, SaaS landing pages

**Structure:**
```
┌─────────────────┬────────────────┐
│                 │                │
│  HEADLINE       │    [Visual]    │
│  supporting     │    [Image]     │
│  text here      │    [or Demo]   │
│                 │                │
│  [ CTA ]        │                │
│                 │                │
└─────────────────┴────────────────┘
```

**CSS:**
```css
.hero-split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 48px;
  align-items: center;
  padding: 80px 48px;
  max-width: 1280px;
  margin: 0 auto;
}
.hero-split .content { max-width: 520px; }
.hero-split h1 {
  font-size: clamp(36px, 4vw, 56px);
  font-weight: 700;
  line-height: 1.15;
  letter-spacing: -0.02em;
  margin-bottom: 20px;
}
.hero-split .visual {
  border-radius: 12px;
  overflow: hidden;
  box-shadow: 0 20px 60px rgba(0,0,0,0.12);
}
@media (max-width: 768px) {
  .hero-split {
    grid-template-columns: 1fr;
    text-align: center;
    padding: 48px 24px;
  }
}
```

**Design tips:**
- Text side: 45-55% width. Visual side gets the rest
- Visual should have slight perspective or depth (shadow, rotation)
- On mobile: stack vertically, visual below text

### Pattern 3: Full-Screen Visual (全屏视觉式)

**When to use:** Creative work, portfolios, event pages, high-impact announcements

**Structure:**
```
┌──────────────────────────────────┐
│                                  │
│     [Full-bleed background       │
│      image or video]             │
│                                  │
│         HEADLINE                 │
│         subtitle                 │
│         [ CTA ]                  │
│                                  │
└──────────────────────────────────┘
```

**CSS:**
```css
.hero-fullscreen {
  position: relative;
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  color: white;
  overflow: hidden;
}
.hero-fullscreen .bg {
  position: absolute;
  inset: 0;
  background: center/cover no-repeat;
  z-index: 0;
}
.hero-fullscreen .bg::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(
    to bottom,
    rgba(0,0,0,0.4) 0%,
    rgba(0,0,0,0.6) 100%
  );
}
.hero-fullscreen .content {
  position: relative;
  z-index: 1;
  max-width: 700px;
  padding: 24px;
}
.hero-fullscreen h1 {
  font-size: clamp(48px, 7vw, 80px);
  font-weight: 700;
  line-height: 1.05;
  letter-spacing: -0.03em;
  text-shadow: 0 2px 20px rgba(0,0,0,0.3);
}
```

**Design tips:**
- Always add an overlay gradient for text readability
- Background image: high quality, relevant, not distracting
- Keep text minimal — the image is the message

### Pattern 4: Card Grid Hero (卡片网格式)

**When to use:** Multi-product showcases, feature overviews, dashboards

**Structure:**
```
┌──────────────────────────────────┐
│  HEADLINE                        │
│  subtitle                        │
│                                  │
│  ┌────────┐ ┌────────┐ ┌──────┐ │
│  │ Card 1 │ │ Card 2 │ │Card 3│ │
│  │ [icon] │ │ [icon] │ │[icon]│ │
│  │ Title  │ │ Title  │ │Title │ │
│  │ desc   │ │ desc   │ │desc  │ │
│  └────────┘ └────────┘ └──────┘ │
└──────────────────────────────────┘
```

**CSS:**
```css
.hero-cards {
  padding: 80px 48px;
  max-width: 1200px;
  margin: 0 auto;
}
.hero-cards h1 {
  font-size: clamp(36px, 4vw, 48px);
  font-weight: 700;
  line-height: 1.15;
  margin-bottom: 16px;
}
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 24px;
  margin-top: 48px;
}
.card {
  padding: 32px;
  border-radius: 12px;
  border: 1px solid var(--color-border);
  background: var(--color-surface);
  transition: box-shadow 0.2s ease, transform 0.2s ease;
}
.card:hover {
  box-shadow: 0 8px 30px rgba(0,0,0,0.08);
  transform: translateY(-2px);
}
.card .icon {
  width: 48px;
  height: 48px;
  border-radius: 12px;
  background: var(--color-primary-light);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 20px;
}
.card h3 {
  font-size: 20px;
  font-weight: 600;
  margin-bottom: 8px;
}
.card p {
  font-size: 15px;
  color: var(--color-muted);
  line-height: 1.5;
}
```

**Design tips:**
- 3 cards = clean row. 4+ cards = consider 2 rows
- Each card should have a visual anchor (icon, illustration)
- Card content should be scannable — title + 1-2 sentence description

### Pattern 5: Narrative Flow (故事叙述式)

**When to use:** Storytelling, long-form content, editorial, onboarding flows

**Structure:**
```
┌──────────────────────────────────┐
│  [Chapter marker / number]       │
│                                  │
│  HEADLINE                        │
│                                  │
│  Paragraph text that tells       │
│  the story, builds the           │
│  narrative arc...                │
│                                  │
│  [Pull quote or key stat]        │
│                                  │
│  More narrative text...          │
│                                  │
│  ────── TRANSITION ──────        │
│                                  │
│  [Next section begins...]        │
└──────────────────────────────────┘
```

**CSS:**
```css
.narrative {
  max-width: 680px;
  margin: 0 auto;
  padding: 80px 24px;
}
.narrative .chapter-marker {
  font-size: 14px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--color-primary);
  margin-bottom: 24px;
}
.narrative h1 {
  font-size: clamp(32px, 4vw, 48px);
  font-weight: 700;
  line-height: 1.2;
  margin-bottom: 32px;
}
.narrative p {
  font-size: 18px;
  line-height: 1.7;
  color: var(--color-text);
  margin-bottom: 24px;
}
.narrative .pull-quote {
  font-size: 28px;
  font-weight: 500;
  line-height: 1.3;
  color: var(--color-primary);
  border-left: 3px solid var(--color-primary);
  padding-left: 24px;
  margin: 48px 0;
}
.narrative .transition {
  text-align: center;
  margin: 64px 0;
  font-size: 24px;
  color: var(--color-border);
  letter-spacing: 0.2em;
}
```

**Design tips:**
- Narrow column width (600-700px) for readability
- Use pull quotes to break up long text
- Add visual transitions (lines, shapes, spacing) between sections

---

## Card Patterns

Cards are the building blocks of modern UI. These patterns cover the most common use cases.

### Minimal Card (极简卡片)

```css
.card-minimal {
  padding: 24px;
  border: 1px solid var(--color-border);
  border-radius: 8px;
  background: var(--color-surface);
}
.card-minimal h3 {
  font-size: 18px;
  font-weight: 600;
  margin-bottom: 8px;
}
.card-minimal p {
  font-size: 14px;
  color: var(--color-muted);
  line-height: 1.5;
}
```

### Image Card (图文卡片)

```css
.card-image {
  border-radius: 12px;
  overflow: hidden;
  border: 1px solid var(--color-border);
  background: var(--color-surface);
}
.card-image img {
  width: 100%;
  height: 200px;
  object-fit: cover;
}
.card-image .body {
  padding: 20px;
}
.card-image .tag {
  display: inline-block;
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--color-primary);
  margin-bottom: 8px;
}
.card-image h3 {
  font-size: 18px;
  font-weight: 600;
  margin-bottom: 8px;
}
.card-image p {
  font-size: 14px;
  color: var(--color-muted);
  line-height: 1.5;
}
```

### Data Card (数据卡片)

```css
.card-data {
  padding: 24px;
  border-radius: 12px;
  border: 1px solid var(--color-border);
  background: var(--color-surface);
}
.card-data .metric {
  font-size: 36px;
  font-weight: 700;
  line-height: 1;
  margin-bottom: 4px;
}
.card-data .label {
  font-size: 14px;
  color: var(--color-muted);
  margin-bottom: 16px;
}
.card-data .trend {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 13px;
  font-weight: 600;
}
.card-data .trend.up { color: var(--color-success); }
.card-data .trend.down { color: var(--color-error); }
```

### Interactive Card (交互卡片)

```css
.card-interactive {
  padding: 24px;
  border-radius: 12px;
  border: 1px solid var(--color-border);
  background: var(--color-surface);
  cursor: pointer;
  transition: all 0.2s ease;
}
.card-interactive:hover {
  border-color: var(--color-primary);
  box-shadow: 0 4px 20px rgba(0,0,0,0.08);
  transform: translateY(-2px);
}
.card-interactive:active {
  transform: translateY(0);
  box-shadow: 0 2px 8px rgba(0,0,0,0.06);
}
.card-interactive .arrow {
  opacity: 0;
  transform: translateX(-4px);
  transition: all 0.2s ease;
}
.card-interactive:hover .arrow {
  opacity: 1;
  transform: translateX(0);
}
```

---

## Button Patterns

Buttons must have clear visual hierarchy: primary > secondary > tertiary.

```css
/* Primary: For the main action */
.btn-primary {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 12px 24px;
  font-size: 15px;
  font-weight: 600;
  color: white;
  background: var(--color-primary);
  border: none;
  border-radius: 8px;
  cursor: pointer;
  transition: all 0.15s ease;
}
.btn-primary:hover {
  filter: brightness(1.1);
  box-shadow: 0 4px 12px color-mix(in oklch, var(--color-primary) 30%, transparent);
}

/* Secondary: For supporting actions */
.btn-secondary {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 12px 24px;
  font-size: 15px;
  font-weight: 600;
  color: var(--color-text);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: 8px;
  cursor: pointer;
  transition: all 0.15s ease;
}
.btn-secondary:hover {
  background: var(--color-bg);
  border-color: var(--color-text);
}

/* Tertiary: For low-priority actions */
.btn-tertiary {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 16px;
  font-size: 14px;
  font-weight: 500;
  color: var(--color-muted);
  background: transparent;
  border: none;
  cursor: pointer;
  transition: color 0.15s ease;
}
.btn-tertiary:hover { color: var(--color-text); }
```

---

## Navigation Patterns

### Top Navigation (顶部导航)

```css
.nav-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 48px;
  border-bottom: 1px solid var(--color-border);
  background: color-mix(in oklch, var(--color-bg) 80%, transparent);
  backdrop-filter: blur(12px);
  position: sticky;
  top: 0;
  z-index: 100;
}
.nav-top .logo {
  font-size: 18px;
  font-weight: 700;
  color: var(--color-text);
}
.nav-top .links {
  display: flex;
  gap: 32px;
  list-style: none;
}
.nav-top .links a {
  font-size: 14px;
  font-weight: 500;
  color: var(--color-muted);
  text-decoration: none;
  transition: color 0.15s;
}
.nav-top .links a:hover { color: var(--color-text); }
.nav-top .links a.active { color: var(--color-primary); }
```

---

## Section Patterns

### Feature Section (功能展示)

```css
.feature-section {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 64px;
  align-items: center;
  padding: 80px 48px;
  max-width: 1200px;
  margin: 0 auto;
}
/* Alternate: even sections reverse the order */
.feature-section:nth-child(even) {
  direction: rtl;
}
.feature-section:nth-child(even) > * {
  direction: ltr;
}
```

### Stats Section (数据统计)

```css
.stats-section {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 48px;
  padding: 64px 48px;
  text-align: center;
}
.stat-item .number {
  font-size: 48px;
  font-weight: 700;
  line-height: 1;
  color: var(--color-primary);
  margin-bottom: 8px;
}
.stat-item .label {
  font-size: 15px;
  color: var(--color-muted);
}
```

### Testimonial Section (用户评价)

```css
.testimonial {
  max-width: 700px;
  margin: 0 auto;
  padding: 80px 24px;
  text-align: center;
}
.testimonial blockquote {
  font-size: 24px;
  font-weight: 400;
  line-height: 1.5;
  font-style: italic;
  color: var(--color-text);
  margin-bottom: 24px;
}
.testimonial .author {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
}
.testimonial .avatar {
  width: 48px;
  height: 48px;
  border-radius: 50%;
  background: var(--color-border);
}
.testimonial .name {
  font-size: 15px;
  font-weight: 600;
}
.testimonial .role {
  font-size: 13px;
  color: var(--color-muted);
}
```

---

## CTA Patterns

### Inline CTA (内嵌行动号召)

```css
.cta-inline {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 32px;
}
```

### Banner CTA (横幅行动号召)

```css
.cta-banner {
  background: var(--color-primary);
  color: white;
  padding: 48px;
  border-radius: 16px;
  text-align: center;
  margin: 80px auto;
  max-width: 900px;
}
.cta-banner h2 {
  font-size: 32px;
  font-weight: 700;
  margin-bottom: 12px;
}
.cta-banner p {
  font-size: 18px;
  opacity: 0.85;
  margin-bottom: 24px;
}
.cta-banner .btn {
  background: white;
  color: var(--color-primary);
  padding: 14px 28px;
  border-radius: 8px;
  font-weight: 600;
  border: none;
  cursor: pointer;
}
```

---

## Footer Pattern

```css
.footer {
  border-top: 1px solid var(--color-border);
  padding: 48px;
  margin-top: 80px;
}
.footer-grid {
  display: grid;
  grid-template-columns: 2fr repeat(3, 1fr);
  gap: 48px;
  max-width: 1200px;
  margin: 0 auto;
}
.footer-brand { max-width: 280px; }
.footer-brand .logo {
  font-size: 18px;
  font-weight: 700;
  margin-bottom: 12px;
}
.footer-brand p {
  font-size: 14px;
  color: var(--color-muted);
  line-height: 1.5;
}
.footer-col h4 {
  font-size: 13px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  margin-bottom: 16px;
}
.footer-col a {
  display: block;
  font-size: 14px;
  color: var(--color-muted);
  text-decoration: none;
  margin-bottom: 10px;
  transition: color 0.15s;
}
.footer-col a:hover { color: var(--color-text); }
```

---

## Responsive Breakpoints

Use these standard breakpoints for all patterns above:

```css
/* Mobile first */
@media (min-width: 640px)  { /* sm: Tablets */ }
@media (min-width: 768px)  { /* md: Small laptops */ }
@media (min-width: 1024px) { /* lg: Desktops */ }
@media (min-width: 1280px) { /* xl: Large screens */ }
```

**Rule:** Every grid pattern should collapse to single-column below 768px. No horizontal scrolling ever.


path: references/design-review-guide.md
content: # Design Review Guide

> **Load when:** 用户要求 review 一个设计、比较多个方向、或在交付前想做一次专家视角审查
> **Skip when:** 还在最初搭骨架，尚未形成可审查的设计
> **Why it matters:** 没有统一评审维度时，设计反馈很容易沦为“好像差点意思”
> **Typical failure it prevents:** 反馈空泛；找不到真正的问题；修复建议无法执行

## Review Dimensions

默认从 5 个维度看：

1. `Direction Fit`
   - 这个设计和目标气质、品牌或场景是否匹配
2. `Visual Hierarchy`
   - 主次是否清晰，用户知道先看哪里
3. `Craft Quality`
   - 对齐、间距、颜色、字重是否足够精细
4. `Functional Clarity`
   - 设计是否服务任务，而不是被装饰绑架
5. `Distinctiveness`
   - 是否有辨识度，还是落入常见模板

## Severity Levels

输出问题时，尽量按严重度排序：

- `Critical`
  - 不修会直接影响理解、使用或交付
- `Important`
  - 方向没错，但会明显拉低质量
- `Polish`
  - 细节优化项

## Output Structure

评审时优先给：

- 1 句总体判断
- 3-5 条按严重度排序的问题
- 最后给 `Quick Wins`

## Quick Wins Rule

如果用户时间有限，最后附上 3 条最高杠杆修复项：

- 改完就会明显提升整体质量
- 不要求大改结构
- 用户或下一个 agent 可以直接执行


path: references/design-system-creation.md
content: # Design System Creation

> **Load when:** User asks to create a design system, CSS variable system, token structure, or wants to establish visual consistency across multiple artifacts
> **Skip when:** Project already has a design system (DESIGN.md, tokens, CSS variables)
> **Why it matters:** Creating a design system from scratch requires structure. Without guidance, AI produces inconsistent token names, incomplete categories, and unusable variable systems.
> **Typical failure it prevents:** Token names that don't follow a convention, missing categories (spacing but no shadows), variables that aren't actually used in the design

## Token Structure

A minimal design system has 5 token categories. Each uses CSS custom properties.

### Color tokens

```css
:root {
  /* Identity — the brand palette */
  --color-primary:       oklch(0.55 0.18 55);   /* Main accent */
  --color-primary-light: oklch(0.85 0.08 55);   /* Hover, badges */
  --color-primary-dark:  oklch(0.35 0.20 55);   /* Active states */

  /* Neutral — the structural palette */
  --color-bg:    oklch(0.98 0.01 55);   /* Page background */
  --color-surface: oklch(0.95 0.02 55); /* Card background */
  --color-border:  oklch(0.80 0.02 55); /* Lines, dividers */
  --color-text:    oklch(0.15 0.02 55); /* Primary text */
  --color-muted:   oklch(0.60 0.04 55); /* Secondary text */

  /* Semantic — purpose-specific */
  --color-success: oklch(0.55 0.15 150);
  --color-warning: oklch(0.70 0.15 85);
  --color-error:   oklch(0.55 0.20 25);
}
```

### Spacing tokens

See `frontend-design.md` for the full 8px grid system. Short version:

```css
:root {
  --space-1: 4px;   --space-2: 8px;   --space-3: 16px;
  --space-4: 24px;  --space-5: 32px;  --space-6: 48px;
  --space-7: 64px;
}
```

### Typography tokens

```css
:root {
  --font-primary: 'DM Sans', sans-serif;
  --font-secondary: 'Source Serif 4', serif;

  --text-xs:   12px;  --text-sm:  14px;  --text-base: 16px;
  --text-lg:   20px;  --text-xl:  24px;  --text-2xl: 32px;
  --text-3xl:  40px;  --text-4xl: 56px;

  --weight-normal: 400; --weight-medium: 500; --weight-bold: 700;
  --leading-tight: 1.2; --leading-normal: 1.5; --leading-relaxed: 1.8;
}
```

### Shadow tokens

```css
:root {
  --shadow-sm: 0 1px 2px rgba(0,0,0,0.05);
  --shadow-md: 0 4px 12px rgba(0,0,0,0.08);
  --shadow-lg: 0 8px 30px rgba(0,0,0,0.12);
}
```

### Border radius tokens

```css
:root {
  --radius-sm: 4px;  --radius-md: 8px;  --radius-lg: 12px;
  --radius-xl: 20px; --radius-full: 9999px;
}
```

## Component Library

After defining tokens, build these base components. Each should use only tokens, not hardcoded values.

| Component | Required variants | Key tokens used |
|-----------|-------------------|-----------------|
| Button | primary, secondary, ghost | `--color-primary`, `--radius-md`, `--space-2` |
| Card | default, elevated | `--shadow-md`, `--radius-lg`, `--space-3` |
| Input | default, focused, error | `--border`, `--radius-sm`, `--color-error` |
| Badge | neutral, success, warning, error | `--radius-full`, semantic colors |
| Heading | h1-h4 | `--text-4xl` to `--text-xl`, `--weight-bold` |
| Body text | base, small, large | `--text-base`, `--text-sm`, `--text-lg` |

### Button example (token-only)

```css
.btn-primary {
  background: var(--color-primary);
  color: var(--color-bg);
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-md);
  font-family: var(--font-primary);
  font-weight: var(--weight-medium);
  border: none;
  cursor: pointer;
}
.btn-primary:hover { background: var(--color-primary-light); }
.btn-primary:active { background: var(--color-primary-dark); }
```

## Reverse Extraction

When the user has existing HTML but no design system:

1. Read the HTML file
2. Identify repeated values (colors, sizes, fonts)
3. Group into token categories
4. Replace hardcoded values with `var(--token-name)` references
5. Verify the design looks identical after extraction

### Extraction regex patterns

```bash
# Find repeated colors
grep -oP '#[0-9a-fA-F]{3,8}' page.html | sort | uniq -c | sort -rn
# Find repeated font-size values
grep -oP 'font-size:\s*\d+px' page.html | sort | uniq -c | sort -rn
# Find repeated spacing values
grep -oP '(padding|margin|gap):\s*\d+px' page.html | sort | uniq -c | sort -rn
```

## Naming Convention

Use this pattern for all tokens:

```
--{category}-{property}-{variant}
```

Examples: `--color-primary-dark`, `--text-xl`, `--shadow-lg`, `--radius-md`.

Avoid: `--blue`, `--big-text`, `--shadow1` (semantic-free names break when values change).

path: references/frontend-design.md
content: # Frontend Design

> **Load when:** Project has no design system, or user says "make it look good" / "design a screen" without specifying visual direction
> **Skip when:** Project has existing DESIGN.md, token files, or CSS variable system
> **Why it matters:** Without a design system, AI defaults to generic web tropes (Inter, blue gradients, rounded containers). This reference gives concrete aesthetic starting points.
> **Typical failure it prevents:** Generic-looking output that violates the "avoid AI slop" guideline; fonts and colors that clash instead of harmonize

## Visual Taste Principles

### Principle 1: Distinctive Typography Creates Identity

**Why it works:** Typography is the fastest way to establish visual identity. Generic system fonts (Inter, Roboto, Arial) are invisible because they're everywhere. Distinctive fonts create recognition and personality.

**How to apply:** Choose fonts that match the project's emotional tone. Pair a distinctive primary font with a complementary secondary font for hierarchy. Use weight variation (400/500/700) within the same family before switching fonts.

**Positive examples:**
- Startup/SaaS: DM Sans (clean geometric) + Source Serif 4 (professional serif)
- Creative portfolio: Space Grotesk (distinctive sans) + Playfair Display (classic serif)
- Dev tools: JetBrains Mono (monospace identity) + Outfit (modern sans)

**Negative examples:**
- Inter everywhere (no identity, looks like every other SaaS)
- Mixing 3+ font families (visual chaos)
- Using decorative fonts for body text (readability suffers)

### Principle 2: Perceptual Color Harmony Over RGB

**Why it works:** oklch color space ensures colors feel equally bright and saturated to human eyes. RGB/hex colors can look muddy or clash even when mathematically "correct" because they don't match human perception.

**How to apply:** Pick one identity color, then generate harmonious variants by rotating hue (±30° for analogous, 180° for complement). Keep lightness and chroma consistent across the palette for visual coherence.

**Positive examples:**
- `oklch(0.55 0.18 240)` for primary, `oklch(0.55 0.18 270)` for accent (30° rotation, same L/C)
- Surface at L=0.95, text at L=0.15, accent at L=0.55 (clear contrast hierarchy)

**Negative examples:**
- Random hex colors picked from a gradient generator (no perceptual consistency)
- Using pure black (#000) and pure white (#fff) (harsh, no warmth)
- Accent colors with wildly different chroma values (some pop, some fade)

### Principle 3: Intentional Whitespace Creates Hierarchy

**Why it works:** Whitespace isn't empty space, it's active design. More space around an element = more importance. Consistent spacing creates rhythm and makes content scannable.

**How to apply:** Use an 8px base grid. Scale by multiples (4px, 8px, 16px, 24px, 32px, 48px, 64px). Give hero sections 60% whitespace, data-dense sections 40% whitespace. One dominant element per section gets the most breathing room.

**Positive examples:**
- Hero with 64px top/bottom padding, 32px between headline and subhead
- Card grid with 24px gap, 16px internal padding
- Section breaks using 48-64px vertical spacing

**Negative examples:**
- Equal spacing everywhere (no hierarchy, everything feels the same)
- Cramped layouts with 4-8px everywhere (claustrophobic, hard to scan)
- Random spacing values (13px, 19px, 27px) that break the grid

### Principle 4: One Visual Weight Per Section

**Why it works:** Human attention is limited. When everything screams for attention (bold text, bright colors, large size), nothing stands out. One dominant element per section creates clear focal points.

**How to apply:** Pick the most important element in each section. Make it 2-3x larger, bolder, or brighter than everything else. Everything else supports it with smaller size, lighter weight, or muted color.

**Positive examples:**
- Hero: 72px headline, 18px body text (4x size difference)
- Feature card: Bold 24px title, regular 16px description
- Stats section: 80px number, 18px label

**Negative examples:**
- Everything bold (no hierarchy, visual noise)
- Headline and body text the same size (no entry point)
- Multiple competing focal points (split attention, confusion)

### Principle 5: Consistent Depth Language

**Why it works:** Shadows and borders create depth. Mixing shadow styles creates visual inconsistency. Pick one depth language and use it everywhere.

**How to apply:** Choose either subtle shadows (`0 1px 3px rgba(0,0,0,0.1)`) for modern/flat or dramatic shadows (`0 8px 30px rgba(0,0,0,0.12)`) for depth. Use borders (`1px solid var(--border)`) for dense layouts where shadows would clutter. Never mix shadow styles on the same page.

**Positive examples:**
- All cards use `0 1px 3px rgba(0,0,0,0.1)` (consistent subtle depth)
- All separators use `1px solid oklch(0.80 0.02 0)` (consistent borders)
- Modals use `0 8px 30px rgba(0,0,0,0.12)`, nothing else does (clear hierarchy)

**Negative examples:**
- Some cards with subtle shadows, others with dramatic shadows (inconsistent)
- Mixing borders and shadows on similar elements (visual confusion)
- Heavy shadows on every element (muddy, claustrophobic)

## Font Selection

**Recommended fonts by project type:**

| Type | Primary | Secondary | Rationale |
|------|---------|-----------|-----------|
| Startup / SaaS | `DM Sans` | `Source Serif 4` | Clean geometric, professional serif for emphasis |
| Creative / Portfolio | `Space Grotesk` | `Playfair Display` | Distinctive geometric sans, classic serif contrast |
| Technical / Dev tool | `JetBrains Mono` | `Outfit` | Monospace identity, modern sans for body |
| Presentation / Deck | `Bricolage Grotesque` | `Instrument Serif` | Warm grotesque, elegant serif |
| Mobile app UI | `Plus Jakarta Sans` | `Fraunces` (only as accent) | Friendly sans, quirky serif accent |
| Minimal / Editorial | `Sora` | `Cormorant Garamond` | Precise sans, refined serif |

Load from Google Fonts: `<link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;700&family=Source+Serif+4:wght@400;600&display=swap" rel="stylesheet">`

## Color System (oklch)

When no design system exists, build a palette using oklch for perceptual uniformity.

### Method

1. Pick one **identity color** (the brand/accent) — ask the user or pick from context
2. Generate 3 harmonious colors using oklch hue rotation:
   - Complement: rotate hue by 180°
   - Analogous: rotate by ±30°
3. Set lightness range: surface=0.95, text=0.15, muted=0.6, accent=0.55
4. Map to CSS custom properties

```css
:root {
  --surface: oklch(0.95 0.02 hue);
  --text:    oklch(0.15 0.02 hue);
  --muted:   oklch(0.60 0.04 hue);
  --accent:  oklch(0.55 0.18 hue);
  --accent-light: oklch(0.85 0.08 hue);
  --border:  oklch(0.80 0.02 hue);
}
```

Replace `hue` with the actual value (0-360). Example: warm orange = hue 55.

### Common starting palettes

| Mood | Hue | Accent oklch | Use case |
|------|-----|-------------|----------|
| Warm | 55 | oklch(0.55 0.18 55) | Consumer, lifestyle |
| Cool | 240 | oklch(0.55 0.18 240) | Enterprise, data |
| Fresh | 150 | oklch(0.55 0.15 150) | Health, sustainability |
| Bold | 25 | oklch(0.60 0.22 25) | Entertainment, gaming |
| Neutral | 0 (achromatic) | oklch(0.50 0.02 0) | Dev tools, docs |

## Spacing System

Use an 8px base grid. Scale by multiples of 4px for density control.

| Token | Value | Use |
|-------|-------|-----|
| `--space-1` | 4px | Icon gaps, tight inline |
| `--space-2` | 8px | Button padding, list items |
| `--space-3` | 16px | Card padding, section gaps |
| `--space-4` | 24px | Section padding, card margins |
| `--space-5` | 32px | Page margins, hero spacing |
| `--space-6` | 48px | Section breaks |
| `--space-7` | 64px | Full-bleed separators |

## Composition Rules

1. **Contrast hierarchy**: Headings 2-3x body size. Body 16-18px for web, 24px+ for 1920x1080 slides.
2. **Visual weight**: One element dominant per section. Everything else supports it.
3. **Whitespace ratio**: 60% space, 40% content for hero/cover. 40% space, 60% content for data-dense sections.
4. **Alignment**: One alignment per section (left, center, or right). Mix across sections, not within.
5. **Depth**: Use only 1 shadow level across the entire page. Either `0 1px 3px rgba(0,0,0,0.1)` for subtle or `0 8px 30px rgba(0,0,0,0.12)` for dramatic. Never both.
6. **Borders**: Prefer `1px solid var(--border)` over shadows for separation in dense layouts. Shadows for isolation in sparse layouts.

path: references/interactive-prototype.md
content: # Interactive Prototype

> **Load when:** Building a multi-screen interactive prototype with navigation, state, or transitions
> **Skip when:** Single-screen static design, slide deck, or landing page
> **Why it matters:** Multi-screen prototypes need routing, state management, and transition patterns that single-screen templates don't provide
> **Typical failure it prevents:** Hardcoding all screens visible at once, no navigation, broken state on screen switches, no transition feel

## Navigation Patterns

Before choosing a navigation pattern, decide what kind of prototype this is:

- `Overview board`
  - Best when the goal is to compare multiple screens or states side by side
- `Flow demo`
  - Best when the goal is to click through one path end to end

Do not build a fully interactive flow when the user only needs a screen overview, and do not fake a flow with disconnected static cards when the user explicitly wants clickable behavior.

Choose based on the app type. Each pattern includes a React + Babel implementation snippet.

### Tab Bar (mobile apps, 3-5 main sections)

```jsx
function TabBar({ tabs, activeTab, onTabChange }) {
  return React.createElement('nav', {
    style: { display: 'flex', justifyContent: 'space-around', padding: '8px 0', borderTop: '1px solid var(--border)' }
  }, tabs.map((tab, i) =>
    React.createElement('button', {
      key: i, onClick: () => onTabChange(i),
      style: { flex: 1, textAlign: 'center', opacity: activeTab === i ? 1 : 0.5, background: 'none', border: 'none', cursor: 'pointer' }
    }, tab.label)
  ));
}

// Usage: state-driven screen switching
const [activeTab, setActiveTab] = React.useState(0);
```

### Sidebar (desktop apps, dashboards, 5+ sections)

```jsx
function Sidebar({ items, active, onSelect }) {
  return React.createElement('aside', {
    style: { width: '240px', height: '100vh', padding: '16px', background: 'var(--surface)', borderRight: '1px solid var(--border)' }
  }, items.map((item, i) =>
    React.createElement('button', {
      key: i, onClick: () => onSelect(i),
      style: { display: 'block', width: '100%', padding: '12px', textAlign: 'left', background: active === i ? 'var(--accent-light)' : 'none', border: 'none', cursor: 'pointer', borderRadius: '6px', marginBottom: '4px' }
    }, item.label)
  ));
}
```

### Wizard / Step flow (forms, onboarding, checkout)

```jsx
function Wizard({ steps, currentStep, onStepChange }) {
  const total = steps.length;
  return React.createElement('div', null,
    React.createElement('div', {
      style: { display: 'flex', gap: '8px', marginBottom: '24px' }
    }, steps.map((s, i) =>
      React.createElement('div', {
        key: i,
        style: { flex: 1, height: '4px', borderRadius: '2px', background: i <= currentStep ? 'var(--accent)' : 'var(--border)' }
      })
    )),
    steps[currentStep],
    React.createElement('div', { style: { display: 'flex', gap: '12px', marginTop: '24px' } },
      currentStep > 0 && React.createElement('button', { onClick: () => onStepChange(currentStep - 1), style: { padding: '8px 16px', borderRadius: '6px', border: '1px solid var(--border)', cursor: 'pointer' } }, 'Back'),
      currentStep < total - 1 && React.createElement('button', { onClick: () => onStepChange(currentStep + 1), style: { padding: '8px 16px', borderRadius: '6px', background: 'var(--accent)', color: 'var(--surface)', border: 'none', cursor: 'pointer' } }, 'Next')
    )
  );
}
```

## State Management

For prototypes, keep state simple. Use these patterns based on complexity:

| Complexity | Pattern | When to use |
|------------|---------|-------------|
| 2-3 screens | `useState` for active screen index | Tab bar, simple nav |
| 5+ screens | `useReducer` with screen + form state | Wizards, multi-step forms |
| URL sync needed | `hash routing` | When back button should work, or sharing specific screen URLs |

### Hash routing snippet

```js
const [screen, setScreen] = React.useState(location.hash.slice(1) || 'home');
React.useEffect(() => {
  const onHash = () => setScreen(location.hash.slice(1) || 'home');
  window.addEventListener('hashchange', onHash);
  return () => window.removeEventListener('hashchange', onHash);
}, []);
// Navigate: location.hash = '#settings'
```

## Mobile Mockup Rules

For mobile prototypes:

- Prefer `ios_frame.jsx` or `android_frame.jsx` instead of hand-drawing the shell
- Respect safe areas, tap targets, and obvious navigation conventions
- Use realistic content density for the product type
- Avoid filling every screen with decorative cards that say nothing about the product

If the product is data-heavy, the screen should visibly carry that density. If it is content-first, typography and reading rhythm should lead.

## Verification Rule

Before delivery, click through the primary path at least once:

- entry screen
- one mid-flow state
- one success or destination state

If that path is broken, the prototype is not done.

## Transitions

### Page transition (fade + slide)

```jsx
function PageTransition({ children, direction = 'forward' }) {
  return React.createElement('div', {
    style: {
      animation: direction === 'forward' ? 'slideIn 0.3s ease-out' : 'slideBack 0.3s ease-out',
    }
  }, children);
}

// Add to <style>:
// @keyframes slideIn { from { opacity: 0; transform: translateX(20px); } to { opacity: 1; transform: translateX(0); } }
// @keyframes slideBack { from { opacity: 0; transform: translateX(-20px); } to { opacity: 1; transform: translateX(0); } }
```

### Micro-interaction (button press feedback)

```css
.interactive:hover { transform: scale(1.02); transition: transform 0.15s ease; }
.interactive:active { transform: scale(0.98); }
```

## Prototype Structure Template

A complete multi-screen prototype follows this pattern:

```html
<!DOCTYPE html>
<html>
<head>
  <script src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
  <script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
  <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
  <style>/* CSS variables + animations */</style>
</head>
<body>
  <div id="root"></div>
  <script type="text/babel">
    // Navigation component (tab/sidebar/wizard)
    // Screen components (ScreenA, ScreenB, ScreenC)
    // App component with state management
    ReactDOM.createRoot(document.getElementById('root')).render(<App />);
  </script>
</body>
</html>
```


path: references/junior-designer-workflow.md
content: # Junior Designer Workflow

> **Load when:** 新任务、模糊任务、范围较大任务，或用户还没确认视觉方向
> **Skip when:** 只是局部微调、修 bug、或用户已经把方向钉死
> **Why it matters:** 设计里最贵的错误是方向错了却做到最后才暴露
> **Typical failure it prevents:** 闷头做完一整版才发现理解错了；只给最终稿不给过程确认点；用户无法低成本纠偏

## Core Mindset

把自己当成“会主动汇报的 junior designer”，不是闷头放大招的黑箱。

## Pass 1: Assumptions + Placeholder

开工后的第一版不追求完美，先追求方向可见。

至少要明确：

- 你对受众和目标的假设
- 你理解的页面主焦点
- 你还不确定的部分
- 哪些内容先用 placeholder

必要时把这些假设写进 HTML 注释里，方便用户和后续 agent 读。

## Pass 2: Core Build

用户确认方向后，再进入真正的布局、视觉和组件实现。

这时候重点是：

- 把 placeholder 变成真实结构
- 如果需要，提供 2-3 个方向或 tweak 切换
- 在完成 40%-70% 时再给用户看一次

不要憋到 100% 才第一次展示。

## Pass 3: Polish

整体方向稳定后，再打磨：

- 字重和字号
- 留白和节奏
- 动画 timing
- hover / pressed / focused 细节
- 文案密度

## Pass 4: Verify + Deliver

最后才做：

- 结构验证
- 截图验证
- 交互路径验证
- 导出验证（如果需要）

## What to Show the User

用户中途查看时，重点展示：

- 你现在押的方向是什么
- 还没定的是什么
- 用户最值得现在决定的 1-3 个问题

不要把阶段性汇报写成冗长复盘。


path: references/platform-tools.md
content: # Platform Tool Reference

> **Load when:** Needing to export output (PPTX/PDF/inline HTML), mapping the current host's preview/screenshot tools, or verifying the preview workflow
> **Skip when:** Only building and previewing designs — no export needed
> **Why it matters:** Tool names are non-obvious and export scripts require specific setup; wrong tool names break the verification flow
> **Typical failure it prevents:** Using the wrong preview path, missing `npm install` for export scripts, or skipping console/screenshot verification

## Tool Mapping

Different hosts expose different names for preview, browser automation, and screenshot capture. Treat the table below as a capability map, not a hardcoded API contract.

| Need | Generic path | Typical implementation |
|---|---|---|
| Copy a starter scaffold | Shell copy | `cp skills/cc-design/assets/templates/...` or host scaffold-copy tool |
| Open preview | Browser preview | Playwright, host HTML preview, or system browser |
| Check console output | Browser console logs | Playwright console API or host log tool |
| Capture screenshots | Browser screenshot | Playwright screenshot or host screenshot tool |
| Export PPTX / PDF / self-contained HTML | Local scripts in `scripts/` | Equivalent built-in export tools, or the local scripts |

## Preview & Screenshots

| Need | Capability | Notes |
|---|---|---|
| Open HTML for preview | Browser navigate/open | Prefer `file:///absolute/path/to/file.html` |
| Take screenshot | Browser screenshot | Full-page or region screenshot both work |
| Check console errors | Browser console logs | Read errors and warnings before delivery |
| Execute JS in page | Browser evaluate | Useful for structure checks and layout state |
| Get page snapshot | DOM / accessibility snapshot | Better than screenshot for understanding structure |
| Run multi-step browser code | Browser automation | Use only when simple preview tools are insufficient |
| Open in system browser | Shell | macOS: `open <file>`, Linux: `xdg-open <file>` |

### Typical verification flow

```
1. Open the HTML file in a browser preview
2. Read console logs for errors/warnings
3. Take a screenshot and visually verify
4. Fix any issues, repeat
```

## Export Scripts

Scripts live in the skill's `scripts/` directory. First run requires `npm install`:

```bash
cd skills/cc-design/scripts && npm install && cd -
```

For Playwright-backed export flows, also install the browser binary:

```bash
npx playwright install chromium
```

### PPTX export

```bash
node skills/cc-design/scripts/gen_pptx.js --input slides.html --output deck.pptx --mode screenshots
# --mode editable   → native text & shapes
# --mode screenshots → flat images, pixel-perfect (requires Playwright)
```

### Inline HTML

```bash
node skills/cc-design/scripts/super_inline_html.js --input page.html --output page-inline.html
# Bundles HTML + all linked CSS/JS/images into a single self-contained file
```

### Print to PDF

```bash
node skills/cc-design/scripts/open_for_print.js --input page.html --output page.pdf
# Uses Playwright to render and export as PDF
```


path: references/question-protocol.md
content: # Question Protocol

> **Load when:** Understand step (step 1) when the user's scope is unclear, ambiguous, or incomplete
> **Skip when:** User gave a complete spec (format, fidelity, content, constraints all specified)
> **Why it matters:** Without structured questions, AI asks vague "what do you want?" prompts that waste turns. Structured questions converge scope in 1-2 rounds.
> **Typical failure it prevents:** 5+ rounds of back-and-forth clarification; building the wrong thing because key constraints were never asked about

## Question Groups

Ask these in order. Skip any group where the user already provided the answer. Prefer one batched question set instead of a back-and-forth drip feed.

### Group 1: Design Context

| Question | Options | Why |
|----------|---------|-----|
| Is there an existing design system, UI kit, token file, or codebase? | A) Yes, reuse it / B) Only screenshots or references / C) No, start fresh | Decides whether to reuse or fall back |
| Do we have brand assets or reference material? | A) Brand guide / B) URL or screenshots / C) Specific brand to study / D) Nothing yet | Routes to `design-context.md` and possibly `brand-asset-protocol.md` |
| Does the task mention a real product or brand? | A) Yes, specific product / B) Yes, general brand style / C) No | Determines whether fact verification and asset protocol are needed |

### Group 2: Deliverable Shape

| Question | Options | Why |
|----------|---------|-----|
| What format should this be? | A) Slide deck / B) Landing page / C) Mobile mockup / D) Interactive prototype / E) Visual direction only | Determines routing, template, and verification style |
| How many screens or directions? | A) 1-3 / B) 4-8 / C) 10+ | Affects build strategy and scope |
| Fidelity level? | A) Wireframe / B) Mid-fi / C) High-fidelity | Determines effort and polish level |

### Group 3: Variation and Interaction

| Question | Options | Why |
|----------|---------|-----|
| Do you want one polished direction or multiple options? | A) One / B) 2-3 options / C) A direction map first | Routes to `design-direction-advisor.md` or tweaks/canvas flow |
| What should vary? | A) Visual style / B) Layout / C) Density / D) Motion / E) Color only | Prevents meaningless variations |
| Do you want tweak controls after delivery? | A) No / B) A few key toggles / C) Full variant panel | Determines whether `tweaks-system.md` is needed |

### Group 4: Technical Constraints

| Question | Options | Why |
|----------|---------|-----|
| Needs to be responsive? | A) Fixed size / B) Responsive / C) Mobile only | Affects structure and scaling |
| Export needed? | A) Preview only / B) PPTX / C) PDF / D) Self-contained HTML | Routes to export constraints early |
| Accessibility requirement? | A) Standard / B) WCAG AA / C) Higher than AA | Affects contrast, sizing, and touch targets |

## Convergence Rule

After asking these groups, you should have enough to route and build. If scope is still unclear after one round, ask one targeted follow-up, then proceed with the best safe assumption. Do not keep the user in endless clarification.

## Assumption Defaults

When the user skips a question, apply these defaults:

| Unanswered | Default |
|------------|---------|
| Context | Search the workspace first, then assume no system only if nothing is found |
| Format | Landing page or screen-level artifact that best matches the request |
| Fidelity | Medium |
| Variation | One strong direction unless user asks for options |
| Brand | Start from current project context; otherwise use `frontend-design.md` |
| Responsive | Match the most likely real usage for the output |
| Export | Just preview |
| Accessibility | Standard |


path: references/react-babel-setup.md
content: # React + Babel Setup for Inline JSX

> **Load when:** Building React prototypes with inline JSX
> **Skip when:** Working with plain HTML/CSS only
> **Why it matters:** Pinned versions and integrity hashes prevent runtime breakage; scope rules prevent global variable collisions between component files
> **Typical failure it prevents:** React version mismatch, `styles` object overwrites across components, Babel scope isolation breaking component sharing

Use these exact script tags with pinned versions and integrity hashes when writing React prototypes with inline JSX. Do not use unpinned versions (e.g. react@18) or omit the integrity attributes.

```html
<script src="https://unpkg.com/react@18.3.1/umd/react.development.js" integrity=[REDACTED_SECRET] crossorigin="anonymous"></script>
<script src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.development.js" integrity=[REDACTED_SECRET] crossorigin="anonymous"></script>
<script src="https://unpkg.com/@babel/standalone@7.29.0/babel.min.js" integrity=[REDACTED_SECRET] crossorigin="anonymous"></script>
```

Import helper or component scripts using script tags. Avoid using `type="module"` on script imports — it may break things.

## Scope Rules

**CRITICAL: When defining global-scoped style objects, give them SPECIFIC names.** If you import >1 component with a `styles` object, it will break. Each must have a unique name based on the component:

```js
const terminalStyles = { ... };  // NOT const styles = { ... }
```

**CRITICAL: Multiple Babel script files don't share scope.** Each `<script type="text/babel">` gets its own scope when transpiled. Export components to `window` at the end of each component file:

```js
// At the end of components.jsx:
Object.assign(window, {
  Terminal, Line, Spacer,
  Gray, Blue, Green, Bold,
  // ... all components that need to be shared
});
```


path: references/starter-components.md
content: # Starter Components

> **Load when:** Needing device frames, slide deck shells, presentation grids, or animation scaffolds
> **Skip when:** Designing from scratch with existing components, or building something none of the templates cover
> **Why it matters:** Avoids hand-rolling scaling logic, device bezels, and animation engines — these are tricky to get right
> **Typical failure it prevents:** Hand-written scale transforms that break on resize, slide decks without proper letterboxing, animation timing drift

Starter components are ready-made scaffolds in the `assets/templates/` directory. Copy them to your project instead of hand-drawing device bezels, deck shells, or presentation grids.

Kinds include the file extension — some are plain JS (load with `<script src>`), some are JSX (load with `<script type="text/babel" src>`).

## How to use

If your host exposes a built-in starter-component tool, you can use that. Otherwise copy the files directly into the working project.

```bash
# Copy a template to your project
cp skills/cc-design/assets/templates/<component>.<ext> ./<component>.<ext>
```

Or use `Read` to read the template, then `Write` to create a customized version in your project.

| Component | File | Type | When to Use |
|---|---|---|---|
| `deck_stage` | `assets/templates/deck_stage.js` | Plain JS | ANY slide presentation — handles scaling, keyboard nav, slide-count overlay, speaker-notes postMessage, localStorage persistence, print-to-PDF |
| `design_canvas` | `assets/templates/design_canvas.jsx` | JSX (React) | Presenting 2+ static options side-by-side — a grid layout with labeled cells for variations |
| `ios_frame` | `assets/templates/ios_frame.jsx` | JSX (React) | Design needs to look like a real iPhone screen — includes device bezel with status bar |
| `android_frame` | `assets/templates/android_frame.jsx` | JSX (React) | Design needs to look like a real Android phone screen — includes device bezel with status bar and keyboard |
| `macos_window` | `assets/templates/macos_window.jsx` | JSX (React) | Desktop window chrome with traffic lights |
| `browser_window` | `assets/templates/browser_window.jsx` | JSX (React) | Desktop window chrome with tab bar |
| `animations` | `assets/templates/animations.jsx` | JSX (React) | Timeline-based animation engine (Stage + Sprite + scrubber + Easing) — for animated video or motion-design output |

## Animations Detail

When using `animations.jsx`, you get: `<Stage>` (auto-scale + scrubber + play/pause), `<Sprite start end>`, `useTime()`/`useSprite()` hooks, `Easing`, `interpolate()`, and entry/exit primitives. Build scenes by composing Sprites inside a Stage.

Only fall back to Popmotion (`https://unpkg.com/popmotion@11.0.5/dist/popmotion.min.js`) if the starter genuinely can't cover the use case.

## Deck Stage Detail

For slide decks, do not hand-roll scaling — copy `assets/templates/deck_stage.js` to your project and put each slide as a direct child `<section>` of the `<deck-stage>` element. The component handles:
- Scaling (fixed 1920x1080 canvas letterboxed on black via `transform: scale()`)
- Keyboard/tap navigation
- Slide-count overlay
- localStorage persistence
- Print-to-PDF (one page per slide)
- Auto-tags every slide with `data-screen-label` and `data-om-validate`
- Posts `{slideIndexChanged: N}` to parent for speaker notes sync

Prev/next controls must be **outside** the scaled element so they stay usable on small screens.

## Speaker Notes

When adding speaker notes for slides (only when explicitly asked), add to `<head>`:

```html
<script type="application/json" id="speaker-notes">
[
    "Slide 0 notes",
    "Slide 1 notes"
]
</script>
```

The page MUST call `window.postMessage({slideIndexChanged: N})` on init and on every slide change. `deck_stage.js` handles this automatically.


path: references/tweaks-system.md
content: # Tweaks System

> **Load when:** Generating multiple design variants or enabling real-time in-page adjustments
> **Skip when:** Producing a single static design with no variations
> **Why it matters:** Provides a unified, self-contained adjustment UI so users can toggle variants without file proliferation
> **Typical failure it prevents:** Creating separate HTML files per variant instead of one file with toggleable options; losing user adjustments on refresh

Tweaks let the user toggle in-page controls to adjust design aspects — colors, fonts, spacing, copy, layout variants, etc. You design the Tweaks UI; it lives inside the prototype as a self-contained panel.

## Implementation

The tweaks system is a pure in-page toggle — no parent frame communication needed.

### 1. Floating toggle button

Add a fixed-position button in the bottom-right corner that shows/hides the tweaks panel:

```html
<style>
  #tweaks-toggle {
    position: fixed; bottom: 20px; right: 20px; z-index: 9999;
    width: 44px; height: 44px; border-radius: 50%;
    background: #1a1a1a; color: #fff; border: none;
    font-size: 20px; cursor: pointer; box-shadow: 0 2px 8px rgba(0,0,0,0.3);
    display: flex; align-items: center; justify-content: center;
  }
  #tweaks-panel {
    position: fixed; bottom: 74px; right: 20px; z-index: 9998;
    width: 280px; max-height: 60vh; overflow-y: auto;
    background: #1a1a1a; color: #e0e0e0; border-radius: 12px;
    padding: 16px; box-shadow: 0 4px 20px rgba(0,0,0,0.4);
    display: none; font-family: system-ui, sans-serif; font-size: 13px;
  }
  #tweaks-panel.visible { display: block; }
  #tweaks-panel label { display: block; margin-bottom: 10px; }
  #tweaks-panel input[type="color"] { width: 40px; height: 28px; border: none; cursor: pointer; }
  #tweaks-panel input[type="range"] { width: 100%; }
  #tweaks-panel select { width: 100%; padding: 4px; }
</style>

<button id="tweaks-toggle" title="Tweaks">⚙</button>
<div id="tweaks-panel">
  <h3 style="margin:0 0 12px; font-size:14px; color:#fff;">Tweaks</h3>
  <!-- Add your tweak controls here -->
</div>
```

### 2. Toggle logic

```js
const toggle = document.getElementById('tweaks-toggle');
const panel = document.getElementById('tweaks-panel');

// Restore visibility from localStorage
const tweaksVisible = localStorage.getItem('cc-tweaks-visible') === 'true';
if (tweaksVisible) panel.classList.add('visible');

toggle.addEventListener('click', () => {
  panel.classList.toggle('visible');
  localStorage.setItem('cc-tweaks-visible', panel.classList.contains('visible'));
});
```

### 3. Default values with persistence

Wrap tweakable defaults in comment markers for easy editing:

```js
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "primaryColor": "#D97757",
  "fontSize": 16,
  "dark": false
}/*EDITMODE-END*/;

// Load from localStorage or use defaults
const tweaks = { ...TWEAK_DEFAULTS, ...JSON.parse(localStorage.getItem('cc-tweaks') || '{}') };

function applyTweaks() {
  document.documentElement.style.setProperty('--primary', tweaks.primaryColor);
  document.documentElement.style.setProperty('--font-size', tweaks.fontSize + 'px');
  // Apply other tweaks...
  localStorage.setItem('cc-tweaks', JSON.stringify(tweaks));
}

applyTweaks();
```

The block between `EDITMODE-BEGIN` and `EDITMODE-END` markers **must be valid JSON** (double-quoted keys and strings). The skill uses these markers to find and update default values via the `Edit` tool.

## Tips

- Keep the Tweaks surface small — a compact floating panel
- Hide controls entirely when Tweaks is off; the design should look final
- If the user asks for multiple variants of a single element, use tweaks to cycle through options
- Even if the user doesn't ask for tweaks, add a couple by default — expose interesting possibilities
- Title your panel **"Tweaks"** so the naming is consistent


path: references/verification-protocol.md
content: # Verification Protocol

> **Load when:** Reaching the Verify step (step 5) in the workflow, or preparing to deliver a final artifact
> **Skip when:** Still in the Build step iterating on intermediate changes
> **Why it matters:** Platform-native design tools run two-phase verification automatically. Without this protocol, defects slip through to delivery.
> **Typical failure it prevents:** Delivering artifacts with console errors, broken scaling, invisible text, or misaligned elements

If the current host exposes different tool names, use the equivalent browser preview, console-log, screenshot, and page-evaluate capabilities.

## Phase 1: Structural Verification

Run these checks first. Any failure = stop and fix before proceeding to Phase 2.

| Check | Method | Pass criteria | Fix if fail |
|-------|--------|---------------|-------------|
| Console errors | `browser_console_messages` (level: error) | 0 errors | Read error message, fix source, re-verify |
| Console warnings | `browser_console_messages` (level: warning) | 0 warnings about missing resources | Fix asset paths or missing imports |
| Layout structure | `browser_snapshot` | All expected elements present in accessibility tree | Check rendering conditions (display:none, conditional logic) |
| Fixed-size scaling | `browser_evaluate` — check deck_stage or stage transform | Content fills intended viewport without overflow | Verify transform:scale calculation, check container dimensions |
| Asset loading | `browser_evaluate` — check `document.querySelectorAll('[src],[href]')` | No broken links (404) | Fix relative paths, ensure assets exist |

### Console check command

```
browser_console_messages → level: error
browser_console_messages → level: warning
```

If Phase 1 has any failure: fix the issue, re-navigate the page, re-run Phase 1. Do not proceed to Phase 2 until Phase 1 passes clean.

## Phase 2: Visual Verification

Run these after Phase 1 passes. Requires human judgment via screenshot review.

| Check | Method | Pass criteria |
|-------|--------|---------------|
| Screenshot taken | `browser_take_screenshot` | Screenshot captured successfully |
| Typography | Visual check on screenshot | Headings distinct from body, no text < 12pt (print) or < 24px (1080p slides) |
| Alignment | Visual check on screenshot | Elements aligned to grid, no drift, consistent margins |
| Contrast | Visual check on screenshot | Text readable against background, no low-contrast pairs |
| Spacing | Visual check on screenshot | Consistent use of spacing tokens, no arbitrary gaps |
| Tweaks panel | Visual check (if applicable) | Tweaks toggle visible, panel functional, no UI artifacts when hidden |
| Content | Visual check on screenshot | No placeholder text left in, no filler content |

### Visual review flow

```
1. browser_take_screenshot → capture full page
2. Review screenshot against criteria above
3. If any criterion fails → fix in Build step, re-verify from Phase 1
4. If all pass → proceed to Deliver step
```

## Phase 3: Task-Specific Verification

After structural and visual checks, add the checks that match the artifact type:

| Artifact | Extra checks |
|---|---|
| Mobile / interactive prototype | Click through the core path once; verify state switches, navigation, and no dead buttons on the main route |
| Multi-option design board | Verify labels are visible and each option is genuinely different |
| Slide deck | Check first slide, one middle slide, and last slide; confirm scaling and readable type on all three |
| Exported asset | Verify the target file actually exists and matches the expected format |

For prototypes, do not stop at a static screenshot if the main value is interaction.

## Fix Loop

The verification loop is strict:

```
Phase 1 fail → fix → re-navigate → Phase 1 again
Phase 2 fail → fix → re-navigate → Phase 1 + Phase 2 again
```

Never skip Phase 1 to "just check visuals." Structural errors cause visual defects that are symptoms, not root causes.


path: scripts/gen_pptx.js
content: #!/usr/bin/env node
/**
 * gen_pptx.js — Export HTML slide deck to PPTX
 *
 * Modes:
 *   --mode editable    → Parse HTML for text/shapes, create native PPTX elements
 *   --mode screenshots → Headless browser screenshots per slide, embed as images
 *
 * Usage:
 *   node gen_pptx.js --input slides.html --output deck.pptx
 *   node gen_pptx.js --input slides.html --output deck.pptx --mode screenshots
 */

const fs = require('fs');
const path = require('path');
const PptxGenJS = require('pptxgenjs');
const parseArgs = require('./lib/parse_args');

async function main() {
  const opts = parseArgs(process.argv, { input: '', output: 'output.pptx', mode: 'editable' });

  if (!opts.input) {
    console.error('Usage: node gen_pptx.js --input slides.html --output deck.pptx [--mode editable|screenshots]');
    process.exit(1);
  }

  const inputPath = path.resolve(opts.input);
  if (!fs.existsSync(inputPath)) {
    console.error(`Error: Input file not found: ${inputPath}`);
    process.exit(1);
  }

  const pptx = new PptxGenJS();
  pptx.layout = 'LAYOUT_WIDE'; // 13.33 x 7.5 inches (16:9)

  if (opts.mode === 'screenshots') {
    await exportScreenshots(pptx, inputPath);
  } else {
    await exportEditable(pptx, inputPath);
  }

  const outputPath = path.resolve(opts.output);
  await pptx.writeFile({ fileName: outputPath });
  console.log(`PPTX saved to: ${outputPath}`);
}

async function exportEditable(pptx, inputPath) {
  let chromium;
  try {
    const { chromium: pw } = require('playwright');
    chromium = pw;
  } catch {
    // Fallback to regex parsing if Playwright is not installed
    const html = fs.readFileSync(inputPath, 'utf-8');
    const slideRegex = /<section[^>]*>([\s\S]*?)<\/section>/gi;
    const slides = [];
    let match;
    while ((match = slideRegex.exec(html)) !== null) slides.push(match[1]);
    if (slides.length === 0) {
      console.error('No <section> elements found. Install Playwright for DOM-based parsing: npm install playwright');
      process.exit(1);
    }
    exportEditableRegex(pptx, slides);
    return;
  }

  const browser = await chromium.launch();
  const page = await browser.newPage({ viewport: { width: 1920, height: 1080 } });
  await page.goto(`file://${inputPath}`);

  const slideData = await page.evaluate(() => {
    const sections = document.querySelectorAll('section');
    return Array.from(sections).map(sec => {
      const bg = sec.style.background || sec.style.backgroundColor || '';
      const headings = Array.from(sec.querySelectorAll('h1,h2,h3,h4,h5,h6')).map(h => ({
        level: parseInt(h.tagName[1]),
        text: h.textContent.trim()
      }));
      const paragraphs = Array.from(sec.querySelectorAll('p')).map(p => ({
        text: p.textContent.trim()
      }));
      return { bg, headings, paragraphs };
    });
  });

  for (const slideInfo of slideData) {
    const slide = pptx.addSlide();
    if (slideInfo.bg) {
      const bgColor = slideInfo.bg.replace('#', '').replace(/^rgb\(/, '').replace(/\)$/, '');
      slide.background = { color: bgColor };
    }
    let y = 0.5;
    for (const h of slideInfo.headings) {
      if (!h.text) continue;
      const fontSize = Math.max(18, 44 - (h.level - 1) * 6);
      slide.addText(h.text, { x: 0.5, y, w: '90%', h: 1, fontSize, bold: h.level <= 2, color: '333333', fontFace: 'system-ui' });
      y += 0.8;
    }
    for (const p of slideInfo.paragraphs) {
      if (!p.text) continue;
      slide.addText(p.text, { x: 0.5, y, w: '90%', h: 0.6, fontSize: 18, color: '555555', fontFace: 'system-ui' });
      y += 0.5;
    }
  }

  await browser.close();
}

function exportEditableRegex(pptx, slides) {
  for (const slideHtml of slides) {
    const slide = pptx.addSlide();

    // Extract text content
    const textRegex = /<h[1-6][^>]*>([\s\S]*?)<\/h[1-6]>/gi;
    const bodyTextRegex = /<p[^>]*>([\s\S]*?)<\/p>/gi;
    const bgRegex = /background(?:-color)?:\s*([^;]+)/i;

    // Try to extract background
    const bgMatch = slideHtml.match(bgRegex);
    if (bgMatch) {
      const bgColor = bgMatch[1].trim();
      slide.background = { color: bgColor.replace('#', '') };
    }

    // Extract headings
    let y = 0.5;
    let hMatch;
    const textRegex2 = /<h(\d)[^>]*>([\s\S]*?)<\/h\1>/gi;
    while ((hMatch = textRegex2.exec(slideHtml)) !== null) {
      const level = parseInt(hMatch[1]);
      const text = hMatch[2].replace(/<[^>]*>/g, '').trim();
      if (!text) continue;

      const fontSize = Math.max(18, 44 - (level - 1) * 6);
      slide.addText(text, {
        x: 0.5, y, w: '90%', h: 1,
        fontSize, bold: level <= 2,
        color: '333333',
        fontFace: 'system-ui',
      });
      y += 0.8;
    }

    // Extract paragraphs
    const pRegex = /<p[^>]*>([\s\S]*?)<\/p>/gi;
    let pMatch;
    while ((pMatch = pRegex.exec(slideHtml)) !== null) {
      const text = pMatch[1].replace(/<[^>]*>/g, '').trim();
      if (!text) continue;

      slide.addText(text, {
        x: 0.5, y, w: '90%', h: 0.6,
        fontSize: 18, color: '555555',
        fontFace: 'system-ui',
      });
      y += 0.5;
    }
  }
}

async function exportScreenshots(pptx, inputPath) {
  // Check if playwright is available
  let chromium;
  try {
    const { chromium: pw } = require('playwright');
    chromium = pw;
  } catch {
    console.error('Screenshots mode requires Playwright. Install with: npm install playwright && npx playwright install chromium');
    process.exit(1);
  }

  const browser = await chromium.launch();
  const page = await browser.newPage({ viewport: { width: 1920, height: 1080 } });
  await page.goto(`file://${inputPath}`);

  // Navigate through slides and screenshot each
  const sections = await page.$$eval('section', (els) => els.length);
  for (let i = 0; i < sections; i++) {
    await page.evaluate((idx) => {
      const secs = document.querySelectorAll('section');
      secs.forEach((s, j) => s.style.display = j === idx ? '' : 'none');
    }, i);

    await page.waitForTimeout(200);
    const screenshot = await page.screenshot({ type: 'png' });

    const slide = pptx.addSlide();
    slide.addImage({ data: `image/png;base64,${screenshot.toString('base64')}`, x: 0, y: 0, w: '100%', h: '100%' });
  }

  await browser.close();
}

main().catch(console.error);


path: scripts/lib/parse_args.js
content: /**
 * parse_args.js — Shared CLI argument parser for export scripts.
 *
 * Usage:
 *   const parseArgs = require('./lib/parse_args');
 *   const opts = parseArgs(process.argv, {
 *     input: '', output: 'output.pptx', mode: 'editable'
 *   });
 */

function parseArgs(args, defaults = {}) {
  const opts = { ...defaults };
  for (let i = 2; i < args.length; i++) {
    if (args[i].startsWith('--') && args[i + 1]) {
      const key = args[i].replace(/^--/, '');
      opts[key] = args[++i];
    }
  }
  return opts;
}

module.exports = parseArgs;

path: scripts/open_for_print.js
content: #!/usr/bin/env node
/**
 * open_for_print.js — Render HTML and export as PDF via Playwright
 *
 * Usage:
 *   node open_for_print.js --input page.html --output page.pdf
 */

const fs = require('fs');
const path = require('path');

function parseArgs(args) {
  const opts = { input: '', output: 'output.pdf' };
  for (let i = 2; i < args.length; i++) {
    if (args[i] === '--input' && args[i + 1]) opts.input = args[++i];
    else if (args[i] === '--output' && args[i + 1]) opts.output = args[++i];
  }
  return opts;
}

async function main() {
  const opts = parseArgs(process.argv);
  if (!opts.input) {
    console.error('Usage: node open_for_print.js --input page.html --output page.pdf');
    process.exit(1);
  }

  const inputPath = path.resolve(opts.input);
  if (!fs.existsSync(inputPath)) {
    console.error(`Error: Input file not found: ${inputPath}`);
    process.exit(1);
  }

  let chromium;
  try {
    const { chromium: pw } = require('playwright');
    chromium = pw;
  } catch {
    console.error('PDF export requires Playwright. Install with: npm install playwright && npx playwright install chromium');
    process.exit(1);
  }

  const browser = await chromium.launch();
  const page = await browser.newPage();

  await page.goto(`file://${inputPath}`, { waitUntil: 'networkidle' });

  // Check if it's a slide deck — if so, show all slides for printing
  const isDeck = await page.evaluate(() => {
    const stage = document.querySelector('deck-stage');
    if (stage) {
      stage.querySelectorAll('section').forEach(s => {
        s.style.display = '';
        s.style.pageBreakAfter = 'always';
      });
      return true;
    }
    return false;
  });

  const outputPath = path.resolve(opts.output);

  await page.pdf({
    path: outputPath,
    format: isDeck ? undefined : 'A4',
    width: isDeck ? '1920px' : undefined,
    height: isDeck ? '1080px' : undefined,
    printBackground: true,
    margin: isDeck ? { top: 0, right: 0, bottom: 0, left: 0 } : undefined,
  });

  await browser.close();
  console.log(`PDF saved to: ${outputPath}`);
}

main().catch(console.error);


path: scripts/package.json
content: {
  "name": "cc-design-scripts",
  "private": true,
  "description": "Export utility scripts for cc-design skill",
  "scripts": {
    "pptx": "node gen_pptx.js",
    "inline": "node super_inline_html.js",
    "pdf": "node open_for_print.js"
  },
  "dependencies": {
    "playwright": "^1.53.0",
    "pptxgenjs": "^3.12.0"
  }
}


path: scripts/super_inline_html.js
content: #!/usr/bin/env node
/**
 * super_inline_html.js — Bundle HTML + all linked assets into a single self-contained file
 *
 * Inlines: <link> CSS, <script src> JS, <img src> as data URLs
 *
 * Usage:
 *   node super_inline_html.js --input page.html --output page-inline.html
 */

const fs = require('fs');
const path = require('path');
const parseArgs = require('./lib/parse_args');

function fileToDataUrl(filePath, mimeType) {
  const data = fs.readFileSync(filePath);
  return `data:${mimeType};base64,${data.toString('base64')}`;
}

function getMimeType(ext) {
  const types = {
    '.png': 'image/png', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg',
    '.gif': 'image/gif', '.svg': 'image/svg+xml', '.webp': 'image/webp',
    '.ico': 'image/x-icon', '.css': 'text/css', '.js': 'text/javascript',
    '.woff': 'font/woff', '.woff2': 'font/woff2', '.ttf': 'font/ttf',
  };
  return types[ext.toLowerCase()] || 'application/octet-stream';
}

function inlineHtml(html, baseDir) {
  let result = html;

  // Inline <link rel="stylesheet" href="...">
  result = result.replace(/<link\s+[^>]*rel=["']stylesheet["'][^>]*href=["']([^"']+)["'][^>]*\/?>/gi, (match, href) => {
    if (href.startsWith('http') || href.startsWith('data:')) return match;
    const filePath = path.resolve(baseDir, href);
    if (!fs.existsSync(filePath)) return match;
    const css = fs.readFileSync(filePath, 'utf-8');
    return `<style>\n${css}\n</style>`;
  });

  // Inline <script src="...">
  result = result.replace(/<script\s+[^>]*src=["']([^"']+)["'][^>]*><\/script>/gi, (match, href) => {
    if (href.startsWith('http') || href.startsWith('data:')) return match;
    // Skip external CDN scripts
    if (href.includes('unpkg.com') || href.includes('cdn.')) return match;
    const filePath = path.resolve(baseDir, href);
    if (!fs.existsSync(filePath)) return match;
    const js = fs.readFileSync(filePath, 'utf-8');
    return `<script>\n${js}\n</script>`;
  });

  // Inline <img src="...">
  result = result.replace(/<img\s+[^>]*src=["']([^"']+)["']/gi, (match, src) => {
    if (src.startsWith('http') || src.startsWith('data:')) return match;
    const filePath = path.resolve(baseDir, src);
    if (!fs.existsSync(filePath)) return match;
    const ext = path.extname(filePath);
    const dataUrl = fileToDataUrl(filePath, getMimeType(ext));
    return match.replace(src, dataUrl);
  });

  // Inline url() only within <style> blocks and style="" attributes (avoid matching JS strings)
  // Process <style> blocks first
  result = result.replace(/<style[^>]*>([\s\S]*?)<\/style>/gi, (match, cssContent) => {
    const inlined = cssContent.replace(/url\(["']?([^"')]+)["']?\)/gi, (urlMatch, urlPath) => {
      if (urlPath.startsWith('http') || urlPath.startsWith('data:')) return urlMatch;
      const filePath = path.resolve(baseDir, urlPath);
      if (!fs.existsSync(filePath)) return urlMatch;
      const ext = path.extname(filePath);
      const dataUrl = fileToDataUrl(filePath, getMimeType(ext));
      return `url("${dataUrl}")`;
    });
    return `<style>${inlined}</style>`;
  });

  // Process style="" attributes
  result = result.replace(/style=["']([^"']*url\([^"']*?)[^"']*["']/gi, (match) => {
    return match.replace(/url\(["']?([^"')]+)["']?\)/gi, (urlMatch, urlPath) => {
      if (urlPath.startsWith('http') || urlPath.startsWith('data:')) return urlMatch;
      const filePath = path.resolve(baseDir, urlPath);
      if (!fs.existsSync(filePath)) return urlMatch;
      const ext = path.extname(filePath);
      const dataUrl = fileToDataUrl(filePath, getMimeType(ext));
      return `url("${dataUrl}")`;
    });
  });

  return result;
}

function main() {
  const opts = parseArgs(process.argv, { input: '', output: '' });
  if (!opts.input) {
    console.error('Usage: node super_inline_html.js --input page.html --output page-inline.html');
    process.exit(1);
  }

  const inputPath = path.resolve(opts.input);
  if (!fs.existsSync(inputPath)) {
    console.error(`Error: Input file not found: ${inputPath}`);
    process.exit(1);
  }

  const html = fs.readFileSync(inputPath, 'utf-8');
  const baseDir = path.dirname(inputPath);
  const inlined = inlineHtml(html, baseDir);

  if (opts.output) {
    const outputPath = path.resolve(opts.output);
    fs.writeFileSync(outputPath, inlined, 'utf-8');
    console.log(`Inlined HTML saved to: ${outputPath}`);
  } else {
    process.stdout.write(inlined);
  }
}

main();


Security scan verdicts — ClawScan: clean | static analysis: suspicious | VirusTotal: clean | SkillSpector: suspicious (HIGH)