CSS 压缩与美化

全球通用工具

粘贴一份样式表或一个页面,工具会给出行为完全一致的最小版本;反过来,也能把同一份文件排成一行一条声明,方便人阅读。每删掉一个字节都会被计数并归类,因此结果自带一份审计报告:去掉了多少条注释、多少段连续空白、多少个位于 `}` 前的分号、多少个 `0px` 长度、多少个可缩短的颜色。

可以粘贴完整样式表或完整页面,内容不会被截断。页面初始会载入一份可替换的示例样式表。

压缩会删除不参与渲染的内容;美化只改变排版,绝不改变内容。

自动模式会读取文本。主动选择 JavaScript,会带你去看它被拒绝的原因。

以 `/*!` 开头的注释是许可声明惯例,除非你另行选择,否则会保留。

只有两种改写,且都有断言:长度里的 `0px` 变成 `0`,以及 `#ffffff` 在三组两位重复时变成 `#fff`。

留空时会根据粘贴的内容生成:`styles.min.css`、`page.min.html`。

边输入边更新。所有计算都在你的浏览器里完成,数据不会离开本机。

压缩后的 CSS

-39.5%

296 B 变为 179 B:删除了 1 条注释、32 段连续空白和 3 个块末分号,其余内容与原文一致。

输入
296 B
输出
179 B
节省
117 B
删除的注释
1

压缩后的 CSS

/*! danfio sample sheet */ .card{color:#fff;margin:0 auto;padding:10px 20px;width:calc(100% - 40px)}.card :hover{background:#eee}@media (min-width:700px){.card{padding:20px 40px}}

可以复制,也可以下载。框里的字节就是文件里的字节。

改动明细与数量

项目数量说明
已删除注释1所有 /*! 许可声明均已保留
折叠的连续空白32绝不进入字符串、url()、pre、textarea、script 或 style 内部
右花括号前删除的分号3块末分号本来就不表达任何含义
缩短的零长度1仅限长度:0px 变成 0
缩短的颜色2仅限声明值:#ffffff 变成 #fff

这些数字是这一遍处理实际做了什么,而不是它可能做什么;把它们打印出来,正是为了这一点。

刻意没有做的事

  • 不合并规则,不重排属性,也不添加浏览器前缀:这些都可能改变哪条规则胜出,属于能看到整个项目的构建工具的决定。
  • 除了零长度和重复的十六进制两位,不改写任何值。把 `font-weight: bold` 改成 `700`,或缩短 `margin: 4px 2px`,都需要知道每个属性的语法,而这一遍处理刻意不具备这种知识。
  • 选择器里的冒号绝不收紧,因为 `a :hover` 与 `a:hover` 是不同的选择器;`calc()` 里 `+`、`-` 两侧的空格属于算术,而不是装饰。
  • 自定义属性保存的是作者写下的任何内容,所以 `--x: 0px` 保持原样:这个值会在别处被替换,而它的类型无法从这里判断。

需要的是一个页面,而不是一条正则,因为 CSS 里有三个字符、HTML 里有一个字符所承载的含义,会被空白和注释掩盖。`a :hover` 不等于 `a:hover`;`calc(100% - 20px)` 需要空格,而 `margin: 1px -2px` 也需要自己的那个空格;`--x: 0px` 是一个这一遍处理无法做类型检查的值;`<pre>` 块里的空白则是浏览器真的会渲染的内容。上述每一处都在这里按名字处理,并在结果下方的列表中再次点名。JavaScript 完全不会被压缩,页面会说明原因,而不是做一个看起来合理的猜测。

关于这个工具

压缩样式表或页面,删除的是浏览器本来就不会渲染的空白,也只删这些。页面会收紧 CSS 与 HTML,保留 `calc()` 内部以及行内元素之间那些不可省略的空格,并把同一份文件美化回人能阅读的样子。

不需要账号,不需要登录,也没有任何东西要安装:粘贴或拖入文件,结果就出现在同一个标签页里。压缩器按规则逐条读取样式表,而不是靠一条正则去匹配;因此它无法归类的值——字符串、自定义属性、`url()` 路径——会被原样复制,而不是靠猜。

节省是真实的,但幅度有限,页面从不会把字节数说成性能预算:传输压缩本身已经会折叠重复空格,所以收益主要落在传输和解析时间上。不成对的文件会被拒绝,并给出所在行与列。对于一个绝不能弄坏文件的处理步骤来说,这份谨慎正是意义所在。

它是怎么工作的

压缩器可以不问自删的东西

会删除的只有五类。注释,除非它以 `/*!` 开头——那是许可声明的惯例写法,因此由一个默认开启的开关保留。连续空白先折叠成一个空格,再在浏览器不会渲染的位置删除:紧邻花括号、分号或逗号,位于文件开头或结尾,或者两侧任意一边是块级元素。紧挨 `}` 的分号会被删除,因为块本来就到此结束。在声明值内部,只有表示长度的 `0px` 会变成 `0`。六位颜色值如果三组两位完全相同,就缩成三位。

其余内容一律保持原样。不合并规则,不重排属性,不添加浏览器前缀,不把 `bold` 改成 `700`,不缩短 `margin: 4px 2px`,也不把颜色换成另一种写法。这些变换需要知道每个属性的语法,以及整个项目的层叠关系;而一个只能看到一个文件的工具,两样都不具备。输出的风格是刻意选择的:你可以把它和输入做 diff,并看懂每一处差异。

粗心的压缩器会弄坏样式表的三个地方

第一个是选择器里的冒号。`.card :hover` 选中的是 `.card` 内部被悬停的后代元素,`.card:hover` 选中的则是卡片本身。两者都合法、都很常见,差别只在那一个空格。因此这一遍处理只在声明块内部、属性名刚刚结束的位置收紧冒号。媒体特性如 `(min-width: 700px)` 同理:两种写法都合法,所以收紧它是安全的,也确实会收紧。

第二个是算术。`calc(100% - 20px)` 要求 `-` 两侧有空格,因为 `100% -20px` 是解析错误,而不是减法。但 `margin: 1px -2px` 是两个值,第二个为负,它的空格同样不可省略。因此只要 `+` 或 `-` 出现在未被识别为算术的函数之外,处理就会保留空格;而在 `calc`、`min`、`max`、`clamp` 及其同类函数内部,两侧空格都会保留。选择器又是另一回事:`li + a` 和 `li+a` 含义相同,所以相邻兄弟组合符确实会被收紧。

第三个是无法解释的值。自定义属性保存的是作者写下的任何内容——`--gap: 0px` 可能被替换进长度、时间或背景列表——所以它的值原样复制,其中的颜色或零值也一并不动。字符串和 `url(...)` 出于同样的原因原样复制:`content` 字符串里的 `/*` 不是注释,`url(https://host/a/*b*/c.png)` 是真实路径。只有做到这些,上面那些统计数字才值得信任。

HTML 中哪些空白真的会被渲染

HTML 有一处空白有意义,也有一处没有。在默认的 `white-space: normal` 下,连续空白折叠成一个空格,而当这个空格两侧都是行内内容时,它会被保留:同一行里的两个链接仍然是两个链接,中间隔一个空格。紧邻块级元素、位于块开头或块结尾的空白根本不会被渲染,会直接删除——格式化过的页面之所以能省下大量字节,几乎全部来自这里,因为那些空白都是缩进。

无法归类的部分一律按行内处理:多花一个字节,但不会改变布局。有四种元素完全不动:`pre`、`textarea`、`script` 和 `style` 逐字原样复制,因为它们的内容是文本而不是标记——粘贴进来的页面会原样保留代码块和脚本。注释会被删除,但以 `<!--!` 开头的许可声明和 `<!--[if ...]>` 条件注释例外,后者承载的是面向特定浏览器的标记。标签只整理内部:属性之间一个空格,`=` 两侧不留空格,属性值保持原样,自闭合斜杠保留,因为在内联 SVG 里正是这个斜杠结束元素。

诚实的边界是 `white-space: pre-wrap`:这样设置的元素确实会渲染空白,而文件文本里没有任何信息说明这一点。如果这个差异重要,请把那段内容放进 `pre`——本工具能识别它——或者干脆保持页面原样。

美化方向,以及两个方向为何能往返一致

另一个方向什么都不删。样式表按一行一条声明排版,缩进跟随嵌套层级,因此 `@media` 块读起来就是一个块;页面按一行一个块级元素排版,行内内容留在它所属的那一行——`<p>Hello <b>world</b></p>` 仍是一行,因为拆开行内内容只是格式化源码,而不是格式化页面。注释保留在作者放置的位置,原本独占一行就仍然独占一行。属性空白的整理方式与压缩时相同。

因为两个方向对“哪些空白存在”使用同一套判断,它们可以组合:对美化后的文件再压缩,得到的字节与直接压缩原文件相同;美化两次与美化一次结果相同。这两条性质都经过逐项核对,而不是假设。不成对的文件——有 `{` 没有 `}`、字符串或注释没有闭合、`<pre>` 没有结束标签——会被拒绝,并给出行号、列号和问题位置下的插入符,而不是被处理到一半、看起来却像完成了。

示例

示例使用页面自带的默认输入,只改设置;下面的结果与你在页面上看到的完全一致。

示例样式表压缩之后

压缩后的 CSS

-39.7%

297 B 变为 179 B:删除了 1 条注释、33 段连续空白和 3 个块末分号,其余内容与原文一致。

输入: 297 B · 输出: 179 B · 节省: 118 B · 删除的注释: 1

`.card` 上方的注释消失,许可声明保留;`0px` 变成 `0`,两个颜色各去掉重复的两位,行末分号消失,而 `calc(100% - 40px)` 里的空格原封不动,因为那是算术。

保留横幅,删掉旁边的说明注释

粘贴 CSS 或 HTML: /*! (c) 2026 danfio - MIT */ /* spacing helper */ .card { margin: 0px auto; }

压缩后的 CSS

-38.0%

79 B 变为 49 B:删除了 1 条注释、6 段连续空白和 1 个块末分号,其余内容与原文一致。

输入: 79 B · 输出: 49 B · 节省: 30 B · 删除的注释: 1

`/*!` 注释是许可声明的惯例,所以默认保留;普通注释则不保留。关掉横幅开关,文件会直接从 `.card` 开始。

同一份样式表美化之后

处理方向: 美化 - 排版成便于人阅读的形式

美化后的 CSS

19 行

297 B 变为 297 B。没有删除任何内容——每条注释、每个值、每个选择器都还在,改变的只有排版。

输入: 297 B · 输出: 297 B · 保留的注释: 0 · 改动的值: 无

这个方向不删除任何内容:每条注释、每个值、每个选择器都在原来的位置,唯一的变化是排版。

决定两个不同选择器的那一个空格

粘贴 CSS 或 HTML: .card :hover { color : red; }

压缩后的 CSS

-25.8%

31 B 变为 23 B:删除了 0 条注释、6 段连续空白和 1 个块末分号,其余内容与原文一致。

输入: 31 B · 输出: 23 B · 节省: 8 B · 删除的注释: 0

冒号前的空格决定了是悬停卡片本身,还是悬停卡片内部的元素,因此保留;而声明中冒号两侧的空格没有语义,会被收紧。

一段脚本,被明确拒绝

粘贴 CSS 或 HTML: const add = (a, b) => a + b; console.log(add(1, 2));

这里不压缩 JavaScript

未实现

本页压缩 CSS 与 HTML,不会触碰 JavaScript;这是一个决定,而不是缺失的功能。

字符数: 52 · 行数: 2 · 字节数: 52 B

文本被自动识别为 JavaScript,随后以五条理由拒绝,说明扫描器为什么无法安全处理它。改为粘贴脚本所在的那个页面,`<script>` 就会逐字原样复制。

常见问题

我的样式表或页面会被上传到某个地方吗?

不会。文本由运行在浏览器标签页里的固定规则读取、重写和计数,页面背后没有接口,唯一的请求就是页面自身。正因如此,把客户样式表或私有页面粘贴到这里才是合理的。页面静态 HTML 中展示的示例结果是在生成页面时用示例样式表算出来的,所以关闭 JavaScript 的访客仍能看到一次真实的压缩,而不是一个空框。

为什么不压缩我的 JavaScript?

因为在那里靠猜会以难以察觉的方式弄坏代码。换行可能结束一条语句,删掉它就可能把两条语句粘成一条含义不同的语句。一个 `/` 是除法还是正则的开头,取决于它前面的词法单元。模板字面量里可以包含字符串、正则,甚至另一个模板。URL 字符串里的 `//` 不是注释,而位于行首的 `-->` 在严格模式和宽松模式下行为不同。这些都需要理解代码结构的读取方式,而不是简单的模式扫描,所以页面明确拒绝 JavaScript 并列出五条理由。请使用 esbuild、terser,或你的打包工具已经在跑的压缩器;另外注意,粘贴进来的 HTML 页面里的 `<script>` 会被逐字原样复制,不会被改动。

压缩会弄坏我的样式表吗?

这五项删除——注释、不参与渲染的空白、右花括号前的分号、作为长度的 `0px`、三组两位相同的六位颜色——在 CSS 规范里两种写法都可以互换,而且每一项都在发布前逐项核对过实际行为,而不是只在文档里描述。仍可能让你意外的是文件之外的上游环节:你的发布流程里某个环节把注释当作标记,例如 `/* purge */` 提示或 `@import` 顺序注释;或者有工具读取的是你的源码,而不是运行它的浏览器。如果你的流水线会用 grep 处理 CSS,请在最后一步再压缩。

它会合并规则、删除重复选择器或添加浏览器前缀吗?

不会,而且这些都不是疏忽。合并两条选择器相同的规则,或重排一条规则里的声明,会在涉及优先级和 `!important` 时改变哪条声明生效;删除一条“重复”声明,只有在你清楚每个浏览器如何处理它时才是安全的;添加前缀则需要知道你支持哪些浏览器,这是关于你的项目而不是关于这个文件的问题。这些都属于能看到整个项目的构建工具,本页会直接说明,而不是硬做。

为什么 `0px` 会变成 `0`,而 `0%`、`0deg`、`0s` 保持不动?

因为 `0px` 和 `0` 表示同一个长度,除此之外没有任何区别。百分比不是长度——`width: 0%` 要相对于包含块解析;角度和时间则完全是各自的类型:`hue-rotate(0)` 不是 `hue-rotate(0deg)`,它是无效的;`transition: 0` 也不是 `transition: 0s`。同样的理由也适用于 `calc()` 里的零:那里的数字是表达式的一部分,虽然 `calc(0 + 1px)` 合法,但保持算术原样,比逐种情况去证明更值得信任。

`!important`、`@media` 和 `@supports` 会怎样?

不会有任何变化。`!important` 是值里的一个词法单元,原样保留;at 规则保留自己的前导部分,只收紧媒体特性本来就允许两种写法的那处空格。前导部分里唯一绝不触碰的是冒号前的空格,因为选择器中这个空格区分伪类和后代选择器,而这一遍处理只能靠“是否位于声明块内部”来区分选择器与媒体特性,它用的也正是这项检查。

能不能反过来用,去读一个压缩过的文件?

可以,那就是美化方向。它不删除任何内容:样式表会变成一行一条声明并带缩进,注释留在原处;页面会变成一行一个块级元素,行内内容留在自己那一行。属性空白的整理方式相同。输出并不声称还原了原作者当年的排版——没有人能恢复别人选择过的换行——但它是一份人能读、diff 也能处理的文件。

资料来源与统计口径

  • 清理规则遵循浏览器解析 CSS 的语法:注释、多余空白、`}` 前的分号、`0px` 与重复的十六进制两位。
  • 会改变含义的空格都保留,例如 `calc(100% - 20px)` 与 `a :hover`;字符串与自定义属性原样不动。
  • 括号不成对会连同行号被拒绝;全部在浏览器里完成,不上传。

最近复核于 2026-09-17.

本页在您的浏览器本地运行,请保留原文件备份;不涉及财务建议。 阅读完整免责声明。