CSS / Sass styleguides

Guidelines for writing Sass code at sgalinski Internet Services

Formal Rules

  • All comments / namings are in english language.
  • Indentations are done with the tab character.
  • The first character within a comment must be a space.
  • Use one new line to separate selectors, two new lines to separate logical blocks.
  • Nested selectors start with a blank line.
  • Every project is based on normalize.scss, which will be installed via NPM and imported at the very beginning of main.scss.
  • Third-party stylesheets get also imported in main.scss.
  • There will be no need to reference an additional stylesheet in the template, or via TypoScript.
.classname {
	somestyle: directive;

	&:before {
		// ...
	}
}

Basic File Structure

We’ve got the full power of Sass imports, so we don’t need to produce many .css files, while we still can split our code into multiple .scss files. This means, our Sass code should be as modular as possible and will likely be broken down into many different files, but we will ship very few .css files. Ideally, we only have one .css file that contains all styles that are used on almost the entire page (layout, basic components, etc). If the website application consists of very different sections (e.g. a user area that differs a lot from the rest of the website, or simply adds a lot more styles), it might be sensible to deliver a second stylesheet for that area only. This allows us to make proper use of browser caching.

Directory / File Purpose
Sass  
+ main.scss This file only imports other .scss files.
+ base  
   + _base.scss contains the element selector styles.
   + _colors.scss contains all color definitions that are used in the project.
   + _typography.scss contains all font-families, and basic typographic styles in the project.
   + _layout.scss contains only layout related styles, like grids, page-wrapper, etc.
   + _helper.scss contains helper mixins and utilities, like clearfix.
   + _settings.scss contains project specific variables, like transitions, width, etc.
+ layouts contains all layout specific styles
+ templates contains all template specific styles
+ components contains all component styles
   + plugins contains all plugin specific styles/overrides.
      + _df-contentslide.scss  
      + _sg-news.scss  
   + _plugins.scss contains only imports of all files underneath ./plugins
   + _navigation.scss  
   + _search.scss  
   + _button.scss  
   + _alert.scss  
   + _modal.scss  
   +    . . .  

If component styles get too complex, they can be moved to their own subfolder and be splitted into several files. See plugin styles above for an example.

Basic Styling

As we do not want to clutter the global namespace, almost every style on the website derives from a component style. This means most styles will be applied through class names. To offer some basic styling, the _base.scss file does contain all element selectors that provide a common ground to start from. This is more or less a theme specific extension of what normalize does. It will set things like the main font-family, h1 — h6 styles and things like that.

That way, you can always throw in something like a bare form, without giving it a specific component style. Things that are not themed through a component will not look special, but not out of place, either.

Class Names

  • Do not concatenate words in selectors by any characters other than hyphens, in order to improve understanding and scannability. class names are written in lowercase.
  • Do not use incomprehensible abbreviations just to save characters.
  • A class name describes a specific version of a component. Modifiers are part of the class name; there are no modifier classes. This leads to more code, but gzip resolves that issue. The Sass extends feature hasn’t any benefits in this case.
  • The class name gets more specific from left to right.
  • Code gets shared via mixins.
  • Classes that are applied for usage within JavaScript are prefixed with .js-. This makes refactoring and scanning code much easier. Therefore, .js-classes will never appear inside your Sass files, because they do not apply any styles. This only applies for classes that are used as a status information in JavaScript.

Correct

.loading-spinner {}


.modal-small
.button-danger
.color-link-dark 

@mixin button () {}

.button-danger {
	@include button;
}

Wrong

.loadingSpinner {}
.loading_spinner {}
.loadingspinner {}
.mdl-sml
.button.danger
.dark-link-color

Layering

  • Use sane values for z-index.
  • Try to use low numbers, do not just throw in 99999999, unless you are really sure there will never be an element above yours.
  • Comment every usage of z-index, so you’ll know when changing those values can be done safely.
.slider-header-hero-unit {
	z-index: 10; // push slider above main layer
}

Distances

  • All block-level elements get an initial margin applied as a base rule (e.g. inside the base.css).
  • Vertical margins are changed only via the top-margin. Try to stick to this rule, as it will avoid issues with margin-collapse. Document when you have to break this rule.

File Names

  • File names are written in upper camel case.

Sets

A set is a collection of components that fit visually together. Components shall only be used through their implementation of a set. A set provides the class names that will be used in the actual markup. Those class names are composed of the set's prefix and the component class name.

// default inline form with customized controls
.default-form-inline-custom-controls {
    @include form-inline-custom-controls;

    background-color: $default-color-brand-2;
}

Sets can also extend other sets. This can be useful if you need to namespace e.g. the styles of a certain area, or a custom plugin, that shares the look and feel of the default set, but adds tweaks and enhancements that are not needed anywhere else. An example could be a plugin that uses the default button set, but adds certain icons to them and overwrites the width, to make them fit to its special template. Sets can extend other sets, or introduce their own variant of a component at each level of the templating model. A template might introduce its own variant of a call-to-action button, or a page might have to tweak some distances to achieve a unique type of layout that is unlikely to be needed elsewhere.

.sg-project-calculator-button-checkout {
    @extends .default-button-primary;
    background-color: $default-color-brand-2;
    width: 100%;
}

Components

  • Organize your code by components, not by pages. Pages can be rearranged and components can be moved.
  • A component can have multiple variants (e.g. button, button-large, button-danger, etc.)
  • A variant of a component gets its own class name, code can be shared via mixins.
// A primary Call-to-Action button that is only used inside of a hero unit
@mixin button-primary-cta-hero-unit () {

}

To keep the contents of the components folder organized to a certain extend, here are a couple of subfolders you will find in every project:

Plugins

The plugins folder contains styles for all third-party components. Those might be TYPO3 extensions, as well as NPM packages.

Partials

Components that don't fit elsewhere.

Selectors

  • Do not couple CSS to specific markup, as this will break eventually.
  • Use an additional class name if you need more abstraction.
  • Element selectors should only be used to apply basic styling / resets (like normalize.css does).
  • Also, avoid using element selectors to increase specificity. This couples CSS to specific markup as well and will eventually result in excessive use of !important.
  • IDs are for use in JavaScript only.
  • If a rule applies to multiple selectors, each selector gets its own line

Correct

.slide-body {     
	.slide-arrow {
		// ...    
	}             
}                 



h1,               
h2,               
h3 {              
	// ...
}

Wrong

.slide-body {
	div {
		// ...
	}
}

div.slide-body {}

h1, h2, h3 {
	// ...
}

Nesting

  • Do not couple CSS to specific markup, as this will break eventually.
  • Use an additional class name if you need more abstraction.
  • Element selectors should only be used to apply basic styling / resets (like normalize.css does).
  • Also, avoid using element selectors to increase specificity. This couples CSS to specific markup as well and will eventually result in excessive use of !important.
  • IDs are for use in JavaScript only.
  • If a rule applies to multiple selectors, each selector gets its own line.
.navigation ul li a {}

// can be refactored to

.navigation a {}

Order

  • Rules that change often are defined first (margin, padding).

Typography

  • Typography must be very consistent throughout a project.
  • You usually only have a couple of different font-sizes and line-heights. Use Presets!
  • font-size and line-height are only used with variables.
  • A base-font-size and base-line-height are provided in the initial setup.
  • Use fixed values if they are required at the specific location! This prevents side-effects! Example: An horizontal main menu. Document it!
  • line-height is always a unitless number.

Shorthands

  • Using shorthands makes sense when you are first defining an element, or if you have to override all values that are affected by the shorthand notation.
  • If you modify a component and just have to override single properties, only overwrite those you need to alter.
@mixin button() {
	margin: 20px 5px 8px;
}

.button-large {
	@include button;
	margin-bottom: 10px; // keep the inherited values and only modify what you need to
}

Colors

  • Colors are only used with variables.
  • This makes project-wide changes a lot more easy.
  • Use fixed values if they are required at the specific location! This prevents side-effects! Document it!
  • Tip: a good approach can be to collect all colors in specific variables during development and to break them down afterwards, until there is no repetition of Hexcodes
$color-ui-orange: #64781F;
$color-alert-danger-background: #FF0000;
$color-brand-1: #7881FF;

Comments

  • Comments always start with a space.
  • Comments are written in english language.
// right
//wrong

Vendor Prefixes

  • Don't use them
  • Keep your Sass code clean, Autoprefixer will handle this.

Inheritance

  • Mixins that are used for inheriting styles are included before any other style declaration.
  • This makes the inheritance clear.
  • This assures that variant specific styles are not overwritten by the parent.
.button-danger {
	@include button;
	background-color: $color-button-danger-background;
}

Modularity

  • Use modifiers where it makes sense.
  • Never put site specific styles into extensions!
  • Basic UI component styles (e.g. loading-spinners) belong to the project_base.
  • Every extension or library must be configured to make use of these base styles. Either via mixins or html classes.
  • This will keep the user experience consistent; only one source of truth.

Media Queries

  • Media Queries are implemented directly inside the component.
  • Only the component itself can know how it should be displayed at certain sizes (or on print).
  • The web evolved, there are tons of different screen sizes. Only three global breakpoints do not make sense anymore.
  • Media Queries are applied directly after the normal styles.
  • Think mobile-first: the smallest size is outside the media query, styles inside media queries build up to the largest size.
  • Media Queries are use via a breakpoint-min, breakpoint-max and breakpoint-between mixin.
.navigation-main {
	// ...

	@include breakpoint-min(560px) {
		// ...
	}

	@include breakpoint-between(300px, 1060px) {
		// ...
	}
}