mirror of
https://gitlab.com/gitlab-org/gitlab-foss.git
synced 2025-08-10 01:31:45 +00:00
530 lines
17 KiB
Markdown
530 lines
17 KiB
Markdown
---
|
|
stage: none
|
|
group: unassigned
|
|
info: Any user with at least the Maintainer role can merge updates to this content. For details, see https://docs.gitlab.com/ee/development/development_processes.html#development-guidelines-review.
|
|
---
|
|
|
|
# SCSS style guide
|
|
|
|
## Utility Classes
|
|
|
|
In order to reduce the generation of more CSS as our site grows, prefer the use
|
|
of utility classes over adding new CSS. In complex cases, CSS can be addressed
|
|
by adding component classes.
|
|
|
|
### Where are CSS utility classes defined?
|
|
|
|
Utility classes are generated by [Tailwind CSS](https://tailwindcss.com/). There are three ways to view Tailwind CSS classes:
|
|
|
|
- [GitLab Tailwind CSS documentation](https://gitlab-org.gitlab.io/frontend/tailwind-documentation): A documentation site specific to the GitLab Tailwind configuration. It is a searchable list of all available Tailwind CSS classes.
|
|
- [Tailwind CSS autocomplete](#tailwind-css-autocomplete): Can be used in VS Code or RubyMine.
|
|
- [Tailwind CSS config viewer](https://gitlab-org.gitlab.io/gitlab-ui/tailwind-config-viewer/): A visual view of Tailwind CSS classes specific to our design system (spacing, colors, sizing, etc). Does not show all available Tailwind CSS classes.
|
|
|
|
### What CSS utility classes are deprecated?
|
|
|
|
Classes in [`utilities.scss`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/assets/stylesheets/utilities.scss)
|
|
and [`common.scss`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/assets/stylesheets/framework/common.scss)
|
|
are being deprecated. Classes in [`common.scss`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/assets/stylesheets/framework/common.scss)
|
|
that use non-design-system values should be avoided. Use classes with conforming values instead.
|
|
|
|
Avoid [Bootstrap's Utility Classes](https://getbootstrap.com/docs/4.3/utilities/).
|
|
|
|
NOTE:
|
|
While migrating [Bootstrap's Utility Classes](https://getbootstrap.com/docs/4.3/utilities/)
|
|
to the [GitLab UI](https://gitlab.com/gitlab-org/gitlab-ui/-/blob/main/doc/css.md#utilities)
|
|
utility classes, note both the classes for margin and padding differ. The size scale used at
|
|
GitLab differs from the scale used in the Bootstrap library. For a Bootstrap padding or margin
|
|
utility, you may need to double the size of the applied utility to achieve the same visual
|
|
result (such as `ml-1` becoming `gl-ml-2`).
|
|
|
|
### Tailwind CSS
|
|
|
|
As of August 2024, we are using [Tailwind CSS](https://tailwindcss.com/) as our CSS utilities provider.
|
|
This replaces the previous, custom-built solution. See the [Tailwind CSS design document](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/tailwindcss/)
|
|
for motivation, proposal, and implementation details.
|
|
|
|
#### Tailwind CSS basics
|
|
|
|
Below are some Tailwind CSS basics and information about how it has been
|
|
configured to use the [Pajamas design system](https://design.gitlab.com/). For a
|
|
more in-depth guide see the [official Tailwind CSS documentation](https://tailwindcss.com/docs/utility-first).
|
|
|
|
##### Prefix
|
|
|
|
We have configured Tailwind CSS to use a
|
|
[prefix](https://tailwindcss.com/docs/configuration#prefix) so all utility classes are prefixed with `gl-`.
|
|
When using responsive utilities or state modifiers the prefix goes after the colon.
|
|
|
|
**Examples:** `gl-mt-5`, `lg:gl-mt-5`.
|
|
|
|
##### Responsive CSS utility classes
|
|
|
|
[Responsive CSS utility classes](https://tailwindcss.com/docs/responsive-design) are prefixed with the breakpoint name, followed by the `:` character.
|
|
The available breakpoints are configured in [tailwind.defaults.js#L44](https://gitlab.com/gitlab-org/gitlab-ui/-/blob/7c0fb4b07a0f0d0a58dd0137831412dbf53ea498/tailwind.defaults.js#L482)
|
|
|
|
**Example:** `lg:gl-mt-5`
|
|
|
|
##### Hover, focus, and other state modifiers
|
|
|
|
[State modifiers](https://tailwindcss.com/docs/hover-focus-and-other-states)
|
|
can be used to conditionally apply any Tailwind CSS class. Prefix the CSS utility class
|
|
with the name of the modifier, followed by the `:` character.
|
|
|
|
**Example:** `hover:gl-underline`
|
|
|
|
##### `!important` modifier
|
|
|
|
You can use the [important modifier](https://tailwindcss.com/docs/configuration#important-modifier) by adding `!` to the beginning of the CSS utility class. When using in conjunction with responsive utility classes or state modifiers the `!` goes after the `:` character.
|
|
|
|
**Examples:** `!gl-mt-5`, `lg:!gl-mt-5`, `hover:!gl-underline`
|
|
|
|
##### Spacing and sizing CSS utility classes
|
|
|
|
Spacing and sizing CSS utility classes (e.g. `margin`, `padding`, `width`, `height`) use our spacing scale defined in
|
|
[src/tokens/build/tailwind/tokens.cjs](https://gitlab.com/gitlab-org/gitlab-ui/-/blob/7c0fb4b07a0f0d0a58dd0137831412dbf53ea498/src/tokens/build/tailwind/tokens.cjs). See [https://gitlab-org.gitlab.io/frontend/tailwind-documentation/margin](https://gitlab-org.gitlab.io/frontend/tailwind-documentation/margin) for available CSS utility classes.
|
|
|
|
**Example:** `gl-mt-5` is `margin-top: 1rem;`
|
|
|
|
##### Color CSS utility classes
|
|
|
|
Color CSS utility classes (e.g. `color` and `background-color`) use colors defined in
|
|
[src/tokens/build/tailwind/tokens.cjs](https://gitlab.com/gitlab-org/gitlab-ui/-/blob/7c0fb4b07a0f0d0a58dd0137831412dbf53ea498/src/tokens/build/tailwind/tokens.cjs).
|
|
See [https://gitlab-org.gitlab.io/frontend/tailwind-documentation/text-color](https://gitlab-org.gitlab.io/frontend/tailwind-documentation/text-color) for available CSS utility classes.
|
|
|
|
**Example:** `gl-text-red-500` is `color: var(--red-500, #dd2b0e);`
|
|
|
|
#### Building the Tailwind CSS bundle
|
|
|
|
When using Vite or Webpack with the GitLab Development Kit, Tailwind CSS watches for file changes to
|
|
build detected utilities on the fly.
|
|
|
|
To build a fresh Tailwind CSS bundle, run `yarn tailwindcss:build`. This is the script that gets
|
|
called internally when building production assets with `bundle exec rake gitlab:assets:compile`.
|
|
|
|
However the bundle gets built, the output is saved to `app/assets/builds/tailwind.css`.
|
|
|
|
#### Tailwind CSS autocomplete
|
|
|
|
Tailwind CSS autocomplete lists all available classes in your code editor.
|
|
|
|
##### VS Code
|
|
|
|
NOTE:
|
|
If you are having trouble with slow autocomplete you may need to [increase the amount of memory the TS server is allowed to use](../type_hinting.md#vs-code-settings).
|
|
|
|
Install the [Tailwind CSS IntelliSense](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss)
|
|
extension. For HAML and custom `*-class` prop support these are the recommended settings:
|
|
|
|
```json
|
|
{
|
|
"tailwindCSS.experimental.classRegex": [
|
|
["class: [\"|']+([^\"|']*)[\"|']+", "([a-zA-Z0-9\\-:!/]+)"],
|
|
["(\\.[\\w\\-.]+)[\\n\\=\\{\\s]", "([\\w\\-]+)"],
|
|
["[a-z]+-class(?:es)?=\"([^'\"]*)\""]
|
|
],
|
|
"tailwindCSS.emmetCompletions": true
|
|
}
|
|
```
|
|
|
|
##### RubyMine
|
|
|
|
Tailwind CSS autocomplete is [enabled by default](https://www.jetbrains.com/help/ruby/tailwind-css.html).
|
|
For full HAML and custom `*-class` prop support these are the recommended updates to the default settings:
|
|
|
|
```json
|
|
{
|
|
"includeLanguages": {
|
|
"haml": "html"
|
|
},
|
|
"emmetCompletions": true,
|
|
"experimental": {
|
|
"classRegex": [
|
|
["class: [\"|']+([^\"|']*)[\"|']+", "([a-zA-Z0-9\\-:!/]+)"],
|
|
["(\\.[\\w\\-.]+)[\\n\\=\\{\\s]", "([\\w\\-]+)"],
|
|
["[a-z]+-class(?:es)?=\"([^'\"]*)\""]
|
|
]
|
|
}
|
|
}
|
|
```
|
|
|
|
### Where should you put new utility classes?
|
|
|
|
Utility classes are generated by [Tailwind CSS](https://tailwindcss.com/) which
|
|
supports most CSS features. If there is something that is not available we should
|
|
update [tailwind.defaults.js](https://gitlab.com/gitlab-org/gitlab-ui/-/blob/7c0fb4b07a0f0d0a58dd0137831412dbf53ea498/tailwind.defaults.js) in GitLab UI.
|
|
|
|
### When should you create component classes?
|
|
|
|
We recommend a "utility-first" approach.
|
|
|
|
1. Start with utility classes.
|
|
1. If composing utility classes into a component class removes code duplication and encapsulates a clear responsibility, do it.
|
|
|
|
This encourages an organic growth of component classes and prevents the creation of
|
|
one-off non-reusable classes. Also, the kind of classes that emerge from "utility-first"
|
|
tend to be design-centered (for example, `.button`, `.alert`, `.card`) rather than
|
|
domain-centered (for example, `.security-report-widget`, `.commit-header-icon`).
|
|
|
|
Inspiration:
|
|
|
|
- <https://tailwindcss.com/docs/utility-first>
|
|
- <https://tailwindcss.com/docs/extracting-components>
|
|
|
|
### Leveraging Tailwind CSS in HTML and in stylesheets
|
|
|
|
When writing component classes, it's important to effectively integrate Tailwind CSS's utility classes to
|
|
maintain consistency with the design system and keeping the CSS bundles small.
|
|
|
|
**Utility CSS Classes in HTML vs. in stylesheets:**
|
|
|
|
By using the utility classes directly in the HTML, we can keep the CSS file size smaller and adhere
|
|
to the utility-first philosophy. By avoiding to combine utility classes with custom styles in one components class
|
|
unless absolutely necessary, we can prevent confusion and potential conflicts.
|
|
|
|
- **Reasons for the Preference:**
|
|
- **Smaller CSS File Size:** Utilizing utility classes directly can lead to more compact CSS files and
|
|
promote a more consistent design system.
|
|
- **Clarity and Maintainability:** When utility classes are used in HTML, it's clearer how styles are
|
|
applied, reducing the risk of conflicts and regressions.
|
|
|
|
- **Potential Issues with Combining Styles:**
|
|
- **Conflicts:** If utility classes and custom styles are combined in a single class, conflicts can arise,
|
|
especially when the styles have interdependencies.
|
|
- **Regressions:** It becomes less obvious how styles should resolve, leading to possible regressions
|
|
or unexpected behavior.
|
|
|
|
By following these guidelines, we can create clean, maintainable stylesheets that leverage Tailwind CSS effectively.
|
|
|
|
#### 1. Use utility classes directly in HTML (preferred approach)
|
|
|
|
For better maintainability and to adhere to the utility-first principle, add utility classes directly
|
|
to the HTML element. A component class should primarily contain only the non-utility CSS styles.
|
|
In the following example, you add the utility classes `gl-fixed` and `gl-inset-x-0`, instead of adding
|
|
`position: fixed; right: 0; left: 0;` to the SCSS file:
|
|
|
|
```html
|
|
<!-- Bad -->
|
|
<div class="my-class"></div>
|
|
|
|
<style>
|
|
.my-class {
|
|
top: $header-height;
|
|
min-height: $comparison-empty-state-height;
|
|
position: fixed;
|
|
left: 0px;
|
|
right: 0px;
|
|
}
|
|
</style>
|
|
|
|
<!-- Good -->
|
|
<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. Apply utility classes in component classes (when necessary)
|
|
|
|
Sometime it might not feasible to use utility classes directly in HTML and you need to include them in our
|
|
custom SCSS files. Then, you might want to inherit style definitions from the design system without needing to figure
|
|
out the relevant properties or values. To simplify this process, you can use Tailwind CSS's
|
|
[`@apply` directive](https://tailwindcss.com/docs/reusing-styles#extracting-classes-with-apply)
|
|
to include utilities' style definitions in your custom styles.
|
|
|
|
Using `@apply` is _encouraged_ for applying CSS properties that depend on the design system (e.g. `margin`, `padding`).
|
|
For CSS properties that are unit-less (e.g `display: flex`) it is okay to use CSS properties directly.
|
|
|
|
```scss
|
|
// Bad
|
|
.my-class {
|
|
margin-top: 0.5rem;
|
|
}
|
|
|
|
// Okay
|
|
.my-class {
|
|
display: flex;
|
|
}
|
|
|
|
// Good
|
|
.my-class {
|
|
@apply gl-mt-5 gl-flex;
|
|
}
|
|
```
|
|
|
|
The preferred way to use `@apply` is to combine multiple CSS classes in a single line or at most two,
|
|
like in the example above. This approach keeps the CSS concise and easy to read:
|
|
|
|
```css
|
|
// Good
|
|
.my-class {
|
|
@apply gl-mt-5 gl-flex gl-items-center;
|
|
}
|
|
```
|
|
|
|
Avoid splitting classes across multiple lines, as shown below.
|
|
|
|
```css
|
|
// Avoid
|
|
@apply gl-mt-5;
|
|
@apply gl-flex;
|
|
@apply gl-items-center;
|
|
```
|
|
|
|
The reason for this is that IDE extensions might only be able to detect conflicts when
|
|
the CSS Classes are in one line:
|
|
|
|
```css
|
|
// ✅ Conflict detected: 'gl-bg-black' applies the same CSS properties as 'gl-bg-white'.(cssConflict)
|
|
@apply gl-bg-white gl-bg-black;
|
|
|
|
// ❌ No conflict detected
|
|
@apply gl-bg-white;
|
|
@apply gl-bg-black;
|
|
```
|
|
|
|
The exception to this rule is when working with `!important`. Since `!important` applies to
|
|
the entire line, each class that requires it should be applied on its own line. For instance:
|
|
|
|
```css
|
|
@apply gl-flex gl-items-center;
|
|
@apply gl-mt-5 #{!important};
|
|
```
|
|
|
|
This ensures that `!important` applies only where intended without affecting other classes in the same line.
|
|
|
|
## Responsive design
|
|
|
|
Our UI should work well on mobile and desktop. To accomplish this we use [CSS media queries](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_media_queries/Using_media_queries). In general we should take a mobile first approach to media queries. This means writing CSS for mobile, then using min-width media queries to override styles on desktop. A exception to this rule is setting the display mode on child components. For example when hiding `GlButton` on mobile we don't want to override the display mode set by our component CSS so we should use a max-width media query such as `max-lg:gl-hidden`.
|
|
|
|
### Tailwind CSS classes
|
|
|
|
```html
|
|
<!-- Bad -->
|
|
<div class="gl-mt-5 max-lg:gl-mt-3"></div>
|
|
|
|
<!-- Good -->
|
|
<div class="gl-mt-3 md:gl-mt-5"></div>
|
|
|
|
<!-- Bad -->
|
|
<div class="gl-mt-3 sm:max-lg:gl-mt-5"></div>
|
|
|
|
<!-- Good -->
|
|
<div class="gl-mt-3 sm:gl-mt-5 lg:gl-mt-3"></div>
|
|
|
|
<!-- Bad -->
|
|
<!-- Changing the display mode of child components can cause visual regressions. -->
|
|
<gl-button class="gl-hidden lg:gl-flex">Edit</gl-button>
|
|
|
|
<!-- Good -->
|
|
<gl-button class="max-lg:gl-hidden">Edit</gl-button>
|
|
```
|
|
|
|
### Component classes
|
|
|
|
```scss
|
|
// Bad
|
|
.class-name {
|
|
@apply gl-mt-5 max-lg:gl-mt-3;
|
|
}
|
|
|
|
// Good
|
|
.class-name {
|
|
@apply gl-mt-3 lg:gl-mt-5;
|
|
}
|
|
|
|
// Bad
|
|
.class-name {
|
|
display: block;
|
|
|
|
@include media-breakpoint-down(lg) {
|
|
display: flex;
|
|
}
|
|
}
|
|
|
|
// Good
|
|
.class-name {
|
|
display: flex;
|
|
|
|
@include media-breakpoint-up(lg) {
|
|
display: block;
|
|
}
|
|
}
|
|
```
|
|
|
|
## Naming
|
|
|
|
Filenames should use `snake_case`.
|
|
|
|
CSS classes should use the `lowercase-hyphenated` format rather than
|
|
`snake_case` or `camelCase`.
|
|
|
|
```scss
|
|
// Bad
|
|
.class_name {
|
|
color: #fff;
|
|
}
|
|
|
|
// Bad
|
|
.className {
|
|
color: #fff;
|
|
}
|
|
|
|
// Good
|
|
.class-name {
|
|
color: #fff;
|
|
}
|
|
```
|
|
|
|
Avoid making compound class names with SCSS `&` features. It makes
|
|
searching for usages harder, and provides limited benefit.
|
|
|
|
```scss
|
|
// Bad
|
|
.class {
|
|
&-name {
|
|
color: orange;
|
|
}
|
|
}
|
|
|
|
// Good
|
|
.class-name {
|
|
color: #fff;
|
|
}
|
|
```
|
|
|
|
Class names should be used instead of tag name selectors.
|
|
Using tag name selectors is discouraged because they can affect
|
|
unintended elements in the hierarchy.
|
|
|
|
```scss
|
|
// Bad
|
|
ul {
|
|
color: #fff;
|
|
}
|
|
|
|
// Good
|
|
.class-name {
|
|
color: #fff;
|
|
}
|
|
|
|
// Best
|
|
// prefer an existing utility class over adding existing styles
|
|
```
|
|
|
|
Class names are also preferable to IDs. Rules that use IDs
|
|
are not-reusable, as there can only be one affected element on
|
|
the page.
|
|
|
|
```scss
|
|
// Bad
|
|
#my-element {
|
|
padding: 0;
|
|
}
|
|
|
|
// Good
|
|
.my-element {
|
|
padding: 0;
|
|
}
|
|
```
|
|
|
|
## Nesting
|
|
|
|
Avoid unnecessary nesting. The extra specificity of a wrapper component
|
|
makes things harder to override.
|
|
|
|
```scss
|
|
// Bad
|
|
.component-container {
|
|
.component-header {
|
|
/* ... */
|
|
}
|
|
|
|
.component-body {
|
|
/* ... */
|
|
}
|
|
}
|
|
|
|
// Good
|
|
.component-container {
|
|
/* ... */
|
|
}
|
|
|
|
.component-header {
|
|
/* ... */
|
|
}
|
|
|
|
.component-body {
|
|
/* ... */
|
|
}
|
|
```
|
|
|
|
## Selectors with a `js-` Prefix
|
|
|
|
Do not use any selector prefixed with `js-` for styling purposes. These
|
|
selectors are intended for use only with JavaScript to allow for removal or
|
|
renaming without breaking styling.
|
|
|
|
## Selectors with Util CSS Classes
|
|
|
|
Do not use utility CSS classes as selectors in your stylesheets. These classes
|
|
are likely to change, requiring updates to the selectors and making the
|
|
implementation harder to maintain. Instead, use another existing CSS class or
|
|
add a new custom CSS class for styling elements. This approach improves
|
|
maintainability and reduces the risk of bugs.
|
|
|
|
```scss
|
|
// ❌ Bad
|
|
.gl-mb-5 {
|
|
/* ... */
|
|
}
|
|
|
|
// ✅ Good
|
|
.component-header {
|
|
/* ... */
|
|
}
|
|
```
|
|
|
|
## Selectors with ARIA attributes
|
|
|
|
Do not use any attribute selector with ARIA for styling purposes. These
|
|
attributes and roles are intended for supporting assistive technology.
|
|
The structure of the components annotated with ARIA might change
|
|
and so its styling. We need to be able to move these roles and attributes
|
|
to different elements, without breaking styling.
|
|
|
|
```scss
|
|
// Bad
|
|
&[aria-expanded=false] &-header {
|
|
border-bottom: 0;
|
|
}
|
|
|
|
// Good
|
|
&.is-collapsed &-header {
|
|
border-bottom: 0;
|
|
}
|
|
```
|
|
|
|
## Using `extend` at-rule
|
|
|
|
Usage of the `extend` at-rule is prohibited due to
|
|
[memory leaks](https://gitlab.com/gitlab-org/gitlab/-/issues/323021) and
|
|
[the rule doesn't work as it should](https://sass-lang.com/documentation/breaking-changes/extend-compound/).
|
|
|
|
## Linting
|
|
|
|
We use [stylelint](https://stylelint.io) to check for style guide conformity. It uses the
|
|
ruleset in `.stylelintrc` and rules from
|
|
[our SCSS configuration](https://gitlab.com/gitlab-org/frontend/gitlab-stylelint-config).
|
|
`.stylelintrc` is located in the home directory of the project.
|
|
|
|
To check if any warnings are produced by your changes, run `yarn lint:stylelint`
|
|
in the GitLab directory. Stylelint also runs in GitLab CI/CD to
|
|
catch any warnings.
|
|
|
|
If the Rake task is throwing warnings you don't understand, SCSS Lint's
|
|
documentation includes [a full list of their rules](https://stylelint.io/user-guide/rules/).
|