Skip to main content
Svelte allows you to pass CSS custom properties (CSS variables) to components, enabling dynamic theming and flexible component styling.

Passing CSS Custom Properties

You can pass both static and dynamic CSS custom properties to components:
<Slider
	bind:value
	min={0}
	max={100}
	--track-color="black"
	--thumb-color="rgb({r} {g} {b})"
/>

How It Works

The Svelte compiler transforms CSS custom properties into wrapper elements. The above code essentially desugars to:
<svelte-css-wrapper style="display: contents; --track-color: black; --thumb-color: rgb({r} {g} {b})">
	<Slider
		bind:value
		min={0}
		max={100}
	/>
</svelte-css-wrapper>
The display: contents ensures the wrapper doesn’t affect layout. However, it will affect CSS selectors that use the > combinator to target an element directly inside the component’s container.

SVG Elements

For SVG elements, Svelte uses a <g> element instead:
<g style="--track-color: black; --thumb-color: rgb({r} {g} {b})">
	<Slider
		bind:value
		min={0}
		max={100}
	/>
</g>

Using CSS Variables in Components

Inside the component, read these custom properties using var(...) with optional fallback values:
<!--- file: Slider.svelte --->
<style>
	.track {
		background: var(--track-color, #aaa);
	}

	.thumb {
		background: var(--thumb-color, blue);
	}
</style>

<div class="track">
	<div class="thumb"></div>
</div>
The fallback values (#aaa and blue) are used when the custom properties aren’t provided.

Inheriting from Parent Elements

You don’t have to specify values directly on the component. As long as custom properties are defined on a parent element, the component can use them:
<!--- file: App.svelte --->
<div style="--track-color: purple;">
	<Slider bind:value min={0} max={100} />
</div>

Global CSS Variables

It’s common to define custom properties on the :root element in a global stylesheet so they apply to your entire application:
/* global.css */
:root {
	--primary-color: #4CAF50;
	--secondary-color: #2196F3;
	--text-color: #333;
	--background-color: #fff;
}
Components can then consume these variables:
<style>
	.button {
		background: var(--primary-color);
		color: var(--background-color);
	}
</style>

Practical Examples

Themeable Card Component

<!--- file: Card.svelte --->
<style>
	.card {
		background: var(--card-bg, white);
		border: 1px solid var(--card-border, #ddd);
		padding: var(--card-padding, 1rem);
		border-radius: var(--card-radius, 4px);
	}

	.card-title {
		color: var(--card-title-color, black);
		font-size: var(--card-title-size, 1.5rem);
	}
</style>

<div class="card">
	<h2 class="card-title">
		<slot name="title" />
	</h2>
	<slot />
</div>
Usage:
<Card 
	--card-bg="#f5f5f5"
	--card-border="#ccc"
	--card-title-color="#333"
>
	<span slot="title">Custom Card</span>
	This card is themed with CSS variables.
</Card>

Dark Mode Toggle

<!--- file: App.svelte --->
<script>
	let darkMode = $state(false);
</script>

<div 
	class="app"
	style:--bg-color={darkMode ? '#1a1a1a' : '#ffffff'}
	style:--text-color={darkMode ? '#ffffff' : '#000000'}
>
	<button onclick={() => darkMode = !darkMode}>
		Toggle {darkMode ? 'Light' : 'Dark'} Mode
	</button>
	<Card>
		<span slot="title">Themed Content</span>
		This content adapts to the theme.
	</Card>
</div>

<style>
	.app {
		background: var(--bg-color);
		color: var(--text-color);
		min-height: 100vh;
		padding: 2rem;
	}
</style>

Dynamic Component Colors

<!--- file: ColorfulList.svelte --->
<script>
	let items = [
		{ name: 'Red Item', color: '#ff0000' },
		{ name: 'Green Item', color: '#00ff00' },
		{ name: 'Blue Item', color: '#0000ff' }
	];
</script>

{#each items as item}
	<ListItem --item-color={item.color}>
		{item.name}
	</ListItem>
{/each}
<!--- file: ListItem.svelte --->
<style>
	.item {
		background: var(--item-color, gray);
		color: white;
		padding: 1rem;
		margin: 0.5rem 0;
		border-radius: 4px;
	}
</style>

<div class="item">
	<slot />
</div>
CSS custom properties are a powerful way to create themeable, reusable components. They cascade through the component tree and can be overridden at any level.