简介:UI UX Pro Max 是一款专为 Claude Code、Cursor、Windsurf 等 AI 编程助手打造的设计智能技能包,面向需要快速搭建专业界面的开发者与产品设计人员。包内通过结构化数据库与语义搜索,自动识别产品类型并输出设计风格、配色、字体、图表及 UX 规范,适配 SaaS、医疗、电商、移动应用等多元场景。压缩包共 104 个文件,以 CSV 设计数据、Markdown 说明文档、TypeScript/Python 脚本及 JSON 配置为主,其中 CSV 承载风格、配色、字体等分类信息,MD 与脚本提供调用说明,整体仅 1.53MB,轻量且便于携带与复用。内容覆盖 57 种设计风格、95 种配色方案、56 组字体搭配、24 种图表类型、29 种落地页结构和 98 条 UX 准则,同时支持 React、Next.js、Vue、Flutter 等 8 大技术栈,输入提示即可获得规范建议与响应式代码参考,并附有各技术栈的代码示例。目前已有 1706 人学习/下载,适合希望提升界面产出效率的中高级开发者直接集成到工作流中。
1. UI UX Pro Max 是什么:先用流水线思维拆掉三次返工
第一次看到“UI UX Pro Max 一站式AI界面设计智能工具(Python 源码)”这个标题,很多人的第一反应是“又一个人工智能画 UI 的玩具”。实际上,如果你把需求从“画图”换成“把界面设计流程自动化”,它解决的就是我每天都在经历的返工问题:产品口述一个页面 → 设计师画稿 → 前端照着还原。这三步里任何一次修改,都意味着后面全部重来。而这个标题指向的工具,是用 Python 源码把“描述→设计规范→多平台界面代码”揉成一条流水线,替代掉中间最耗时的两段。
它适合的人也很明确:独立开发者不想为了一个详情页反复调 CSS;前端新手需要先拿到能跑的结构再学样式;后端同学想快速给管理系统套一个像样的界面。核心词是“一站式”和“多平台”——一份设计输入,同时输出 Web、桌面和移动端代码。下面我会按自己实现这类工具的工程路径,讲清楚选型、代码骨架和那些踩过的坑。
2. 为什么用 Python 做 UI/UX 智能生成:选型理由与核心流程
2.1 从需求文本到界面代码:中间表示(IR)的设计
如果你让大模型直接生成一个完整的 HTML 页面,十次里有八次会得到“远看能看,近看全乱”的结果:标题字号一会儿 24px 一会儿 28px,按钮间距靠 margin 硬撑,颜色代码五花八门。原因不是模型能力不够,而是缺少一套“给机器看的 UI 视觉规范”。常见的解决方案,是让模型先输出一份中间表示(IR),而不是直接输出代码。
IR 在界面生成里一般分两层。第一层是设计令牌(Design Tokens),记录品牌级的视觉基础:主色、成功色、警告色、文字颜色、字体大小、间距基数、圆角和阴影。第二层是组件树,描述页面由哪些组件构成以及它们如何嵌套。组件树只表达“登录页上半部分是卡片,卡片里有两个输入框和一个按钮”,不关心具体像素。这样可以做到结构可校验、视觉可替换、渲染层可复用,整个架构很像编译器里的 AST。
设计 IR 时很容易犯两个错误:一是把组件类型放得太多,比如把“带下拉的单选”也定义为独立组件,模型输出时频繁出错;二是字段结构写得太灵活,比如 props 允许任意键,结果同一类型在不同页面里出现不同形状。我一般会限制在 10 到 20 个组件类型,并且把 props 的必填项说清楚。这个约束越清晰,后面做 pydantic 校验就越省力。
2.2 Python 生态里最适合做这件事的三个库组合
在 Python 生态里搭这类工具,我固定用三个库:pydantic、jinja2、以及大模型 SDK。pydantic 用来定义和校验 IR 的数据结构,大模型返回的 JSON 先经过它,缺字段、类型错误都会被拦下来。jinja2 负责把 IR 渲染成不同平台的代码,模板化比逐个字符串拼接可靠得多,以后要支持新平台只需再写一个模板文件。大模型 SDK 则根据你实际用的服务选择,OpenAI 系或者本地的 ollama Python 包都可以。
选 Python 而不选 Node.js,是因为这里的工作重心不是写前端,而是写“从 IR 到多套模板”的代码生成逻辑。Python 的类型校验和模板引擎都足够成熟,且写数据清洗、重试、日志这类胶水代码非常顺手。如果本机还没装 Python,建议直接装 3.10 以上版本,pydantic v2 和类型注解在旧版本上会有各种兼容问题。Windows 上安装时记得勾选“Add Python to PATH”,不然后面执行任何命令都会扑空。
有人问为什么不用现成的 UI 生成框架?框架大多绑定某个技术栈,比如只出 React 代码或只出 Vue 代码。而标题里要的是多平台。Python 作为中间层,可以把同一份 IR 送给不同模板,分别生成 Web 页面、Flutter 组件和 Qt 界面。设计令牌归设计令牌,组件树归组件树,平台差异全部放在模板和映射表里,这也是我认为最可持续的架构。
2.3 一个最小可用流程:描述 -> 设计令牌 -> 组件树 -> 代码
我实际在本地跑通的最小流程是四步:用户输入一句需求,比如“一个带侧边栏的个人中心页面,顶部是用户信息卡片,下面是订单列表”;大模型返回一份 JSON,包含 tokens 和 tree;pydantic 校验并修复默认值;最后 jinja2 渲染成目标平台代码。核心代码少得惊人,大概只有四五行业务逻辑,但每一步都有讲究。
raw_ir = llm_response_to_json(user_prompt) ir_model = DesignIR.model_validate(raw_ir) web_html = render_template("web.html.j2", tokens=ir_model.tokens, tree=ir_model.tree)这段逻辑里最关键的是model_validate。它会把 JSON 按 pydantic 定义的结构重新解析一遍,组件类型不认识直接抛异常,按钮没写文案也能自动补齐。很多人跳过这一步,直接把模型输出丢给渲染函数,结果浏览器打开一片空白,还要从头排查是不是模型输出被截断了。
另一个重要点是日志。模型输出失败时,不要只记录“调用失败”,要把原始响应和校验错误同时打出来。我见过的大多数返工都浪费在“不知道为什么失败”上。先跑通这条四步流程,再考虑优化细节。温度先固定 0.2,max_tokens 给 1500,后面我会详细展开参数为什么这样设。
3. 把最小可用版跑通:Python 源码骨架与三个关键代码块
3.1 定义界面描述 DSL:用 JSON 描述组件层级
要复刻“UI UX Pro Max”这类工具的底层逻辑,第一步是先把界面描述语言定下来。我习惯用 JSON 同时承载设计令牌和组件树。组件树里的每个节点叫 Component,包含 type、props、children 三个字段。type 是组件类型,props 是控制该组件行为的键值对,children 表示内部嵌套。
下面是一个登录页的 IR 示例,结构简单但足够说明问题:
{ "tokens": { "color_primary": "#1677ff", "color_bg": "#f5f5f5", "radius": 6, "font_size_base": 14 }, "tree": { "type": "page", "props": {"title": "登录"}, "children": [ { "type": "form", "props": {"width": 320, "justify": "center"}, "children": [ {"type": "input", "props": {"label": "账号", "placeholder": "请输入邮箱"}}, {"type": "input", "props": {"label": "密码", "type": "password"}}, {"type": "button", "props": {"variant": "primary", "text": "登录"}} ] } ] } }这段 JSON 很重要,因为它就是模型与大模型接口之间的“协议”。我一般会把允许的组件类型写死在系统提示词里,不让模型发明新组件。否则模型偶尔会输出一个type: "super_button"之类的东西,渲染层根本不知道该怎么处理。设计 DSL 时,尽量用与 Element UI 近似的命名,比如button、input、form,这样后续给前端同学审代码时,他们一眼就能看懂结构。
3.2 用大模型接口把需求转成设计令牌
协议定好后,接下来就是让大模型把自然语言需求转成 JSON。这里最容易翻车的是模型不按要求输出,返回一段 markdown 代码块,或者在 JSON 前后加解释文字。解决办法有两个:一是使用支持 JSON mode 的接口,二是把系统提示词写得足够“凶狠”。下面是 OpenAI 风格的最低调用代码:
from openai import OpenAI import json client = OpenAI() SYSTEM_PROMPT = """ 你是 UI 架构师。把用户需求转成 JSON,格式如下: {"tokens": {...}, "tree": {...}} 你只能使用这些组件类型:page, nav, form, input, button, card, list, list_item, text。 不要输出任何解释,不要包含代码块标记,只输出 JSON 本身。 """ resp = client.chat.completions.create( model="gpt-4o-mini", response_format={"type": "json_object"}, messages=[ {"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": "一个极简登录页面,居中显示,带账号密码和登录按钮"}, ], temperature=0.2, max_tokens=1500, ) ir = json.loads(resp.choices[0].message.content) print(ir["tree"]["type"])这里response_format是关键参数,它能强制模型输出合法 JSON,避免手动清理字符串的麻烦。温度设 0.2 是为了让组件树足够稳定,同一个需求两次生成的结构差异很小。max_tokens=1500对登录页这种简单页面已经足够,但如果描述的是包含数据表格和弹窗的后台页面,最好提到 2000 以上,防止组件树被截断。
如果你不想调用云服务,也可以换成本地模型,比如通过 ollama 的 Python 包调用 qwen2.5 7B。但要注意,本地模型在 JSON mode 上的稳定性参差不齐,返回体里经常混入换行和注释,所以后续 pydantic 校验和重试机制一定不能省。
3.3 从设计令牌渲染出多平台代码(Web/桌面/移动)
IR 校验通过后,剩下的事情就交给模板引擎。我用 jinja2 为每个目标平台准备一套模板,比如web.html.j2、flutter.dart.j2、qt.qml.j2。模板里通过递归宏遍历组件树,并根据 tokens 填充视觉变量。下面是一个最简化的 Web 模板:
<!-- templates/web.html.j2 --> <!DOCTYPE html> <html> <head> <style> .page { max-width: {{ tree.props.get('width', 480) }}px; margin: 0 auto; font-family: system-ui, sans-serif; background: {{ tokens.color_bg }}; } .btn-primary { background: {{ tokens.color_primary }}; border-radius: {{ tokens.radius }}px; } </style> </head> <body> {% macro render(node) %} {% if node.type == "page" %} <div class="page"> {% for child in node.children %}{{ render(child) }}{% endfor %} </div> {% elif node.type == "form" %} <form> {% for child in node.children %}{{ render(child) }}{% endfor %} </form> {% elif node.type == "input" %} <label> {{ node.props.get("label") }} <input placeholder="{{ node.props.get('placeholder', '') }}" type="{{ node.props.get('type', 'text') }}" /> </label> {% elif node.type == "button" %} <button class="btn-{{ node.props.get('variant', 'default') }}"> {{ node.props.get("text") }} </button> {% endif %} {% endmacro %} {{ render(tree) }} </body> </html>渲染代码很简单:
from jinja2 import Environment, FileSystemLoader env = Environment( loader=FileSystemLoader("templates"), autoescape=True, ) html = env.get_template("web.html.j2").render(tree=ir["tree"], tokens=ir["tokens"]) with open("out.html", "w", encoding="utf-8") as f: f.write(html)autoescape=True是我必开的参数,因为模型可能给文案里塞入<script>,不转义等于直接把一个 XSS 漏洞放进了生成结果。模板渲染的核心思想是:组件类型固定时,每个平台都有自己的一套映射规则,比如page在 Web 里是div,在 Flutter 里是Scaffold,在 Qt 里是顶层Window。新增平台时不需要改 IR 和大模型提示词,只需新增一个模板文件。
3.4 参数怎么设:温度、Top-p、max_tokens 与 UI 稳定性的关系
上次给团队分享这个方案,有同事问“为什么我生成的界面每次都不一样?”答案基本都出在参数上。UI 生成是结构化输出任务,不是文案创作,需要的不是天马行空,而是可复现。我建议用下面这张表作为起步配置:
| 参数 | 常见范围 | 对 UI 生成的影响 |
|---|---|---|
| temperature | 0.1 到 0.3 | 越高布局越自由,但结构越容易漂移 |
| top_p | 0.1 到 0.5 | 越过滤低概率词汇,输出越聚焦 |
| max_tokens | 1000 到 2000 | 太低会让组件树被截断,代码残缺 |
| presence_penalty | 0 | 非零可能导致组件命名不一致 |
| frequency_penalty | 0 | 非零会削弱重复结构的稳定性 |
我常用的起点是temperature=0.2, top_p=0.3, max_tokens=1500。如果你的需求包含复杂表格和多标签页,优先加 max_tokens,不要加温度。温度高了之后,同一句话生成的页面可能在两种视觉风格之间横跳,但结构完全不一致,这会让后续校验非常痛苦。
还有一个本地模型特有的坑:有些开源模型在max_tokens设置过小时会提前停止输出,甚至返回空字符串。遇到这种情况,先把max_tokens调到 2000 以上,再检查是不是模型量化版本太激进。记住,参数调的不是“更聪明”,而是“更稳定”。对 UI 生成工具来说,稳定比聪明更有价值。
4. 避免生成结果“一眼假”:设计系统约束与多平台校验
4.1 为什么要先锁设计系统:颜色、间距、字体尺寸
去掉设计约束后,AI 生成的页面会出现一个经典症状:所有元素都对齐了,但看起来就是“一眼假”。原因是模型对间距的理解是离散的,它可能上一个区块用 16px,下一个区块用 14px,肉眼看着粗细不一致。解决这个问题,要在 IR 里预设一套设计令牌,并严格限制取值的可能范围,而不是让模型随意给数字。
下面是一个典型的设计令牌片段:
tokens: color_primary: "#1677ff" color_success: "#52c41a" color_warning: "#faad14" color_error: "#ff4d4f" color_text: "#262626" color_text_secondary: "#8c8c8c" spacing_base: 8 font_size_base: 14 font_size_lg: 18 radius_sm: 4 radius_md: 6这里最关键的是把间距基数定成 8。spacing 只允许 8、16、24、32 这些倍数,生成结果才可能出现整齐的留白。Element UI 这类组件库的视觉规范里就有类似规则,这也是它能保持批量页面协调的原因。没有这套约束,大模型会发挥出“人类的创造力”,但界面设计恰恰需要统一性。
4.2 在 Python 里写规则校验器:检查组件树是否合规
设计令牌定好了,接下来要防止模型输出语法正确但语义不合理的组件树。我之前用 pydantic 写过一个递归校验器,每次校验都检查组件类型、必填 props 和按钮文案。代码骨架如下:
from pydantic import BaseModel, Field, model_validator from typing import List, Dict, Any class Component(BaseModel): type: str props: Dict[str, Any] = Field(default_factory=dict) children: List["Component"] = Field(default_factory=list) @model_validator(mode="after") def check_known_type(self): allowed = { "page", "nav", "form", "input", "button", "card", "list", "list_item", "text" } if self.type not in allowed: raise ValueError(f"unknown component type: {self.type}") if self.type == "button" and "text" not in self.props: # 兜底,而不是直接拒绝 self.props["text"] = "未命名按钮" if self.type == "input" and "label" not in self.props: raise ValueError("input component requires label prop") return self Component.model_rebuild()这里我故意把“按钮缺 text”做成自动兜底,而不是抛异常。因为工具最终要输出一个能预览的页面,用户看到“未命名按钮”还能继续改;但如果直接拒绝,整个生成就中断了。反过来,“input 缺 label”会直接影响语义和可访问性,这种必须抛错并让模型重试。规则要分层:致命错误拒绝,非致命错误自动修复。
4.3 多平台目标如何映射:从 Web 到 Flutter/Qt 的差异表
IR 校验通过后,多平台渲染的本质是一张映射表。组件类型到平台控件的映射看起来简单,但 props 的语义差异才是真正需要维护的地方。下表是我在做多端输出时常用的映射关系:
| IR 组件 | Web (HTML) | Flutter | Qt/QML |
|---|---|---|---|
| page | div | Scaffold | PageWindow |
| nav | nav | NavigationBar | TabBar |
| form | form | Form | FormLayout |
| input | input | TextField | TextField |
| button | button | ElevatedButton | Button |
| card | div.card | Card | Frame |
映射表只是第一步。variant这样的 props 在 Web 里渲染成 CSS class,在 Flutter 里可能需要切换不同 widget,比如variant: "primary"时用ElevatedButton,variant: "text"时用TextButton。这层差异不能塞进 IR,而是应该放在模板的 if 分支里。IR 保持平台无关,换平台只改模板,这是多平台方案最值得坚持的边界。
4.4 交互状态:hover/focus/disabled 的兜底生成
AI 生成的组件树本质是静态结构,它不会主动为按钮生成 hover 效果,也不会考虑输入框获得焦点时的边框颜色。如果直接渲染,得到的页面虽然结构完整,但用户操作起来像在摸一张照片。我加了一层“交互状态兜底”,在渲染前自动为组件补充状态样式。
state_snippets = { "button": { "hover": ".btn-primary:hover { opacity: 0.85; }", "disabled": ".btn[disabled] { cursor: not-allowed; opacity: 0.5; }", }, "input": { "focus": "input:focus { border-color: #1677ff; outline: none; }", } }这些片段会在模板渲染时合并到<style>中。对于 Flutter 或 Qt,事件绑定和状态回调则要在模板层生成为onPressed: () {}或onClicked: {}。另一个容易忘的是暗黑模式。我习惯在 tokens 里预留color_bg_dark和color_text_dark,并通过 CSS 媒体查询自动切换。加了这层兜底后,生成结果至少从“静态图”变成了“可交互的原型”,这对内部评审非常有用。
5. UI 生成工具避坑指南:5 个高频问题从现象到解决
5.1 生成结果“看起来能跑,一运行就塌”
现象是:用浏览器直接打开生成的 HTML 一切正常,但把它嵌进 Vue 或 React 项目后,页面直接白屏。原因在于模型只生成了静态 HTML 片段,缺少框架需要的根组件、模板包裹和事件绑定。前端项目对文件结构有硬性要求,不是一段<div>就能跑。
解决方法是把“框架骨架”写死在模板里,模型只负责生成页面内容。比如 Vue 模板固定输出<template> ... </template>,React 模板固定输出export default function App() { return ... }。让模型写 node 类型,而不是写文件结构。这个调整之后,运行崩溃的比例从四成降到了几乎为零。
5.2 API 返回不稳定:JSON 解析失败和字段缺失
现象:十次调用有两三次json.loads直接抛异常,或者 pydantic 报“field required”。原因未必是模型能力差,更常见的是response_format没开,模型在 JSON 前后加了说明文字;或者输出太长被截断,组件树只有上半截。
我现在的做法是写一个三层防线:先开 JSON mode,再把模型返回字符串里的 markdown 代码块标记剥离,最后用 pydantic 校验,失败就自动重试。重试代码很简单:
def call_with_retry(attempts=3): for i in range(attempts): try: raw = llm_call() cleaned = raw.strip().removeprefix("```json").removesuffix("```") return DesignIR.model_validate(json.loads(cleaned)) except Exception: continue raise RuntimeError("重试次数耗尽,请检查提示词或模型配置")注意attempts不要设太多次,否则模型调用成本会成倍增加。重试之间可以稍微增加temperature,从 0.2 提到 0.3,打破模型的卡死状态。
5.3 中文与多语言环境下的字体和文案溢出
现象:生成页面在中文输入下频繁溢出,按钮文字“确 定”被截断,下拉框高度不够。原因是模型在生成代码时默认了拉丁字体和宽度假设,没有考虑中日韩文字更长的问题。
解决时必须做两件事:第一,模板的 font-family 要显式包含中文字体栈,比如"PingFang SC", "Microsoft YaHei", "Noto Sans CJK SC";第二,固定宽度的组件要加overflow-wrap: break-word或min-width。最稳妥的做法是在测试阶段分别用中英文文案跑一遍渲染,并截图对比,避免只测英文页面。
5.4 多平台代码不能一份通吃:组件语义差异
现象:同一个button节点,Web 生成结果可以点击,Qt 生成结果只有文字外观,鼠标放上去没有反应。原因是在 IR 里没有表达“这个按钮点击之后干什么”的抽象,平台模板不知道该生成什么回调。
我在 IR 里为交互组件增加了一个action字段,用对象描述行为,比如{"type": "submit", "target": "/login"}。Web 模板渲染成onsubmit,Flutter 模板渲染成onPressed,Qt 模板渲染成onClicked。这样交互行为跨平台保持一致,而不是只在 Web 端能点。这个问题不解决,所谓“多平台”就只是一张皮。
5.5 可访问性与暗黑模式被忽略的坑
现象:生成的界面在暗黑模式下文字和背景对比度不足;键盘 Tab 切换时看不到焦点。原因是模型和模板都没考虑 WCAG 对比度标准。
我的做法是在校验阶段加一个简单对比度检查,主色#1677ff上不能直接压白色小号文字。同时模板默认加上:focus-visible样式,比如outline: 2px solid #1677ff。对需要暗黑模式的网站,tokens 里要有color_bg_dark、color_text_dark这样的语义变量,而不是写死具体颜色。这些细节直接影响产品可用性,也是 UI 自动化测试里最容易漏掉的用例。
6. 让它稳定可用:回归截图、样本注入与质量门禁
6.1 用回归截图对比验证每次改动没破坏 UI
当模板越来越多,一个不小心就可能让所有页面的按钮样式崩掉。我习惯用 Playwright 做“回归截图对照”:每次修改提示词或模板后,自动打开生成页面截图,计算文件哈希,和上一次对比。如果哈希变化超出预期,立刻停下来检查。
from playwright.sync_api import sync_playwright import hashlib def capture_and_hash(path: str): with sync_playwright() as p: browser = p.chromium.launch() page = browser.new_page(viewport={"width": 390, "height": 844}) page.goto(f"file:///{path}") page.screenshot(path="shot.png") with open("shot.png", "rb") as f: return hashlib.md5(f.read()).hexdigest()viewport我一般设置 390 宽、844 高,优先模拟移动端场景,因为移动端最容易暴露溢出和间距问题。桌面端再单独跑一组 1440 宽的截图。这个流程跑起来后,每次改动都能在几分钟内发现问题,而不是等 UI 自动化测试到上线前一晚才翻车。
6.2 把生成结果转成设计稿再反哺:Figma/Element UI 的互操作
很多人以为工具输出代码就结束了,其实可以再往前走一步:把 IR 里的设计令牌导出为样式变量,再灌回设计工具或者 Element UI 的主题配置里。这样团队可以直接在已有设计系统里复用 AI 生成的颜色和间距,保持全站视觉一致。
我通常会写一个export_tokens_to_css_variables的函数,把color_primary之类的令牌转成--color-primary。对于 Flutter,则输出一个theme类的常量文件。这个能力让“AI 生成”从一次性玩具变成了可以沉淀设计资产的生产工具。
6.3 评估生成质量:用启发式评分给 UI 打分
最后是质量门禁。在没有人工介入前,怎么判断这次生成值不值得交给用户?我写了一个简单的启发式评分,只算五个硬指标:
def quality_score(ir: dict) -> int: score = 0 if "color_primary" in ir.get("tokens", {}): score += 20 if len(ir.get("tree", {}).get("children", [])) > 0: score += 20 if all(node.get("props") is not None for node in walk(ir["tree"])): score += 20 if check_contrast(ir.get("tokens", {})): score += 20 if "color_bg_dark" in ir.get("tokens", {}): score += 20 return score我习惯把 60 分设为重新生成的门槛。低于 60 分直接换一批参数重跑,高于 60 分才进入截图对比环节。这个评分不是给作品打分,而是当作自动化流水线里的安全阀,减少无意义的返工。
我现在的习惯是,任何新的生成需求都先写一个最小 IR 样本,把完整链路跑通后才交给大模型自由发挥。这个顺序帮我避开了至少一半的“玄学问题”。希望这篇笔记能帮你把“UI UX Pro Max”这个方向真正落进自己的项目里,少踩几个坑。
本文还有配套的精品资源,点击获取