Skip to content
Snippets Groups Projects
Commit 6e7ecb58 authored by quietone's avatar quietone
Browse files

convert to md

parent b89717be
No related branches found
No related tags found
No related merge requests found
Showing
with 3778 additions and 3 deletions
The Drupal coding standards apply to code within Drupal and its contributed modules. These standards are version-independent and "always-current".
The Drupal coding standards apply to code within Drupal and its contributed modules. These standards are version independent and "always-current".
All new code should follow the current standards, regardless of (core) version.
Existing code in older versions may be updated. For large code-bases (like Drupal core), updating the code of a previous version for the current standards may be too huge of a task.
Existing code in older versions may be updated. For large codebases (like Drupal core), updating the code of a previous version for the current standards may be too huge of a task.
Comments and names should use US English spelling.
Coding standard fixes are done by rule not individual files.
The video tutorial, Understanding the Drupal Coding Standards, explains the standards, why they are important, and how to use them.
The video tutorial, [Understanding the Drupal Coding Standards](https://drupalize.me/videos/understanding-drupal-coding-standards?p=2012), explains the standards, why they are important, and how to use them.
The Drupal coding standards for Accessibility.
# Composer package naming conventions
With Drupal adopting [Composer](https://getcomposer.org/) as dependency manager, the community has to follow a naming convention for composer package names to avoid conflicts.
How you define your own package with a composer.json can be found in [Add a composer.json file to define your module as a PHP package](https://www.drupal.org/node/2514612).
In general, Composer [only](https://getcomposer.org/doc/01-basic-usage.md#package-names) allows for package names like so: `vendor/project`. You cannot use more than two levels. This leads to the following conventions.
## Drupal Projects
Any project hosted on drupal.org is considered a _Drupal Project_. This contains Drupal core, modules, themes, profiles, …. Drupal already has an existing namespace for these based on the project URL `https://www.drupal.org/project/PROJECT`.
### Convention
Projects must use the package name:
```php
drupal/PROJECT
```
where `PROJECT` is the part from the URL.
### Examples
* _Drupal_ is a project: [https://www.drupal.org/project/drupal](https://www.drupal.org/project/drupal)`drupal/drupal`
* _ctools_ is a contrib project: [https://www.drupal.org/project/ctools](https://www.drupal.org/project/ctools)`drupal/ctools`
* _Views_ in Drupal 7 is a contrib project: [https://www.drupal.org/project/views](https://www.drupal.org/project/views)`drupal/views`
* _Core_ is a subtree of Drupal: [https://www.drupal.org/project/core](https://www.drupal.org/project/core)`drupal/core`
* _Datetime_ is a module within Drupal Core: [https://www.drupal.org/project/datetime](https://www.drupal.org/project/datetime)`drupal/datetime`
Some project URLs are not accessible or point to another project, as they are reserved names. In most cases those are sub-modules or sub-themes of existing projects, like Drupal core.
## Modules, Themes and Profiles
Multiple modules, themes and profiles might be part of a single Drupal project, for example `system, node, standard` in [Drupal](https://www.drupal.org/project/drupal) or `page_manager, views_content` in [ctools](https://www.drupal.org/project/ctools). As Drupal won't let you run two modules with the same name, modules, themes, profiles and Drupal projects share the same namespace.
### Convention
Sub-modules, -themes and profiles must use the package name.
```php
drupal/SUBPROJECT
```
where `SUBPROJECT` is the machine name of the module, theme or profile.
This will not conflict with `drupal/PROJECT`, as Project names share the same namespace with modules, themes and profiles due to the way Drupal works.
Over time, some projects may merge into or were split from another project. For example _Views_ was merged into _Drupal 8_ or _[Page Manager](https://www.drupal.org/project/page_manager)_ was separated from _ctools_ for a Drupal 8 release. Because of different version numbers, these cases can be resolved by utilizing [Composer's replace property](https://getcomposer.org/doc/04-schema.md#replace) and/or using different composer repositories.
In the case naming conflicts cannot be resolved, _Drupal Projects_ shall take precedence over (sub-)modules, themes and profiles.
For avoiding dependency issues, Drupal projects declaring their own composer.json, should also add their submodules and -themes to the `replace`\-section.
### Examples
* Module `devel_generate` from [Devel](https://www.drupal.org/project/devel)`drupal/devel_generate`
* Drupal 8 core's Views module can be specified as `drupal/views`, but the dependency will be resolved as a contrib replacement by `drupal/core`.
Based on this convention, meta-packages or subtree-splits could be provided for every module, theme and profile.
## Components
Drupal projects may also contain custom components (like PHP libraries). As those components are not bound to any namespace, they are likely to conflict with a given Drupal project, module, theme, etc..
### Convention
Package names must be prefixed with their parent's name and a dash (`-`), in the case it will use the `drupal/` vendor:
```php
drupal/PARENT-COMPONENT
```
where `PARENT` is the name of the parent package and `COMPONENT` a sufficient name for the component.
Since the `-` (dash) is **not** used in the existing drupal namespace, it _shouldn't_ conflict in anyway to what is currently in Drupal.
### Examples
* [Datetime](https://github.com/drupal/drupal/tree/8.0.x/core/lib/Drupal/Component/Datetime) becomes `drupal/core-datetime`
* [Diff](https://github.com/drupal/drupal/tree/8.0.x/core/lib/Drupal/Component/Diff) becomes `drupal/core-diff`
* This also allows contrib to expose components like so: `drupal/panels-renderer` or `drupal/ds-builder`
# CSS architecture (for Drupal 9)
Note: This document aims to apply emerging best-practices for CSS to Drupal 8/9. As we implement these ideas in Drupal 8, this document may need to be updated.
[Skip to best practices](#best-practices)
## Goals
The goals of good CSS should not be so different from those of good software engineering. Well-architected CSS, like PHP or JavaScript, should be:
### 1\. Predictable
CSS throughout Drupal core and contributed modules should be consistent and understandable. Changes should do what you would expect without side-effects.
### 2\. Reusable
> CSS rules should be abstract and decoupled enough that you can build new components quickly from existing parts without having to recode patterns and problems you’ve already solved. – _Philip Walton, [CSS Architecture](http://engineering.appfolio.com/2012/11/16/css-architecture/)_
### 3\. Maintainable
As new components and features are needed, it should be easy to add, modify and extend CSS without breaking (or refactoring) existing styles.
### 4\. Scalable
CSS should be easy to manage for a single developer or for large, distributed teams (like Drupal’s).
## The Component
Components are the discrete, purpose-built visual elements that make up the UI of a site or app. Components consist of HTML, CSS, and often – but not always – JavaScript. They are our navbars, dialogs, buttons and carousels. Components can be simple (such as icon containers and buttons) or complex enough to be themselves composed of other components.
## Common CSS Pitfalls
To better understand the best practices provided below, it can be helpful to review some common approaches that impede our goals of predictability, maintainability, reusability and scalability.
### Pitfall: Modifying components based on context
```php
/* Modifying a component when it’s in a sidebar. */
.sidebar .component {}
```
This may seem natural, but actually makes CSS less predictable and maintainable. Sooner or later you’re going to need that component style somewhere other than a sidebar! Or, the reverse may happen: a new developer places a component in the sidebar and gets an unexpectedly different appearance.
### Pitfall: Relying on HTML structure
Mirroring a markup structure in our CSS selectors makes the resulting styles easy to break (with markup changes) and hard to reuse (because it’s tied to very specific HTML). This pitfall comes in several forms:
* Overly complex selectors: `nav > ul > li > a`, `article p:first-child`
* Qualified selectors: `a.button`, `ul.nav`
### Pitfall: Overly generic class names
Similar to the pitfall of styling a component based on context, it’s common to ‘scope’ a component’s parts under the parent component using a descendant selector:
```php
.widget {}
.widget .title {}
.widget .content {}
```
This CSS might seem economical, but tends to be counterproductive: `.title` and `.content` are too generic. A stand-alone `.title` component created later will affect widget titles, likely without intending to.
### Pitfall: Making a rule do too much
Applying positioning, margins, padding, colors, borders and text styles all in a single rule overloads the rule, making it difficult or impossible to reuse if some parts (say, background, borders and padding) need to be applied to a similar component later on.
### Pitfall: Needing to undo styles
Creating style rules that undo other rules, like `.component-no-padding` makes CSS over-complex, hard to understand and maintain, and bloats the stylesheet. Needing such styles usually indicates that some existing rules are doing too much.
## Best Practices
### 1\. Avoid reliance on HTML structure
* CSS should define the appearance of an element anywhere and everywhere it appears.
* Use classes to assign appearance to markup. Never use id selectors in CSS.
* Keep selectors short. The best selector is a single class or element!
* Sometimes multi-part selectors are pragmatic. For example:
```php
/**
* Add a horizontal rule between adjacent list rows
*
* Could be part of an implementation of the Pears “slats” component:
* http://pea.rs/content/slats-html5
*/
.slat + .slat {
border-top: 1px solid #cccccc;
}
```
However, extra care should be taken when using multi-part selectors:
1. Avoid elements with no native semantics (div, span) in multi-part selectors.
2. Avoid the descendent selector (e.g. `.my-list li`) where possible, especially for components that may wrap other components. The descendant selector has a habit of unintentionally affecting nested elements. Prefer the child selector: `.my-list > li`.
3. Avoid more than 2 [combinators](http://reference.sitepoint.com/css/combinators) in a selector. The following rule is maxed out: `.my-list > li > a`.
4. If in doubt, add a class and style the element directly.
### using their own classes
To avoid relying on markup structure and overly-generic class names, define a component’s element explicitly, prefixing them with the component’s name followed by two underscores:
```php
.component {}
/* Component elements */
.component__header {}
.component__body {}
```
**Note** that there is no need to reflect DOM structure in the class name; for example, do not replace `.menu li a` with `.menu__item__link`. The class `.menu__link` should be sufficiently specific.
### 3\. Extend components using modifier classes
Create component variants explicitly, adding a suffix with the variant name preceded by two dashes. In order to keep the stylesheet DRY, this modifier class should only contain the styles needed to extend the original. This means that **both** base and modifier classes **must** appear together in the markup:
CSS
```php
/* Button component */
.button {
/* styles */
}
/* Button modifier class */
.button--primary {
/* modifications and additions */
}
```
HTML
```php
<!-- Button variant is created by applying both component and modifier classes -->
<button class="button button--primary">Save</button>
```
### 4\. Separate Concerns
Components should not be responsible for their positioning or layout within the site. Never apply widths or heights except to elements that natively have these properties (e.g. images have these properties, so it's okay to use CSS to modify their width and height). Within components, separate structural rules from stylistic rules.
Separate style from behavior by using dedicated classes for JavaScript manipulation rather than relying on classes already in use for CSS. This way, we can modify classes for style purposes without fear of breaking JS, and vice versa. To make the distinction clear, classes used for JavaScript manipulation should be prefixed with 'js-'. These JavaScript hooks must never be used for styling purposes. See the section ‘[Formatting Class Names](#formatting)’ for more information on naming conventions.
Avoid applying inline styles using JavaScript. If the behaviour is describing a state change, apply a class name describing the state (e.g. 'is-active'), and allow CSS to provide the appearance. Only use inline styles applied via JavaScript when the value of the style attributes must be computed at runtime.
Drupal **8/9** uses the [SMACSS](http://smacss.com/book/) system to conceptually categorize CSS rules. Note that some SMACSS nomenclature has been changed to avoid confusion with existing Drupal terminology.
1. #### [Base]
Base rules consist of styling for HTML elements only, such as used in a CSS reset or [Normalize.css](https://github.com/necolas/normalize.css/). Base rules should never include class selectors.
To avoid ‘undoing’ styles in components, base styles should reflect the simplest possible appearance of each element. For example, the simplest usage of the `ul` element may be completely unstyled, removing list markers and indents and relying on a component class for other applications.
2. #### [Layout]
Arrangement of elements on the page, including grid systems.
> Grid systems should be thought of as shelves. They contain content but are not content in themselves. You put up your shelves then fill them with your stuff \[i.e. components\]. – _Harry Roberts, [CSS Guidelines](https://cssguidelin.es/)_
3. #### [Component (SMACSS “module”)]
Reusable, discrete UI elements; components should form the bulk of Drupal’s CSS.
4. #### [State]
Styles that deal with transient changes to a component’s appearance. Often, these are client-side changes that occur as the user interacts with the page, such as hovering links or opening a modal dialog. In some cases, states are static for the life of the page and are set from the server, such as the active element in main navigation. The main ways to style state are:
* Custom classes, often but not always applied via JavaScript. These should be prefixed with `.is-`, e.g. `.is-transitioning`, `.is-open`;
* pseudo-classes, such as `:hover` and `:checked`;
* HTML attributes with state semantics, such as `details[open]`;
* media queries: styles that alter appearance based on the immediate browser environment.
5. #### [Theme]
Purely visual styling, such as border, box-shadow, colors and backgrounds, font properties, etc. Ideally, these should be separated enough from a component’s structure to be “swappable”, and omitting these entirely should not break the component’s functionality or basic usability.
### 5\. Name Components Using Design Semantics
While the HTML5 specification mentions that class names should “describe the nature of the content,” there’s no reason for this. HTML elements already impart semantics on the content and machines cannot derive content-level semantics from class names (with the narrow exception of [microformats](http://microformats.org/).)
> Class names should communicate useful information to developers. – _Nicolas Gallagher, [About HTML Semantics and Front-End Architecture](http://nicolasgallagher.com/about-html-semantics-front-end-architecture/)_
Class names used as CSS hooks should reflect _design_ semantics over content semantics. In general, they should reflect the intent and purpose of the design element they represent.
Note that this does not preclude presentational class names. Grid system classes such as `.grid-3`, utility classes such as `.leader` and `.trailer` (for adding whitespace based on a [baseline grid](http://alistapart.com/article/settingtypeontheweb)) and `.text-center` are all examples of presentational classes that represent visual semantics. They are meaningful to developers, and highly reusable.
This does not mean going back to classes like `.blue-box`. This is obviously a bad class name since it does not reflect the visual meaning, only one very surface attribute. It’s useful to ask “why is this a box, and why blue?”. Thinking about this, we might realize that this box is actually a version of the style used throughout our site for notifications, and this particular shade of blue is used for non-urgent notifications:
```php
.notification { /* general styles for all notifications */ } .notification--info { /* blue color adjustments */ }
```
#### Note for core developers
Since classes should represent design semantics and Drupal core must be design-agnostic, core default markup should be exceedingly cautious about what classes are included. This applies especially to the use of presentational class names.
#### Note for module developers
Since modules are responsible for providing the default HTML implementation, module developers should make their best effort to find an existing theme hook to use and to insert a design-derived class name, possibly one had already found in core. If the module’s content has no default design, the class name should be based on how the content is built; often this can just be the name of the module (e.g. `class="views"`.)
Module developers should ensure that themers can replace/augment any module-provided class.
### 6\. Formatting Class Names
Class names should use full words rather than abbreviations. Styles for a button component should use e.g. `class="button"` rather than `class="btn"`
Class names for components should always use a dash between words. Use `class="button-group"` rather than `class="buttongroup"`
The HTML class attribute has been made to do a lot. Drupal uses a naming convention to make clear what a particular class is for and what other parts of the system it affects:
```php
/* Component Rules */
.component-name
.component-name--variant
.component-name__sub-object
.component-name__sub-object--variant /* this configuration should be uncommon */
/* Layout Rules */
.layout-layout-method /* e.g. '.layout-container' */
.grid-*
/**
* State Classes
*
* These are always applied via JavaScript, and describe a non-default state.
*/
.is-state /* e.g. '.is-active' */
/**
* Functional JavaScript Hooks
*
* When querying or manipulating the DOM from JavaScript, prefer dedicated
* classes not used for styling (or the id attribute).
* If using classes, prefix them with 'js-' to mark them for JS use.
* These 'js-' classes should not appear in stylesheets.
*/
.js-behaviour-hook /* e.g. .js-slider, .js-dropdown */
```
### Closing Note: Specificity, ids and `!important`
Avoid using the id selector in CSS. There is no general benefit to using the 'id' attribute as a CSS hook, and it has serious downsides in the form of increased selector specificity.
Although it should be avoided in CSS, there are many excellent uses for the 'id' attribute:
* A performant JavaScript hook;
* An anchor within the document for links ([http://yoururl.com#anchor](http://yoururl.com#anchor));
* An anchor for associating labels with form elements or for associating DOM elements using ARIA attributes.
The `!important` flag should be used sparingly and appropriately, and in general should be restricted to themes. Since it overrides all external stylesheet rules, it is useful for states that must supersede all others, no matter the component variant. For example, error or disabled states are appropriate places to use `!important`, since they must be consistent and always apply when present. Never use `!important` to resolve specificity problems for general CSS rules. Additionally, Drupal core and contrib modules should avoid the `!important` flag, since all module CSS should be easy to override.
## Case Study
As an example and guide to developers, take the progress bar from the [proposed Seven style guide](http://groups.drupal.org/node/283223#Progress):
![Progress bar component and small variant](/files/10.progress.png)
This might be marked up and styled as follows, using the standards described above:
HTML
```php
<div class="progress">
<!--
.label is a separate component, used for form labels, but can be applied
to non-label tags, and can be extended with a modifier class.
-->
<label class="label">Installing Node Module</label>
<!-- progress element (sub-object) -->
<div class="progress__track">
<!--
Component sub-objects don’t need more than the component name
as a prefix; progress__track__bar is verbose and unnecessary.
Inline styles shown here would be applied by JavaScript using
the independent 'js-' class.
-->
<div class="progress__bar js-progress-percent" style="width: 63%"></div>
</div>
<div class="progress__description">
<!--
.layout-pull and .layout-push are LTR/RTL-agnostic utility classes for floating;
these should not appear in core default markup since they are not
design-independent.
-->
<div class="layout-pull">Installed 15 of 24 modules</div>
<strong class="layout-push">63%</strong>
</div>
<!--
Note that appearance is not tied to the tag; the appearance of the
<button> element would be reset in the base layer and styled as
needed by applying classes.
Since this element uses an icon, it could be refactored to use a generic
icon container component.
-->
<button class="progress__cancel" href="#" title="cancel">cancel</button>
</div>
<!--
Modifier classes create component variants; modifier classes must
be used with the original class in the markup
-->
<div class="progress progress--small">
<label class="label label--small">Uploading syndey-opera-house-sunset.jpg</label>
<div class="progress__track">
<div class="progress__bar js-progress-percent" style="width: 29%"></div>
</div>
<div class="progress__description">
<div class="layout-pull">Uploaded 221 of 762KB</div>
<strong class="layout-push">29%</strong>
</div>
<a class="progress__cancel" href="#" title="cancel"><span class="visually-hidden">cancel</span></a>
</div>
```
CSS (styles omitted)
```php
/**
* Progress Bar component
*/
.progress {}
.progress__track {}
.progress__bar {}
.progress__description {}
.progress__cancel {}
.progress__cancel:focus,
.progress__cancel:hover {}
/**
* Progress Bar small variant
*/
.progress--small .progress__track {}
.progress--small .progress__bar {}
.progress--small .progress__cancel {}
```
Note that the CSS relies almost exclusively on single class selectors; the exception is the small variant. This uses descendant selector, but is the recommended approach for styling sub-objects within variant components. Here’s why: firstly, it avoids extreme class names such as .progress--small\_\_bar which begin to harm the understandability of the code. Second, since we have avoided overly-generic class names and since both parts of the selector are highly targeted classes, there is little risk of accidentally styling a further child element. Third, scoping in this case is unlikely to break reusability, since we should not expect a Progress track outside of the Progress element.
We are still serving of our goals well, and we greatly simplify the required markup: we only need to add the variant class to the component itself: `<div class="progress progress--small"><!-- all internals remain the same --></div>`.
## Acknowledgments
Portions of this guide are based heavily on:
* Philip Walton, [CSS Architecture](http://engineering.appfolio.com/2012/11/16/css-architecture/)
* Johnathan Snook, [SMACSS](http://smacss.com/book/)
* Nicolas Gallagher, [About HTML semantics and front-end architecture](http://nicolasgallagher.com/about-html-semantics-front-end-architecture/)
Additional Influence and Resources:
* Nicole Sullivan, [OOCSS](http://coding.smashingmagazine.com/2011/12/12/an-introduction-to-object-oriented-css-oocss/)
* Harry Roberts, [Code Smells in CSS](http://csswizardry.com/2012/11/code-smells-in-css/)
* Harry Roberts, [CSS-Guidelines](http://cssguidelin.es)
To minimize friction when contributing to CSS, the front-end developers of the Drupal community have reached consensus about our coding standards for:
* Formatting CSS code.
* CSS architecture, including goals, pitfalls and best practices.
* Grouping rulesets into files.
Despite our natural range of working styles and coding preferences, we value collaboration and ease of development, so we have attempted to explain our standards clearly in the following documents.
> "Part of being a good steward to a successful project is realizing that writing code for yourself is a Bad Idea™. If thousands of people are using your code, then **write your code for maximum clarity**." - Idan Gazit
## Acknowledgements
This guide is based upon the works, [_Principles of writing consistent, idiomatic CSS_](https://github.com/necolas/idiomatic-css) by Nicolas Gallagher and [_Scalable and Modular Architecture for CSS_](http://smacss.com) (SMACSS) by Jonathan Snook with a hat tip to Nicole Sullivan’s ground-breaking [_Object Oriented CSS_](http://www.stubbornella.org/content/2009/02/12/css-doesn’t-suck-you’re-just-doing-it-wrong/).
# CSScomb settings for Drupal (CSS formatting and sort tool)
[CSScomb](https://github.com/csscomb/csscomb.js "Make your code beautiful") formats and sorts CSS properties in **css, scss, sass or less** files. An explanation of the options can be found at [https://github.com/csscomb/csscomb.js/blob/master/doc/options.md](https://github.com/csscomb/csscomb.js/blob/master/doc/options.md).
CSScomb is available as:
* a [command-line tool](https://github.com/csscomb/csscomb.js/blob/master/doc/usage-cli.md " command line usage")
* a [plugin](https://github.com/csscomb/sublime-csscomb "csscomb/sublime-csscomb | GitHub") for [Sublime Text](http://www.sublimetext.com/3 " The text editor you'll fall in love with")
* a [plugin](https://atom.io/packages/csscomb "Atom Editor Plugin for CSScomb | atom.io") for [Atom](https://atom.io/ " A hackable text editor for the 21st Century by GitHub")
* a [plugin](https://github.com/csscomb/grunt-csscomb "csscomb/grunt-csscomb | GitHub") for [Grunt](http://gruntjs.com/ " The JavaScript Task Runner") (post-processing)
* a [plugin](https://www.npmjs.com/package/gulp-csscomb "gulp-csscomb | npm packages") for [Gulp](http://gulpjs.com/ "gulp.js - the streaming build system") (post-processing)
* an [external tool](https://github.com/csscomb/jetbrains-csscomb "csscomb/jetbrains-csscomb | GitHub") for [PhpStorm](https://www.jetbrains.com/phpstorm/ "The Most Intelligent PHP IDE | JetBrains PhpStorm")
* a [plugin](https://github.com/csscomb/vim-csscomb "csscomb/vim-csscomb | GitHub") for [Vim](https://www.drupal.org/node/1389006 "Vimrc - Vim Plugin for Drupal | Drupal.org").
The used values in the snippet below are in accordance with the recommendations in the [CSS formatting guidelines](https://www.drupal.org/node/1887862 "CSS formatting guidelines | Drupal.org"). The sort order of CSS properties is based on the [Yandex configuration file](https://github.com/csscomb/csscomb.js/blob/master/config/yandex.json "csscomb.js/yandex.json | GitHub") and improved for SASS/LESS by [putting variables first](https://github.com/csscomb/csscomb.js/blob/master/doc/options.md#sort-order-vs-preprocessors "csscomb.js/options.md at master · csscomb/csscomb.js · GitHub") (they have to be declared before being used).
Copy/paste the configuration below into the CSScomb package settings file under _Preferences_ in Sublime Text (after installation of the plugin). For most other use cases, put it in a file named `.csscomb.json` [in your local home folder](https://github.com/csscomb/csscomb.js/blob/master/doc/configuration.md#where-to-put-config "Where to put the config file?") (usually named _www_) to make it applicable to all your projects.
```yaml
{
"config": {
"exclude": [
"core/**",
"modules/**",
"profiles/**",
"sites/**",
"themes/**",
"tests/**",
"config/**",
"includes/**",
"tmp/**",
"vendor/**",
"node_modules/**",
"bower_components/**",
"lib/**",
"src/**",
"img/**",
"images/**",
"icons/**",
"js/**",
"javascript/**",
"scripts/**",
"jquery/**",
".git/**"
],
"always-semicolon": true,
"remove-empty-rulesets": true,
"color-case": "lower",
"color-shorthand": true,
"element-case": "lower",
"eof-newline": true,
"leading-zero": true,
"quotes": "double",
"sort-order-fallback": "abc",
"space-after-colon": " ",
"space-after-combinator": " ",
"space-after-opening-brace": "\n",
"space-after-selector-delimiter": "\n",
"space-before-colon": "",
"space-before-combinator": " ",
"space-before-opening-brace": " ",
"space-before-selector-delimiter": "",
"space-between-declarations": "\n",
"block-indent": 2,
"strip-spaces": true,
"space-before-closing-brace": "\n",
"unitless-zero": true,
"tab-size": 2,
"lines-between-rulesets": false,
"verbose": true,
"sort-order": [
[
"$charset",
"$import",
"$namespace",
"$extend",
"$variable",
"$include",
"position",
"z-index",
"top",
"right",
"bottom",
"left",
"display",
"visibility",
"float",
"clear",
"overflow",
"overflow-x",
"overflow-y",
"-ms-overflow-x",
"-ms-overflow-y",
"-webkit-overflow-scrolling",
"clip",
"zoom",
"flex",
"flex-direction",
"flex-basis",
"flex-flow",
"flex-grow",
"flex-shrink",
"flex-wrap",
"order",
"flex-order",
"flex-pack",
"align",
"align-self",
"justify-content",
"align-items",
"align-content",
"flex-align",
"-webkit-box-sizing",
"-moz-box-sizing",
"box-sizing",
"width",
"min-width",
"max-width",
"height",
"min-height",
"max-height",
"margin",
"margin-top",
"margin-right",
"margin-bottom",
"margin-left",
"padding",
"padding-top",
"padding-right",
"padding-bottom",
"padding-left",
"table-layout",
"-webkit-columns",
"-moz-columns",
"columns",
"-webkit-column-span",
"-moz-column-span",
"column-span",
"-webkit-column-width",
"-moz-column-width",
"column-width",
"-webkit-column-count",
"-moz-column-count",
"column-count",
"-webkit-column-fill",
"-moz-column-fill",
"column-fill",
"-webkit-column-gap",
"-moz-column-gap",
"column-gap",
"-webkit-column-rule",
"-moz-column-rule",
"column-rule",
"-webkit-column-rule-width",
"-moz-column-rule-width",
"column-rule-width",
"-webkit-column-rule-style",
"-moz-column-rule-style",
"column-rule-style",
"-webkit-column-rule-color",
"-moz-column-rule-color",
"column-rule-color",
"empty-cells",
"caption-side",
"border-spacing",
"border-collapse",
"$counter-style",
"list-style",
"list-style-position",
"list-style-type",
"list-style-image",
"content",
"quotes",
"counter-reset",
"counter-increment",
"resize",
"cursor",
"-webkit-user-select",
"-moz-user-select",
"-ms-user-select",
"user-select",
"nav-index",
"nav-up",
"nav-right",
"nav-down",
"nav-left",
"-webkit-transition",
"-moz-transition",
"-ms-transition",
"-o-transition",
"transition",
"-webkit-transition-delay",
"-moz-transition-delay",
"-ms-transition-delay",
"-o-transition-delay",
"transition-delay",
"-webkit-transition-timing-function",
"-moz-transition-timing-function",
"-ms-transition-timing-function",
"-o-transition-timing-function",
"transition-timing-function",
"-webkit-transition-duration",
"-moz-transition-duration",
"-ms-transition-duration",
"-o-transition-duration",
"transition-duration",
"-webkit-transition-property",
"-moz-transition-property",
"-ms-transition-property",
"-o-transition-property",
"transition-property",
"-webkit-transform",
"-moz-transform",
"-ms-transform",
"-o-transform",
"transform",
"-webkit-transform-origin",
"-moz-transform-origin",
"-ms-transform-origin",
"-o-transform-origin",
"transform-origin",
"$keyframes",
"-webkit-animation",
"-moz-animation",
"-ms-animation",
"-o-animation",
"animation",
"-webkit-animation-name",
"-moz-animation-name",
"-ms-animation-name",
"-o-animation-name",
"animation-name",
"-webkit-animation-duration",
"-moz-animation-duration",
"-ms-animation-duration",
"-o-animation-duration",
"animation-duration",
"-webkit-animation-play-state",
"-moz-animation-play-state",
"-ms-animation-play-state",
"-o-animation-play-state",
"animation-play-state",
"-webkit-animation-timing-function",
"-moz-animation-timing-function",
"-ms-animation-timing-function",
"-o-animation-timing-function",
"animation-timing-function",
"-webkit-animation-delay",
"-moz-animation-delay",
"-ms-animation-delay",
"-o-animation-delay",
"animation-delay",
"-webkit-animation-iteration-count",
"-moz-animation-iteration-count",
"-ms-animation-iteration-count",
"-o-animation-iteration-count",
"animation-iteration-count",
"-webkit-animation-direction",
"-moz-animation-direction",
"-ms-animation-direction",
"-o-animation-direction",
"animation-direction",
"text-align",
"-webkit-text-align-last",
"-moz-text-align-last",
"-ms-text-align-last",
"text-align-last",
"vertical-align",
"white-space",
"text-decoration",
"text-emphasis",
"text-emphasis-color",
"text-emphasis-style",
"text-emphasis-position",
"text-indent",
"-ms-text-justify",
"text-justify",
"text-transform",
"letter-spacing",
"word-spacing",
"-ms-writing-mode",
"text-outline",
"text-transform",
"text-wrap",
"text-overflow",
"-ms-text-overflow",
"text-overflow-ellipsis",
"text-overflow-mode",
"-ms-word-wrap",
"word-wrap",
"word-break",
"-ms-word-break",
"-moz-tab-size",
"-o-tab-size",
"tab-size",
"-webkit-hyphens",
"-moz-hyphens",
"hyphens",
"pointer-events",
"direction",
"unicode-bidi",
"orphans",
"widows",
"opacity",
"filter:progid:DXImageTransform.Microsoft.Alpha(Opacity",
"-ms-filter:\\'progid:DXImageTransform.Microsoft.Alpha",
"-webkit-filter",
"-ms-filter",
"filter",
"-ms-interpolation-mode",
"color",
"border",
"border-collapse",
"border-width",
"border-style",
"border-color",
"border-top",
"border-top-width",
"border-top-style",
"border-top-color",
"border-right",
"border-right-width",
"border-right-style",
"border-right-color",
"border-bottom",
"border-bottom-width",
"border-bottom-style",
"border-bottom-color",
"border-left",
"border-left-width",
"border-left-style",
"border-left-color",
"-webkit-border-radius",
"-moz-border-radius",
"border-radius",
"-webkit-border-top-left-radius",
"-moz-border-radius-topleft",
"border-top-left-radius",
"-webkit-border-top-right-radius",
"-moz-border-radius-topright",
"border-top-right-radius",
"-webkit-border-bottom-right-radius",
"-moz-border-radius-bottomright",
"border-bottom-right-radius",
"-webkit-border-bottom-left-radius",
"-moz-border-radius-bottomleft",
"border-bottom-left-radius",
"-webkit-border-image",
"-moz-border-image",
"-o-border-image",
"border-image",
"-webkit-border-image-source",
"-moz-border-image-source",
"-o-border-image-source",
"border-image-source",
"-webkit-border-image-slice",
"-moz-border-image-slice",
"-o-border-image-slice",
"border-image-slice",
"-webkit-border-image-width",
"-moz-border-image-width",
"-o-border-image-width",
"border-image-width",
"-webkit-border-image-outset",
"-moz-border-image-outset",
"-o-border-image-outset",
"border-image-outset",
"-webkit-border-image-repeat",
"-moz-border-image-repeat",
"-o-border-image-repeat",
"border-image-repeat",
"outline",
"outline-width",
"outline-style",
"outline-color",
"outline-offset",
"background",
"filter:progid:DXImageTransform.Microsoft.AlphaImageLoader",
"background-color",
"background-image",
"background-repeat",
"background-attachment",
"background-position",
"background-position-x",
"-ms-background-position-x",
"background-position-y",
"-ms-background-position-y",
"-webkit-background-clip",
"-moz-background-clip",
"background-clip",
"background-origin",
"-webkit-background-size",
"-moz-background-size",
"-o-background-size",
"background-size",
"box-decoration-break",
"-webkit-box-shadow",
"-moz-box-shadow",
"box-shadow",
"filter:progid:DXImageTransform.Microsoft.gradient",
"-ms-filter:\\'progid:DXImageTransform.Microsoft.gradient",
"text-shadow",
"$font-face",
"font",
"font-family",
"src",
"$font-feature-values",
"$swash",
"$annotation",
"$ornaments",
"$stylistic",
"$styleset",
"$character-variant",
"font-variant-alternates",
"font-size",
"font-weight",
"font-style",
"font-variant",
"font-size-adjust",
"font-stretch",
"font-effect",
"font-emphasize",
"font-emphasize-position",
"font-emphasize-style",
"font-smooth",
"line-height",
"$media",
"$supports",
"$document",
"$page",
"$viewport"
]
]
}
}
```
### Note
To have ruleset groups divided by an empty line, replace the empty lines above with:
```php
],
[
```
### Alternatives
There is [csscombx](https://www.npmjs.com/package/csscombx), which is a fork of [Csscomb](https://github.com/csscomb/csscomb.js) tool specifically intended for Drupal. It has a default configuration file with exactly the same content as the example above, so you don't need to make any additional set up.
How to use.
First, you need to install [nodejs](https://nodejs.org/en/download/) on your system. It has the **npm** - a package manager for many popular javascript projects.
Then, install [csscombx](https://www.npmjs.com/package/csscombx) globally:
```php
npm install csscombx --global
```
Depending on where you've installed **npm** the command above may require **sudo** prepended to it.
After installing **cd** into your theme's root folder (or any folder containing style files to be formatted) and make a so-called dry run without changing anything:
```php
csscombx -vl ./
```
The **\-v** option on the command stands for verbose and **\-l** for lint. It scans the current directory recursively for all **css**, **scss**, **sass** and **less** files and reports their status relative to the settings in the [drupal.json](https://github.com/drugan/csscombx/blob/master/config/drupal.json) configuration file of the tool. If you want to change default settings just copy the content of the file and paste into **.csscombx.json** file in the root of your theme, a particular style folder, webroot folder or any other folder where you require your customized formatting settings to be applied. Note that any of a project's child folders may have its own **.csscombx.json** file with different settings.
When you are ready for actual changes, run this command passing desirable folder and/or particular file paths as arguments:
```php
csscombx -v ./my-styles public/styles.css
```
Sometimes you may want to automate the formatting task above and run it whenever the style file(s) are saved after being edited. For that case, you can use the [gulp-csscombx](https://www.npmjs.com/package/gulp-csscombx) plugin.
How to use.
Install [gulp-cli](https://www.npmjs.com/package/gulp-cli) globally:
```php
npm rm --global gulp && npm install --global gulp-cli
```
Create **package.json** file in the root of your theme, module or any other project and put this into it:
```yaml
{
"name": "whatever",
"version": "1.0.0",
"dependencies": {
"gulp": "^3.9.1",
"gulp-csscombx": "^4.2.0"
}
}
```
Then run this command:
```php
npm install
```
If you already have a **package.json** file then just run this command without touching the file:
```php
npm install gulp --save && npm install gulp-csscombx --save
```
Then create a **gulpfile.js** file in the root of your theme, module or any other project and put this into it:
```php
'use strict';
var gulp = require('gulp');
var csscombx = require('gulp-csscombx');
// @see https://github.com/gulpjs/gulp/blob/master/docs/API.md#gulpsrcglobs-options
var mySrcStyles = ['./**.scss', './my-src/**.less', '!./client/bad.css', './gallery/*.sass'];
gulp.task('style', function() {
return gulp.src(mySrcStyles)
// Process mySrcStyles according Drupal coding standards.
.pipe(csscombx())
// Save the tree of processed folders/files in the ./my-dest-styles folder
// or mySrcStyles if you want formattted files saved in the same place.
.pipe(gulp.dest('./my-dest-styles'));
});
gulp.task('watch-my-styles', function() {
// Run the 'style' task whenever mySrcStyles are edited and saved.
var watcher = gulp.watch(mySrcStyles, ['style']);
// Emit a change event notification on the console if you need one.
watcher.on('change', function(event) {
console.log('File ' + event.path + ' was ' + event.type + ', running tasks...');
});
});
```
Adjust **mySrcStyles** variable's value for the content you want to be formatted and run this command:
```php
gulp watch-my-styles
```
You'll see that execution of the command is halted and waits while any of the **mySrcStyles** will be changed. Do it and look in the console again. It should report that some files are automatically formatted. When you are done with developing your styles just go to the console and press **Ctrl+C** keyboard shortcut to terminate the gulp watcher's task.
If you think that some of the [drupal.json](https://github.com/drugan/csscombx/blob/master/config/drupal.json) default configuration file's settings do not comply with Drupal CSS coding standards then please do the following:
Create a [new issue](https://www.drupal.org/node/add/project-issue/drupal) for the **CSS** component of the Drupal core.
Post a [comment on this page](https://www.drupal.org/node/2399303/discuss) to notify all the participants involved.
[Create the issue](https://github.com/drugan/csscombx/issues/new) or just make a pull request on the [csscombx](https://github.com/drugan/csscombx).
Also, if you have some difficulties with the **gulp-csscombx** plugin then post the issue on [its GitHub repository](https://github.com/drugan/gulp-csscombx/issues).
# CSS file organization
Note: This document describes a file organization and aggregation strategy that has not yet been implemented. See this core issue for that on-going work: [#1921610: \[Meta\] Architect our CSS](https://www.drupal.org/project/drupal/issues/1921610 "Status: Active")
## File Structure
Rulesets should be grouped into logical files that enforce the separation of concerns within the CSS, that can be aggregated efficiently and that can be easily overridden by themers.
Drupal follows a SMACSS-style categorization of its CSS rules: The State and Theme categories are not frequently used with modern CSS.
1. Base —Should include HTML element styling (e.g. a, ul, etc), typography, root scooped CSS custom properties, resets and utility classes.
2. Layout —Overall layout of the page including page template layout, region layout, etc.
3. Component (SMACSS “module”) — The majority of a theme’s styles will be within components. Components should be scoped to their own CSS files. Example components include pager, navigation, footer, etc.
4. State (not used often) — This was originally created for styles that modified the states of components, however it is now best practice to include component states within their respective stylesheet.
5. Theme (not used often) — This was originally created to apply styles that would affect the overall look and feel of the theme (such as colors), however this can now be done more effectively by modifying CSS custom properties.
For more information about Drupal’s use of SMACSS, see the [“Separate Concerns” section of the CSS Architecture page](http://drupal.org/node/1887918#separate-concerns).
These categories help us keep CSS styles with different purposes from getting intertwined with one another. However, they don’t directly dictate file structure. Drupal recommends the following guidelines for structuring CSS across files:
### CSS files for Drupal modules
All of a module's styles should be placed in a `css/` sub-directory and broken into one or more of the following files:
`module_name.module.css`: This file should hold the minimal styles needed to get the module's functionality working. This includes layout, component and state styles.
`module_name.theme.css`: This file should hold extra styles to make the module's functionality aesthetically pleasing. This usually just consists of theme styles.
`module_name.admin.css`: This file should hold the minimal styles needed to get the module's admin screens working. This includes layout, component and state styles. On admin screens, the module may choose to load the \*.module.css in addition to the \*.admin.css file.
`module_name.admin.theme.css`: This file should hold extra styles to make the module's admin screens aesthetically pleasing. This usually just consists of theme styles.
Note: Modules should never have any base styles. Drupal core's modules do not have any base styles. Instead Drupal core uses the Normalize.css library augmented with a drupal.base.css library.
If a module attaches a CSS file to a template file, the CSS file should be named the same as the template file, e.g. the system-plugin-ui-form.html.twig CSS file should be named system-plugin-ui-form.css
### CSS files for Drupal themes
1. Always separate Base, Layout, and Component styles into their own files. (Drupal will aggregate these separate files into one file, so there is no performance problem with this practice.)
2. For complex themes, consider placing each component or component family in its own file.
3. State rules, including media queries, should be included with the component to which they apply.
4. Theme rules may or may not have their own file(s). [Starter themes](http://drupal.org/node/323993) are encouraged to separate their theme CSS into separate files. Otherwise, include theme rules with their corresponding component.
Most themes will have at least the following:
```yaml
css:
base: base.css
layout: layout.css
component: components.css
```
Optionally, these can be further broken out. Below the base/layout/component level, naming is up to the module or theme. A more complex file structure using normalize.css and a mobile-first approach might look like this:
```yaml
css:
base: css/base/normalize.css css/base/elements.css
layout: css/layout/layout.css css/layout/layout--medium.css css/layout/layout--wide.css
component: css/components/button.css css/components/dropdown.css css/components/pagination.css css/components/tabs.css …
theme: css/theme/theme--light.css css/theme/theme--dark.css
```
Note: Module styles are always loaded before theme styles and the SMACSS groups cannot be interlaced between modules and themes. For example, loading a normalize.css in a theme's "base" SMACSS group will appear in the CSS cascade order only _after_ all other stylesheets defined in core and contrib modules even when they're defined in a higher SMACSS group like "component" and "theme". Follow [#3046636: LibraryDiscoveryParser overwrites theme CSS group ordering](https://www.drupal.org/project/drupal/issues/3046636 "Status: Needs work") for more information.
## Aggregating CSS
In Drupal 7 and earlier, the “group” option and the “every page” option of `drupal_add_css()` controlled which files would be aggregated together. The groups were `CSS_SYSTEM`, `CSS_DEFAULT` (for Drupal modules), and `CSS_THEME` (for Drupal themes) and the `every_page` option was a simple boolean flag. This meant there were up to 6 aggregated files.
In Drupal 8 and later, themes now have the ability to override stylesheets without affecting the “every page” option of the original stylesheet. See [http://drupal.org/node/1876600](http://drupal.org/node/1876600). This means conditionally-loaded CSS remains conditionally loaded.
This also means it’s now simple for themes to affect conditionally-loaded styles, so all the theme-added CSS no longer needs to load after all conditionally-loaded styles. This greatly simplifies the default grouping strategy that Drupal can employ. Instead of 6 aggregated files, we can just have 2:
1. The “Every page” aggregate. Includes:
1. Base styles from libraries (like Normalize.css and drupal.base.css) and then Drupal themes.
2. Layout styles from Drupal modules and then Drupal themes.
3. Component styles (and their associated states and themes) from Drupal modules and then Drupal themes.
4. State styles that are not included with components, from Drupal modules and then Drupal themes.
5. Theme styles that are not included with components, from Drupal modules and then Drupal themes.
2. The conditionally-loaded aggregate. Includes:
1. Layout styles from Drupal modules and then Drupal themes.
2. Component styles (and their associated states and theme) from Drupal modules and then Drupal themes.
3. State styles that are not included with components, from Drupal modules and then Drupal themes.
4. Theme styles that are not included with components, from Drupal modules and then Drupal themes.
_Note: there should never be conditionally-loaded base styles._
The order of styles within an aggregate are determined by the “weight” option of `drupal_add_css()` which will be:
```php
/**
* The default weight for CSS rules that style HTML elements ("base" styles).
*/
const CSS_BASE = -200;
/**
* The default weight for CSS rules that layout a page.
*/
const CSS_LAYOUT = -100;
/**
* The default weight for CSS rules that style design components (and their associated states and themes.)
*/
const CSS_COMPONENT = 0;
/**
* The default weight for CSS rules that style states and are not included with components.
*/
const CSS_STATE = 100;
/**
* The default weight for CSS rules that style theme and are not included with components.
*/
const CSS_THEME = 200;
```
The aggregated file itself is determined by the "group" option and the "every page" flag of drupal\_add\_css(). The group option defaults to the `CSS_AGGREGATE_DEFAULT` constant. Drupal core does not provide any other aggregate constants, but contrib can define their own aggregate groups.
### SMACSS and Sass/Compass
SMACSS when combined with Sass/Compass concept can provide much control over output css. We can use Sass Globbing concept to have multiple .scss files and only one css file with the use of partials.
## Whitespace
### Indentation
Use 2 spaces for each level of indentation, the same standard as Drupal’s PHP and JavaScript code.
* Declarations (property/value pairs) should be indented one level relative to their selector.
* Rulesets within a media query should be indented one level relative to the media statement.
* Comments should maintain the indentation of their declaration or ruleset.
```php
.tabs__tab {
display: none;
margin: 0;
margin-block-end: calc(-1 * var(--tabs-border-width));
&.is-active {
display: flex;
}
}
```
### Blank lines
* In general, separate each ruleset by a blank line when using PostCSS.
* If a ruleset has a preceding Doxygen-style or single-line-style comment that describes it, place a blank line before the comment.
* If two rulesets have no interleaving blank line, they must be logically related. If they are not logically related to each other, add a blank line and a comment describing the second ruleset.
```php
.tabs {
--tabs-height: var(--sp3);
--tabs-padding-inline: var(--sp1-5);
--tabs-active-border-size: 6px;
--tabs-highlight-color: var(--color--primary-50); /* Minimum 3:1 contrast ratio against --tabs-background-color and --tabs-background-color-hover. */
display: flex;
flex-direction: column;
}
/**
* I'm a Doxygen-style comment. Woohoo!
*/
.tabs__tab {
display: none;
margin: 0;
margin-block-end: calc(-1 * var(--tabs-border-width));
/* Show tabs when JavaScript disabled. */
@nest html:not(.js) & {
display: flex;
}
```
### Line endings
* There MUST NOT be any whitespace (spaces or tabs) at the end of lines.
* All text files should end with a single blank line.
* Files should be formatted with Unix line endings (a newline character, denoted as `\n` or `LF`), which is also the default in Mac OS X.
Tip: configure your editor to “show invisibles”. This will allow you to eliminate end-of-line whitespace, eliminate unintended blank-line whitespace, and avoid polluting commits.
Drupal 8 and above includes an [EditorConfig](http://editorconfig.org/) file in [its root directory](https://git.drupalcode.org/project/drupal/-/tree/11.x?ref_type=heads) to help maintain these whitespace conventions.
## Comments
Well commented code is extremely important. Take time to describe components, how they work, their limitations, and the way they are constructed. Don't leave others guessing as to the purpose of uncommon or non-obvious code.
To stay consistent with the rest of Drupal's code base, we borrow some of the CSS comment styles from the [Doxygen and comment formatting conventions](https://www.drupal.org/docs/develop/standards/php/api-documentation-and-comment-standards) for PHP files.
### File comments
Each file should start with a comment describing what the file does. Note that a blank line should follow a file comment. And keep line-lengths to 80 columns, when possible. For more information, see the [PHP file comment standards](https://www.drupal.org/docs/develop/standards/php/api-documentation-and-comment-standards).
### Single line comments describing a ruleset
Short comments describing a ruleset can be kept to one line.
### Multi-line comments describing a ruleset
When describing a ruleset or set of rulesets, any comment that requires 2 or more lines (wrapped to 80 characters) must follow the Doxygen comment style (also called a “docblock”).
### Multi-line comments inside a ruleset
Within a ruleset, multi-line comments are preceded with a `/*` and terminated by a `*/`. Text is intended to maintain the text’s left alignment.
### Single-line comments
When describing a property or ruleset, any comment that can be written inside the 80 character line length limit can use a simple CSS comment style.
Example of all comment styles:
```php
/*
* @file
* Example CSS file to demonstrate all variations of CSS comments. This file uses
* modern syntax including nesting, and `:dir()` pseudo-class that PostCSS
* transpiles into browser-readable CSS.
*/
.component {
position: relative; /* Anchor pseudo-element. */
z-index: 502; /* Appear above toolbar. */
/* This is a multiline comment WITHIN a ruleset. It describes the next rule
when there is not enough space on the same line as the rule. We try to
make it not go very far over 80 characters. We want to make it very verbose
to help out future developers. */
visibility: hidden;
transform: translateX(50%); /* LTR */
border-radius: var(--border-radius);
will-change: transform; /* Needed for Safari 16. */
/*
* This is a multiline comment that describes an entire ruleset (even if
* nested). We try to make it not go very far over 80 characters. We want to
* make it very verbose to help out future developers.
*/
&[aria-expanded="true"] {
visibility: visible;
}
/* Short comment outside ruleset. */
&::marker {
display: none;
}
&:dir(rtl) {
transform: translateX(-50%);
}
}
```
## Properties where browsers do not have or support CSS logical properties
Certain properties do not have a “logical properties” equivalent (such as `transform`). For these direction specific rules, add a `/* LTR */` comment on the same line preceded by a single space. Follow with an additional ruleset (or nested ruleset if using PostCSS) containing the inverse property/values.
```php
.my-component {
transform: translateX(20px); /* LTR */
}
[dir="rtl"] .my-component {
transform: translateX(-20px);
}
```
## Rulesets
* Use one selector per line when a ruleset has a group of selectors separated by commas.
* When possible, consider using functional pseudo-classes like `:is()`, `:not()or` `:where()` that allow combining of selectors.
* Include one declaration per line in a declaration block.
```php
/**
* Example of one selector per line.
*/
.my-component,
.my-other-component,
.yet-another-component {
background-color: #cccccc;
}
/**
* Example of the :is() pseudo-class.
*/
:is(.my-component, .my-other-component, .yet-another-component) {
background-color: #cccccc;
}
```
## Properties
* In a declaration, the property name should be immediately followed by a colon, then a single space, and then the property’s value.
* Include a semicolon at the end of all declarations.
* For property values that require quotes, use double quotes instead of single quotes, e.g. font-family: "Arial Black", Arial, sans-serif; and content: " ";.
* Default to rem units, unless it creates an undesired effect.
* Note that if you are using PostCSS, this is automatic via the PostCSS PxToRem plugin.
* Quote attribute values in selectors, e.g. input\[type="checkbox"\].
* Where allowed, avoid specifying units for zero-values, e.g. use `margin: 0;` instead of `margin: 0px;`.
* Include a space after each comma in comma-separated property or function values.
* Do not use spaces around the parentheses in a function, e.g. color: rgba(0, 0, 0, 0.8);
* Use lower case function names, correct: color: rgba(0, 0, 0, 0.8);
## Declaration order
The declarations in a ruleset should be ordered so that the purpose of the declaration block is most obvious. Clarity should be the guiding principle.
1. Positioning properties include: `position`, `float`, `clear`, `inset`, `top`, `right`, `bottom`, `left`, `direction`, and `z-index` plus logical properties like `block-start`, `block-end`, `inline-start` and `inline-end`.
2. Box model properties include:
1. display
2. sizing (like block-size or inline-size as logical properties, and width and height and the (max|min) variation or each of them)
3. Margins and the logical property equivalents, plus their various longhand forms (margin-block, margin-top, margin-inline-end…)
4. Paddings and the logical property equivalents, plus their various longhand forms (padding-block, padding-top, padding-inline-end…)
5. Borders and the logical property equivalents, plus their various longhand forms.
6. box-sizing
3. Other declarations.
Within each of the above groups, properties can be grouped alphabetically or grouped with like properties next to each other, e.g. putting font and text properties next to each other. Drupal’s coding standards are purposefully vague here because there is no consensus on this issue (as of 2013), but we respect each other’s abilities and preferences.
If not automatically added by autoprefixer (which is part of the PostCSS build process), vendor prefixed properties should be directly before their non-prefixed version. This allows the official version of the property to override any inconsistencies in the vendor-prefixed versions once those browsers implement the official property. If browser bugs or cross-browser issues necessitate any deviation from this ordering, it should be clearly documented.
Again, the order of properties is meant to reinforce the purpose of the ruleset. As such, it is much more important to add comments to the ruleset than to worry about property ordering.
- - -
The text of this guideline was originally based on the [_Principles of writing consistent, idiomatic CSS_](https://github.com/necolas/idiomatic-css) by Nicolas Gallagher.
# What to look for when reviewing CSS
There are many things to consider when reviewing CSS against our CSS standards. This is a guide of things to consider and how to present the review when looks at core CSS.
## Architecture guidelines
### Is all the code still in use?
There are times when markup in core is changed but some CSS files that rely on a particular class or ID have not been updated. Check to make sure that the CSS still applies correctly.
Example: [#2405213: Remove admin-options component from Seven theme since it is not used](https://www.drupal.org/project/drupal/issues/2405213 "Status: Closed (fixed)")
### Is some code redundant?
Some CSS is written so it can override CSS that is loaded before it, as Drupal 8 becomes more aligned to the standards, there is less overlap between CSS selectors. There may be some code in place that is no longer required to override previous values. There is sometimes code that overrides browser defaults that are identical.
Example: [Refactor 'admin-panel' CSS component](https://www.drupal.org/node/2399939#comment-9509045)
> ```php
> +++ b/core/themes/seven/css/components/admin-panel.css
> @@ -1,20 +1,23 @@
> +.panel,
> +.panel__content {
> padding: 0;
> clear: left;
> }
> ```
> I think we can delete this CSS, padding is 0 by default anyway so we don't need that. I don't know what clear left does here. Let's try deleting it and seeing what comes up
### Are the components named correctly?
Does the name of the component correctly describe the design semantics?
Example: [#2399939: Refactor 'admin-panel' CSS component](https://www.drupal.org/project/drupal/issues/2399939 "Status: Closed (fixed)")
### Should the code be abstracted out into a common reusable class?
In some situations, the custom CSS written by a module or theme could be abstracted out into a component class that can be reused across modules. The aim in Drupal 8 is to have as much CSS reusable as possible, reducing the need for custom CSS and increasing consistency.
Examples:
[#2017257: Create generic layout classes](https://www.drupal.org/project/drupal/issues/2017257 "Status: Closed (fixed)")
[#2336141: Create reusable color classes](https://www.drupal.org/project/drupal/issues/2336141 "Status: Closed (fixed)")
### Are the selectors correct?
The CSS standards recommend very short and simple selectors, replacing them with component and sub-component classes.
### Is the code in the correct file?
For themes, each CSS component should live in it's own file with it's sub-components. All the CSS that effects one component should be in the same place. There are still a few situations where the styling for one element is spread out across several files.
Some files could be broken down into even smaller files.
Example: [#2408653: Remove lists.css from Bartik, add it's current code directly to the components it references.](https://www.drupal.org/project/drupal/issues/2408653 "Status: Closed (fixed)")
## Formatting guidelines
1. Add a File comment to the top of the stylesheet - see [here](https://www.drupal.org/node/1887862#file-comments) for guidelines.
2. Check any other comments are formatted correctly - see [here](https://www.drupal.org/node/1887862#comments) for guidelines.
3. Check Whitespace is being used correctly, this includes indentations and line breaks - see [here](https://www.drupal.org/node/1887862#whitespace) for guidelines.
4. Check the formatting of rulesets, properties and media queries are correct - see [here](https://www.drupal.org/node/1887862#format) for guidelines.
5. As mentioned above, check existing RTL styles are formatted correctly - see [here](https://www.drupal.org/node/1887862#rtl) for guidelines.
icon-w-drupal.png

106 KiB

JavaScript code should be documented with documentation headers that are very similar to the [PHP documentation headers](http://drupal.org/node/1354), with modifications due to using the [JSDoc3](https://jsdoc.app/) parser as the first step in parsing the code and documentation. We generally follow the PHP standards as much as possible, with the following changes:
* All JavaScript items (methods, object constructors and properties, functions, variables, etc.) need to have documentation headers, or they will not be recognized by the parser (unlike the API module, which picks up all PHP items whether or not they have documentation headers). Only behaviors are documented specifically, see the [behavior documentation example](#documenting-behaviors).
* Not all of the @tags we use for PHP are supported. See below for the tags available and their order of declaration.
* To indicate the data type for a `@param` or `@return` tag, put the data type in `{}` brackets: `@param {TheType} paramName` or `@return {TheType}`. For non-object data, use `number`, `string`, `bool`, `null`, `undefined`, `object`, `function`, `Array`. For particular objects, use the constructor name; this could be a built-in JavaScript class (`Date`, `RegExp`), a DOM element (`HTMLElement`, `HTMLInputElement`), a Drupal-specific class (`Drupal.Ajax`), etc.
* Additional tag: like `@throws`, which documents exceptions being thrown by a PHP or JavaScript function, use `@fires` to document events that are triggered by a JavaScript function. In addition, if the event is a custom event (as opposed to a standard event like a key press), add a documentation block immediately before the first line of code within a function that triggers the event, with an `@event` tag, to document the event itself (see sample below for details). Only include one `@event` block for each custom event, but use `@fires` in each function that triggers the custom event.
* Additional tag: when documenting an object that is not being used as a namespace or class, use `@prop {type} name` tags to document its properties (these work like `@param` for function parameters).
* Some additional notation is required in many cases to help JSDoc figure out what type of item is being documented.
* Use `@name` to tell JSDoc the name of what is being documented, if it is not the same as the name in the code (usually because it is a function name like `DropButton` rather than including the class name like `Drupal.DropButton`).
* Use `@constructor` to indicate that a function is intended to be a class constructor.
* Use `@namespace` to indicate that an object is intended as a namespace.
* You do not need to use `@function` in most cases - JSDoc will assume anything declared as a function is a regular function or method, unless one of the tags above overrides this determination.
### Tag order
Tags available should be declared in the following order:
```php
@global
@typedef
@var
@name
@namespace
@constructor
@callback
@event
@function
@augments
@lends
@type
@prop
@param
@return
@throws
@fires
@listens
@ingroup
@deprecated
@see
@todo
@ignore
```
Here's a sample:
### Documenting a JavaScript file
```php
/**
* @file
* Provides some feature
*
* The extra line between the end of the @file docblock
* and the file-closure is important.
*/
(function ($) {
"use strict";
})();
```
### Documenting behaviors
```php
/**
* Attaches the table drag behavior to tables.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Specific description of this attach function goes here.
* @prop {Drupal~behaviorDetach} detach
* Specific description of this detach function goes here.
*/
Drupal.behaviors.tableDrag = {
attach: function (context, settings) {
// ...
},
detach: function (context, settings, trigger) {
// …
}
};
```
### Documenting usual constructs
```php
/**
* Holds JavaScript settings and other information for Drupal.
*
* @namespace
*/
var Drupal = {
// ...
/**
* Holds behaviors for Drupal.
*
* @namespace
*/
'behaviors': {},
// ...
};
/**
* Returns the value of foo for the current widget.
*
* Description of this ordinary function in the Drupal namespace goes here.
*
* @return
* The value of foo in the current widget.
*/
Drupal.getCurrentFoo = function () {
// ...
};
/**
* Constructs a table drag object.
*
* Provides the ability to drag to manipulate a table and its fields.
*
* @constructor
*
* @param {HTMLTableElement} table
* DOM object for the table to be made draggable.
* @param {object} tableSettings
* Settings for the table.
*/
Drupal.tableDrag = function (table, tableSettings) {
// ...
}
/**
* Hides the columns containing weight and parent form elements.
*
* @fires event:columnschange
*
* @see Drupal.tableDrag.showColumns
*/
Drupal.tableDrag.prototype.hideColumns = function() {
// ...
/**
* Indicates that columns have changed in a table.
*
* @param {string} type
* Type of change: 'show' or 'hide'.
*
* @event columnschange
*/
$('table.tableDrag-processed').trigger('columnschange', 'hide');
// ...
};
/**
* Shows the columns containing weight and parent form elements.
*
* @fires columnschange
*
* @see Drupal.tableDrag.hideColumns
*/
Drupal.tableDrag.prototype.showColumns = function() {
// This event is documented in Drupal.tableDrag.hideColumns
$('table.tabledrag-processed').trigger('columnschange', 'hide');
};
```
This page covers DOM and Drupal specific code styles.
## JavaScript code placement
JavaScript code SHOULD NOT be embedded in the HTML where possible, as it adds significantly to page weight with no opportunity for mitigation by caching and compression.
**Beginner's note aside:** "In Drupal 7, there are four primary methods of adding JavaScript to Drupal." See [Managing JavaScript in Drupal 7](https://www.drupal.org/node/756722).
## Use literal expressions
Code SHOULD use literal expressions instead of the `new` operator:
* Use `[]` instead of `new Array()`
* Use `{}` instead of `new Object()`
It is RECOMMENDED to use literal expressions instead of the wrapper forms `new Number`, `new String`, `new Boolean` in situations where the literal expression is the same. However, you MAY use object instances in which it matters:
```php
var literalNum = 0;
var objectNum = new Number(0);
if (literalNum) { } // false because 0 is a false value, will not be executed.
if (objectNum) { } // true because objectNum exists as an object, will be executed.
if (objectNum.valueOf()) { } // false because the value of objectNum is 0.
```
## "with" statement
The `with` statement MUST NOT be used, since it is not possible to use `with` with enabled strict mode.
Instead, you SHOULD use the explicit longer version:
```php
foo.bar.foobar.abc = true;
foo.bar.foobar.xyz = true;
```
Alternatively, you MAY use references:
```php
var o = foo.bar.foobar;
o.abc = true;
o.xyz = true;
```
## Avoiding unreachable code
To prevent unreachable code, a `return`, `break`, `continue`, or `throw` statement SHOULD be followed by a `}` or `case` or `default`.
## ` is evil
`eval()` SHOULD NOT be used.
The browser has to create an entirely new scripting environment (just like creating a new web page), import all variables from the current scope, execute the script, collect the result, and export the variables back into the original environment. Additionally, the code cannot be cached for optimization purposes. It is both the most powerful and most misused method in JavaScript.
Note that JavaScript implicitly uses `eval()` for some other language constructs.
You SHOULD NOT use the `Function` constructor, and you SHOULD NOT pass strings to `setTimeout()` or `setInterval()`.
## Preventing XSS
All output to the browser that has been provided by a user SHOULD be escaped through `Drupal.checkPlain()` first.
This is similar to Drupal's PHP `check_plain()` and encodes special characters in a plain-text string for display as HTML.
## Modifying the DOM
When adding new HTML elements to the DOM, you SHOULD NOT use `document.createElement()`.
For cross-browser compatibility reasons and also in an effort to reduce file size, you SHOULD use the jQuery equivalent.
```php
this.popup = $('<div id="autocomplete"></div>')[0];
```
Avoid this:
```php
this.popup = document.createElement('div');
this.popup.id = 'autocomplete';
```
## Specific Stuff
Drupal 6 saw the introduction of JavaScript theming and translation of JavaScript files.
### Theming
There is a theming mechanism for JavaScript code. Any modules containing JavaScript which produces HTML content MUST provide default theme functions in the Drupal.theme.prototype namespace.
### String Translation
All strings in JavaScript files SHOULD be wrapped in `Drupal.t()`, which is an equivalent of the well-known [`t()`](http://api.drupal.org/t) function.
Likewise, there is an equivalent to [`format_plural()`](http://api.drupal.org/format_plural), named `Drupal.formatPlural()`.
Their parameter order is exactly like their server-side counterparts.
**See also:**
* [JavaScript coding standards](https://www.drupal.org/node/172169).
* [jQuery coding standards](https://www.drupal.org/node/1720586).
Use [eslint](http://eslint.org/docs/user-guide/command-line-interface) for Drupal JS coding standards. See the [eslint settings information](https://www.drupal.org/node/1955232). Drupal uses 'eslint-config-airbnb' as ESLint shareable config. Therefore it's reasonable to use ['Airbnb JavaScript Style Guide'](https://github.com/airbnb/javascript/) as Drupal JS coding standard.
## Indenting
* All code MUST indent using two (2) space characters,
* All code MUST NOT indent using tab characters,
* All code MUST NOT end with trailing whitespace.
## Semicolons
JavaScript allows optional "semi-colon insertion". Drupal standards do not.
* All statements (except `for, function, if, switch, try, while`) MUST be followed by a semi-colon (`;`),
* Return values MUST start on the same line as the `return` keyword.
**EXCEPTIONS:**
* Anonymous functions assigned to a variable MUST be followed by a semi-colon.
```php
Drupal.behaviors.tableSelect = function (context) {
// Statements...
};
```
* do/while control structures MUST be followed by a semi-colon
```php
do {
// Statements...
} while (condition);
```
## File-closure
All JavaScript code MUST be declared inside a closure wrapping the whole file.
```php
/**
* @file
*/
(() => {
// All the JavaScript for this file.
})();
```
## CamelCasing
For variables that are not [constants](#constants) or [constructors](#constructors), multi-word variables and functions SHOULD be lowerCamelCased.
The first letter of each variable or function SHOULD be lowercase, and the first letter of subsequent words SHOULD be capitalized. There SHOULD NOT be underscores between the words.
In case a variable contains a jQuery object, the variable MUST start with a dollar sign (`$`):
```php
let $form = $('#search-block-form');
let $inputs = $form.find('input');
```
## Variables and Arrays
Due to enabled strict mode, an undeclared variable will halt the script, and on old browsers such variables are implicitly exported into global scope.
All variables MUST be declared with let or const before they are used and SHOULD be declared only once. All variables SHOULD be declared at the beginning of a function.
Each variable assignment SHOULD be declared on a separate line - including variables that are only declared but do not get a value assigned.
```php
let anArray = [];
let eventCallback = function () {};
let curTableDragSetting;
let curTableDragIndex;
```
### Global Variables
Drupal JavaScript MUST NOT define global variables.
### Constants
Pre-defined constants SHOULD be all-uppercase and words separated by underscores: `UPPER_UNDERSCORED`.
Variables added via PHP SHOULD be [lowerCamelCased](#camelcasing), so that they are consistent with other JavaScript variables:
```php
$element['#attached']['js'][] = array(
'data' => array('myModule' => array('basePath' => base_path())),
'type' => 'setting',
);
```
This variable would then be referenced:
```php
Drupal.settings.myModule.basePath;
```
### Arrays
Arrays SHOULD be formatted with one space separating each element and the assignment operator, if applicable:
```php
let someArray = ['hello', 'world'];
```
If the line is longer than 80 characters, each element SHOULD be broken into its own line, and indented one level.
Use trailing comma after last element in multi line arrays. Trailing commas simplify adding and removing items to objects and arrays, since only the lines you are modifying must be touched. Also this leads to cleaner git diffs, when an item is added or removed from an object or array.
```php
// bad
let fruits = [
'apples',
'banana',
'pineapple'
];
// good
let fruits = [
'apples',
'banana',
'pineapple',
];
```
### Typeof
In type comparisons, the value tested MUST NOT be wrapped in parenthesis.
```php
if (typeof myVariable === 'string') {
// ...
}
```
## Functions
### Function and method names
Function names SHOULD begin with the name of the module or theme declaring the function, so as to avoid name collisions.
```php
Drupal.behaviors.tableDrag = function (context) {
Object.keys(Drupal.settings.tableDrag).forEach(function (base) {
$('#' + base).once('tabledrag', addBehavior);
});
};
```
### Function Declarations
* The `function` keyword MUST be followed by one space.
* Named functions MUST NOT have a space between the function name and the following left parenthesis.
* Optional arguments (using default values) SHOULD be defined at the end of the function signature.
* Every function SHOULD attempt to return a meaningful value.
```php
Drupal.behaviors.tableDrag = function (context) {
// ...
this.clickCallback = function (e) {
return false;
};
// ...
};
function funStuff(field, settings) {
const settings = settings || Drupal.settings;
alert("This JS file does fun message popups.");
return field;
}
// Anonymous:
() => {}
// Named:
function closeDialog() {}
```
Note: The above examples code are lacking JSDoc and comments, only for clarity.
### Function Calls
Functions SHOULD be called with no spaces between the function name, the opening parenthesis, and the first parameter.
There SHOULD be one space between commas and each parameter, and there SHOULD NOT be a space between the last parameter, the closing parenthesis, and the semicolon.
```php
let foobar = foo(bar, baz, quux);
```
There SHOULD be one space on either side of an equals sign used to assign the return value of a function to a variable.
## Constructors
Constructors are functions that are designed to be used with the `new` prefix. The `new` prefix creates a new object based on the function's prototype, and binds that object to the function's implied this parameter. JavaScript doesn't issue compile-time warning or run-time warnings if a required `new` is omitted. If you do not use the `new` prefix, no new object will be made and operations will be bound to the global object instead.
Constructor functions MUST be given names with an initial uppercase character.
A function with an initial uppercase name MUST NOT be called without a `new` operator.
```php
function CollapsibleDetails(node) {}
let collapsibleDetail = new CollapsibleDetails(element);
```
## Comments
Inline documentation for source files MUST follow the [JavaScript API documentation and comment standards](https://drupal.org/node/2183405) (based on JSDoc).
Non-JSDoc comments are strongly RECOMMENDED.
A general rule of thumb is that if you look at a section of code and think "Wow, I don't want to try and describe that", you SHOULD comment it before you forget how it works. Comments MAY be removed by JS compression utilities later, so they don't negatively impact the file download size.
Non-JSDoc comments SHOULD use capitalized sentences with punctuation. Comments SHOULD be on a separate line, immediately before the code line or block they reference.
```php
// Unselect all other checkboxes.
doSomething();
```
If each line of a list needs a separate comment, comments MAY be placed on the same line and MAY be formatted to a uniform indent for readability.
C style comments (`/* */`) and standard C++ comments (`//`) are both allowed.
## String Concatenation
Expressions SHOULD be separated with one space before and after the `+` operator to improve readability.
```php
let string = 'Foo' + bar;
string = bar + 'foo';
string = bar() + 'foo';
string = 'foo' + 'bar';
```
The concatenating assignment operator (`+=`) SHOULD be separated with one space on each side as with the assignment operator:
```php
let string += 'Foo';
string += bar;
string += baz();
```
## Control Structures
Control statements MUST have one space between the control keyword and opening parenthesis, to distinguish them from function calls.
Control structures MUST always use curly braces, even in situations where they are optional. Having them increases readability and decreases the likelihood of logic errors being introduced when new lines are added.
These include `if, for, while, switch`.
Example `if` statement (the most complicated one):
```php
if (condition1 || condition2) {
action1();
}
else if (condition3 && condition4) {
action2();
}
else {
defaultAction();
}
```
### switch
For `switch` statements:
```php
switch (condition) {
case 1:
action1();
break;
case 2:
action2();
break;
default:
defaultAction();
}
```
### try
For `try/catch` statements:
```php
try {
// Statements...
}
catch (error) {
// Error handling...
}
finally {
// Statements...
}
```
### for in
The body of every `for in` statement MUST be wrapped in an `if` statement that performs the filtering. It MAY select for a particular type or range of values, or it can exclude functions, or it can exclude properties from the prototype.
```php
for (let variable in object) {
if (filter) {
// Statements...
}
}
```
You MUST use the `hasOwnProperty` method to distinguish the true members of the object, which SHOULD be placed inside the loop, not on the same line:
```php
for (let variable in object) {
if (object.hasOwnProperty(variable)) {
// Statements...
}
}
```
## Operators
### Comparisons
The `==` and `!=` operators do type coercion before comparing. This can lead to unexpected errors.
Strict equality MUST be used in comparisons (`===` or `!==`).
### Comma Operator
You SHOULD NOT use the comma operator, with the exception of the control part in `for` statements.
The comma operator causes the expressions on either side of it to be executed in left-to-right order, and returns the value of the expression on the right.
```php
let x = (y = 3, z = 9);
```
This sets `x` to `9`. This can be confusing for users not familiar with the syntax and makes the code more difficult to read and understand.
As of Drupal 8, we use [ESLint](http://eslint.org/) to make sure our JavaScript code is consistent and free from syntax error and leaking variables and that it can be properly minified.
> ESLint is a tool to detect errors and potential problems in JavaScript code.
ESLint is a Node.js module and is integrated into a number of IDEs. (See [installation and usage](http://eslint.org/docs/user-guide/getting-started#installation-and-usage) instructions.) One of the fastest ways to install all necessary ESLint dependencies is using [eslint-config-drupal](https://www.npmjs.com/package/eslint-config-drupal).
The following set of configuration options has been agreed upon. (See [ESLint change notice](https://www.drupal.org/node/2274223).) Configuration is improved when possible, always use the latest stable ESLint version.
These configuration files ship with Drupal and can be used from there.
* [.eslintrc.json](https://git.drupalcode.org/project/drupal/-/blob/11.x/.eslintrc.json)
* [.eslintignore](https://git.drupalcode.org/project/drupal/-/blob/11.x/.eslintignore)
In a Drupal folder these configuration files will be automatically detected and used by ESLint when it is invoked from within the code base.
## Contrib modules
Contrib modules should also conform to ESLint requirements.
In special cases, some rules need to be changed for specific modules (if they use a third party library and they need to use a new global variable), in that case the module can create it's own `.eslintrc.json` turning some rules on or off and adding global variables they require. When ESLint runs, all the configuration files present in the directory tree are merged together: see [Configuration Cascading and Hierarchy](https://eslint.org/docs/latest/use/configure/configuration-files#cascading-and-hierarchy) for more details. For example, the Google Analytics module uses third-party code that defines the global ”ga”, which can be allowed by adding the following code to `.eslintrc.json` in the module directory:
```php
{
"extends": [
"drupal"
],
"globals": {
"ga": true
}
}
```
## Checking custom JavaScript with ESLint
ESLint can be installed in order to check that custom JavaScript follows standards. Here is how:
```php
# Before running these commands, install node.js, npm, and npx
npm install eslint eslint-config-airbnb eslint-plugin-yml --save-dev
npm i eslint-config-drupal
npx eslint modules/custom/
npx eslint themes/custom/
```
## Prefix variables that point to jQuery objects with a dollar sign($)
In any part of your code it should be easy to understand which variables are jQuery objects and which are not.
**Incorrect**
When assigning a jQuery object to a variable:
```php
var foo = $('.foo');
```
When assigning a jQuery object to a property:
```php
var object = { bar: $('.bar') };
```
**Correct**
When assigning a jQuery object to a variable:
```php
var $foo = $('.foo');
```
When assigning a jQuery object to a property:
```php
var object = { $bar: $('.bar') };
```
### Avoid compatibility issues
jQuery is evolving quickly but attempts to maintain backward compatibility with previous releases. Although multiple jQuery versions are not officially supported within a single Drupal major release, many sites use the [jQuery update](http://drupal.org/project/issues/jquery_update) module to take advantage of performance and bug fixes offered by newer versions of jQuery. Custom modules using jQuery should keep up to date with syntax best-practices in order to avoid conflicts with updated versions.
### Chaining
For chaining selector you can either use CSS method $(' a b > c') or JavaScript chaining $('a').find('b').children('c'). Second method is slightly faster([test](http://jsperf.com/jquery-chaning/2)). In both cases reduce the weight of selector(if possible). In case .children() and .find() will return the same results, use .find() method ([test](http://jsperf.com/jquery-chaning/2)).
**Incorrect**
```php
this.$el
.find('.contextual-links')
.prop('hidden', !isOpen);
```
**Correct**
```php
this.$el.find('.contextual-links')
.prop('hidden', !isOpen);
```
## Event Delegation
Every event (e.g. click, mouseover, etc.) in JavaScript “bubbles” up the DOM tree to parent elements. This is incredibly useful when you want many elements to call the same function. Instead of binding an event listener function to all of them, you can bind it once to their parent, and have it figure out which node triggered the event.
In examples of jQuery code, you may often see events simply `return false;` to prevent the default behavior of that event. This type of prevention is often [misused](http://fuelyourcoding.com/jquery-events-stop-misusing-return-false/) and also prevents the "bubbling" propagation of the event. This effect is not always [desired](https://drupal.org/node/1749782). If you wish to either prevent the default behavior or stop propagation, they should be explicitly defined.
**Incorrect**
```php
$('.item').click(function(event) {
...
// Calls both event.preventDefault() and event.stopPropagation().
return false;
});
```
**Correct (Drupal 7)**
```php
$menus.delegate('.item', 'click', function(event) {
...
// Prevents the default behavior (ie: click).
event.preventDefault();
// Prevents propagation (ie: "bubbling").
event.stopPropagation();
});
```
**Correct (Drupal 8)**
```php
$menus.on('click', '.item', function(event) {
...
// Prevents the default event behavior (ie: click).
event.preventDefault();
// Prevents the event from propagating (ie: "bubbling").
event.stopPropagation();
});
```
## Functions
Separate your functions into individual functions and call them when needed.
**Incorrect**
```php
// Can be called only on click.
$('.btn').click( function() { ... });
```
**Correct**
```php
// Can be called anywhere.
function clickFunction() { ... };
$('div').click( function() {clickFunction() });
```
## Context
Always try to give your selectors a context. The default context will search the entire page's DOM. If the context is a cached selection, use find().
**Incorrect**
```php
// This has no context and is very slow.
var $element = $('.element');
```
**Improved**
```php
// Search only in #sidebar.
var $element = $('.element', '#sidebar');
```
**Correct**
```php
var $sidebar = $('#sidebar');
var $element = $sidebar.find('.element');
```
## Using #id or .class
Finding elements by the tag ID is _much_ faster ([test](http://jsperf.com/id-vs-class-vs-tag-selectors/2)) than by class name. If your target element appears on the page only once, select it by #id. In case when you have more than one, use class but descend from an #id. See the above [context](#context) code examples.
## jQuery.attr()
**This applies only for D6 and D7** because D8 will ship with jQuery 1.9+ which instead uses .prop() for property.
Wrong use of .attr is the cause of the many compatibility problems. Use booleans to set a property, not an empty string. Also, do not assume that property values that are returned are always booleans. `.attr('checked')` could return either `true` or `'checked'` depending on jQuery version and markup.
**Incorrect**
```php
$element.attr('disabled', '');
if ($element.attr('checked') === true) { ... }
```
**Correct**
```php
$element.attr('disabled', false);
$element.attr('disabled', true);
if ($element.attr('checked')) { ... }
```
## jQuery.each()
This method is indeed very powerful (and appropriate) when dealing with existing instantiated jQuery objects and the need to iterate on them.
It is often misused when needing to iterate over simple native JavaScript arrays or objects. Using native JavaScript `for` loops are _300-1000 times_ faster than jQuery.each() ([test](http://jsperf.com/jquery-each-vs-quickeach/83)).
**Incorrect**
```php
var array = [ ... ];
$.each(array, function(i, item) { ... });
```
**Correct**
```php
var array = [ ... ];
var i;
for (i = 0, len = array.length; i < len; i += 1) {
var element = array[i];
}
```
logo.png 0 → 100644
logo.png

293 KiB

# Drupal Markup Style Guide
The [Drupal Markup Style Guide](http://groups.drupal.org/node/6355) is a document in progress on how to:
* Create good HTML/markup
* Create good base templates for themers to stylize
* Create good CSS classes
site_name: Coding Standards
repo_url: https://git.drupalcode.org/project/coding_standards
repo_name: drupal/coding_standards
theme:
name: material
logo: icon-w-drupal.png
favicon: logo.png
palette:
# Palette toggle for automatic mode
- media: '(prefers-color-scheme)'
primary: blue
toggle:
icon: material/brightness-auto
name: Switch to light mode
# Palette toggle for light mode
- media: '(prefers-color-scheme: light)'
primary: blue
scheme: default
toggle:
icon: material/brightness-7
name: Switch to dark mode
# Palette toggle for dark mode
- media: '(prefers-color-scheme: dark)'
primary: blue
scheme: slate
toggle:
icon: material/brightness-4
name: Switch to system preference
features:
plugins:
- search
- privacy:
enabled: !ENV [CI, false]
\ No newline at end of file
This page is a collection of the complete API documentation examples, which you can use as starting points to writing documentation that conforms to the Drupal project's [API documentation standards](/node/1354).
## Files
[General standards for file documentation](http://drupal.org/coding-standards/docs#file)
### Module file (\*.module)
```php
<?php
/**
* @file
* Attaches custom data fields to Drupal entities.
*/
```
### Install file (\*.install)
```php
<?php
/**
* @file
* Install, update and uninstall functions for the System module.
*/
```
### Include file (\*.inc)
```php
<?php
/**
* @file
* Media module integration for the Media module.
*/
```
### PHP theme template file (\*.tpl.php -- base implementation)
[Special standards for tpl.php files](http://drupal.org/coding-standards/docs#templates)
```php
<?php
/**
* @file
* Displays a block.
*
* Available variables:
* - $block->subject: Block title.
* (list the other variables here)
*
* @see template_preprocess_block()
*
* @ingroup themeable
*/
(HTML/PHP code for template starts here)
```
### File containing a single class
[Object-oriented coding standards](https://www.drupal.org/docs/develop/coding-standards/object-oriented-code).
```php
<?php
namespace Drupal\commerce\Element;
use Drupal\commerce\EntityHelper;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element\FormElement;
/**
* Provides a form input element for selecting one or multiple entities.
*
* The element is transformed based on the number of available entities:
* 1..#autocomplete_threshold: Checkboxes/radios element, based on #multiple.
* >#autocomplete_threshold: entity autocomplete element.
* If the element is required, and there's only one available entity, a hidden
* form element can be used instead of checkboxes/radios.
*
* Properties:
* - #target_type: The entity type being selected.
* - #multiple: Whether the user may select more than one item.
* - #default_value: An entity ID or an array of entity IDs.
* - #hide_single_entity: Whether to use a hidden element when there's only one
* available entity and the element is required.
* - #autocomplete_threshold: Determines when to use the autocomplete.
* - #autocomplete_size: The size of the autocomplete element in characters.
* - #autocomplete_placeholder: The placeholder for the autocomplete element.
*
* Example usage:
* @code
* $form['entities'] = [
* '#type' => 'commerce_entity_select',
* '#title' => t('Stores'),
* '#target_type' => 'commerce_store',
* '#multiple' => TRUE,
* ];
* @end
*
* @FormElement("commerce_entity_select")
*/
class EntitySelect extends FormElement {
```
### PHP theme template file (\*.tpl.php -- theme-specific override )
[Special standards for tpl.php files](http://drupal.org/coding-standards/docs#templates) Note that there is no @ingroup themeable in the override!
```php
<?php
/**
* @file
* Displays a block.
*
* Available variables:
* - $block->subject: Block title.
* (list the other variables here)
*
*
* @see template_preprocess_block()
*/
(HTML/PHP code for template starts here)
```
## Functions
[Documentation standards for functions](http://drupal.org/coding-standards/docs#functions)
### Generic functions
```php
/**
* Returns data from the persistent cache.
*
* Data may be stored as either plain text or as serialized data. cache_get
* will automatically return unserialized objects and arrays.
*
* @param int $cid
* The cache ID of the data to retrieve.
* @param string $bin
* The cache bin to store the data in. Valid core values are 'cache_block',
* 'cache_bootstrap', 'cache_field', 'cache_filter', 'cache_form',
* 'cache_menu', 'cache_page', 'cache_path', 'cache_update' or 'cache' for
* the default cache.
*
* @return mixed
* The value from the cache, or FALSE on failure.
*
* @see cache_set()
*/
function cache_get($cid, $bin = 'cache') {
```
### Callback functions
[Standards for documenting callback functions](http://drupal.org/coding-standards/docs#callbacks) -- these are standard-format callback functions that are passed to other functions as arguments.
Callback used in only one API function:
```php
/**
* Sorts structured arrays by weight.
*
* Callback for uasort() within foo_bar().
*/
function element_sort($a, $b) {
```
Callback used in a few API functions:
```php
/**
* Sorts structured arrays by weight.
*
* Callback for uasort() within:
* - foo()
* - bar()
*/
function element_sort($a, $b) {
```
Callback used in many functions, where some explanation is needed for the otherwise standard function arguments:
\[NOTE: NEEDS STANDARDS UPDATE - data types on @param/@return!\]
```php
/**
* Sorts a structured array by the 'weight' element.
*
* Note that the sorting is by the 'weight' array element, not by the render
* element property '#weight'.
*
* Callback for uasort() used in various functions.
*
* @param $a
* First item for comparison. The compared items should be associative arrays
* that optionally include a 'weight' element. For items without a 'weight'
* element, a default value of 0 will be used.
* @param $b
* Second item for comparison.
*/
function drupal_sort_weight($a, $b) {
```
### Hook definition functions
[Standards for documenting hook definitions](http://drupal.org/coding-standards/docs#hooks)
\[NOTE: NEEDS STANDARDS UPDATE - data types on @param/@return, and function body needs to be provided, as it's part of the documentation.\]
```php
/**
* Define the Field API schema for a field structure.
*
* This hook MUST be defined in .install for it to be detected during
* installation and upgrade.
*
* @param $field
* A field structure.
*
* @return
* An associative array with the following keys:
* - columns: An array of Schema API column specifications, keyed by column
* name. This specifies what comprises a value for a given field. For
* example, a value for a number field is simply 'value', while a value for
* a formatted text field is the combination of 'value' and 'format'. It is
* recommended to avoid having the column definitions depend on field
* settings when possible. No assumptions should be made on how storage
* engines internally use the original column name to structure their
* storage.
* - indexes: (optional) An array of Schema API indexes definitions. Only
* columns that appear in the 'columns' array are allowed. Those indexes
* will be used as default indexes. Callers of field_create_field() can
* specify additional indexes, or, at their own risk, modify the default
* indexes specified by the field-type module. Some storage engines might
* not support indexes.
* - foreign keys: (optional) An array of Schema API foreign keys
* definitions.
*/
function hook_field_schema($field) {
```
### hook implementation
[Standards for documenting hook implementations](http://drupal.org/coding-standards/docs#hookimpl)
```php
/**
* Implements hook_menu().
*/
function search_menu() {
```
### hook\_update\_N implementation (update function)
[Drupal API documentation standards for functions](https://drupal.org/coding-standards/docs#functions)
```php
/**
* Upgrade the node type table and fix node type 'module' attribute to avoid name-space conflicts.
*/
function node_update_7000() {
// Rename the module column to base.
```
### form-generating function (including validate/submit)
[Form-generating functions](https://drupal.org/coding-standards/docs#forms)
\[needs an example\]
### callbacks
\[needs an example\]
[Standards for documenting hook\_menu() callbacks](http://drupal.org/coding-standards/docs#menu-callback)
\[NEEDS STANDARDS UPDATE - data type on parameter, and it needs @return as well!!\]
```php
/**
* Page callback: Displays the dashboard.
*
* @param $launch_customize
* Whether to launch in customization mode right away. TRUE or FALSE.
*/
function dashboard_admin($launch_customize = FALSE) {
```
### render API callback
[Render API callback functions](https://drupal.org/coding-standards/docs#render)
(needs an example)
### themeable function
[Standards for documenting themeable functions](http://drupal.org/coding-standards/docs#themeable)
```php
/**
* Returns HTML for a form.
*
* @param array $variables
* An associative array containing:
* - element: An associative array containing the properties of the element.
* Properties used: #action, #method, #attributes, #children
*
* @ingroup themeable
*/
function theme_form($variables) {
```
## Classes
[Standards for documenting classes and interfaces](http://drupal.org/coding-standards/docs#classes)
### Class with a namespace
```php
<?php
namespace Drupal\Core\Plugin\Context;
use Drupal\Component\Plugin\Exception\ContextException;
use Drupal\Core\Cache\CacheableDependencyInterface;
use Drupal\Core\Plugin\ContextAwarePluginInterface;
/**
* Provides methods to handle sets of contexts.
*/
class ContextHandler implements ContextHandlerInterface {
```
### Class without a namespace
(needs example)
### Interface
\[NEEDS STANDARDS UPDATE - first line does not conform to standards\]
```php
namespace Drupal\Component\Plugin;
use Drupal\Component\Plugin\Exception\PluginException;
/**
* Interface for defining context aware plugins.
*
* Context aware plugins can specify an array of context definitions keyed by
* context name at the plugin definition under the "context" key.
*/
interface ContextAwarePluginInterface extends PluginInspectionInterface {
```
### Member function
(needs example)
### Member function that overrides base class method
\[NEEDS STANDARDS UPDATE: first line needs class name and probably namespace\]
```php
/**
* Overrides prepareTimezone().
*
* Override basic component timezone handling to use Drupal's
* knowledge of the preferred user timezone.
*/
protected function prepareTimezone($timezone) {
```
### Member function that implements interface method
```php
/**
* Implements ArrayAccess::offsetExists().
*/
public function offsetExists($offset) {
```
### Member constant
(needs example)
### Member variable
(needs example)
### Plugin annotation
[Standards for plugin annotation](http://drupal.org/coding-standards/docs#Plugin)
(needs example)
## Miscellaneous examples
### Constant
(needs link to standards)
\[NEEDS GRAMMAR UPDATE - this example is not well written. Maybe pick a different constant? Also, where did this come from? We're looking for a define() here I think, not a class constant.\]
```php
/**
* The block or element is the same for every user and page that it is visible.
*/
const DRUPAL_CACHE_GLOBAL = 0x0008;
```
### Global variable
(needs example, and you might note that these are documented in separate api.php files)
### Bullet lists
[Standards for using lists](http://drupal.org/coding-standards/docs#lists)
(needs example)
### Sections, sub-sections, and in-page references
[Standards for using sections](http://drupal.org/coding-standards/docs#section)
(needs example)
### Code samples (@code)
[Standards for using @code](http://drupal.org/coding-standards/docs#code)
```php
* For example, one might wish to return a list of the most recent 10 nodes
* authored by a given user. Instead of directly issuing the SQL query
* @code
* SELECT n.nid, n.title, n.created FROM node n WHERE n.uid = $uid LIMIT 0, 10;
* @endcode
```
### @link
[Standards for using @link](http://drupal.org/coding-standards/docs#link)
```php
* This framework creates a PHP macro language that allows the server to
* instruct JavaScript to perform actions on the client browser. When using
* forms, it can be used with the #ajax property.
* The #ajax property can be used to bind events to the Ajax framework. By
* default, #ajax uses 'system/ajax' as its path for submission and thus calls
* ajax_form_callback() and a defined #ajax['callback'] function.
* However, you may optionally specify a different path to request or a
* different callback function to invoke, which can return updated HTML or can
* also return a richer set of
* @link ajax_commands Ajax framework commands @endlink.
```
### @see
[Standards for using @see](http://drupal.org/coding-standards/docs#see)
```php
/**
* Respond to a custom menu creation.
*
* This hook is used to notify modules that a custom menu has been created.
* Contributed modules may use the information to perform actions based on the
* information entered into the menu system.
*
* @param \Drupal\system\Plugin\Core\Entity\Menu $menu
* A menu entity.
*
* @see hook_menu_update()
* @see hook_menu_delete()
*/
function hook_menu_insert($menu) {
```
### Defining a topic/group
[Standards for using @defgroup, @ingroup, and @addtogroup](http://drupal.org/coding-standards/docs#defgroup)
(needs example)
### @ingroup and @addtogroup
[Standards for using @defgroup, @ingroup, and @addtogroup](http://drupal.org/coding-standards/docs#defgroup)
```php
* @ingroup php_wrappers
*/
function drupal_parse_url($url) {
```
(needs example for addtogroup)
This diff is collapsed.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment