首页 / Svelte 5 入门教程 / 自定义属性与嵌套样式

Svelte 5 入门教程

自定义属性与嵌套样式

本教程共 50 篇 · 第 28 篇 · 更新于 2026-08-05 · 约 6 分钟阅读

SvelteSvelte 5CSS变量自定义属性嵌套样式

28. 自定义属性与嵌套样式

本节目标:学会通过 CSS 自定义属性(CSS 变量)向组件传递样式参数,理解嵌套 <style> 元素的行为,掌握组件主题定制的技巧。

CSS 自定义属性透传

Svelte 有个很酷的功能:你可以直接在组件上写 CSS 自定义属性(CSS 变量),Svelte 会帮你透传进去。

打个比方,自定义属性就像你给组件发了一张”装修清单”——告诉它墙壁刷什么颜色、地板用什么材质,组件照着清单来装修。

<!-- App.svelte -->
<script>
	import Slider from './Slider.svelte';

	let value = $state(50);
	let r = $state(0);
	let g = $state(0);
	let b = $state(0);
</script>

<!-- 直接在组件上写 CSS 变量 -->
<Slider
	bind:value
	min={0}
	max={100}
	--track-color="black"
	--thumb-color="rgb({r} {g} {b})"
/>
<!-- Slider.svelte -->
<style>
	.track {
		background: var(--track-color, #aaa); /* 第二个参数是兜底值 */
	}

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

透传原理

Svelte 在编译时会自动给组件包一层容器,把自定义属性作为 style 写在容器上。

编译前:

<Slider --track-color="black" --thumb-color="red" />

编译后(简化版):

<svelte-css-wrapper style="display: contents; --track-color: black; --thumb-color: red;">
	<Slider />
</svelte-css-wrapper>

display: contents 让包装元素不占据布局空间,所以不会影响你的页面结构。

Note

对于 SVG 元素,Svelte 用 <g> 标签代替 <svelte-css-wrapper> 作为包装元素,因为 SVG 里有意义的标签有限。

在组件内读取自定义属性

组件内部用 var(--属性名, 兜底值) 来读取父组件传来的 CSS 变量。兜底值在变量未定义时使用。

<!-- Button.svelte -->
<script>
	let { children } = $props();
</script>

<button class="btn">
	{@render children?.()}
</button>

<style>
	.btn {
		/* 读取外部传入的颜色,没传就用默认值 */
		background: var(--btn-bg, #6c5ce7);
		color: var(--btn-text, white);
		padding: var(--btn-padding, 8px 16px);
		border-radius: var(--btn-radius, 4px);
		border: none;
		cursor: pointer;
	}
</style>

使用时可以直接传变量定制样式:

<Button --btn-bg="#e17055" --btn-radius="20px">
	橙色圆角按钮
</Button>

<Button --btn-bg="#00b894" --btn-padding="12px 24px">
	绿色大按钮
</Button>

CSS 变量的继承性

CSS 自定义属性遵循 CSS 继承规则。你不必非得在组件标签上写——只要在任意祖先元素上定义了,组件就能读到。

<style>
	:global(:root) {
		/* 全局定义,所有组件都能用 */
		--primary-color: #6c5ce7;
		--text-color: #2d3436;
	}
</style>

<div style="--card-bg: #f5f5f5;">
	<!-- Card 可以读到 --card-bg 和 --primary-color -->
	<Card />
</div>
Tip

常见的做法是在全局样式表的 :root 上定义一组主题变量,所有组件共享。需要局部定制时再在组件标签上覆盖。

动态 CSS 变量

CSS 变量的值可以包含 JavaScript 表达式,实现动态样式。

<script>
	import Progress from './Progress.svelte';

	let progress = $state(45);
</script>

<!-- 变量值随 progress 实时变化 -->
<Progress
	--progress={progress + '%'}
	--progress-color={progress > 80 ? 'red' : 'green'}
/>

<input type="range" bind:value={progress} min="0" max="100" />

嵌套 style 元素

一个组件只能有一个顶层 <style> 标签。但你可以把 <style> 嵌套在其他元素或逻辑块中——此时它会被原样插入到 DOM 中,不做作用域处理。

<div>
	<style>
		/* 这段 CSS 会被原样插入 DOM */
		/* 不做作用域隔离,影响全局 */
		div {
			color: red;
		}
	</style>

	这段文字会变红
</div>
Note

嵌套的 <style> 标签不会被 Svelte 处理——没有作用域隔离,没有编译优化。它就是你往 DOM 里塞了一段原生 CSS。使用时要小心样式污染。

嵌套 style 的使用场景

嵌套 <style> 很少用,但有些场景确实需要:

<script>
	let dynamicCSS = $state('');
	let { children } = $props();
</script>

<!-- 从后端获取 CSS 并动态注入 -->
<div>
	<style>
		{@html dynamicCSS}
	</style>
	{@render children?.()}
</div>
Caution

动态注入 CSS 有 XSS 风险。确保内容来源可信,不要直接注入用户输入的 CSS。

完整示例:可定制主题的卡片

把自定义属性和 CSS 变量组合起来,做一个支持主题定制的卡片组件。

<!-- ThemeCard.svelte -->
<script>
	let { title, content } = $props();
</script>

<div class="card">
	<h3 class="title">{title}</h3>
	<p class="content">{content}</p>
</div>

<style>
	.card {
		background: var(--card-bg, white);
		border: 1px solid var(--card-border, #e0e0e0);
		border-radius: var(--card-radius, 8px);
		padding: var(--card-padding, 20px);
		box-shadow: var(--card-shadow, 0 2px 4px rgba(0,0,0,0.1));
	}

	.title {
		color: var(--card-title-color, #333);
		font-size: var(--card-title-size, 18px);
		margin: 0 0 8px 0;
	}

	.content {
		color: var(--card-text-color, #666);
		font-size: 14px;
		margin: 0;
	}
</style>

使用时可以灵活定制:

<script>
	import ThemeCard from './ThemeCard.svelte';
</script>

<!-- 默认风格 -->
<ThemeCard title="默认卡片" content="使用默认样式" />

<!-- 暗色主题 -->
<ThemeCard
	title="暗色卡片"
	content="深色背景"
	--card-bg="#2d3436"
	--card-border="#636e72"
	--card-title-color="#dfe6e9"
	--card-text-color="#b2bec3"
/>

<!-- 强调风格 -->
<ThemeCard
	title="强调卡片"
	content="大圆角带阴影"
	--card-radius="20px"
	--card-shadow="0 8px 16px rgba(108, 92, 231, 0.3)"
	--card-title-color="#6c5ce7"
/>

本节回顾

  • Svelte 支持在组件标签上直接写 CSS 自定义属性(--var-name),自动透传到组件内
  • 透传原理是编译时在组件外包一层 display: contents 的容器
  • 组件内用 var(--name, 兜底值) 读取,兜底值在变量未定义时生效
  • CSS 变量遵循继承规则,在祖先元素或 :root 上定义也能被子组件读到
  • 一个组件只能有一个顶层 <style>,嵌套的 <style> 不做作用域处理
  • 嵌套 <style> 会原样插入 DOM,需注意样式污染和 XSS 风险