Styling
Datastar provides attributes for controlling CSS classes and inline styles dynamically based on signal values. These attributes let you create responsive interfaces that change appearance as your application state changes, without writing JavaScript event handlers or DOM manipulation code.
Conditional Classes
data-class:* (Datastar)
The data-class:* attribute adds or removes a CSS class based on whether an expression evaluates to true. Replace * with the class name you want to control:
<div data-signals="{isActive: true}">
<button data-class:active="$isActive">
Click Me
</button>
</div>When isActive is true, the button has the active class. When isActive becomes false, the class is removed automatically.
Multiple Classes
You can control multiple classes independently:
<div data-signals="{
isDisabled: true,
hasError: false
}">
<button
data-class="{
'cursor-not-allowed': $isDisabled,
'text-red-500': $hasError
}"
class="px-4 py-2 rounded">
Submit
</button>
</div>Expressions
The condition can be any JavaScript expression:
<div data-signals="{count: 12}">
<div
data-class="{'bg-yellow-500 text-white': $count > 10}">
Count: <span data-text="$count"></span>
</div>
</div>The bg-yellow-500, text-white classes are added when count exceeds 10.
Multiple Class Names
You can add or remove multiple classes together by separating them with spaces:
<div data-signals="{isPremium: true}">
<div
data-class="{'bg-amber-600 text-white font-bold': $isPremium}"
>
Premium Content
</div>
</div>When isPremium is true, all three classes (bg-amber-600, text-white, font-bold) are added. When false, all are removed.
Dynamic Inline Styles
data-style:* (Datastar)
The data-style:* attribute sets inline CSS properties dynamically. Replace * with the CSS property name:
<div data-signals="{progress: 75}">
<div
data-style:width="$progress + '%'"
class="bg-blue-500 h-4 rounded">
</div>
</div>As progress changes, the width style updates immediately. Use standard CSS property names in kebab-case.
Multiple Style Properties
Control different properties independently:
<div data-signals="{
boxColor: 'blue',
boxSize: 100,
boxOpacity: 0.8
}">
<div
data-style:background-color="$boxColor"
data-style:width="$boxSize + 'px'"
data-style:height="$boxSize + 'px'"
data-style:opacity="$boxOpacity">
</div>
</div>Conditional Styles
Use ternary operators to conditionally set styles:
<div data-signals="{theme: 'dark'}">
<div data-style:color="$theme === 'dark' ? 'white' : 'black'"
data-style:background-color="$theme === 'dark' ? '#1a1a1a' : '#ffffff'">
Content adapts to theme
</div>
</div>Common Patterns
Active Navigation Links
<div data-signals="{currentPage: 'home'}">
<nav>
<a href="/home"
data-class:active="$currentPage === 'home'"
data-on:click="$currentPage = 'home'">
Home
</a>
<a href="/about"
data-class:active="$currentPage === 'about'"
data-on:click="$currentPage = 'about'">
About
</a>
<a href="/contact"
data-class:active="$currentPage === 'contact'"
data-on:click="$currentPage = 'contact'">
Contact
</a>
</nav>
</div>
<style>
.active {
font-weight: bold;
color: blue;
border-bottom: 2px solid blue;
}
</style>Form Validation States
<div data-signals="{
email: '',
errors: {}
}">
<input
type="email"
data-bind="email"
data-class:border-red-500="$errors.email"
data-class:border-gray-300="!$errors.email"
class="border-2 rounded px-3 py-2" />
<div data-error="email" class="text-red-500 text-sm"></div>
</div>The input border turns red when there's an error, gray otherwise.
Loading States
<div data-signals="{isLoading: false}">
<button
data-class:opacity-50="$isLoading"
data-class:cursor-not-allowed="$isLoading"
data-attr:disabled="$isLoading"
data-on:click="$isLoading = true">
<span data-show="!$isLoading">Submit</span>
<span data-show="$isLoading">Processing...</span>
</button>
</div>Theme Switching
<div data-signals="{theme: 'dark'}">
<div
data-class="{
'bg-white text-black': $theme === 'light',
'bg-gray-900 text-white': $theme === 'dark'
}" class="min-h-screen p-8">
<button
data-on:click="$theme = ($theme === 'light' ? 'dark' : 'light')"
class="px-4 py-2 rounded cursor-pointer">
Toggle Theme
</button>
<div>Current: <span data-text="$theme"></span></div>
<div class="mt-8">
<h1 class="text-3xl font-bold">Themed Content</h1>
<p class="mt-4">This content adapts to the current theme.</p>
</div>
</div>
</div>Status Indicators
<div data-signals="{status: 'pending'}">
<span
class="px-3 py-1 rounded-full text-sm font-medium transition-all duration-300"
data-class="{
'bg-yellow-100 text-yellow-800': $status === 'pending',
'bg-green-100 text-green-800': $status === 'completed',
'bg-red-100 text-red-800': $status === 'failed'
}">
<span data-text="$status"></span>
</span>
</div>Hover Effects with State
<div data-signals="{isHovered: false}">
<div
data-on:mouseenter="$isHovered = true"
data-on:mouseleave="$isHovered = false"
data-class:shadow-lg="$isHovered"
data-class:scale-105="$isHovered"
class="p-6 border rounded transition-all duration-300">
Hover over me
</div>
</div>Working with Tailwind CSS
Datastar's styling attributes work seamlessly with Tailwind CSS:
<div data-signals="{priority: 'high'}">
<div
class="text-black px-4 py-2 rounded"
data-class="{
'bg-red-500': $priority === 'low',
'bg-yellow-500': $priority === 'medium',
'bg-green-500': $priority === 'high'
}">
Priority: <span data-text="$priority"></span>
</div>
</div>Dark Mode
Combine with Tailwind's dark mode classes:
<div data-signals="{darkMode: false}">
<div data-class:dark="$darkMode">
<div class="bg-white dark:bg-gray-800 text-black dark:text-white p-8">
This content respects dark mode
</div>
</div>
</div>Learn More
- Display & Binding - Bind data to elements
- Events - Handle user interactions that trigger style changes
- Computed & Effects - Calculate styles based on multiple signals
- Datastar Documentation - Complete reference for styling attributes

