Tables of Contents
- Case 1: Dyanmic CSS Class Binding to List Item
- Case 2: Dyanmic CSS Syyle Bindning to High Priority Checkbox
- Resources
- Github Repositories
- Github Pages
On day 9, I will show how Vue 3, SvelteKit, and Angular perform dynamic CSS Class and Style binding. There is an example of CSS Class binding and Style binding. When an item is purchased, strikeout class is applied. When an item is considered high priority, priority class is used. When the high priority checkbox is checked in the Add Item Form, the text is bolded by the font-weight style.
Case 1: Dynamic CSS Class Binding to List Item
- Vue 3 application
<script setup lang="ts">
import { Icon } from '@iconify/vue'
import { ref, computed } from 'vue'
type Item = { id: number; label: string; highPriority: boolean; purchased: boolean }
const items = ref<Item[]>([])
const togglePurchase = (item: Item) => {
item.purchased = !item.purchased
}
</script>
<template>
<ul>
<div class="list-item" v-for="item in items" :key="item.id">
<li
:class="[{ priority: item.highPriority }, { strikeout: item.purchased }]"
@click="togglePurchase(item)"
>
{{ item.id }} - {{ item.label }}
</li>
</div>
</ul>
</template>
In Vue 3, an element's CSS class can be bound to an array or Object. In this example, I passed an array to the CSS class of the <li> elements.
{ strikeout: item.purchased }
- when the purchased
property of an item is true, the strikeout
class is enabled. Otherwise, the class is disabled. When clicking the <li> element, the click event occurred, and the togglePurchase
function toggled the property. As a result, the strikeout
class was programmatically applied.
{ priority: item.highPriority }
- similarly, the priority
class is applied to the <li> element when the highPriority
property of an item is true. When users check the High Priority checkbox and save the item, the property is true, and the text is displayed in a different text color.
If we want to pass Object to the class, the solution is :class="{ priority: item.highPriority, strikeout: item.purchased }"
- SvelteKit application
<script lang="ts">
import Icon from '@iconify/svelte';
type Item = { id: number; label: string; purchased: boolean; higherPriority: boolean };
let items = $state([] as Item[]);
function togglePurchased(item: Item) {
item.purchased = !item.purchased;
newItem = '';
newItemHigherPriority = false;
}
</script>
<ul>
{#each items as item (item.id)}
<div class="list-item">
<li class={[item.purchased && 'strikeout', item.higherPriority && 'priority']}>{item.id} - {item.label}</li>
<button class="btn" onclick={() => togglePurchased(item)} aria-label="purchase an item">
<Icon icon={!item.purchased ? "ic:baseline-check" : "ic:baseline-close" } />
</button>
</div>
{/each}
</ul>
The Svelte compiler issues a warning that <li> should not be clickable. Therefore, I add a button to invoke the togglePurchase
function to toggle the purchased
property.
Like Vue 3, an element’s CSS class can be bound to an array or Object. In this example, I passed an array to the CSS class of the <li> elements.
{ item.purchased && 'strikeout' }
- when the purchased
property of an item is true, the strikeout
class is enabled. Otherwise, the class is disabled.
{ item.highPriority && 'priority' }
- similarly, the priority class is applied to the <li> element when the highPriority
property of an item is true. When users check the High Priority checkbox and save the item, the property is true, and the text is displayed in a different text color.
If we want to pass Object to the class, the solution is :class="{{ priority: item.highPriority, strikeout: item.purchased }}"
- Angular 19 application
import { ChangeDetectionStrategy, Component, computed, signal } from '@angular/core';
type Item = { id: number; label: string; purchased: boolean; highPriority: boolean };
@Component({
selector: 'app-shopping-cart',
imports: [],
template: `
<ul>
@for (item of items(); track item.id) {
@let itemClasses =
{
priority: item.highPriority,
strikeout: item.purchased,
};
<div class="list-item">
<li [class]="itemClasses" (click)="togglePurchase(item)">
{{ item.id }} - {{ item.label }}
</li>
</div>
}
</ul>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ShoppingCartComponent {
items = signal<Item[]>([]);
newItem = signal('');
newItemHighPriority = signal(false);
togglePurchase(item: Item) {
this.items.update((items) =>
return items.map((element) => (element.id === item.id ? { ...element, purchased: !element.purchased } : element));
});
this.newItem.set('');
this.newItemHighPriority.set(false);
}
}
Angular introduced the @let
syntax, which can create a temporary variable in the HTML template.
@let itemClasses = {
priority: item.highPriority,
strikeout: item.purchased,
};
itemClasses
is { priority }
when item.highPriority
is true. itemClasses
is { strikeout }
when item.purchased
is true. Multiple classes can be merged into an Object. When both properties are true, itemClasses
is { priority, strikeout }
.
Similar to attribute binding, box syntax is used for class binding. [class]="itemClasses"
passes the itemClasses
temporary variable to the CSS class of the <li> element.
Case 2: Dynamic Style Binding to High Priority Checkbox
- Vue 3 application
When the newItemHighPriority
ref is true, the font-weight style
of the High Priority checkbox is set to bold
. Otherwise, it is set to normal
.
{ font-weight: newItemHighPriority ? 'bold': 'normal' }
is passed to style attribute to apply the inline style.
<script setup lang="ts">
import { Icon } from '@iconify/vue'
import { ref, computed } from 'vue'
const newItemHighPriority = ref(false)
const saveItem = () => { … same logic as before … }
</script>
<template>
<form v-if="isEditing" @submit.prevent="saveItem">
<label>
<input type="checkbox" v-model="newItemHighPriority" />
<span :style="{ 'font-weight': newItemHighPriority ? 'bold' : 'normal' }">
High Priority</span
>
</label>
</form>
</template>
- SvelteKit application
When the newItemHighPriority
rune is true, the font-weight
style of the High Priority checkbox is set to bold
. Otherwise, it is set to normal
.
style:font-weight={ newItemHighPriority ? 'bold': 'normal' }
passes the CSS value to the font-weight
inline style.
<script lang="ts">
import Icon from '@iconify/svelte';
let newItemHigherPriority = $state(false);
async function handleSubmit(event: SubmitEvent) {
... same logic as before ...
}
</script>
<form method="POST" onsubmit={handleSubmit}>
<label>
<input id="higherPriority" name="higherPriority" type="checkbox" bind:checked={newItemHigherPriority}
/>
<span style:font-weight={newItemHigherPriority ? 'bold' : 'normal'}> Higher Priority</span>
</label>
</form>
- Angular 19 application
When the newItemHighPriority
signal is true, the font-weight
style of the High Priority checkbox is set to bold
. Otherwise, it is set to normal
.
Both style and class use the box syntax for binding. [style.fontWeight]="newItemHighPriority ? 'bold': 'normal'"
. The ternary expression returns bold
when true and normal
otherwise.
The CSS style uses the camel case in Angular. For example, "font-weight" replaces the dash character with a blank, and "weight" is capitalized. Therefore, font-weight
is converted to fontWeight
.
The style also accepts Object. The equivalent is [style]={ fontWeight: newItemHighPriority: 'bold' : 'normal' }
.
import { ChangeDetectionStrategy, Component, computed, signal } from '@angular/core';
import { FormsModule } from '@angular/forms';
@Component({
selector: 'app-shopping-cart',
imports: [FormsModule],
template: `
<form (ngSubmit)="saveItem()">
<label>
<input type="checkbox" [(ngModel)]="newItemHighPriority" name="newItemHighPriority" />
<span [style.fontWeight]="newItemHighPriority() ? 'bold' : 'normal'"> High Priority</span>
</label>
</form>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ShoppingCartComponent {
newItemHighPriority = signal(false);
saveItem() { … same logic as before … }
}
Resources
- Vue 3 Class and Style Binding: https://vuejs.org/guide/essentials/class-and-style.html#binding-inline-styles
- Svelte Class and Style Binding:
- Angular Class and Style Binding: https://angular.dev/guide/templates/binding#css-class-and-style-property-bindings
Github Repositories
- https://github.com/railsstudent/fundamental-vue3
- https://github.com/railsstudent/fundamental-angular
- https://github.com/railsstudent/fundamental-svelte
Github Pages
- https://railsstudent.github.io/fundamental-vue3/
- https://railsstudent.github.io/fundamental-angular
- https://railsstudent.github.io/fundamental-svelte
We have successfully updated the shopping cart component to pass values to CSS classes or inline styles programmatically.
`
Top comments (3)
nice seeing all three side by side like this tbh - i remember wrestling with class binding the first time, so it's always a relief when frameworks make it simple. you ever find one method just feels smoother to maintain long-term, or is it kinda all personal preference?
Super clear side by side breakdown, really makes it easy to pick up the subtle differences! Have you thought about including React or SolidJS in this comparison too?
I heard SolidJS is a "Better React," so I am open to learning SolidJS.
My immediate goal is to get better at Vue 3 for work and Svelte where I heard is very performant.