M3 草图为何直出代码
Material 3 Expressive 视觉规范复杂、组件众多,前端照搬设计稿时圆角、配色、动态总对不齐。m3e-canvas 把浏览器手绘直接转成可运行的 vibe code,画完即交付。
源仓库: lnkiai/m3e-canvas
m3e-canvas 是 lnkiai 在 GitHub 上开源的浏览器内 Material 3 Expressive 草图工具,目标是把 M3 草图直接变成可运行的前端代码。它在 GitHub Trending 2026-09-05 榜单上以 ⭐1577 进入前百,核心卖点是”画完即代码”——设计师在画布上拖出 Material 3 Expressive 风格的屏,再借助 vibe coding 流水线把结构化数据喂给 LLM,直接产出 Jetpack Compose 或 React 组件源码,省掉 Figma 切图、手写 tokens 的重复劳动。
1. M3 设计稿转代码,为何总差一点?
现象:Figma 出的图给到前端,圆角、配色、动效几乎都”差不多”,但几乎总是差一点。设计师和前端互相甩锅,版本迭代三四次也合不上。 根因:Material 3 Expressive 引入了 tone-based color、emphasized motion、shape morph 等动态属性,传统 sketch-to-code 工具只看静态布局,丢掉了状态、tokens、动效曲线,导致生成结果”形似神不似”。 解法:m3e-canvas 在导出时把组件的状态、shape、tokens 序列化为 JSON,再让 LLM 据此生成代码。这种”中间表示”既能被 AI 读懂,又保留了 M3 Expressive 的语义:
import { exportDsl } from 'm3e-canvas/sdk';
const dsl = exportDsl(canvasNode, {
framework: 'react',
includeTokens: true,
includeMotion: true,
});
2. 浏览器画稿如何直接驱动 AI 出代码?
现象:设计稿 → Figma → 切图 → 代码这条链路动辄一两天,期间还要走 handoff、review、再迭代三四个来回。 根因:缺少结构化的”中间表示”,AI 只能拿到一张位图或裸 SVG,没办法理解层级和语义,只能按图索骥猜像素位置。 解法:m3e-canvas 用 SolidJS + IndexedDB 在浏览器里维护节点关系,导出 DSL 后通过 vibe coding prompt 喂给本地或远端模型。这条路径跳过了 Figma,节省至少一个工时:
{
"component": "M3.Button",
"state": "filled",
"shape": "full",
"tokens": { "color": "primary", "elevation": 1 }
}
3. vibe coding 输出为何总需要二次手改?
现象:让 AI 直接生成 M3 组件,出来的代码风格不统一、命名随意、缺 a11y,每次还得人工校对一遍才能合入主干。
根因:prompt 里只有”做一个按钮”这种粗粒度指令,缺少 design tokens、a11y 规范、命名约定这些上下文。
解法:m3e-canvas 启动时加载 m3e.config.ts,把 tokens、命名空间、a11y 规则塞进 system prompt,LLM 输出即可直接落地:
// m3e.config.ts
export default {
tokens: { primary: '#6750A4', radius: 20 },
a11y: { minTouchTarget: 48 },
naming: 'PascalCase',
};
4. 不联网如何跑 m3e-canvas?
现象:很多设计工具强依赖云端,断网就废,企业内网和保密项目更麻烦。 根因:渲染引擎、字体、组件库常走 CDN,离线就 404。 解法:m3e-canvas 用 Vite + SolidJS,所有依赖都通过 npm 安装,离线也能 build。导出 DSL、调用本地 Ollama 也都不需要外网:
git clone https://github.com/lnkiai/m3e-canvas
cd m3e-canvas && npm install
npm run dev # 打开 http://localhost:5173
5. 私有设计系统如何复用 m3e-canvas 的导出?
现象:公司设计系统在 M3 基础上加了十几个私有组件,AI 生成的代码没法直接用。
根因:通用工具不认识私有组件,只能猜 props 和事件名。
解法:在 m3e.config.ts 里注册 component registry,导出 DSL 时优先匹配私有组件:
// m3e.config.ts
export default {
registry: {
'M3.Button': '@/components/MyButton',
'M3.Card': '@/components/MyCard',
},
};
本文参考:GitHub Trending 2026-09-05、lnkiai/m3e-canvas README、r/ClaudeAI 9 月初相关讨论、Hacker News #show hn 帖。