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> compiles 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.