自定义属性与嵌套样式
本教程共 50 篇 · 第 28 篇 · 更新于 2026-08-05 · 约 6 分钟阅读
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 风险