Style Directive
The style: directive binds a single CSS property to a value.
Change the value, the property updates.
Everything else about the element's look stays in CSS where it belongs.
Basic Usage
Write the property name after the colon, and the value in braces:
<script>
let textColor = 'royalblue'
let bgColor = '#4f46e5'
</script>
<div style:color={textColor}>Colored text</div>
<div class="badge" style:background-color={bgColor}>With background</div>
<style>
.badge {
color: white;
padding: 4px 8px;
border-radius: 4px;
}
</style>
Property names are kebab-case, exactly as in CSS
(background-color, font-size,
border-radius).
Set a property to null, undefined or
'' and JAF removes it from the element.
px. style:width={200} sets nothing, because the
browser rejects a length with no unit - write
style:width={200 + 'px'}. Unitless properties
(opacity, z-index, flex-grow) take a
plain number.
Values That Change
The value is any expression. When something it reads changes, the property is reset - no re-render, no string building:
<script>
let opacity = 100
let size = 140
</script>
<div class="controls">
<label>
Opacity <strong>{opacity}%</strong>
<input type="range" min="0" max="100" bind:value={opacity} />
</label>
<label>
Width <strong>{size}px</strong>
<input type="range" min="80" max="260" bind:value={size} />
</label>
</div>
<div class="swatch" style:opacity={opacity / 100} style:width={size + 'px'}>
drag a slider
</div>
<style>
.controls {
display: flex;
flex-wrap: wrap;
gap: 1.25rem;
margin-bottom: 1rem;
}
.controls label {
display: flex;
flex-direction: column;
gap: 0.35rem;
font-size: 0.8125rem;
}
.controls input[type="range"] {
width: 11rem;
height: 1.25rem;
accent-color: #6366f1;
appearance: auto;
-webkit-appearance: auto;
cursor: pointer;
}
.swatch {
padding: 1rem;
border-radius: 8px;
background: linear-gradient(135deg, #6366f1, #22d3ee);
color: white;
font-weight: 600;
text-align: center;
}
</style>
opacity takes the number straight; width gets
+ 'px'. That is the whole rule from the callout above, in one
example.
With Tailwind
Utility classes cover everything you know at author time. They cannot express
a number your app computes at runtime - so keep the classes for the look, and
use style: for the one value that moves:
<script>
let progress = 40
function step(amount) {
progress = Math.min(100, Math.max(0, progress + amount))
}
</script>
<div class="max-w-sm text-sm">
<div class="flex justify-between font-medium">
<span>Uploading</span>
<span>{progress}%</span>
</div>
<!-- Tailwind draws the track and the bar. style: supplies the one
value a utility class cannot know: today's number. -->
<div class="mt-2 h-3 overflow-hidden rounded-full bg-slate-200 dark:bg-slate-700">
<div class="h-full rounded-full bg-sky-500 transition-[width] duration-300" style:width={progress + '%'}></div>
</div>
<div class="mt-3 flex gap-2">
<button class="rounded-md border border-slate-300 px-3 py-1.5 dark:border-slate-600" onclick="step(-10)">
-10%
</button>
<button class="rounded-md bg-sky-500 px-3 py-1.5 text-white hover:bg-sky-600" onclick="step(10)">
+10%
</button>
</div>
</div>
The track, the colours, the rounding and even the transition are Tailwind
utilities. Only style:width is dynamic.
Reaching for w-[40%] here would not work: Tailwind generates
classes by scanning your source, so it can only produce widths you have
literally written down.
CSS Custom Properties
style: also sets custom properties, which is the tidiest way to
feed a value to CSS that already knows what to do with it - including child
elements:
<script>
let brandColor = '#3B82F6'
let spacing = 16
</script>
<div style:--primary={brandColor} style:--spacing={spacing + 'px'}>
<p style="color: var(--primary); margin: var(--spacing)">
Styled with CSS variables
</p>
</div>
Styled with CSS variables