Help us learn about your current experience with the documentation. Take the survey.

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-5lg:gl-mt-5

响应式 CSS 工具类

响应式 CSS 工具类 以断点名称为前缀,后跟 : 字符。可用的断点在 tailwind.defaults.js#L44 中配置。

示例lg:gl-mt-5

悬停、焦点和其他状态修饰符

状态修饰符 可用于有条件地应用任何 Tailwind CSS 类。在 CSS 工具类前加上修饰符名称,后跟 : 字符。

示例hover:gl-underline

!important 修饰符

您可以通过在 CSS 工具类开头添加 ! 来使用 important 修饰符。当与响应式工具类或状态修饰符一起使用时,! 放在 : 字符后面。

示例!gl-mt-5lg:!gl-mt-5hover:!gl-underline

间距和尺寸 CSS 工具类

间距和尺寸 CSS 工具类(例如 marginpaddingwidthheight)使用我们在 src/tokens/build/tailwind/tokens.cjs 中定义的间距比例。可用的 CSS 工具类请参见 https://gitlab-org.gitlab.io/frontend/tailwind-documentation/margin

示例gl-mt-5margin-top: 1rem;

颜色 CSS 工具类

颜色 CSS 工具类(例如 colorbackground-color)使用在 src/tokens/build/tailwind/tokens.cjs 中定义的颜色。可用的 CSS 工具类请参见 https://gitlab-org.gitlab.io/frontend/tailwind-documentation/text-color

示例gl-text-subtlecolor: 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

何时应该创建组件类?

我们推荐"工具类优先"的方法。

  1. 从工具类开始。
  2. 如果将工具类组合成组件类可以消除代码重复并封装明确的职责,那就这样做。

这鼓励组件类的有机增长,避免创建一次性不可重用的类。此外,从"工具类优先"中产生的类往往是设计为中心的(例如 .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-fixedgl-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 属性(例如 marginpadding),使用 @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_casecamelCase

// 不好的做法
.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 的文档包含 其规则的完整列表