引言
"Figma 的问题不是功能不好,是你的设计数据不属于你,设计师和开发者之间永远隔着一道翻译层。"
这是"每日一个开源项目"系列的第137篇文章。今天的主角是 Penpot——一个用 Clojure/ClojureScript 全栈构建的开源设计协作工具。
2022 年,Figma 宣布以 200 亿美元被 Adobe 收购,设计圈开始认真讨论开源替代方案。Penpot 在那之后迎来了增长,但它的核心思路早在 Kaleidos 创立它的时候就确定了:设计工具应该是开放的,设计数据应该是标准格式的,设计和开发之间不应该存在翻译层。
三个"应该",对应三个真正的工程决策:MPL-2.0 开源协议 + Docker 自托管,SVG 作为原生存储格式,设计属性直接映射 CSS(Grid、Flex、字体、间距)。
你将学到什么
- SVG 原生格式的含义:为什么这是消灭设计-开发鸿沟的关键
- CSS 映射设计属性:Grid/Flex 布局工具如何与 CSS 对齐
- Clojure/ClojureScript 全栈选型的逻辑
- Docker Compose 自托管的部署结构
- 实时协作的 WebSocket 架构
- 设计系统(组件、色板、文本样式)的完整支持
- 与 Figma 的本质差异:不是功能对比,是哲学对比
前置知识
- 使用过设计工具(Figma、Sketch 或类似工具)
- 了解 SVG 格式的基本概念
- 了解 CSS Flexbox 和 Grid 的基本用法
项目背景
项目简介
Penpot 是一个基于 Web 的开源设计和原型协作平台,作为 Figma/Sketch 的开源替代品构建。它的核心技术决定是:以 SVG 作为原生存储格式,设计属性与 CSS 规范对齐。
这两个决定的含义是:
- 导出的设计文件是标准 SVG,可以在任何支持 SVG 的工具里打开和编辑
- 设计师在 Penpot 里设置的 Flex 布局、Grid 布局、间距属性,和开发者在 CSS 里写的属性是同一套语言
设计师和开发者之间的"翻译工作"(把 Figma 的约束系统手动转换成 CSS 数值)在这个架构下大幅缩短。
项目由西班牙数字产品工作室 Kaleidos 创建并开源,现在由 Penpot 团队持续维护,有商业化托管版本(penpot.app)和可自托管的社区版本。
作者/团队介绍
- 组织: Kaleidos / Penpot Team
- 技术栈: Clojure(后端)+ ClojureScript(前端)+ PostgreSQL
- License: Mozilla Public License 2.0(MPL-2.0)
- 官网: penpot.app
项目数据
- ⭐ GitHub Stars: 35,000+
- 🍴 Forks: 1,700+
- 📄 License: MPL-2.0
- 🌐 支持语言:西班牙语、英语、多语言社区贡献
主要功能
核心作用
传统设计工具链(Figma 模式):
设计师在 Figma 中设计
↓
导出 PDF/PNG 或使用 Figma Dev Mode
↓
开发者手动读取:字体大小、颜色、间距、Flex 对齐方式...
↓
在 CSS 中重新写一遍
Penpot 模式:
设计师在 Penpot 中设计(属性直接是 CSS 属性)
↓
开发者查看面板,看到的直接是 CSS Grid/Flex 属性
↓
复制 CSS 片段或直接理解样式规则
使用场景
- 团队设计协作:多人实时编辑同一文件,无需 Figma 订阅,适合预算有限或数据安全要求高的团队
- 企业私有化部署:金融、医疗等对数据主权有要求的行业,Docker 自托管在内网运行
- 设计系统管理:组件库、色板、文本样式、图标集,组织设计规范
- 原型交互设计:添加过渡动画、交互流程,输出可点击原型
- 设计-开发协作:CSS 原生属性让前端开发者直接读取设计意图,减少沟通成本
- 开源项目的设计资产:设计文件可以像代码一样开源、协作、版本管理
快速开始
Docker Compose 自托管(推荐方式):
# 下载官方 docker-compose 配置
wget https://raw.githubusercontent.com/penpot/penpot/main/docker/images/docker-compose.yaml
# 启动(包含后端、前端、PostgreSQL、Redis、邮件服务)
docker compose -p penpot -f docker-compose.yaml up -d
访问 http://localhost:9001,注册第一个账号(默认为管理员)。
核心环境变量:
# docker-compose.yaml 中的关键配置
PENPOT_FLAGS=enable-registration enable-login-with-password
PENPOT_DATABASE_URI=postgresql://penpot/penpot
PENPOT_REDIS_URI=redis://redis/0
# 使用对象存储(可选,默认本地文件系统)
PENPOT_STORAGE_BACKEND=s3
PENPOT_STORAGE_S3_BUCKET=your-bucket
升级:
docker compose -p penpot -f docker-compose.yaml pull
docker compose -p penpot -f docker-compose.yaml up -d
核心特性
SVG 原生格式
Penpot 的文件格式本质上是结构化的 SVG。这意味着:
- 不存在专有二进制格式
- 设计文件可以被任何 SVG 解析器读取
- 导出即标准格式,无需专用导出插件
CSS 对齐的设计属性
在 Penpot 里,当你设置一个容器的布局时,你操作的概念和 CSS 是一一对应的:
Penpot 布局面板 ↔ CSS 等价
────────────────────────────────────────
Flex 布局方向:Row ↔ flex-direction: row
主轴对齐:Space Between ↔ justify-content: space-between
交叉轴对齐:Center ↔ align-items: center
行间距 16px ↔ row-gap: 16px
内边距 12px ↔ padding: 12px
Grid 布局同理——Penpot 的 Grid 工具直接暴露 grid-template-columns、grid-template-rows、gap 等 CSS Grid 概念。
设计系统支持
- 组件:创建主组件(Main Component),在文件内任意复制为实例(Instance);修改主组件,所有实例自动更新
- 组件覆写:实例可以在保持组件结构的同时覆写特定属性(文字、颜色)
- 共享库:把一个文件的资产库(组件、色板、文本样式、图标)共享给团队其他文件
- 色板系统:项目级别和文件级别的调色板,支持颜色变量
- 文本样式:定义命名文本样式(H1、Body、Caption),跨文件复用
实时协作
多人同时编辑同一文件:
- 用户光标实时可见(显示其他协作者位置)
- 评论系统(在设计稿上添加定点评论,@提及队员)
- 版本历史(可回滚到历史版本)
- 文件权限(查看者 / 编辑者分级)
项目详细剖析
Clojure/ClojureScript 全栈
Penpot 是罕见的、在生产中大规模使用 Clojure 全栈的开源项目之一。
后端:Clojure(运行在 JVM)
├── HTTP API 服务(RESTful)
├── WebSocket 实时协作处理
├── 文件存储管理
└── PostgreSQL 数据访问
前端:ClojureScript(编译到 JavaScript)
├── 基于 React 的 UI 渲染(rum 库)
├── 画布渲染(SVG-based)
├── 状态管理(基于不可变数据结构)
└── WebSocket 客户端(实时同步)
数据库:PostgreSQL
├── 用户、团队、项目数据
└── 设计文件元数据(文件内容存储为独立格式)
Redis:
└── 会话管理、实时协作状态
选用 Clojure/ClojureScript 的技术逻辑:
- 同构数据共享:前后端使用相同的数据结构描述设计文件,减少序列化/反序列化摩擦
- 不可变数据结构:天然支持撤销/重做历史(Undo/Redo)——每次操作产生新状态,旧状态自动保留
- 函数式编程:复杂的图形变换(矩阵运算、路径操作)用纯函数表达,测试和推理更容易
SVG 数据模型
Penpot 的设计文件在内部以树状数据结构表示,每个元素对应一个 SVG 节点:
;; 一个矩形元素的内部表示(简化)
{:type :rect
:id "uuid-xxx"
:name "Button Background"
:x 100, :y 200
:width 200, :height 48
:fill [{:fill-color "#3355FF" :fill-opacity 1}]
:r1 8 :r2 8 :r3 8 :r4 8 ; border-radius
:layout-item-margin {:m1 0 :m2 16 :m3 0 :m4 16} ; 在父 Flex 容器中的 margin
}
这个数据结构可以直接序列化为 SVG 属性,没有"设计软件内部格式 → 导出格式"的二次转换。
实时协作架构
客户端 A(设计师)
│ 移动元素操作
▼
WebSocket 连接
│ 发送操作(Operation/Transaction)
▼
后端 WebSocket 处理器
│
├── 持久化到 PostgreSQL
│
├── 广播给同文件的其他客户端
│
└── 客户端 B(另一个设计师)接收 → 应用操作 → 更新本地状态
操作是以"操作类型 + 参数"描述的,不是发送完整文件,而是发送增量变更(类似 CRDT 的思路,但实现更简单)。
原型交互系统
Penpot 的原型交互基于"帧(Frame)→ 帧"的跳转模型:
页面帧 A(登录页)
│ 点击"登录"按钮
│ 触发器:点击
│ 动画:滑入(右→左,300ms)
▼
页面帧 B(首页)
支持的触发器:点击、悬停、鼠标按下、鼠标松开、进入/离开视口 支持的过渡动画:瞬间、溶解、滑入、滑出、弹出 支持的导航动作:跳转帧、叠加帧、替换帧、滚动到元素
这足以表达大多数移动端和 Web 产品的交互流程。
部署架构
完整的 Docker Compose 部署包含:
┌─────────────────────────────────────────┐
│ Nginx (反向代理) │
│ localhost:9001 │
└──────┬──────────────┬────────────────────┘
│ │
▼ ▼
┌──────────┐ ┌──────────────┐
│ Frontend │ │ Backend │
│ (静态文件) │ │ (Clojure) │
│ :3449 │ │ :6060 │
└──────────┘ └──────┬───────┘
│
┌─────────────┼──────────────┐
▼ ▼ ▼
┌─────────┐ ┌──────────┐ ┌──────────┐
│Postgres │ │ Redis │ │ 文件存储 │
│ :5432 │ │ :6379 │ │(本地/S3) │
└─────────┘ └──────────┘ └──────────┘
与 Figma 的本质差异
| 维度 | Penpot | Figma |
|---|---|---|
| 数据所有权 | 完全自托管,数据在你的服务器 | 数据在 Figma/Adobe 云端 |
| 文件格式 | SVG 原生,可用标准工具打开 | 专有 .fig 格式 |
| CSS 映射 | 设计属性直接对应 CSS 概念 | 需要 Dev Mode 翻译 |
| 价格 | 自托管免费;云托管有免费层 | 免费层受限;专业版订阅 |
| 插件生态 | 社区插件,规模较小 | 成熟庞大的插件生态 |
| 性能 | 浏览器 SVG 渲染,极大文件稍慢 | 原生渲染引擎,性能更优 |
| 多人协作 | 支持,WebSocket 实现 | 支持,成熟度更高 |
| 版本历史 | 支持 | 支持(更完善) |
| 技术开放性 | 完全开源,可二次开发 | 闭源,无法二次开发 |
Figma 在插件生态、渲染性能、高级协作功能上仍有优势。Penpot 的优势在于数据主权、CSS 对齐的开发者友好性、以及作为开源项目的可扩展性。
项目地址与资源
官方资源
- 🌟 GitHub: penpot/penpot
- 🌐 官网 & 云托管: penpot.app
- 📄 文档: help.penpot.app
- 💬 社区: community.penpot.app
相关资源
- Docker Hub 镜像:
penpotapp/backend、penpotapp/frontend - 贡献指南:
CONTRIBUTING.md(项目仓库内)
总结
Penpot 的核心论点是:设计工具的问题不是功能不够,而是数据不开放、格式不标准、设计和开发之间存在永久的翻译层。
SVG 原生格式和 CSS 对齐的属性系统,是这个论点的直接技术回答。一个前端开发者打开 Penpot 的检查面板,看到的 flex-direction: row、gap: 16px、border-radius: 8px 不需要任何翻译——这就是 CSS,他直接写就好了。
Clojure/ClojureScript 的全栈选型是一个有趣的工程决策:不可变数据结构天然支持 Undo/Redo,同构数据模型减少前后端摩擦,函数式编程让复杂图形变换可测试。这些好处对于设计工具这类交互密集的应用来说实实在在。
对于需要数据私有化的企业、预算有限的团队、或者重视设计-开发一体化工作流的团队,Penpot 是目前开源设计工具里产品完成度最高、工程架构最清晰的选项。
探索 PrimeSkills —— 精选 AI Agent 与技能的市场,每一个都经过真实企业工作流验证,去掉浮夸,留下真正有用的。
欢迎访问我的个人主页,发现更多有价值的见解和有趣的产品。