Html Widget logo

Html Widget

Community
PM-Shawn
html-widget

生成可交互的 HTML 可视化组件 — 算法动画、数据图表、概念演示、交互式教程等。当用户需要交互式演示、动画解释、数据探索、UI 原型等场景时触发,直接输出 html 代码块,前端会在安全沙箱中渲染为可交互组件。

Overview

PublisherPM-Shawn
RepositoryAbu-Cowork
Skill namehtml-widget
Stars
380
Forks
86
Bundled files
Instructions only
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • Self-contained

    Everything the model needs lives in the instructions — no extra files to sync.

  • Open source

    Published by PM-Shawn on GitHub. Read the source before you install it.

Installation

Install the Html Widget AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/PM-Shawn/Abu-Cowork.git /tmp/Abu-Cowork
mkdir -p .claude/skills
cp -r /tmp/Abu-Cowork/builtin-skills/html-widget .claude/skills/html-widget
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Html Widget in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Html Widget on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Html Widget is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

你现在帮用户生成 可交互的 HTML 组件。直接在回复中输出 ```html 代码块,前端会在安全沙箱 iframe 中自动渲染为可交互组件。

重要规则

  • 不要调用 generate_image 工具,HTML 代码块就是最终输出
  • 不要调用文件写入工具(write_file 等)把 HTML 保存到本地——这是对话内的临时可视化,不是文件交付物
  • 不要用 Artifacts 模式——直接输出代码块即可
  • 不要调用 report_plan / todo_write——直接输出代码块,不需要规划步骤

何时用可视化 vs 写文件

用户意图做法
"看看"、"分析一下"、"展示"、"解释"、"演示"→ 输出 ```html 代码块(可视化)
"导出"、"保存"、"生成报表"、"做个文件"、"发给领导"→ 调用 write_file(文件交付)
"分析数据,然后做个报表"→ 先可视化展示关键发现,再写文件

输出格式

输出 ```html 代码块,内容为 HTML 片段(不含 <!DOCTYPE><html><head><body> 标签,这些由渲染器自动包裹)。

必须按此顺序<style> → HTML 内容 → <script>

html
<style>
  /* 样式在最前——流式渲染时尽早生效 */
</style>

<div id="app">
  <!-- HTML 结构——用户最先看到的内容 -->
</div>

<script>
  // 逻辑在最后——流式完成后才执行
</script>

尺寸规范

根据内容复杂度选择合适的尺寸:

类型高度适用场景
紧凑型≤150px单个指标卡片、迷你图、简单计数器
标准型150-400px单个图表、数据表格、简单交互
完整型400-700px仪表盘、多图表组合、复杂交互
  • 宽度:始终自适应 100%
  • body 已预设 16px 内边距

设计规范

核心审美原则:克制、留白、精致。宁可简洁到"少了点什么",也不要堆砌到"多了点什么"。每个像素都要有存在的理由。

配色体系

必须使用浅色/白色背景。禁止深色/黑色背景。

基础色(CSS 变量,已预注入)

变量用途
--abu-primary主色/强调色#d97757(暖橙)
--abu-text正文文字#29261b(深棕)
--abu-text-muted次要文字/辅助信息#888579(暖灰)
--abu-bg页面背景#ffffff
--abu-bg-secondary卡片/区块背景#f5f3ee(米白)
--abu-border边框/分割线#e5e2db(浅驼)

数据色板(图表数据系列,按优先级排列):

序号色值名称适用
1#4F46E5靛蓝默认首选、主数据系列
2#0891B2青色对比系列
3#10B981翠绿正向指标(增长/完成)
4#F59E0B琥珀警告/中性指标
5#EF4444赤红负向指标(下降/错误)
6#7C3AED紫色补充系列
7#EC4899玫红补充系列

语义色

  • 成功/增长:#10B981,搭配浅底 #ecfdf5
  • 危险/下降:#EF4444,搭配浅底 #fef2f2
  • 警告:#F59E0B,搭配浅底 #fffbeb
  • 信息:#4F46E5,搭配浅底 #eef2ff

配色原则

  • 白色基底 + 单一强调色。一个组件最多 1 种强调色 + 数据色板
  • 大面积只用白/米白/浅灰,色彩只用于数据点、状态标识、CTA 按钮
  • 图表背景透明,不要给 chart container 加背景色
  • 相邻数据系列色相间距 > 60°,确保色盲友好

间距系统(4px 网格)

所有间距使用 4 的倍数:

token用途
--s-14px图标与文字间距、紧凑元素内边距
--s-28px相关元素间距、小卡片内边距
--s-312px组内元素间距、按钮组 gap
--s-416px段落间距、卡片内边距(默认)
--s-520px区块间距
--s-624px大卡片内边距、区块间隔
--s-832px主要分区间隔

用法示例:padding: 16pxgap: 12pxmargin-bottom: 24px。不要出现 5px、7px、15px 这种非 4 倍数值。

圆角

场景
小元素(标签、徽章)4px
按钮、输入框6px
卡片、面板10px
大容器、模态框12px

统一使用 border-radius,不要混用不同的圆角值。一个组件内圆角值最多 2 种。

字体与排版

  • 字体栈system-ui, -apple-system, "PingFang SC", "Microsoft YaHei", sans-serif
  • 字重:仅 400(正文)和 600(标题/强调),禁用 300/500/700/800
  • 等宽数字:数值型内容加 font-variant-numeric: tabular-nums 确保对齐
层级字号字重行高用途
大标题24px6001.3仪表盘主标题
标题18px6001.4区块标题、图表标题
小标题15px6001.5卡片标题
正文14px4001.6默认正文
辅助12px4001.5标签、图例、脚注、数据来源
超大数字28-36px6001.2KPI 核心数值

中文排版规则

  • 中英文/中文与数字之间加半角空格:GDP 总量 101.4 万亿
  • 数字千分位:¥1,234,567
  • 日期格式:2024 年 3 月2024-03-15
  • 百分比:正数加 + 号 → +12.3%
  • 标点用中文全角:

组件模式

卡片(Card)
css
.card {
  background: var(--abu-bg-secondary);
  border-radius: 10px;
  padding: 16px;
}
.card-title {
  font-size: 12px;
  font-weight: 400;
  color: var(--abu-text-muted);
  text-transform: uppercase;
  letter-spacing: 0.5px;
  margin-bottom: 8px;
}
  • 卡片之间 gap 12px
  • 卡片内不要再嵌套卡片(最多一层)
  • 卡片标题用辅助字号(12px)+ muted 色,不要用大标题
KPI 指标卡
css
.kpi-value {
  font-size: 28px;
  font-weight: 600;
  color: var(--abu-text);
  font-variant-numeric: tabular-nums;
  line-height: 1.2;
}
.kpi-trend {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 12px;
  font-weight: 600;
  margin-top: 4px;
  padding: 2px 6px;
  border-radius: 4px;
}
.kpi-trend.up { color: #10B981; background: #ecfdf5; }
.kpi-trend.down { color: #EF4444; background: #fef2f2; }
  • 趋势指示用 字符 + 语义色背景标签,而不是纯文字
  • 数值居左对齐,不要居中
数据表格(Table)
css
.data-table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  font-size: 13px;
}
.data-table th {
  text-align: left;
  font-weight: 600;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--abu-text-muted);
  padding: 8px 12px;
  border-bottom: 2px solid var(--abu-border);
}
.data-table td {
  padding: 10px 12px;
  border-bottom: 1px solid var(--abu-border);
  color: var(--abu-text);
}
.data-table tr:last-child td { border-bottom: none; }
.data-table tr:hover td { background: var(--abu-bg-secondary); }
  • 表头用小字 + 大写 + muted 色,营造层次感
  • 数字列右对齐 + tabular-nums
  • 不要给表格加外边框,只用行分割线
标签页(Tabs)
css
.tabs {
  display: flex;
  gap: 0;
  border-bottom: 1px solid var(--abu-border);
  margin-bottom: 16px;
}
.tab {
  padding: 8px 16px;
  font-size: 13px;
  color: var(--abu-text-muted);
  border: none;
  background: none;
  cursor: pointer;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  transition: color 0.15s, border-color 0.15s;
}
.tab:hover { color: var(--abu-text); }
.tab.active {
  color: var(--abu-primary);
  border-bottom-color: var(--abu-primary);
  font-weight: 600;
}
徽章/标签(Badge)
css
.badge {
  display: inline-flex;
  align-items: center;
  padding: 2px 8px;
  font-size: 11px;
  font-weight: 600;
  border-radius: 4px;
  letter-spacing: 0.3px;
}
.badge-success { color: #10B981; background: #ecfdf5; }
.badge-danger { color: #EF4444; background: #fef2f2; }
.badge-warning { color: #D97706; background: #fffbeb; }
.badge-info { color: #4F46E5; background: #eef2ff; }
进度条(Progress Bar)
css
.progress-track {
  height: 6px;
  background: var(--abu-border);
  border-radius: 3px;
  overflow: hidden;
}
.progress-fill {
  height: 100%;
  border-radius: 3px;
  background: var(--abu-primary);
  transition: width 0.4s ease;
}
分割线
css
.divider {
  height: 1px;
  background: var(--abu-border);
  margin: 16px 0;
}

按钮

基础按钮样式已预设在渲染器中,直接用 <button> 即可。

css
/* 主要操作 */
.btn-primary {
  background: var(--abu-primary);
  color: #fff;
  border-color: var(--abu-primary);
}
.btn-primary:hover { opacity: 0.9; }

/* 次要操作 */
.btn-secondary {
  background: var(--abu-bg-secondary);
  border-color: var(--abu-border);
}

/* 小按钮 */
.btn-sm { padding: 4px 8px; font-size: 12px; }

/* 按钮组 */
.btn-group { display: inline-flex; gap: 8px; }
  • 按钮文字简洁:2-4 个字(开始 重置 下一步
  • 一组操作最多 1 个主要按钮,其余用默认样式
  • 禁用态:opacity: 0.5; pointer-events: none

布局模式

网格仪表盘
css
.grid-2 { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; }
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
.grid-4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; }
  • KPI 行:3-4 列
  • 图表区:1 列(全宽)或 2 列
  • 不要超过 4 列
Flex 布局
css
.flex-between {
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.flex-center {
  display: flex;
  align-items: center;
  justify-content: center;
}
标准页面结构
┌─ 标题区(标题 + 副标题/数据来源)──────────────────┐
│                                                      │
├─ KPI 行(3-4 个指标卡片)──────────────────────────┤
│                                                      │
├─ 主图表区(全宽或左右分栏)────────────────────────┤
│                                                      │
├─ 辅助区(表格/列表/补充图表)──────────────────────┤
│                                                      │
└─ 脚注(数据来源、时间戳)──────────────────────────┘

图表规范

Chart.js 标准配置
javascript
// 全局默认值(在创建图表前设置)
Chart.defaults.font.family = "system-ui, -apple-system, 'PingFang SC', sans-serif";
Chart.defaults.font.size = 12;
Chart.defaults.color = '#888579';
Chart.defaults.plugins.legend.labels.usePointStyle = true;
Chart.defaults.plugins.legend.labels.boxWidth = 8;
Chart.defaults.plugins.legend.labels.boxHeight = 8;
Chart.defaults.plugins.legend.labels.padding = 16;

// 推荐配置
const chartOptions = {
  responsive: true,
  maintainAspectRatio: false,
  plugins: {
    legend: {
      position: 'bottom',           // 图例放底部,不占标题空间
      labels: { usePointStyle: true, boxWidth: 8, boxHeight: 8, padding: 16 }
    },
    tooltip: {
      backgroundColor: '#29261b',
      titleFont: { size: 13 },
      bodyFont: { size: 12 },
      padding: 10,
      cornerRadius: 6,
      displayColors: true,
    }
  },
  scales: {
    x: {
      grid: { display: false },       // X 轴隐藏网格线
      ticks: { font: { size: 12 } },
      border: { color: '#e5e2db' }
    },
    y: {
      grid: { color: '#f5f3ee' },     // Y 轴用极浅网格线
      ticks: { font: { size: 12 } },
      border: { display: false }
    }
  }
};
ECharts 标准配置
javascript
const echartsTheme = {
  backgroundColor: 'transparent',
  textStyle: { fontFamily: "system-ui, -apple-system, 'PingFang SC', sans-serif" },
  title: { textStyle: { fontSize: 16, fontWeight: 600, color: '#29261b' } },
  legend: { bottom: 0, itemWidth: 12, itemHeight: 8, textStyle: { fontSize: 12, color: '#888579' } },
  grid: { left: 60, right: 20, top: 40, bottom: 50, containLabel: false },
  xAxis: { axisLine: { lineStyle: { color: '#e5e2db' } }, splitLine: { show: false } },
  yAxis: { axisLine: { show: false }, splitLine: { lineStyle: { color: '#f5f3ee' } } },
  tooltip: {
    backgroundColor: '#29261b', borderWidth: 0, textStyle: { color: '#fff', fontSize: 12 },
    padding: [8, 12], extraCssText: 'border-radius: 6px;'
  },
  color: ['#4F46E5', '#0891B2', '#10B981', '#F59E0B', '#EF4444', '#7C3AED', '#EC4899']
};
图表设计原则
  • 标题在图表外部用 HTML 写,不用 Chart.js/ECharts 内置标题
  • 图例放底部,用圆点样式,不要用方块
  • 网格线极淡#f5f3ee)或隐藏,不要用深色
  • 柱状图:圆角 borderRadius: 6,柱宽适中不要太细
  • 折线图:线宽 2px,圆点 4px,hover 6px
  • 饼图/环图:环图优先(更现代),中间显示总计
  • 数值格式化:Y 轴大数字用 亿 单位,tooltip 显示完整数字
  • 脚注生成时间:不要硬编码日期,用 <span class="generated-date"></span> 占位,JS 中用 new Date().toLocaleDateString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit' }).replace(/\//g, '-') 动态填充

动画与交互

  • 过渡:仅 backgroundcolorborder-coloropacitytransform,时长 0.15s
  • hover 效果:卡片 background 微调亮度,按钮 opacity: 0.9background 变色
  • 可点击元素:必须有 cursor: pointer + hover 视觉变化
  • 禁止box-shadow 变化、filter: blur()、大幅 transform 动画(流式渲染不兼容)
  • 图表动画:使用库内置动画即可(Chart.js 默认 1s easing),不要自定义复杂动画

禁止清单

  • ❌ 深色/黑色背景
  • ❌ 渐变(linear-gradient / radial-gradient
  • ❌ 阴影(box-shadow)、模糊(filter: blur
  • position: fixed(iframe 内无效)
  • ❌ localStorage / sessionStorage(sandbox 限制)
  • ❌ fetch / XHR 网络请求(所有数据内联)
  • ❌ ES module import(用 <script src=""> 引入)
  • ❌ 非 4 倍数间距(5px, 7px, 15px 等)
  • ❌ 超过 2 种字重(只能 400 和 600)
  • ❌ 纯装饰性元素(无意义的 icon、分隔符、装饰线条)
  • ❌ 手写 SVG icon(用 Unicode 字符替代:

可用外部库(CDN)

html
<!-- 图表 -->
<script src="https://cdn.jsdelivr.net/npm/chart.js@4"></script>
<script src="https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/d3@7"></script>

<!-- 动画 -->
<script src="https://cdn.jsdelivr.net/npm/animejs@3"></script>

可用 CDN 前缀:cdn.jsdelivr.net / cdnjs.cloudflare.com / unpkg.com / esm.sh

库选择指南

  • 简单柱/线/饼图 → Chart.js(轻量,配置简单)
  • 复杂交互图表、中文场景 → ECharts(功能全,中文友好)
  • 自定义可视化、力导向图 → D3(灵活但代码量大)
  • 动画演示 → anime.js

注意:外部脚本和内联脚本按顺序执行(渲染器已处理加载顺序)。不要用 window.onload 包裹代码,直接写即可。

场景模板

指标卡片(紧凑型)

html
<style>
  .kpi-row { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
  .kpi-card { background: var(--abu-bg-secondary); border-radius: 10px; padding: 16px; }
  .kpi-label { font-size: 11px; font-weight: 400; color: var(--abu-text-muted); text-transform: uppercase; letter-spacing: 0.5px; }
  .kpi-value { font-size: 28px; font-weight: 600; color: var(--abu-text); font-variant-numeric: tabular-nums; line-height: 1.2; margin-top: 8px; }
  .kpi-trend { display: inline-flex; align-items: center; gap: 4px; font-size: 12px; font-weight: 600; margin-top: 8px; padding: 2px 8px; border-radius: 4px; }
  .kpi-trend.up { color: #10B981; background: #ecfdf5; }
  .kpi-trend.down { color: #EF4444; background: #fef2f2; }
</style>
<div class="kpi-row">
  <div class="kpi-card">
    <div class="kpi-label">总销售额</div>
    <div class="kpi-value">¥284.7 万</div>
    <div class="kpi-trend up">↑ 12.3% 环比</div>
  </div>
  <div class="kpi-card">
    <div class="kpi-label">订单量</div>
    <div class="kpi-value">3,842</div>
    <div class="kpi-trend down">↓ 3.1% 环比</div>
  </div>
  <div class="kpi-card">
    <div class="kpi-label">客单价</div>
    <div class="kpi-value">¥741</div>
    <div class="kpi-trend up">↑ 5.8% 环比</div>
  </div>
</div>

数据图表(标准型)

html
<style>
  .chart-header { margin-bottom: 16px; }
  .chart-title { font-size: 18px; font-weight: 600; color: var(--abu-text); }
  .chart-subtitle { font-size: 12px; color: var(--abu-text-muted); margin-top: 4px; }
  .chart-wrap { position: relative; height: 320px; }
</style>
<div class="chart-header">
  <div class="chart-title">2020–2025 年 GDP 增长趋势</div>
  <div class="chart-subtitle">数据来源:国家统计局 · 单位:万亿元</div>
</div>
<div class="chart-wrap">
  <canvas id="chart"></canvas>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4"></script>
<script>
(function() {
  Chart.defaults.font.family = "system-ui, -apple-system, 'PingFang SC', sans-serif";
  Chart.defaults.color = '#888579';
  new Chart(document.getElementById('chart'), {
    type: 'bar',
    data: {
      labels: ['2020', '2021', '2022', '2023', '2024', '2025'],
      datasets: [{
        label: 'GDP(万亿元)',
        data: [101.4, 114.9, 121.0, 126.1, 134.9, 140.2],
        backgroundColor: '#4F46E5',
        borderRadius: 6,
        maxBarThickness: 48,
      }]
    },
    options: {
      responsive: true,
      maintainAspectRatio: false,
      plugins: {
        legend: { display: false },
        tooltip: {
          backgroundColor: '#29261b', cornerRadius: 6, padding: 10,
          titleFont: { size: 13 }, bodyFont: { size: 12 }
        }
      },
      scales: {
        x: { grid: { display: false }, border: { color: '#e5e2db' } },
        y: { grid: { color: '#f5f3ee' }, border: { display: false }, beginAtZero: false }
      }
    }
  });
})();
</script>

交互式演示(标准型)

html
<style>
  .demo-header { margin-bottom: 16px; }
  .demo-title { font-size: 18px; font-weight: 600; color: var(--abu-text); }
  .demo-desc { font-size: 12px; color: var(--abu-text-muted); margin-top: 4px; }
  .stage { position: relative; height: 240px; border: 1px solid var(--abu-border); border-radius: 10px; overflow: hidden; background: var(--abu-bg-secondary); }
  .controls { display: flex; align-items: center; justify-content: center; gap: 8px; margin-top: 12px; }
  .info { font-size: 13px; color: var(--abu-text-muted); text-align: center; margin-top: 8px; }
</style>
<div class="demo-header">
  <div class="demo-title">冒泡排序可视化</div>
  <div class="demo-desc">点击"下一步"逐步观察排序过程</div>
</div>
<div class="stage" id="stage"></div>
<div class="controls">
  <button onclick="prev()">◀ 上一步</button>
  <button class="btn-primary" onclick="next()">下一步 ▶</button>
  <button onclick="autoPlay()">自动播放</button>
</div>
<div class="info" id="info">已就绪,共 8 个元素</div>
<script>
  // 演示逻辑...
</script>

数据表格 + 图表组合(完整型)

html
<style>
  .page-header { margin-bottom: 20px; }
  .page-title { font-size: 24px; font-weight: 600; color: var(--abu-text); }
  .page-subtitle { font-size: 12px; color: var(--abu-text-muted); margin-top: 4px; }
  .kpi-row { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin-bottom: 20px; }
  .kpi-card { background: var(--abu-bg-secondary); border-radius: 10px; padding: 16px; }
  .kpi-label { font-size: 11px; color: var(--abu-text-muted); text-transform: uppercase; letter-spacing: 0.5px; }
  .kpi-value { font-size: 28px; font-weight: 600; color: var(--abu-text); font-variant-numeric: tabular-nums; line-height: 1.2; margin-top: 8px; }
  .kpi-trend { display: inline-flex; align-items: center; gap: 4px; font-size: 12px; font-weight: 600; margin-top: 8px; padding: 2px 8px; border-radius: 4px; }
  .kpi-trend.up { color: #10B981; background: #ecfdf5; }
  .kpi-trend.down { color: #EF4444; background: #fef2f2; }
  .section { margin-bottom: 20px; }
  .section-title { font-size: 15px; font-weight: 600; color: var(--abu-text); margin-bottom: 12px; }
  .chart-wrap { position: relative; height: 300px; background: var(--abu-bg-secondary); border-radius: 10px; padding: 16px; }
  .data-table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: 13px; }
  .data-table th { text-align: left; font-weight: 600; font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px; color: var(--abu-text-muted); padding: 8px 12px; border-bottom: 2px solid var(--abu-border); }
  .data-table td { padding: 10px 12px; border-bottom: 1px solid var(--abu-border); }
  .data-table tr:last-child td { border-bottom: none; }
  .data-table .num { text-align: right; font-variant-numeric: tabular-nums; }
  .footnote { font-size: 11px; color: var(--abu-text-muted); margin-top: 16px; }
</style>

<div class="page-header">
  <div class="page-title">Q4 销售分析报告</div>
  <div class="page-subtitle">2024 年 10–12 月 · 数据截至 2024-12-31</div>
</div>

<div class="kpi-row">
  <div class="kpi-card">
    <div class="kpi-label">总营收</div>
    <div class="kpi-value">¥847 万</div>
    <div class="kpi-trend up">↑ 15.2%</div>
  </div>
  <div class="kpi-card">
    <div class="kpi-label">订单数</div>
    <div class="kpi-value">12,458</div>
    <div class="kpi-trend up">↑ 8.7%</div>
  </div>
  <div class="kpi-card">
    <div class="kpi-label">客单价</div>
    <div class="kpi-value">¥679</div>
    <div class="kpi-trend down">↓ 2.1%</div>
  </div>
  <div class="kpi-card">
    <div class="kpi-label">退货率</div>
    <div class="kpi-value">3.2%</div>
    <div class="kpi-trend up">↑ 0.4pp</div>
  </div>
</div>

<div class="section">
  <div class="section-title">月度趋势</div>
  <div class="chart-wrap"><canvas id="chart"></canvas></div>
</div>

<div class="section">
  <div class="section-title">分品类明细</div>
  <table class="data-table">
    <thead><tr><th>品类</th><th class="num">营收</th><th class="num">占比</th><th class="num">同比</th></tr></thead>
    <tbody>
      <tr><td>电子产品</td><td class="num">¥3,240,000</td><td class="num">38.2%</td><td class="num" style="color:#10B981">+22.1%</td></tr>
      <tr><td>家居用品</td><td class="num">¥2,180,000</td><td class="num">25.7%</td><td class="num" style="color:#10B981">+11.5%</td></tr>
      <tr><td>服装配饰</td><td class="num">¥1,850,000</td><td class="num">21.8%</td><td class="num" style="color:#EF4444">-3.2%</td></tr>
      <tr><td>食品饮料</td><td class="num">¥1,200,000</td><td class="num">14.2%</td><td class="num" style="color:#10B981">+8.9%</td></tr>
    </tbody>
  </table>
</div>

<div class="footnote">数据来源:内部销售系统 · 生成时间:<span class="generated-date"></span></div>

<script src="https://cdn.jsdelivr.net/npm/chart.js@4"></script>
<script>
(function() {
  Chart.defaults.font.family = "system-ui, -apple-system, 'PingFang SC', sans-serif";
  // 动态填充生成日期
  var dateEl = document.querySelector('.generated-date');
  if (dateEl) dateEl.textContent = new Date().toLocaleDateString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit' }).replace(/\//g, '-');

  Chart.defaults.color = '#888579';
  new Chart(document.getElementById('chart'), {
    type: 'line',
    data: {
      labels: ['10 月', '11 月', '12 月'],
      datasets: [
        { label: '营收(万元)', data: [265, 278, 304], borderColor: '#4F46E5', backgroundColor: 'rgba(79,70,229,0.08)', fill: true, tension: 0.3, pointRadius: 4, pointHoverRadius: 6, borderWidth: 2 },
        { label: '订单数(百)', data: [38, 41, 45], borderColor: '#0891B2', backgroundColor: 'transparent', tension: 0.3, pointRadius: 4, pointHoverRadius: 6, borderWidth: 2 }
      ]
    },
    options: {
      responsive: true, maintainAspectRatio: false,
      plugins: {
        legend: { position: 'bottom', labels: { usePointStyle: true, boxWidth: 8, boxHeight: 8, padding: 16 } },
        tooltip: { backgroundColor: '#29261b', cornerRadius: 6, padding: 10 }
      },
      scales: {
        x: { grid: { display: false }, border: { color: '#e5e2db' } },
        y: { grid: { color: '#f5f3ee' }, border: { display: false } }
      }
    }
  });
})();
</script>

UI 原型 / Mockup 规范

当用户要求"画个原型"、"做个 mockup"、"设计一个页面"时,生成高保真静态原型,模拟真实 App/Web 界面。

原型设计原则

  • 看起来像真的 App,不是线框图——用真实文字、真实数据、真实图标
  • 固定宽度模拟设备:手机 375px、平板 768px、桌面 1024px,居中显示
  • 所有文字用中文,数据用逼真的假数据
  • 交互仅限视觉反馈:hover 态、active 态、tab 切换,不做真实业务逻辑
  • 多页面支持:用 JS 切换 display 实现页面导航,不要用 iframe 或多个 HTML 文件

原型色彩

原型不一定用 Abu 品牌色,根据产品类型选择合适的主色:

产品类型推荐主色辅色
企业/SaaS#4F46E5(靛蓝)#6366F1
电商#EF4444(红)或 #F97316(橙)#FCA5A5
社交/内容#0EA5E9(天蓝)#38BDF8
金融#0891B2(青)或 #1D4ED8(深蓝)#67E8F9
健康/生活#10B981(绿)#6EE7B7
工具/效率#6366F1(紫)或 #8B5CF6#A78BFA
通用/默认#4F46E5#818CF8

原型组件模式

手机顶部导航栏(Status Bar + Nav)
css
.phone-frame {
  width: 375px;
  margin: 0 auto;
  background: #fff;
  border-radius: 20px;
  overflow: hidden;
  border: 1px solid var(--abu-border);
}
.status-bar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 8px 20px;
  font-size: 12px;
  font-weight: 600;
  color: #1a1a1a;
}
.nav-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 8px 16px;
  font-size: 17px;
  font-weight: 600;
}
底部标签栏(Tab Bar)
css
.tab-bar {
  display: flex;
  border-top: 1px solid #f0f0f0;
  background: #fff;
  padding: 6px 0 20px;
}
.tab-item {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  font-size: 10px;
  color: #999;
  cursor: pointer;
}
.tab-item.active { color: var(--app-primary); }
.tab-icon { font-size: 20px; }
列表项(List Cell)
css
.list-cell {
  display: flex;
  align-items: center;
  padding: 12px 16px;
  gap: 12px;
  border-bottom: 1px solid #f5f5f5;
  cursor: pointer;
}
.list-cell:hover { background: #fafafa; }
.avatar {
  width: 44px;
  height: 44px;
  border-radius: 10px;
  background: linear-gradient(135deg, #667eea, #764ba2);
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  font-size: 16px;
  font-weight: 600;
  flex-shrink: 0;
}
卡片/内容块
css
.content-card {
  margin: 0 16px 12px;
  background: #f8f8f8;
  border-radius: 12px;
  padding: 16px;
}
输入框 / 搜索框
css
.search-bar {
  margin: 8px 16px;
  padding: 10px 12px;
  background: #f5f5f5;
  border-radius: 10px;
  font-size: 14px;
  color: #999;
  display: flex;
  align-items: center;
  gap: 8px;
}
浮动按钮(FAB)
css
.fab {
  position: absolute;
  right: 20px;
  bottom: 80px;
  width: 56px;
  height: 56px;
  border-radius: 28px;
  background: var(--app-primary);
  color: #fff;
  font-size: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  border: none;
}

多页面导航模式

多页面原型通过 JS 切换 display 实现,所有页面 HTML 共存于 DOM 中,用 data-page 属性标识。

核心架构

css
/* 页面容器 */
.page { display: none; }
.page.active { display: flex; flex-direction: column; }

/* 转场动画(可选) */
.page { opacity: 0; transition: opacity 0.2s ease; }
.page.active { opacity: 1; }
javascript
// 页面切换(全局函数)
function goTo(pageId) {
  document.querySelectorAll('.page').forEach(p => p.classList.remove('active'));
  document.getElementById(pageId).classList.add('active');
  // 更新底部 tab 高亮
  document.querySelectorAll('.tab-item').forEach(t => {
    t.classList.toggle('active', t.dataset.page === pageId);
  });
}

底部 Tab 切换(主导航):

  • 点击 Tab → goTo('page-xxx') → 同时高亮当前 Tab
  • Tab Bar 固定在 phone-frame 底部,所有页面共享

页面内跳转(子页面/详情页):

  • 列表项点击 → goTo('page-detail') → 顶部 nav 显示返回按钮
  • 返回按钮 → goTo('page-xxx') 回到来源页

页面结构规范

html
<div class="phone-frame">
  <!-- 所有页面 -->
  <div class="page active" id="page-home">
    <!-- 顶部固定区:status bar + nav -->
    <!-- 中间滚动区:内容 -->
    <!-- 底部固定区:tab bar(主页面有,子页面无) -->
  </div>
  <div class="page" id="page-discover">...</div>
  <div class="page" id="page-profile">...</div>
  <div class="page" id="page-detail">...</div>
</div>

页面布局(每个 page 内部)

css
.page {
  position: absolute;
  top: 0; left: 0; right: 0; bottom: 0;
  flex-direction: column;
  overflow: hidden;
}
.page-header { flex-shrink: 0; }
.page-body { flex: 1; overflow-y: auto; -webkit-overflow-scrolling: touch; }
.page-footer { flex-shrink: 0; }

注意

  • 页面数量控制在 3–5 个,不要太多(代码量会爆)
  • 底部 Tab Bar 只在主页面显示(一般 3–5 个 tab),详情页/子页面不显示 Tab Bar
  • 首页默认 class="page active",其余页面无 active
  • 代码量可放宽到 400 行以内(多页面场景)

原型模板:多页手机 App

html
<style>
  :root { --app-primary: #4F46E5; --app-primary-light: #EEF2FF; --app-bg: #f5f5f5; }
  * { box-sizing: border-box; }

  .phone-frame {
    width: 375px; height: 720px; margin: 0 auto;
    background: var(--app-bg); border-radius: 20px;
    overflow: hidden; border: 1px solid #e5e5e5;
    position: relative;
  }

  /* 页面系统 */
  .page { display: none; position: absolute; top: 0; left: 0; right: 0; bottom: 0; flex-direction: column; opacity: 0; transition: opacity 0.2s ease; }
  .page.active { display: flex; opacity: 1; }

  /* 状态栏 */
  .status-bar { display: flex; justify-content: space-between; padding: 10px 20px 6px; font-size: 12px; font-weight: 600; background: #fff; flex-shrink: 0; }

  /* 导航栏 */
  .nav-bar { display: flex; align-items: center; padding: 4px 16px 12px; background: #fff; flex-shrink: 0; }
  .nav-back { font-size: 22px; cursor: pointer; margin-right: 8px; color: var(--app-primary); border: none; background: none; padding: 4px; }
  .nav-title { font-size: 17px; font-weight: 600; flex: 1; }
  .nav-title.large { font-size: 28px; font-weight: 700; }
  .nav-action { font-size: 20px; cursor: pointer; }

  /* 内容区 */
  .page-body { flex: 1; overflow-y: auto; -webkit-overflow-scrolling: touch; }

  /* 底部 Tab Bar */
  .tab-bar { display: flex; background: #fff; border-top: 1px solid #f0f0f0; padding: 6px 0 20px; flex-shrink: 0; }
  .tab-item { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 2px; font-size: 10px; color: #999; cursor: pointer; border: none; background: none; padding: 4px; }
  .tab-item.active { color: var(--app-primary); }
  .tab-icon { font-size: 20px; line-height: 1; }
</style>

<div class="phone-frame">
  <!-- ====== 首页 ====== -->
  <div class="page active" id="page-home">
    <div class="status-bar"><span>9:41</span><span>● ● ●</span></div>
    <div class="nav-bar">
      <div class="nav-title large">首页</div>
      <div class="nav-action">🔔</div>
    </div>
    <div class="page-body">
      <!-- 首页内容... -->
    </div>
    <div class="tab-bar">
      <button class="tab-item active" data-page="page-home" onclick="goTo('page-home')">
        <span class="tab-icon"></span>首页
      </button>
      <button class="tab-item" data-page="page-discover" onclick="goTo('page-discover')">
        <span class="tab-icon"></span>发现
      </button>
      <button class="tab-item" data-page="page-profile" onclick="goTo('page-profile')">
        <span class="tab-icon"></span>我的
      </button>
    </div>
  </div>

  <!-- ====== 发现页 ====== -->
  <div class="page" id="page-discover">
    <div class="status-bar"><span>9:41</span><span>● ● ●</span></div>
    <div class="nav-bar"><div class="nav-title large">发现</div></div>
    <div class="page-body">
      <!-- 发现页内容... -->
    </div>
    <div class="tab-bar">
      <button class="tab-item" data-page="page-home" onclick="goTo('page-home')">
        <span class="tab-icon"></span>首页
      </button>
      <button class="tab-item active" data-page="page-discover" onclick="goTo('page-discover')">
        <span class="tab-icon"></span>发现
      </button>
      <button class="tab-item" data-page="page-profile" onclick="goTo('page-profile')">
        <span class="tab-icon"></span>我的
      </button>
    </div>
  </div>

  <!-- ====== 详情页(子页面,无 Tab Bar) ====== -->
  <div class="page" id="page-detail">
    <div class="status-bar"><span>9:41</span><span>● ● ●</span></div>
    <div class="nav-bar">
      <button class="nav-back" onclick="goTo('page-home')"></button>
      <div class="nav-title">详情</div>
      <div class="nav-action"></div>
    </div>
    <div class="page-body">
      <!-- 详情页内容... -->
    </div>
  </div>
</div>

<script>
function goTo(pageId) {
  document.querySelectorAll('.page').forEach(function(p) { p.classList.remove('active'); });
  document.getElementById(pageId).classList.add('active');
  document.querySelectorAll('.tab-item').forEach(function(t) {
    t.classList.toggle('active', t.dataset.page === pageId);
  });
}
</script>

原型模板:单页手机 App

html
<style>
  :root { --app-primary: #4F46E5; --app-primary-light: #EEF2FF; }
  .phone-frame { width: 375px; margin: 0 auto; background: #fff; border-radius: 20px; overflow: hidden; border: 1px solid #e5e5e5; position: relative; min-height: 680px; }
  .status-bar { display: flex; justify-content: space-between; padding: 10px 20px 6px; font-size: 12px; font-weight: 600; }
  .nav-bar { display: flex; align-items: center; justify-content: space-between; padding: 4px 16px 12px; }
  .nav-title { font-size: 28px; font-weight: 700; }
  .nav-action { font-size: 20px; cursor: pointer; }
</style>

<div class="phone-frame">
  <div class="status-bar">
    <span>9:41</span>
    <span>● ● ●</span>
  </div>
  <div class="nav-bar">
    <div class="nav-title">标题</div>
    <div class="nav-action"></div>
  </div>
  <!-- 页面内容 -->
</div>

原型中的图标

不用引入图标库,使用 Unicode/Emoji 字符模拟:

场景字符
首页
搜索
消息
我的
设置
返回
更多
添加
关闭
勾选
收藏☆ / ★
分享
通知🔔
图片🏞
购物车🛒
点赞♡ / ♥

原型注意事项

  • 原型中允许使用渐变和阴影(与普通 widget 不同),因为原型不需要流式预览效果
  • 头像用渐变色块 + 首字母替代真实图片
  • 图片占位用纯色矩形 + emoji 图标(如 🏞
  • 内容文字用逼真但虚构的数据,不要用 "Lorem ipsum" 或 "测试文字"
  • 底部留出安全区域(padding-bottom: 20px)模拟 iPhone Home Indicator

注意事项

  1. 所有数据内联:直接写在 JS 中,不要 fetch
  2. 初始状态有意义:加载后立即展示内容,不要空白等用户操作
  3. 交互反馈:可点击元素必须有 hover/active 视觉反馈
  4. 中文优先:UI 文字使用中文
  5. 简洁代码:普通组件控制在 200 行以内,多页面原型可放宽到 400 行,不要过度工程化
  6. 流式友好:style 在前,HTML 在中,script 在后——用户先看到结构,最后才激活交互

Frequently asked questions

What does the Html Widget AI skill do?

生成可交互的 HTML 可视化组件 — 算法动画、数据图表、概念演示、交互式教程等。当用户需要交互式演示、动画解释、数据探索、UI 原型等场景时触发,直接输出 html 代码块,前端会在安全沙箱中渲染为可交互组件。

Why use Html Widget on TypingMind?

Because you install it once and use it with any model. Html Widget is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Html Widget in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/PM-Shawn/Abu-Cowork/tree/main/builtin-skills/html-widget. TypingMind reads its SKILL.md and installs it as a skill you can enable per chat.

Which AI models can use Html Widget?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Html Widget?

As many as you like. As long as a model supports skills, you can use Html Widget with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Html Widget AI skill free?

It is published on GitHub by PM-Shawn. Check the repository for licensing terms. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇