shortcut: CC/LESS

Manual:Coding conventions/CSS

From mediawiki.org
Other languages:
See Manual:CSS for additional caveats and tips that aren't mentioned here.

This page describes coding conventions for CSS and LESS stylesheets in the MediaWiki codebase.

Linting[edit source]

We use stylelint as our code quality tool, with a custom config for Wikimedia (stylelint-config-wikimedia). You can use the node module grunt-stylelint to lint your CSS or LESS; MediaWiki and most extensions run it as part of continuous integration. The settings for MediaWiki core can be found in .stylelintrc.json.

Naming[edit source]

See Manual:Interface/IDs and classes for the documentation of common IDs and classes.

Name classes the same way: all lowercase and words broken up by dashes. Use the mw- prefix to avoid conflicts with user-generated class names and IDs from wikitext templates, section headings, gadgets, and other software libraries. Where possible, avoid introducing IDs, and use CSS classes instead.

Some examples:

/* Site-wide elements */
.mw-body,
.mw-headline,
.mw-label,
.mw-input {
}

/* Special pages */
.mw-body-content,
/* - Special:AllPages */
.mw-allpages-table-form,
.mw-allpages-nav {
}

Note that words are broken up by dashes to simplify code readability for non-native English speakers and to provide a better experience to programmers relying on assistive technology, for example screenreader output. .mw-allpages-table-form is a historic class name dating back before above advice. Accordingly it would be .mw-all-pages-table-form.

Constructed class names[edit source]

When constructing class names dynamically in the code, ensure a comment is placed above that lists all the possible class names. See also Manual:Coding conventions#Dynamic identifiers. For example:

// The following classes are used here:
// * mw-editfont-monospace
// * mw-editfont-sans-serif
// * mw-editfont-serif
$class = 'mw-editfont-' . $user->getOption( 'editfont' );

Specificity[edit source]

Provide selectors with as low specificity as possible, preferably a single module class selector. This simplifies overriding them in specific contexts, like certain extensions or in users scripts.

Right (low specificity): Wrong (overspecific, HTML element bound):
.mw-bar {
	border-radius: 2px;
}

.mw-foo {
	background-color: #fff;
}
div#mw-bar-zero {
	border-radius: 2px;
}

div#mw-foo-one {
	background-color: #fff;
}


Whitespace[edit source]

We love whitespace:

  • One selector per line.
  • One property declaration per line.
  • Opening braces for the CSS declaration block on the same line as the (last) selector.
  • Indent each property declaration with a tab.
  • No space before the colon (:).
  • One space after the colon and before the value.
  • One space after commas (, ) in multi-value properties.
  • One space after a starting and before an ending parentheses (( and )) in selectors (ex. :not()) and properties (ex. rgba()).
  • A semi-colon (;) after each declaration (including the last one).
  • Closing braces unindented back to the left.
  • Annotation for CSSJanus and CSSMin should be on their own line, above the CSS declaration they're for.
  • An empty line between one CSS rule set and the next.
.mw-selector,
.mw-some-element input[ type='text' ] {
	background-color: #fff;
	color: #202122;
	float: right;
	font-family: sans-serif;
	text-align: left;
	transform: scale( 1.2 );
}

/* Example that uses CSSJanus and CSSMin annotations */
.mw-look-at-the-left {
	/* @embed */
	background-image: url( images/foobar.svg );
	/* @noflip */
	float: left;
}

Quotes[edit source]

Wikimedia uses single quotes for values that require quotation. Such as the values of the content CSS property, and in attribute selectors.

Quotes are unnecessary in the url() parameter of a background-image declaration. The only case where this could cause problems is when an unescaped closing parenthesis occurs in the path; instead you should URL-escape such characters.

Color property values[edit source]

CSS3 supports many different kinds of color values for CSS properties like background-color, color, border-color, et al. For consistency and compatibility, only use these three:

  • Hex color values like #fff and #f8f9fa. (Use lowercase for better gzip compression[1]! Use shorthand notation when possible.)
  • rgba() values if an alpha transparency is required like rgba( 255, 255, 255, 0.5 ). (Attention: IE8 doesn't support rgba() notation, so always provide a matching declaration before this one with a hex color value as fallback.)
  • transparent color keyword. (Attention: IE8 supports it only for background-color and border. It draws color: transparent; as black.[2])

Prefer all values in lowercase for consistency and optimized file compression.[3]. See also Talk page section.

Avoid other color values (including color names/keywords – think i18n while picturing burlywood, rgb(), hsl(), and hsla() notations). Also make sure that your color contrast ratio of foreground and background (incl. background gradients, and fallback colors) complies to WCAG 2.0 Level AA, ideally Level AAA[4].

Read further at MDN Web Docs.

Sizing units[edit source]

Wikimedia Foundation aims to support a diverse audience and a wide range of technologies and use cases. A very popular accessibility user setting is increasing browser default font size while providing the best design for average users. It also reduces burden of em calculation and predictability on developers to the absolute minimum. Use px for all elements, that are not directly affecting reading or understanding user-interface. Mostly font-size, line-height, icon sizing by background-size or padding.

Variable naming[edit source]

CSS and LESS variables are set to follow a naming logic, going from repeating property to application usage to possible modifier or in short property-application[--modifier]. Variables are generally used for one property, like font-size, there are very few variables that are able to be used in different contexts. For example size variables @size-small.

It's simpler, specifically in big projects with many CSS/LESS files to have single usage variables, even though that might result in a larger variables definition file. See archived discussion for problem description. It's also more fail-safe when the variable contents get changed (think of color versus background-color applications).

CSS variables example:

--background-color-error: #fff36f;
--color-error: #d33;

/* Application */
.mw-error {
	background-color: var( --background-color-error );
	color: var( --color-error );
}

LESS variables example:

/* Background Colors & Colors */
@background-color-base: #fff;
@background-color-error: #fff36f;
@color-base: #000;
@color-error: #d33;
@color-progressive: #36c;

// Positioning
@position-selector: absolute;
@position-offset-selector: -4px;

/* Sizes & Box Model */
@size-selector: 42rem; // Use for both width and height property values.
@border-color-error: @color-error;
@border-size-base: 1px;
@border-style-base: solid;
@border-error: @border-size-base @border-style-base @border-color-error;

/* Fonts */
@font-family-serif: 'Linux Libertine', Georgia, Times, serif;
@font-family-sans: sans-serif;

/* Transitions */
@transition-base: 200ms;

/* Application */
.mw-selector {
	background-image: url( selector.svg );
	background-repeat: no-repeat;
	color: @color-error;
	display: block;
	position: absolute;
	top: 0;
	left: 0;
	width: @size-selector;
	height: @size-selector;
	font-family: @font-family-sans;
}

Vendor prefixes[edit source]

Always put the standardized versions of CSS properties after vendor-prefixed versions. It is important for avoiding bugs in old implementation, like in the following example of -webkit-border-radius. See also https://css-tricks.com/ordering-css3-properties/.

Right: Wrong in several ways:
.bar {
	-webkit-border-radius: 30px 10px;
	border-radius: 30px 10px;
}
.foo {
	background-color: #444;
	background-image: -webkit-gradient( linear, left top, left bottom, from( #444 ), to( #999 ) );
	background-image: -webkit-linear-gradient( top, #444, #999 );
	background-image: -moz-linear-gradient( top, #444, #999 );
	background-image: linear-gradient( to bottom, #444, #999 );
}
.bar {
	border-radius: 30px 10px;
	-webkit-border-radius: 30px 10px;
}

.foo {
	background-image: linear-gradient(top, #444444, #999999);
	background-image: -moz-linear-gradient(top, #444444, #999999);
	background-image: -webkit-linear-gradient(top, #444444, #999999);
	background-image: -webkit-gradient(linear, left top, left bottom, from(#444444), to(#999999));
}

Right, and including annotation:

/* Annotated version */
.foo {
	/* Fallback color in case background-image gradient is not supported */
	background-color: #444;
	/* Safari 4, Chrome 2, iOS 2 */
	background-image: -webkit-gradient( linear, left top, left bottom, from( #444 ), to( #999 ) );
	/* Safari 5.1+, Chrome 10+, iOS 5 */
	background-image: -webkit-linear-gradient( top, #444, #999 );
	/* Firefox 3.6 - 15 */
	background-image: -moz-linear-gradient( top, #444, #999 );
	/* Standard syntax supported by Firefox 16+, Opera 12.5+, IE10+ */
	background-image: linear-gradient( to bottom, #444, #999 );
}

.client-js and .client-nojs[edit source]

MediaWiki outputs class client-nojs on the ‎<html> element on every page. At runtime, JavaScript code replaces this with class client-js. Hence you can use this class in your selector to conditionally show, hide, or customize certain elements depending on whether the browser has JavaScript enabled and is supported by ResourceLoader. Note that for this to be useful, the stylesheet in question must be loaded with OutputPage::addModuleStyles(), not mw.loader (see Developing with ResourceLoader)

Anti-patterns[edit source]

Anti-patterns are mostly covered by use of central 'stylelint-config-wikimedia' already. Please also see the inline comments on master. Beyond there are some special properties/functionalities that deserve further explanation.

Don't rely on px unit based values[edit source]

This is not a general rule, but it is general for a few properties. When a user increases their default font size preferences in a browser due to addressing visual impairments, a widely used accessibility feature, all sizes defined in px are not scaled. In order to let users scale text with this setting, rely on relative sizes like em or rem, specifically on properties that would result in non-scalable text like font-size or line-height or would overflow and be hidden, often with certain width or height limitations.

z-index[edit source]

Avoid using z-index when possible. Instead, try to use the natural stacking order in the DOM. Known exceptions include:

!important[edit source]

Avoid using !important (except for working around upstream code running on the same page that also uses !important, because only !important can override !important).

In most cases you don't need it at all. In other cases it may be the result of a bug elsewhere in the program. In general, to override a rule you use the same selector as the original style rule. Since CSS cascades, this works naturally (styles applied later override styles applied earlier, selectors don't need to be of higher specificity[5]).

If the overriding styles apply before the original styles, the styles got loaded in the wrong order. That should be addressed, but you may resort to workarounds to artificially increase the specificity:

  • Repeat the same selector to increase weight, like .foo.foo.[6]
  • Add or repeat attribute selectors, like [class].
  • Use default elements as ancestor selector (e.g. body .foo, html body .foo).

Add however many points you need. It will still allow multiple stylesheets to use the same technique and each express their specificity. Better than adding in ancestors classes not related to your code. (And more maintainable as they won't change.)

LESS[edit source]

Starting with MediaWiki 1.22, there is native support in ResourceLoader for using LESS (with file extension .less) in place of CSS. Most of the LESS syntax can be formatted using the CSS conventions:

  • Indent nested blocks with 1 tab (same as for indenting declarations inside CSS rules).
  • Don't space-align declarations values inside mixins (same as for CSS rules).
  • No spaces on the outside of the parameter lists in function invocations, mixin uses and mixin definitions (same as for url( image.png ) in CSS).
  • No quotes around parameter values (same as for url( image.png ) in CSS).

Example:

/*
 * You do not need to copy '.background-image' into your code.
 * It is provided by MediaWiki core (in mediawiki.less).
 * It is here as an example of guard syntax.
 */
.background-image( @url ) when ( embeddable( @url ) ) {
	background-image: embed( @url );
	background-image: url( @url )!ie;
}

.background-image( @url ) when not ( embeddable( @url ) ) {
	background-image: url( @url );
}

.mw-example {
	.background-image( images/example.png );
	border: 1px solid #a2a9b1;
	padding: 4px 8px;
	font-size: 1em;

	.mw-example-thing {
		display: inline-block;
		/* @noflip */
		float: left;
		border: 1px solid #c8ccd1;
		border-radius: 2px;
		padding: 4px 8px;
	}
}

There's a few new concepts that don't map to CSS conventions, outlined below.

Structure[edit source]

  • Separate nested CSS rules from the parent declarations by 1 empty line.
  • @noflip tags must be on the line immediately above the declaration, as shown in the example above.

Import of LESS/CSS files[edit source]

  • The filename of an import statement has to include the .less file extension.
    • If the extension was omitted, in a folder with 'foo.css' and 'foo.less' LESS would import the latter.
    • If the code is ever used outside MediaWiki context, for example in Webpack as part of storybook, it will throw an error as it will assume it is a package with an index.css or index.js file meaning the code cannot be used.
  • Use @import to load mixins and variables so that they may be used by the current LESS stylesheet; these are processed synchronously by LESS and will not be present in the generated CSS output. Load mixins first, variables second. Mixins should be free of variable values and parameters in mixin calls provide values with variables.
  • Don't use @import to bundle stylesheets that are related to one another only conceptually; instead, reference the set of files in the styles array of a ResourceLoader module.
  • Don't use @import to import CSS files in LESS files, as the LESS parser will create an invalid import statement based on the physical location of the CSS file. Use @import (inline) instead.

Troubleshooting import[edit source]

If your LESS @import doesn't work please check:

Mixins[edit source]

Mixin names should use hyphen-case, just like CSS class names, property keys and variable names.

They should be prefixed with mixin- to avoid confusing developers who are familiar with CSS, but not LESS and distinguish them from classes, the syntax for which is similar.

As mentioned above, no spaces on the outside of the parameter list and avoid quoting values.

If you need to call a mixin with one or more arguments that contain a comma use a semicolon ; in LESS to separate the arguments. This allows you to use commas in the literal value.

.mixin-example( @function, @properties ) {
	transition-timing-function: @function;
	transition-property: @property;
}

Built-in mixins[edit source]

mediawiki.mixins.less is a LESS library maintained as part of MediaWiki that is automatically available to any LESS stylesheet in MediaWiki. It can be imported from any LESS stylesheet in core, skins, and extensions.

@import 'mediawiki.mixins.less';

.my-create-link {
    .background-image-svg( 'images/create_normal.svg', 'images/create_normal.png' );
    /* As '.background-image-svg' is taking two parameters, they have to be enclosed in quotes */
}

Since MediaWiki 1.27, the built-in mixins contain utilities for using CSS Flexbox. Support is present all browsers that implemented at least one of the specs, including Internet Explorer 10. You need to specify your own fallback for older browsers, e.g. you can use floats for a more basic experience. Mixins available:

// Provided by 'mediwiki.mixins':

.flex-display( @display: flex );

.flex-wrap( @wrap: wrap );

.flex( @grow: 1, @shrink: 1, @width: auto, @order: 1 );

Example usage:

@import 'mediawiki.mixins.less';

.my-container {
	.flex-display();
}
.my-left {
	.flex( 0, 0, auto, 1 );
}
.my-right {
	.flex( 0, 0, auto, 3 );
}
.my-center {
	.flex( 1, 1, auto, 2 );
}
Applied correctly: Applied incorrectly:
.mw-example {
	.mixin-example( ease-in-out; opacity, color );

	/* Expands to: */
	transition-timing-function: ease-in-out;
	transition-property: opacity, color;
}
.mw-example {
	.mixin-example( 'ease-in-out', 'opacity, color' );

	/* Expands to: */
	transition-timing-function: 'ease-in-out';
	transition-property: 'opacity, color';

	/* Values include the quotes, this is invalid CSS and results in the browser ignoring these properties */
}

/* Another bad example: */
.mw-example {
	.mixin-example( ~'ease-in-out', ~'opacity, color' );

	/* Expands to: */
	transition-timing-function: ease-in-out;
	transition-property: opacity, color;

	/* The '~' operator instructs Less to unquote the values.
	 * This produces good CSS but we avoid this pattern in favour of using ';' consistently.
	 */
}


References[edit source]