7: CSS
Phlo uses a compact, semicolon-free CSS syntax inside <style> blocks.
You write rules with colons as separators:
selector: property: value- nested:
A: B: property: value→ output:A B { property: value; }
Rules:
- One line = one declaration. No semicolons in the source; the engine adds them.
- The colon
:separates chain levels as well as property from value. - A backslash
\in nestings "glues" the next selector part to the parent (glue). That next part can be a pseudo (:…), an attribute selector ([…]), and so on. @media (…)may appear inside a selector block; Phlo hoists it to the right place while preserving the current selector.
7.1: `<style>` block
<style>
html: height: 100dvh
body {
background: #947b6c
font-family: Sans-serif
p: line-height: 2em
}
</style>
Output (conceptually):
html { height: 100dvh; }
body { background: #947b6c; font-family: Sans-serif; }
body p { line-height: 2em; }
A <style> block can target one or more bundle namespaces with ns=: <style ns=docs> transpiles into www/docs.css, <style ns=app,docs> into both bundles, and a block without ns= into the defaultNS from data/app.json. See chapter 2 for the namespace model. The same attribute works on <script> blocks.
7.2: Chains & groups
Chain with colons; group with commas, and the full context is applied to each item.
<style>
body: h1, p: \:first-letter: color: green
</style>
- Context:
body - Targets:
h1andp(with the glued:first-letter) - The backslash before
:first-letterglues that part to the preceding selector within the chain.
Output:
body h1:first-letter,
body p:first-letter { color: green; }7.3: Media queries inside a selector
You may write @media (…) inside the selector block; Phlo moves it to the right place and keeps the selector context:
<style>
h1 {
color: white
@media (max-width: 768px): color: black
}
</style>
Output:
h1 { color: white; }
@media (max-width: 768px){
h1 { color: black; }
}7.4: Variables
Phlo supports CSS variables via $names.
You can define variables in :root, or at any other level, but :root is the usual place for global theming.
<style>
:root {
$background: #0d0d0d
$surface: #1a1a1a
$text: #ffffff
$accent: #ff4a00
}
body {
background: $background
color: $text
}
button {
background: $accent
color: $text
}
</style>
Output
:root {
--background: #0d0d0d;
--surface: #1a1a1a;
--text: #ffffff;
--accent: #ff4a00;
}
body {
background: var(--background);
color: var(--text);
}
button {
background: var(--accent);
color: var(--text);
}
👉 Phlo automatically converts $variables to --custom-properties and uses var(--...) where they are referenced.
You can reuse variables anywhere, including inside media queries and nested selectors.
7.5: Dynamic variables
Phlo's frontend engine includes the DOM/CSS.var library, which lets you read and update defined $variables in CSS directly from JavaScript, via the global app.var object.
Every $variable in your CSS automatically becomes available as app.var.<name>.
Example
<style>
:root {
$background: #0d0d0d
$text: #ffffff
}
</style>
<script>
app.var.background = '#000000'
const textColor = app.var.text
</script>
app.var.background = '#000000'→ live-updates the value of--backgroundin the DOM, without a rebuild or reload.const textColor = app.var.text→ reads back the current value.
👉 These updates work in real time in the browser and immediately affect all elements that use the variable.
You can use this for, among other things:
- Theme switches (dark/light mode)
- Dynamically adjusting accent colors based on user input
- Interactive UIs without toggling separate CSS classes
How it works
- The CSS engine converts
$backgroundto--background. - The frontend engine reads/writes it via
document.documentElement.style. app.varprovides a simple proxy object, so you can work with these as if they were plain JS properties.
7.6: Full example
Input
html: height: 100dvh
body {
background: #947b6c
font-family: Sans-serif
p: line-height: 2em
}
body: h1, p: \:first-letter: color: green
h1 {
color: white
@media (max-width: 768px): color: black
}
p {
color: navy
\:last-child: color: yellow
}
Output
body {
background: #947b6c;
font-family: Sans-serif;
}
body h1:first-letter,
body p:first-letter {
color: green;
}
body p {
line-height: 2em;
}
h1 {
color: white;
}
html {
height: 100dvh;
}
p {
color: navy;
}
p:last-child {
color: yellow;
}
@media (max-width: 768px){
h1 {
color: black;
}
}7.7: Best practices
- Use
$variablesfor colors, spacing, and fonts; this makes theming and dark/light modes easy. - Define global theme variables in
:root. - Use selector chains and grouping for compact, readable code.
- Put
@mediaright inside the block; Phlo hoists it to the right place. - Use
\in nestings to glue pseudos or attributes to the parent selector. - No semicolons in your code; Phlo produces correct CSS output.
7.8: Icon sprites
Point icons in data/app.json at one or more folders of PNG files and the build composes them into a single www/icons.png sprite plus the CSS to use them:
{
"icons": "%app/icons/",
"iconNS": "app"
}
Naming convention: save.png becomes class .icon.save; save.dark.png becomes the same class scoped to body.dark, so one icon name can have per-context variants (themes, states). Usage in a view:
<i.icon.save/>
The generated CSS lands in the iconNS bundle (default app) and view() preloads the sprite automatically.
Reference. The DOM resources are documented per node in the Manual, generated from the resource files so it never drifts from the code.
Last updated on 23 August 2026