Styling
This section explains some different ways to style HTML within Marko. From simple inline styles to powerful techniques like CSS Modules for organized and maintainable stylesheets.
Inline Styles
Marko enhances the HTML <style> tag to be processed and optimized by the bundler used in the project. A template may specify any number of <style> tags.
By default, all styles defined in the template are globally scoped. As such, many Marko projects use patterns like BEM to avoid name conflicts.
<style>
/* Bundled and loaded once */
.fancy {
color: green;
}
</style>
<div class="fancy">Hello!</div><style>
/* Bundled and loaded once */
.fancy {
color: green;
}
</style>
<div class="fancy">Hello!</div>style
--
/* Bundled and loaded once */
.fancy {
color: green;
}
--
div class="fancy" -- Hello!style
--
/* Bundled and loaded once */
.fancy {
color: green;
}
--
div class="fancy" -- Hello!The <style> may include a file extension to enable css preprocessors such as scss and less.
<style.scss>
$primary-color: green;
.fancy-scss {
color: $primary-color;
}
</style>
<div class="fancy-scss">Hello!</div>
<style.less>
@primary-color: blue;
.fancy-less {
color: @primary-color;
}
</style>
<div class="fancy-less">Hello!</div><style.scss>
$primary-color: green;
.fancy-scss {
color: $primary-color;
}
</style>
<div class="fancy-scss">Hello!</div>
<style.less>
@primary-color: blue;
.fancy-less {
color: @primary-color;
}
</style>
<div class="fancy-less">Hello!</div>style.scss
--
$primary-color: green;
.fancy-scss {
color: $primary-color;
}
--
div class="fancy-scss" -- Hello!
style.less
--
@primary-color: blue;
.fancy-less {
color: @primary-color;
}
--
div class="fancy-less" -- Hello!style.scss
--
$primary-color: green;
.fancy-scss {
color: $primary-color;
}
--
div class="fancy-scss" -- Hello!
style.less
--
@primary-color: blue;
.fancy-less {
color: @primary-color;
}
--
div class="fancy-less" -- Hello!Inline CSS Modules
If the <style> tag has a Tag Variable, it leverages CSS Modules to expose its classes as an object.
<style/styles>
.foo { border: 1px solid red }
.bar { background: green }
</style>
<div class=styles.foo/>
<div class=[styles.foo, styles.bar]/>
<div class={
[styles.bar]: true
}/>
<div class=styles.foo/><style/styles>
.foo { border: 1px solid red }
.bar { background: green }
</style>
<div class=styles.foo/>
<div class=[styles.foo, styles.bar]/>
<div class={ [styles.bar]: true }/>
<div.${styles.foo}/>style/styles
--
.foo { border: 1px solid red }
.bar { background: green }
--
div class=styles.foo
div class=[styles.foo, styles.bar]
div class={
[styles.bar]: true
}
div class=styles.foostyle/styles
--
.foo { border: 1px solid red }
.bar { background: green }
--
div class=styles.foo
div class=[styles.foo, styles.bar]
div class={ [styles.bar]: true }
div.${styles.foo}This approach allows for scoping of CSS class names without needing to follow naming conventions such as BEM.
You may still provide a custom file extension to enable to use of preprocessors.
<style.scss/styles>
$primary-color: green;
.fancy {
color: $primary-color;
}
</style>
<div class=styles.fancy>Hello</div><style.scss/styles>
$primary-color: green;
.fancy {
color: $primary-color;
}
</style>
<div class=styles.fancy>Hello</div>style.scss/styles
--
$primary-color: green;
.fancy {
color: $primary-color;
}
--
div class=styles.fancy -- Hellostyle.scss/styles
--
$primary-color: green;
.fancy {
color: $primary-color;
}
--
div class=styles.fancy -- HelloDynamic Values
A <style> tag may contain dynamic values, written as ${...} interpolations, keeping state-driven styling next to the rest of the stylesheet.
<let/hue=200>
<style>
.swatch {
/* Updates whenever `hue` changes */
background: hsl(${hue} 80% 50%);
border-radius: 4px;
}
</style>
<input type="range" min="0" max="360" value:parseFloat:=hue>
<div class="swatch"/><let/hue=200>
<style>
.swatch {
/* Updates whenever `hue` changes */
background: hsl(${hue} 80% 50%);
border-radius: 4px;
}
</style>
<input type="range" min="0" max="360" value:parseFloat:=hue>
<div class="swatch"/>let/hue=200
style
--
.swatch {
/* Updates whenever `hue` changes */
background: hsl(${hue} 80% 50%);
border-radius: 4px;
}
--
input type="range" min="0" max="360" value:parseFloat:=hue
div class="swatch"let/hue=200
style
--
.swatch {
/* Updates whenever `hue` changes */
background: hsl(${hue} 80% 50%);
border-radius: 4px;
}
--
input type="range" min="0" max="360" value:parseFloat:=hue
div class="swatch"The stylesheet is still extracted and bundled once. Each interpolation compiles to a CSS custom property whose value Marko keeps up to date as state changes.
Dynamic values are only supported where css expects a declaration value, and apply to elements rendered after the <style> tag. See the <style> reference for details.
Per-Item Values
Because dynamic values apply to the elements after each <style> tag, placing one inside a <for> gives every iteration its own values. Each pass renders its own <style> just before that item's markup, so the interpolations resolve per item.
<nav class="brands">
<for|brand| of=input.brands>
<style>
.brand {
color: ${brand.color};
}
</style>
<a class="brand" href=brand.url>${brand.name}</a>
</for>
</nav><nav class="brands">
<for|brand| of=input.brands>
<style>
.brand {
color: ${brand.color};
}
</style>
<a class="brand" href=brand.url>${brand.name}</a>
</for>
</nav>nav class="brands"
for|brand| of=input.brands
style
--
.brand {
color: ${brand.color};
}
${" "}
--
a class="brand" href=brand.url -- ${brand.name}nav class="brands"
for|brand| of=input.brands
style
--
.brand {
color: ${brand.color};
}
${" "}
--
a class="brand" href=brand.url -- ${brand.name}Each iteration renders a real <style> element among the items, so positional selectors like :nth-child count those elements and miss their targets. Use :nth-of-type, which counts only elements of the same tag and skips the interleaved <style> tags.
Auto-Discovered Styles
Styling files adjacent a custom tag are automatically discovered. These files are imported and processed the same as inline styles.
.fancy {
color: green;
}<div class="fancy">Hello!</div>div class="fancy" -- Hello!When a template is becoming too large it can be helpful to pull its styling into an associated style file such as this.
Imported Styles
Styles may also be imported.
.fancy {
color: red;
}import "./fancy.css";
<div class="fancy">Hello!</div>import "./fancy.css";
<div class="fancy">Hello!</div>import "./fancy.css";
div class="fancy" -- Hello!import "./fancy.css";
div class="fancy" -- Hello!Although generally inline or auto-discovered styles are preferred, importing styles can be helpful when sharing across templates.
Imported CSS Modules
CSS Module files may also be imported.
.fancy {
color: red;
}import * as styles from "./something.module.css";
<div class=styles.fancy/>import * as styles from "./something.module.css";
div class=styles.fancySince most bundlers are configured by default to support css modules for *.module.css files, this should work out of the box. If it is not supported by a bundler there is almost certainly a plugin.
Contributors
Helpful? You can thank these awesome people! You can also edit this doc if you see any issues or want to improve it.