跳到主要内容
文档

样式

显式声明的 shadow root 渲染在带 declarative shadow DOM 的 custom element 内部,全局样式表触及不到。默认是 light root。

适用于 v1.0.0-alpha.15 · 更新于

shadow 边界

路由页面渲染在每页一个的 custom element 内(例如 <blog-post-page>)。当页面类显式选择 root: 'shadow-open' 时,服务端以 declarative shadow DOM 输出其内容,页面自己的 <style> 与组件样式表规则位于 shadow root 中。文档级规则如 .card { ... } 或 h1 { ... } 被限定在 light DOM,永远到不了 shadow 页面内容——而且是静默的:没有 console 警告,也没有构建错误。(编译默认是 light root,此时文档样式是生效的。)

什么能穿过边界

CSS 自定义属性会穿透 shadow 边界继承:在 :root 上定义的 --text-primary、--brand 等在页面内均可读取。:host 从内部为页面元素本身设样式;::slotted() 作用于投影进 slot 的 light DOM 子节点。可继承的文本属性(color、font-family、line-height)同样能穿过。

什么不能

文档样式表中的 class、id 与标签选择器永远无法匹配到 shadow root 内部。全局 reset(* { margin: 0 })、排版规则与工具类体系因此只对文档外壳生效。这是封装的设计意图——也是最常见的第一天陷阱,因为人的第一反应就是全局样式表。

受支持的样式路径

其一:真实的 .css 文件——import 进组件并数组进它的 static styles(#1558)。构建期的 style-asset 管线把每份样式表发射成按内容寻址的资产:shadow 组件在客户端以真实 .css 资产拉取并经 adoptedStyleSheets 采纳(绝不内嵌 JS 字节),服务端则在 DSD 输出里用解析器可见的 <link rel="stylesheet" data-oe-static-styles> 声明同一份资产。light root 的样式表会包在 @scope (<tag>) 中发射,并把 :host 与 ::slotted 投影到 light 树;它的 DSD 模板同样携带这条 <link>,而不是内联 <style>。只有位于项目根之外、且没有任何客户端图能发射的包内样式表才退回内联的内存 StyleSheet,构建会把这种回退报告为 warning。其二:来自所配置 CSS provider 的工具类(默认 Tailwind preset;UnoCSS 经 style-provider 端口接入)——编译器准入静态 class token,构建把它们发射进 light root 的文档样式表与 shadow island 共享的组件样式表,因此表现写在标记里,JavaScript 里不携带任何 CSS 文本。其三:在 :root 上定义 CSS 自定义属性——它们会穿透 shadow 边界继承。文档级 <link rel="stylesheet"> 与 head 里的 <style> 不会作用于 shadow 内容;编译模板中的裸 <style> 标签会被拒绝。

文档全局样式表(不会生效)

/* app/styles.css — linked in the document head */
.card { border: 1px solid silver; }  /* never matches page content */

组件 .css 文件(生效)

/* app/components/page-example.css — 样式表是文件,不是 JS 字符串 */
:host { display: block; }
.card {
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 1rem;
  color: var(--text-primary);
}
// app/components/page-example.tsx — 由 open:compiled-element transform 编译
import { element, OpenElement, type StyleSheetLike } from '@openelement/element';
import styles from './page-example.css';

@element('page-example', { root: 'shadow-open' })
export default class ExamplePage extends OpenElement {
  static override styles: StyleSheetLike[] = [styles];

  render() {
    return <section class='card'>Themed through custom properties.</section>;
  }
}

内联样式字符串已退役:编译模块的 static styles 必须数组 .css import,其余任何写法都会以 OEC9029 使构建失败。

自定义属性实战

starter 在 :root 上定义了一层设计令牌(颜色、字体、间距),正是为了让页面可以完全通过自定义属性来主题化。优先用令牌做主题;组件自身的布局与排版再交给组件的 .css 样式表。

另见