Dojo 中的样式和主题
Dojo 小部件最适合作为简单的组件,每个组件都处理一项责任。它们应该尽可能地封装和模块化,以促进可重用性,同时避免与应用程序可能使用的其他小部件发生冲突。
小部件可以通过常规 CSS 进行样式设置,但为了支持封装和重用目标,每个小部件都应该维护自己的独立 CSS 模块,该模块与小部件的源代码并行存在。这允许小部件独立地进行样式设置,而不会与应用程序中其他地方使用的类似类名发生冲突。
Dojo 区分了几种样式类型,每种类型代表企业 Web 应用程序中样式关注的不同方面和粒度。
- 小部件非主题样式(粒度:每个小部件)
- 小部件正常运行所需的最小样式,这些样式不打算被主题覆盖。小部件在渲染时从其 CSS 模块导入中直接引用这些样式类。
- 小部件主题样式(粒度:每个小部件)
- 可以通过主题覆盖的小部件样式。小部件使用来自
theme
中间件的theme.classes(css)
API,传入需要主题化的 CSS,并在渲染时使用返回的类名。小部件的用户可以根据需要覆盖部分或全部这些类。
- 可以通过主题覆盖的小部件样式。小部件使用来自
- 跨领域样式(粒度:应用程序范围)
- 适用于多个小部件的样式,无论是不同类型的小部件,还是单个小部件类型的多个实例。这些样式通常为应用程序中使用的所有可主题化小部件提供一致的视觉呈现。跨领域样式可以通过多种机制提供/引用
- 提供 应用程序范围的主题
- 指定每个小部件的主题
- 将额外的类传递给小部件
- 在变体模块中定义 css 属性,并在整个小部件样式表中使用它们,以帮助保持一致性和主题变体创建
- 组合类 在 CSS 模块中。
- 在小部件中使用多个 CSS 模块。
- 适用于多个小部件的样式,无论是不同类型的小部件,还是单个小部件类型的多个实例。这些样式通常为应用程序中使用的所有可主题化小部件提供一致的视觉呈现。跨领域样式可以通过多种机制提供/引用
如上所述,Dojo 为应用程序开发人员提供了多种互补机制来提供和覆盖 CSS 样式类,无论是在整个应用程序中还是特定于单个样式规则中的单个样式类。
结构化小部件样式
Dojo 利用 CSS 模块 来提供 CSS 的所有灵活性,但额外的好处是本地化类,以帮助防止在大型应用程序中无意中发生样式冲突。Dojo 还为每个 CSS 模块生成类型定义,允许小部件像任何其他 TypeScript 模块一样导入其 CSS,并在设计时通过 IDE 自动完成以类型安全的方式引用 CSS 类名。
小部件的 CSS 模块文件应该具有 .m.css
扩展名,并且按照惯例通常与它关联的小部件具有相同的名称。具有此扩展名的文件将被处理为 CSS 模块,而不是普通 CSS 文件。
示例
给定小部件的以下 CSS 模块文件
src/styles/MyWidget.m.css
.myWidgetClass {
font-variant: small-caps;
}
.myWidgetExtraClass {
font-style: italic;
}
此样式表可以在相应的小部件中使用,如下所示
src/widgets/MyWidget.ts
import { create, tsx } from '@dojo/framework/core/vdom';
import * as css from '../styles/MyWidget.m.css';
const factory = create();
export default factory(function MyWidget() {
return <div classes={[css.myWidgetClass, css.myWidgetExtraClass]}>Hello from a Dojo widget!</div>;
});
在构建的应用程序中检查这些示例小部件的 CSS 类时,它们将不包含 myWidgetClass
和 myWidgetExtraClass
,而是包含类似于 MyWidget-m__myWidgetClass__33zN8
和 MyWidget-m__myWidgetExtraClass___g3St
的混淆 CSS 类名。
这些混淆的类名被本地化为 MyWidget
元素,并且由 Dojo 的 CSS 模块构建过程确定。通过这种机制,应用程序中的其他小部件也可以使用 myWidgetClass
类名,但具有不同的样式规则,并且不会遇到任何样式集之间的冲突。
警告:混淆的 CSS 类名应被视为不可靠,并且可能会随着应用程序的新构建而改变,因此开发人员不应显式引用它们(例如,如果尝试从应用程序中的其他地方定位元素)。
抽象和扩展样式表
CSS 自定义属性
Dojo 允许使用现代 CSS 功能,例如 自定义属性和 var()
,以帮助抽象和集中应用程序中的常见样式属性。
与其在每个小部件的 CSS 模块中为颜色或字体指定相同的值,不如通过名称引用抽象的自定义属性,然后在 .root
类中将值作为主题 variant
提供。这种分离允许更轻松地维护整个应用程序中的常见样式关注点,并通过更改变量来创建主题变体。
注意:不要将主题变体文件导入小部件的 css 模块;这将在运行时通过 theme.variant()
类处理。
例如
src/themes/MyTheme/variants/default.m.css
.root {
--dark-background: black;
--dark-foreground: lightgray;
--padding: 32px;
}
src/themes/MyTheme/MyWidget.m.css
.root {
margin: var(--padding);
color: var(--dark-foreground);
background: var(--dark-background);
}
Dojo 的默认构建过程将自定义属性原样传播到应用程序的输出样式表中。当仅针对常青浏览器时,这很好,但在还需要针对不支持 CSS 自定义属性标准的浏览器(例如 IE)时,可能会出现问题。为了解决这个问题,应用程序可以在传统模式下构建(dojo build app --legacy
),在这种情况下,Dojo 将在构建时解析自定义属性的值,并在输出样式表中复制它们。一个值将包含原始 var()
引用,另一个值将是传统浏览器在无法处理 var()
值时可以回退的解析值。
CSS 模块组合
将主题应用于 Dojo 小部件会导致小部件的默认样式类被主题中提供的样式类完全覆盖。当仅需要通过主题修改给定样式类中的属性子集时,而其余部分可以保持为默认值,这可能会出现问题。
Dojo 应用程序中的 CSS 模块文件 可以利用 composes:
功能将一组样式从一个类选择器应用到另一个类选择器。当创建一个调整现有主题的新主题时,以及在单个主题中抽象常见样式属性集时,这很有用(请注意,CSS 自定义属性 是抽象单个样式属性值的更标准化方法)。
警告:composes:
的使用可能会很脆弱,例如,当扩展不受当前应用程序直接控制的第三方主题时。第三方进行的任何更改都可能破坏 composes
底层主题的应用程序主题,并且此类破坏可能难以查明和解决。
但是,仔细使用此功能在大型应用程序中可能会有所帮助。例如,集中一组公共属性
src/themes/common/ButtonBase.m.css
.buttonBase {
margin-right: 10px;
display: inline-block;
font-size: 14px;
text-align: left;
background-color: white;
}
src/themes/myBlueTheme/MyButton.m.css
.root {
composes: buttonBase from '../common/ButtonBase.m.css';
background-color: blue;
}
Dojo 样式最佳实践
由于 Dojo 应用程序中的样式主要限定在单个小部件,因此几乎不需要复杂的 selector 定位。Dojo 中的样式应用应该尽可能简单 - 开发人员可以通过遵循一些简单的建议来实现这一点
- 维护封装的小部件样式
- 单个 CSS 模块应该解决单个问题。对于与小部件对齐的模块,这通常意味着只包含单个伴随小部件的样式类。CSS 模块也可以在多个小部件之间共享,例如,应用程序可以定义一个在整个应用程序中共享的公共排版模块。对于 小部件在它们的 TypeScript 代码中引用多个 CSS 模块 来说,这是一种常见的做法。
- 不要通过其样式类在小部件的 CSS 模块或提供小部件样式覆盖的主题之外引用小部件。
- 不要依赖构建应用程序中的样式类名,因为 Dojo 会对它们进行混淆。
- 优先考虑 类级选择器特异性
- 避免选择器嵌套
- 避免使用 BEM 命名约定
- 优先使用与小部件目的相关的描述性类名。
- 避免使用
!important