CSS grammar (level 1)ο
Last Updated: 2026-05-30
Status: π’ APPROVATO β allineato al contratto v0.5.0 (renderer-side chain landed 2026-05-30).
Maintainer: subtask css_builder/ (closed 2026-05-12).
CSS level 1 grammar. Covers rules, selectors, custom properties,
and the @media / @supports / @import at-rules.
Purposeο
Produce CSS stylesheets programmatically. Pairs with CssRenderer
for serialization. Supports the round-trip via
CssBuilder.from_css(...) (parse CSS source into builder code).
Quick startο
from genro_builders.contrib.css import CssBuilderHandler
class Theme(CssBuilderHandler):
def main(self, root):
sheet = root.stylesheet()
sheet.rule(color="red", padding="10px")\
.selector(class_="card")\
._.selector(class_="panel")\
._.cssvar("primary", value="#3498db")
t = Theme(); t.create()
print(t.render())
Output:
.card, .panel {
color: red;
padding: 10px;
--primary: #3498db;
}
Elementsο
Element |
Type |
Sub-tags |
Notes |
|---|---|---|---|
|
container |
|
Top-level container. |
|
container |
|
A CSS rule. Property declarations go as kwargs. |
|
leaf |
β |
A selector clause ( |
|
container |
|
Explicit selector list (rarely needed; |
|
leaf |
β |
A CSS custom property declaration ( |
|
leaf |
β |
|
@media and @supports are passed as kwargs on rule, not as
separate elements: rule(media="(max-width: 600px)", ...).
Common patternsο
Property declarations as kwargsο
A rule accepts CSS properties as kwargs. Hyphenated CSS names use
underscore in Python:
sheet.rule(color="red", font_size="14px", text_align="center")
The renderer converts font_size β font-size on output.
Selectors as childrenο
Each selector is an explicit child element:
rule = sheet.rule(color="red")
rule.selector(class_="card")
rule.selector(class_="panel")
rule.selector(_id="main")
This produces a selector list: .card, .panel, #main { color: red; }.
The class_, id, attr kwargs map to CSS selector syntax (a
class, an id, an attribute selector); classes takes a list for a
multi-class compound. Pseudo-classes/elements attach directly inside
class_ (e.g. class_="card:hover"); anything the structured kwargs
canβt express goes through the opaque raw suffix.
Chaining with ._ο
sheet.rule(color="red")\
.selector(class_="card")\
._.selector(class_="panel")\
._.cssvar("primary", value="#3498db")
Round-trip from CSS sourceο
The builder exposes two classmethods for the reverse direction:
from genro_builders.contrib.css import CssBuilder
# Parse a CSS string into builder Python source:
python_code = CssBuilder.from_css(".card { color: red; }")
# Or parse a file:
python_code = CssBuilder.from_css_file("theme.css")
The output is a complete Python module containing a
CssBuilderHandler subclass whose main(self, root) rebuilds the
input.
Renderο
CSS rendering does not use the universal rendered_item walk
because CSS needs top-level composition (cssvar grouping,
@import ordering, nested @media/@supports blocks). The CSS
renderer overrides RendererBase.render(source, **opts) with a
top-level dispatch that calls into internal helpers
(_render_top_sequence, _render_top_node, β¦) and emits a
stylesheet; CssBuilderHandler.render drives this whole-stylesheet
walk instead of the generic render_children. The result is still
finalized through the standard finalize (a single method: it joins
the fragments and consumes the target).
The only mode is css. Output is multi-line, indented with two
spaces, one property per line. No minification mode yet.
Validation rulesο
selectorkwargs are validated eagerly with regex: invalid selector syntax raises atcreate()time.rulerejects unknown CSS shorthand kwargs only when explicitly spelled out by the schema; most kwargs pass through as declarations.importcssonly valid at the top of astylesheet.
Validation errors raise ValueError at create() time with a
clear pointer to the offending kwarg.
Worked examplesο
01_introduction/ under
../../src/genro_builders/contrib/css/examples/
is a three-view example covering rules, selector lists, custom
properties, and @media kwargs.
Known limitationsο
At-rules beyond
@media,@supports,@importare not supported in level 1:@keyframes,@font-face,@property,@layer,@scopewill be handled in a future subtask.No CSS nesting (the level-3 nested rules proposal). Each rule is a separate top-level child of the stylesheet.
No source maps.
No CSS minification mode.
Referencesο
Source:
src/genro_builders/contrib/css/.Renderer:
src/genro_builders/contrib/css/css_renderer.py.Schema:
src/genro_builders/contrib/css/css_elements.py.Reverse parser:
src/genro_builders/contrib/css/_reverse.py.Contract:
roadmap/architecture-contract.md.