用户样式是以数据形式存储的纯 CSS,由 @malla/renderer 编译。Panda CSS 只负责编辑器自身的样式;它看不到存放在数据库中的用户样式。
存储格式
以断点为第一层级,属性名使用 camelCase,值是带单位的字符串。键为 <breakpoint> 或 <breakpoint>:<state>;组件还会添加 variant:<name>。
json
"styles": {
"base": { "display": "flex", "gap": "12px", "color": "var(--color-brand)" },
"base:hover": { "opacity": "0.9" },
"tablet": { "paddingBlock": "80px" },
"mobile": { "flexDirection": "column" }
}
- 每个键恰好编译成一条规则或一个媒体查询块,面板直接读写
styles[breakpoint]。 - “在 tablet 上被覆盖”意味着该属性存在于
styles.tablet中;删除这个键即可重置。空对象会被移除。 - 令牌就是 CSS 变量:
--color-*、--font-*、--space-*。 - 绑定的值单独存放在
styleBindings中,编译为按元素设置的 CSS 变量,在运行时赋值。
层叠
@layer reset
@layer tokens
@layer classes
@layer nodes
css
@layer reset, tokens, classes, nodes;
@layer classes {
.btn { padding: 12px 20px; background: var(--color-brand); }
.btn:hover { opacity: .9; }
.btn.btn-ghost { background: transparent; }
}
@layer nodes {
.n-hero { padding-block: 120px; }
@media (max-width: 767px) { .n-hero { padding-block: 64px; } }
}
优先级绝不取决于偶然的选择器特异性:类 → 组合类 → 节点。组合类会编译成复合选择器,因为 CSS 不关心 class 属性中名称的顺序。断点采用桌面优先(max-width:tablet 为 991 px,mobile 为 767 px),并在每个层内按顺序输出。
html
<section class="hero n-hero" data-node="hero">…</section>
在编辑器中
- 每个画布框架都有独立的样式层;编辑一个节点只会重新编译该节点的规则,因此拖动滑块时绝不会重新解析整页 CSS。
- 在编辑器中,视口单位会被改写为
calc(n * var(--malla-vh)),这样按内容定尺寸的框架就不会无限增高;发布的 CSS 保留真实单位。
发布时
共享 CSS(令牌、reset、类)在每个快照中只编译一次,文件名带内容哈希;未使用的类会被丢弃;输出经过压缩。