1"""
2Minify CSS to the smallest output that still parses to the same cascade.
3
4The other CSS minifiers on PyPI shrink stylesheets either by stripping whitespace (``rcssmin``) or by rewriting values
5(``csscompressor``, ``cssmin``), but each of them rewrites the *internal* whitespace of a custom-property value, which
6CSS Variables 1 §3 says to preserve; ``cssmin`` and ``css-html-js-minify`` also collapse whitespace inside strings.
7turbohtml applies every value-rewriting optimization that is provably value-safe -- colors collapse to their shortest
8form, numbers and units drop what is redundant, constant ``calc()`` folds, shorthands and adjacent rules merge -- while
9keeping custom-property values and string contents byte-exact, so it is the smallest output that round-trips.
10
11The whole tokenizer, grammar, and value engine run in C (``turbohtml._html._minify_css``), working directly on the
12input's UTF-8 bytes. Every transform is value-safe at any baseline; the baseline only bounds how new the output
13*syntax* may be, so the result always parses to the same cascade as the input.
14
15Importing this module also registers :class:`CSSMinify` with the core, so the HTML serializer's
16``Minify(minify_css=...)`` hook reaches it with a module-state pointer load. Registering from here, which imports only
17:mod:`turbohtml._html`, keeps the reference graph small enough to collect at interpreter shutdown, the way
18:mod:`turbohtml._internal._jsminify` registers its own config type.
19"""
20
21from __future__ import annotations
22
23from dataclasses import dataclass
24
25from turbohtml._html import _minify_css, _minify_css_inline, _register_css_minify
26
27__all__ = [
28 "CSSMinify",
29 "minify_css",
30 "minify_css_inline",
31]
32
33
34@dataclass(frozen=True, slots=True)
35class CSSMinify:
36 """
37 Options for :func:`minify_css` and :func:`minify_css_inline`.
38
39 ``baseline`` is the `Baseline <https://web.dev/baseline>`__ year the output may target: the minifier applies a
40 transform whose output syntax reached Baseline in year ``Y`` only when ``baseline >= Y``. ``None`` (the default)
41 emits only long-interoperable syntax; ``2021`` additionally merges the shorthands that reached Baseline that year
42 (``inset``, the flex ``gap``, the two-value ``overflow``). Every year is value-safe -- the year bounds only how new
43 the output syntax may be, never the cascade it parses to.
44 """
45
46 baseline: int | None = None
47
48
49# Hand the type to the serializer so Minify(minify_css=...) reaches it with a module-state pointer
50# load (speed over a per-call import), not a global; mirrors the other type registers.
51_register_css_minify(CSSMinify)
52
53
54def minify_css(css: str, options: CSSMinify | None = None) -> str:
55 """
56 Minify a full CSS stylesheet.
57
58 :param css: the stylesheet source (rules, at-rules, comments).
59 :param options: the minification options; defaults to :class:`CSSMinify` (the most compatible output).
60 :returns: the minified stylesheet.
61 """
62 return _minify_css(css, (options or CSSMinify()).baseline or 0)
63
64
65def minify_css_inline(css: str, options: CSSMinify | None = None) -> str:
66 """
67 Minify an inline declaration list, the value of an HTML ``style`` attribute.
68
69 Use this rather than :func:`minify_css` when the source is bare declarations (``color:red; margin:0``) with no
70 surrounding selector or braces.
71
72 :param css: the declaration-list source.
73 :param options: the minification options; defaults to :class:`CSSMinify` (the most compatible output).
74 :returns: the minified declaration list.
75 """
76 return _minify_css_inline(css, (options or CSSMinify()).baseline or 0)