SCSS 样式指南
工具类
随着我们网站的不断发展,为了减少生成更多的 CSS,我们推荐使用工具类而不是添加新的 CSS。在复杂情况下,可以通过添加组件类来处理 CSS。
CSS 工具类定义在哪里?
工具类由 Tailwind CSS 生成。有三种方式可以查看 Tailwind CSS 类:
- GitLab Tailwind CSS 文档:一个专门针对 GitLab Tailwind 配置的文档站点。它是所有可用 Tailwind CSS 类的可搜索列表。
- Tailwind CSS 自动补全:可在 VS Code 或 RubyMine 中使用。
- Tailwind CSS 配置查看器:一个针对我们设计系统(间距、颜色、尺寸等)的 Tailwind CSS 类的可视化视图。不显示所有可用的 Tailwind CSS 类。
哪些 CSS 工具类已被弃用?
common.scss 中的类正在被弃用。应避免使用 common.scss 中使用非设计系统值的类。改用具有符合设计系统值的类。
避免使用 Bootstrap 的工具类。
在将 Bootstrap 的工具类 迁移到 GitLab UI 工具类时,请注意边距和填充的类都不同。GitLab 使用的尺寸比例与 Bootstrap 库中使用的比例不同。对于 Bootstrap 的填充或边距工具类,您可能需要将应用的工具类大小加倍才能达到相同的视觉效果(例如 ml-1 变成 gl-ml-2)。
Tailwind CSS
截至 2024 年 8 月,我们使用 Tailwind CSS 作为我们的 CSS 工具类提供商。这取代了之前的自定义解决方案。有关动机、建议和实施详情,请参阅 Tailwind CSS 设计文档。
Tailwind CSS 基础
以下是 Tailwind CSS 基础信息以及它如何配置以使用 Pajamas 设计系统 的说明。有关更深入的指南,请参阅 官方 Tailwind CSS 文档。
前缀
我们已配置 Tailwind CSS 使用 前缀,因此所有工具类都带有 gl- 前缀。当使用响应式工具类或状态修饰符时,前缀放在冒号后面。
示例:gl-mt-5、lg:gl-mt-5。
响应式 CSS 工具类
响应式 CSS 工具类 以断点名称为前缀,后跟 : 字符。可用的断点在 tailwind.defaults.js#L44 中配置。
示例:lg:gl-mt-5
悬停、焦点和其他状态修饰符
状态修饰符 可用于有条件地应用任何 Tailwind CSS 类。在 CSS 工具类前加上修饰符名称,后跟 : 字符。
示例:hover:gl-underline
!important 修饰符
您可以通过在 CSS 工具类开头添加 ! 来使用 important 修饰符。当与响应式工具类或状态修饰符一起使用时,! 放在 : 字符后面。
示例:!gl-mt-5、lg:!gl-mt-5、hover:!gl-underline
间距和尺寸 CSS 工具类
间距和尺寸 CSS 工具类(例如 margin、padding、width、height)使用我们在 src/tokens/build/tailwind/tokens.cjs 中定义的间距比例。可用的 CSS 工具类请参见 https://gitlab-org.gitlab.io/frontend/tailwind-documentation/margin。
示例:gl-mt-5 是 margin-top: 1rem;
颜色 CSS 工具类
颜色 CSS 工具类(例如 color 和 background-color)使用在 src/tokens/build/tailwind/tokens.cjs 中定义的颜色。可用的 CSS 工具类请参见 https://gitlab-org.gitlab.io/frontend/tailwind-documentation/text-color。
示例:gl-text-subtle 是 color: var(--gl-text-color-subtle, #626168);
构建 Tailwind CSS 包
当使用 GitLab Development Kit 与 Vite 或 Webpack 时,Tailwind CSS 会监视文件变化以即时构建检测到的工具类。
要构建新的 Tailwind CSS 包,请运行 yarn tailwindcss:build。这是在使用 bundle exec rake gitlab:assets:compile 构建生产资源时内部调用的脚本。
无论包如何构建,输出都保存在 app/assets/builds/tailwind.css。
Tailwind CSS 自动补全
Tailwind CSS 自动补全会在您的代码编辑器中列出所有可用的类。
VS Code
如果您遇到自动补全速度慢的问题,可能需要 增加 TS 服务器允许使用的内存量。
安装 Tailwind CSS IntelliSense 扩展。对于 HAML 和自定义 *-class 属性支持,这些是推荐的设置:
{
"tailwindCSS.experimental.classRegex": [
["class: [\"|']+([^\"|']*)[\"|']+", "([a-zA-Z0-9\-:!/]+)"],
["(\.[\w\-.]+)[\n\=\{\s]", "([\w\-]+)"],
["[a-z]+-class(?:es)?=\"([^'\"]*)\""]
],
"tailwindCSS.emmetCompletions": true
}RubyMine
Tailwind CSS 自动补全是 默认启用 的。对于完整的 HAML 和自定义 *-class 属性支持,这些是对默认设置的推荐更新:
{
"includeLanguages": {
"haml": "html"
},
"emmetCompletions": true,
"experimental": {
"classRegex": [
["class: [\"|']+([^\"|']*)[\"|']+", "([a-zA-Z0-9\-:!/]+)"],
["(\.[\w\-.]+)[\n\=\{\s]", "([\w\-]+)"],
["[a-z]+-class(?:es)?=\"([^'\"]*)\""]
]
}
}您应该在哪里放置新的工具类?
工具类由 Tailwind CSS 生成,它支持大多数 CSS 功能。如果有不可用的功能,我们应该更新 GitLab UI 中的 tailwind.defaults.js。
何时应该创建组件类?
我们推荐"工具类优先"的方法。
- 从工具类开始。
- 如果将工具类组合成组件类可以消除代码重复并封装明确的职责,那就这样做。
这鼓励组件类的有机增长,避免创建一次性不可重用的类。此外,从"工具类优先"中产生的类往往是设计为中心的(例如 .button、.alert、.card),而不是领域为中心的(例如 .security-report-widget、.commit-header-icon)。
灵感来源:
在 HTML 和样式表中利用 Tailwind CSS
在编写组件类时,有效集成 Tailwind CSS 的工具类以保持与设计系统的一致性并保持 CSS 包的大小较小非常重要。
HTML 中的工具 CSS 类 vs. 样式表中的工具 CSS 类:
通过在 HTML 中直接使用工具类,我们可以保持 CSS 文件更小,并遵循工具类优先的理念。除非绝对必要,否则避免在一个组件类中组合工具类和自定义样式,这样可以避免混淆和潜在的冲突。
-
偏好原因:
- 更小的 CSS 文件大小:直接使用工具类可以生成更紧凑的 CSS 文件,并促进更一致的设计系统。
- 清晰性和可维护性:当工具类在 HTML 中使用时,样式应用方式更清晰,降低了冲突和回归的风险。
-
组合样式可能存在的问题:
- 冲突:如果工具类和自定义样式组合在单个类中,可能会产生冲突,特别是当样式相互依赖时。
- 回归:样式应该如何解析变得不那么明显,可能导致回归或意外行为。
通过遵循这些指南,我们可以创建干净、可维护的样式表,有效利用 Tailwind CSS。
1. 直接在 HTML 中使用工具类(首选方法)
为了更好的可维护性和遵循工具类优先原则,直接将工具类添加到 HTML 元素中。组件类应主要只包含非工具 CSS 样式。在以下示例中,您添加工具类 gl-fixed 和 gl-inset-x-0,而不是在 SCSS 文件中添加 position: fixed; right: 0; left: 0;:
<!-- 不好的做法 -->
<div class="my-class"></div>
<style>
.my-class {
top: $header-height;
min-height: $comparison-empty-state-height;
position: fixed;
left: 0px;
right: 0px;
}
</style>
<!-- 好的做法 -->
<div class="my-class gl-fixed gl-inset-x-0"></div>
<style>
.my-class {
top: $header-height;
min-height: $comparison-empty-state-height;
}
</style>2. 在组件类中应用工具类(必要时)
有时直接在 HTML 中使用工具类可能不可行,您需要将它们包含在我们的自定义 SCSS 文件中。然后,您可能希望从设计系统继承样式定义,而无需找出相关的属性或值。为了简化此过程,您可以使用 Tailwind CSS 的 @apply 指令 将工具类的样式定义包含在您的自定义样式中。
对于依赖设计系统的 CSS 属性(例如 margin、padding),使用 @apply 是推荐的。对于无单位的 CSS 属性(例如 display: flex),可以直接使用 CSS 属性。
// 不好的做法
.my-class {
margin-top: 0.5rem;
}
// 可以接受
.my-class {
display: flex;
}
// 好的做法
.my-class {
@apply gl-mt-5 gl-flex;
}使用 @apply 的首选方式是在单行中组合多个 CSS 类,最多两行,如上例所示。这种方法使 CSS 简洁易读:
// 好的做法
.my-class {
@apply gl-mt-5 gl-flex gl-items-center;
}避免将类拆分为多行,如下所示。
// 避免
@apply gl-mt-5;
@apply gl-flex;
@apply gl-items-center;这样做的原因是 IDE 扩展可能只有在 CSS 类在同一行时才能检测到冲突:
// ✅ 检测到冲突:'gl-bg-subtle' 应用了与 'gl-bg-default' 相同的 CSS 属性。(cssConflict)
@apply gl-bg-default gl-bg-subtle;
// ❌ 未检测到冲突
@apply gl-bg-default;
@apply gl-bg-subtle;此规则的例外是使用 !important 时。由于 !important 适用于整行,每个需要它的类应该单独应用。例如:
@apply gl-flex gl-items-center;
@apply gl-mt-5 #{!important};这确保 !important 只在预期的地方应用,而不影响同一行中的其他类。
响应式设计
我们的 UI 应该在移动设备和桌面上都能良好运行。为此,我们使用 CSS 媒体查询。通常我们应该采用移动优先的媒体查询方法。这意味着先为移动设备编写 CSS,然后使用 min-width 媒体查询来覆盖桌面样式。此规则的例外是设置子组件的显示模式。例如,当在移动设备上隐藏 GlButton 时,我们不想覆盖组件 CSS 设置的显示模式,因此应该使用 max-width 媒体查询,如 max-lg:gl-hidden。
Tailwind CSS 类
<!-- 不好的做法 -->
<div class="gl-mt-5 max-lg:gl-mt-3"></div>
<!-- 好的做法 -->
<div class="gl-mt-3 md:gl-mt-5"></div>
<!-- 不好的做法 -->
<div class="gl-mt-3 sm:max-lg:gl-mt-5"></div>
<!-- 好的做法 -->
<div class="gl-mt-3 sm:gl-mt-5 lg:gl-mt-3"></div>
<!-- 不好的做法 -->
<!-- 更改子组件的显示模式可能导致视觉回归。 -->
<gl-button class="gl-hidden lg:gl-flex">Edit</gl-button>
<!-- 好的做法 -->
<gl-button class="max-lg:gl-hidden">Edit</gl-button>组件类
// 不好的做法
.class-name {
@apply gl-mt-5 max-lg:gl-mt-3;
}
// 好的做法
.class-name {
@apply gl-mt-3 lg:gl-mt-5;
}
// 不好的做法
.class-name {
display: block;
@include media-breakpoint-down(lg) {
display: flex;
}
}
// 好的做法
.class-name {
display: flex;
@include media-breakpoint-up(lg) {
display: block;
}
}命名
文件名应使用 snake_case。
CSS 类应使用 lowercase-hyphenated 格式,而不是 snake_case 或 camelCase。
// 不好的做法
.class_name {
color: #fff;
}
// 不好的做法
.className {
color: #fff;
}
// 好的做法
.class-name {
color: #fff;
}避免使用 SCSS & 功能创建复合类名。这使得搜索用法变得更困难,且提供的有限好处。
// 不好的做法
.class {
&-name {
color: orange;
}
}
// 好的做法
.class-name {
color: #fff;
}应使用类名而不是标签名选择器。不推荐使用标签名选择器,因为它们可能会影响层次结构中不需要的元素。
// 不好的做法
ul {
color: #fff;
}
// 好的做法
.class-name {
color: #fff;
}
// 最佳做法
// 优先使用现有的工具类而不是添加新的样式类名也比 ID 更可取。使用 ID 的规则是不可重用的,因为页面上只能有一个受影响的元素。
// 不好的做法
#my-element {
padding: 0;
}
// 好的做法
.my-element {
padding: 0;
}嵌套
避免不必要的嵌套。包装组件的额外特异性使覆盖变得更困难。
// 不好的做法
.component-container {
.component-header {
/* ... */
}
.component-body {
/* ... */
}
}
// 好的做法
.component-container {
/* ... */
}
.component-header {
/* ... */
}
.component-body {
/* ... */
}带有 js- 前缀的选择器
不要使用任何带有 js- 前缀的选择器进行样式设计。这些选择器仅打算与 JavaScript 一起使用,以便在不破坏样式的情况下进行删除或重命名。
带有工具 CSS 类的选择器
不要在您的样式表中使用工具 CSS 类作为选择器。这些类可能会更改,需要更新选择器并使实现更难维护。相反,使用另一个现有的 CSS 类或添加新的自定义 CSS 类来为元素设置样式。这种方法提高了可维护性并降低了错误风险。
// ❌ 不好的做法
.gl-mb-5 {
/* ... */
}
// ✅ 好的做法
.component-header {
/* ... */
}带有 ARIA 属性的选择器
不要使用任何带有 ARIA 的属性选择器进行样式设计。这些属性和角色旨在支持辅助技术。带有 ARIA 注释的组件结构可能会更改,因此其样式也会更改。我们需要能够将这些角色和属性移动到不同的元素,而不会破坏样式。
// 不好的做法
&[aria-expanded=false] &-header {
border-bottom: 0;
}
// 好的做法
&.is-collapsed &-header {
border-bottom: 0;
}使用 extend at-rule
由于 内存泄漏 和 该规则无法正常工作,禁止使用 extend at-rule。
代码检查
我们使用 stylelint 来检查样式指南的一致性。它使用 .stylelintrc 中的规则集和 我们的 SCSS 配置 中的规则。.stylelintrc 位于项目的主目录中。
要检查您的更改是否产生任何警告,请在 GitLab 目录中运行 yarn lint:stylelint。Stylelint 也在 GitLab CI/CD 中运行以捕获任何警告。
如果 Rake 任务抛出您不理解的警告,SCSS Lint 的文档包含 其规则的完整列表。