Documentation menu
Styling

.szs — CSS with logic

serez-ui styles its GUI with .szs: a small CSS dialect with the same selectors and properties you already know, but where conditions are reactive — re-evaluated every frame against your component state and the live window size. It is parsed in pure Serez-Code (no CSS engine), so it stays tiny and predictable.

Separation of concerns: .szs owns the design (everything visual — color, spacing, alignment, sizing, visibility), while your .szx (JSX) owns the logic and structure — like CSS vs HTML/JS. .szs is a serez-ui feature (parsed by parseCss), not a core built-in; it styles the GUI renderer, and the editor extension highlights .szs files.

Attach a stylesheet

Load the sheet from a file and hand it to your Window before runGui. Stylesheets live in .szs files (not inline strings): Serez-Code interpolates {} inside string literals, so reading from a file keeps the CSS braces literal. Reading a file needs the File permission.

app.useStylesheet(parseCss(File.read("app.szs")))   // before runGui
app.runGui("App", 640, 480)

Selectors & properties

A rule is selector { property: value; }. The selector is a tag name (body, h1, Button, Row, …) or * (every tag). When several rules set the same property, the last matching one wins.

PropertyApplies toValuesEffect
background-colorbody, Button, Input, Select, Checkbox, Dropdown, Textareahex / namefill color
colortext tags + controlshex / nametext color
directionRowcolumnlay a Row out vertically
font-scaleh1 / h2 / h3 / p / span / liintegertext-size multiplier
white-spaceh1 / h2 / h3 / p / span / linowrapkeep on one line (no wrap; may clip)
text-alignh1 / h2 / h3 / p / span / li / Labelleft / center / righthorizontal text alignment
paddingcontainers (div, Col, section, form, ul)integer pxinner spacing
margin-bottomany taginteger pxextra space below the element
gapRowinteger pxspace between Row children
displayany tagnonehide the element (reactive)
font-familytext tags + controls (body = app default)family name / :font aliasproportional text in that font
border-radiusButton, Input, Textarea, Select, Dropdown, ProgressBarinteger pxantialiased rounded corners

Colors are hex (#rgb or #rrggbb) or a basic name (white black red green blue yellow gray). Sizes are plain integers (pixels) — no px unit.

/* app.szs */
body   { background-color: #0f172a; }
h1     { color: #ffd166; font-scale: 3; }
p      { color: #94a3b8; }
Button { background-color: #2563eb; color: #ffffff; }
Not full CSS. The renderer reads the properties above today. The parser itself accepts any property: value, so unknown ones are simply ignored — more get wired to the renderer as serez-ui grows (see Looking ahead).

Fonts — :font and font-family

Declare font files in a :font block (the renderer loads them lazily) and pick families per tag with font-family. System-installed fonts work by name without any block; a downloaded .ttf (e.g. a Google Font) goes through :font. Set it on body and the whole app inherits it.

:font {
    Titular: fonts/Roboto.ttf;        /* alias: path to a .ttf/.otf */
}

body { font-family: "Segoe UI"; }     /* app-wide default (system font) */
h1   { font-family: "Titular"; }      /* the :font alias */

Custom families render proportionally (real glyph advances, antialiased) and text wrapping measures with the actual font. Input / Textarea content stays monospace so caret positioning holds.

Reactive conditions — the logic

Any selector can carry one condition in parentheses, re-checked every frame: selector (var OP literal) { … }. Operators: == != < > <= >=. If the variable is not available, the rule simply does not apply.

body (count == 0) { background-color: #0f172a; }
body (count != 0) { background-color: #14532d; }

The variables come from two places: your component state (via styleVars()) and the built-in width / height (the live window size, in px).

Exposing state — styleVars()

Override styleVars() to publish the state your conditions read. (An optional :import { … } block at the top of the sheet documents which variables you use, but the values are taken from styleVars() / width / height directly.)

public any styleVars() { return [["count", this.count]] }

Responsive — media queries

width and height (px) are always available — no styleVars() needed — so .szs doubles as a responsive system, and it reflows live as the window resizes:

body (width < 600)  { background-color: #1e1b4b; }   /* phone-ish */
body (width >= 600) { background-color: #0f172a; }

Row (width < 600)   { direction: column; }            /* stack the row when narrow */

h1  (width < 600)   { font-scale: 2; }
h1  (width >= 960)  { font-scale: 4; }

Pair this with code-level breakpoints (app.breakpoint()) and automatic Row wrapping — see the serez-ui responsive section.

A complete example

/* counter.szs */
body (count == 0) { background-color: #0f172a; }
body (count != 0) { background-color: #14532d; }

h1 (width < 600)  { font-scale: 2; }
h1 (width >= 600) { font-scale: 3; }
h1 { color: #ffd166; }

Button { background-color: #2563eb; color: #ffffff; }
// counter.sz
class Counter:Window {
    public Counter() { super(); this.count = 0 }

    public any render() {
        return h("div", [], [
            h("h1", [], ["Count: " + this.count]),
            h("Button", [["onClick", () => { this.count = this.count + 1 }]], ["+1"])
        ])
    }

    // expose state to the .szs conditions
    public any styleVars() { return [["count", this.count]] }
}

let app = new Counter()
app.useStylesheet(parseCss(File.read("counter.szs")))
app.runGui("Counter", 520, 360)

Notes & limits

  • One condition per selector — no and/or; write several rules instead.
  • Properties are limited to the table above; other declarations parse but are ignored.
  • Last matching rule wins for a given property.
  • Read the sheet from a .szs file, not an inline string.
  • Colors: hex (#rgb / #rrggbb) or a basic name.

Looking ahead

.szs is intentionally small but built to grow: the parser already accepts arbitrary property: value declarations, so new properties (spacing, borders, alignment) can be wired to the renderer without a new syntax, and conditions/selectors can gain power over time. The surface documented above is what is wired up today — this page tracks it as it expands.

Next: the serez-ui reference (all components + responsive) and the Build a UI guide.