diff --git a/content/contributing/development/coding_guidelines.rst b/content/contributing/development/coding_guidelines.rst index 3227548de..b0dede30c 100644 --- a/content/contributing/development/coding_guidelines.rst +++ b/content/contributing/development/coding_guidelines.rst @@ -921,8 +921,8 @@ Symbols and Conventions .. _contributing/development/js_guidelines: -Javascript and CSS -================== +Javascript +========== Static files organization -------------------------- @@ -969,14 +969,373 @@ More precise JS guidelines are detailed in the `github wiki * where *module_name* is the - technical name of the module ('sale', 'im_chat', ...) or the main route - reserved by the module (for website module mainly, i.e. : 'o_forum' for - *website_forum* module). The only exception for this rule is the - webclient: it simply uses *o_* prefix. -- Avoid using *id* tag -- Use Bootstrap native classes -- Use underscore lowercase notation to name class +.. tabs:: + + .. code-tab:: html SCSS + + .o_foo, .o_foo_bar, .o_baz { + height: $o-statusbar-height; + + .o_qux { + height: $o-statusbar-height * 0.5; + } + } + + .o_corge { + background: $o-list-footer-bg-color; + } + + .. code-tab:: css CSS + + .o_foo, .o_foo_bar, .o_baz { + height: 32px; + } + + .o_foo .o_quux, .o_foo_bar .o_quux, .o_baz .o_qux { + height: 16px; + } + + .o_corge { + background: #EAEAEA; + } + +- four (4) space indents, no tabs; +- columns of max. 80 characters wide; +- opening brace (`{`): empty space after the last selector; +- closing brace (`}`): on its own new line; +- one line for each declaration; +- meaningful use of whitespace. + +.. spoiler:: Suggested Stylelint settings + + .. code-block:: html + + "stylelint.config": { + "rules": { + // https://stylelint.io/user-guide/rules + + // Avoid errors + "block-no-empty": true, + "shorthand-property-no-redundant-values": true, + "declaration-block-no-shorthand-property-overrides": true, + + // Stylistic conventions + "indentation": 4, + + "function-comma-space-after": "always", + "function-parentheses-space-inside": "never", + "function-whitespace-after": "always", + + "unit-case": "lower", + + "value-list-comma-space-after": "always-single-line", + + "declaration-bang-space-after": "never", + "declaration-bang-space-before": "always", + "declaration-colon-space-after": "always", + "declaration-colon-space-before": "never", + + "block-closing-brace-empty-line-before": "never", + "block-opening-brace-space-before": "always", + + "selector-attribute-brackets-space-inside": "never", + "selector-list-comma-space-after": "always-single-line", + "selector-list-comma-space-before": "never-single-line", + } + }, + +.. _contributing/coding_guidelines/scss/properties_order: + +Properties order +---------------- + +Order properties from the "outside" in, starting from `position` and ending with decorative rules +(`font`, `filter`, etc.). + +:ref:`Scoped SCSS variables ` and +:ref:`CSS variables ` must be placed at the very +top, followed by an empty line separating them from other declarations. + +.. code-block:: html + + .o_element { + $-inner-gap: $border-width + $legend-margin-bottom; + + --element-margin: 1rem; + --element-size: 3rem; + + @include o-position-absolute(1rem); + display: block; + margin: var(--element-margin); + width: calc(var(--element-size) + #{$-inner-gap}); + border: 0; + padding: 1rem; + background: blue; + font-size: 1rem; + filter: blur(2px); + } + +.. _contributing/coding_guidelines/scss/naming_conventions: + +Naming Conventions +------------------ + +Naming conventions in CSS are incredibly useful in making your code more strict, transparent and +informative. + +| Avoid `id` selectors, and prefix your classes with `o_`, where `` is the + technical name of the module (`sale`, `im_chat`, ...) or the main route reserved by the module + (for website modules mainly, i.e. : `o_forum` for the `website_forum` module). +| The only exception for this rule is the webclient: it simply uses the `o_` prefix. + +Avoid creating hyper-specific classes and variable names. When naming nested elements, opt for the +"Grandchild" approach. + +.. rst-class:: bg-light +.. example:: + + .. container:: alert alert-danger + + Don't + + .. code-block:: html + +
+
+ + Entry + +
+
+ + .. container:: alert alert-success + + Do + + .. code-block:: html + +
+
+ + Entry + +
+
+ +Besides being more compact, this approach eases maintenance because it limits the need of renaming +when changes occur at the DOM. + +.. _contributing/coding_guidelines/scss/scss_variables: + +SCSS Variables +~~~~~~~~~~~~~~ + +Our standard convention is `$o-[root]-[element]-[property]-[modifier]`, with: + +* `$o-` + The prefix. +* `[root]` + Either the component **or** the module name (components take priority). +* `[element]` + An optional identifier for inner elements. +* `[property]` + The property/behavior defined by the variable. +* `[modifier]` + An optional modifier. + +.. example:: + + .. code-block:: scss + + $o-block-color: value; + $o-block-title-color: value; + $o-block-title-color-hover: value; + +.. _contributing/coding_guidelines/scss/scoped_scss_variables: + +SCSS Variables (scoped) +~~~~~~~~~~~~~~~~~~~~~~~ + +These variables are declared within blocks and are not accessible from the outside. +Our standard convention is `$-[variable name]`. + +.. example:: + + .. code-block:: html + + .o_element { + $-inner-gap: compute-something; + + margin-right: $-inner-gap; + + .o_element_child { + margin-right: $-inner-gap * 0.5; + } + } + +.. seealso:: + `Variables scope on the SASS Documentation + `_ + +.. _contributing/coding_guidelines/scss/mixins: + +SCSS Mixins and Functions +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Our standard convention is `o-[name]`. Use descriptive names. When naming functions, use verbs in +the imperative form (e.g.: `get`, `make`, `apply`...). + +Name optional arguments in the :ref:`scoped variables form +`, so `$-[argument]`. + +.. example:: + + .. code-block:: html + + @mixin o-avatar($-size: 1.5em, $-radius: 100%) { + width: $-size; + height: $-size; + border-radius: $-radius; + } + + @function o-invert-color($-color, $-amount: 100%) { + $-inverse: change-color($-color, $-hue: hue($-color) + 180); + + @return mix($-inverse, $-color, $-amount); + } + +.. seealso:: + - `Mixins on the SASS Documentation `_ + - `Functions on the SASS Documentation `_ + +.. _contributing/coding_guidelines/scss/css_variables: + +CSS Variables +~~~~~~~~~~~~~ + +In Odoo, the use of CSS variables is strictly DOM-related. Use them to **contextually** adapt the +design and layout. + +Our standard convention is BEM, so `--[root]__[element]-[property]--[modifier]`, with: + +* `[root]` + Either the component **or** the module name (components take priority). +* `[element]` + An optional identifier for inner elements. +* `[property]` + The property/behavior defined by the variable. +* `[modifier]` + An optional modifier. + +.. example:: + + .. code-block:: scss + + .o_kanban_record { + --KanbanRecord-width: value; + --KanbanRecord__picture-border: value; + --KanbanRecord__picture-border--active: value; + } + + // Adapt the component when rendered in another context. + .o_form_view { + --KanbanRecord-width: another-value; + --KanbanRecord__picture-border: another-value; + --KanbanRecord__picture-border--active: another-value; + } + +.. _contributing/coding_guidelines/scss/variables_use: + +Use of CSS Variables +-------------------- + +In Odoo, the use of CSS variables is strictly DOM-related, meaning that are used to **contextually** +adapt the design and layout rather than to manage the global design-system. These are typically used +when a component's properties can vary in specific contexts or in other circumstances. + +We define these properties inside the component's main block, providing default fallbacks. + +.. example:: + + .. code-block:: scss + :caption: :file:`my_component.scss` + + .o_MyComponent { + color: var(--MyComponent-color, #313131); + } + + .. code-block:: scss + :caption: :file:`my_dashboard.scss` + + .o_MyDashboard { + // Adapt the component in this context only + --MyComponent-color: #017e84; + } + +.. seealso:: + `CSS variables on MDN web docs + `_ + +.. _contributing/coding_guidelines/scss/css_scss_variables_use: + +CSS and SCSS Variables +~~~~~~~~~~~~~~~~~~~~~~ + +Despite being apparently similar, `CSS` and `SCSS` variables behave very differently. The main +difference is that, while `SCSS` variables are **imperative** and compiled away, `CSS` variables are +**declarative** and included in the final output. + +.. seealso:: + `CSS/SCSS variables difference on the SASS Documentation + `_ + +In Odoo, we take the best of both worlds: using the `SCSS` variables to define the design-system +while opting for the `CSS` ones when it comes to contextual adaptations. + +The implementation of the previous example should be improved by adding SCSS variables in order to +gain control at the top-level and ensure consistency with other components. + +.. example:: + + .. code-block:: scss + :caption: :file:`secondary_variables.scss` + + $o-component-color: $o-main-text-color; + $o-dashboard-color: $o-info; + // [...] + + .. code-block:: text + :caption: :file:`component.scss` + + .o_component { + color: var(--MyComponent-color, #{$o-component-color}); + } + + .. code-block:: text + :caption: :file:`dashboard.scss` + + .o_dashboard { + --MyComponent-color: #{$o-dashboard-color}; + } + +.. _contributing/coding_guidelines/scss/root: + +The `:root` pseudo-class +~~~~~~~~~~~~~~~~~~~~~~~~ + +Defining CSS variables on the `:root` pseudo-class is a technique we normally **don't use** in +Odoo's UI. The practice is commonly used to access and modify CSS variables globally. We perform +this using SCSS instead. + +Exceptions to this rule should be fairly apparent, such as templates shared across bundles that +require a certain level of contextual awareness in order to be rendered properly. diff --git a/content/developer/howtos.rst b/content/developer/howtos.rst index 2e446cff4..071e6c06e 100644 --- a/content/developer/howtos.rst +++ b/content/developer/howtos.rst @@ -9,6 +9,7 @@ Tutorials howtos/rdtraining howtos/discover_js_framework + howtos/scss_tips howtos/website howtos/backend howtos/web_services diff --git a/content/developer/howtos/scss_tips.rst b/content/developer/howtos/scss_tips.rst new file mode 100644 index 000000000..56e24192b --- /dev/null +++ b/content/developer/howtos/scss_tips.rst @@ -0,0 +1,233 @@ +=============================== +Write lean easy-to-maintain CSS +=============================== + +There are many ways to lean and simplify SCSS. The first step is to establish if custom code is +needed at all. + +Odoo's webclient has been designed to be modular, meaning that (potentially all) classes can be +shared across views. Check the code before creating a new class. Chances are that there is already a +class or an HTML tag doing exactly what you're looking for. + +On top of that, Odoo relies on `Bootstrap +`_ (BS), one of the most complete +CSS frameworks available. The framework has been customized in order to match Odoo's design (both +community and enterprise versions), meaning that you can use any BS class directly in Odoo and +achieve a visual result that is consistent with our UI. + +.. warning:: + - The fact that a class achieves the desired visual result doesn't necessarily mean that it's the + right one for the job. Be aware of classes triggering JS behaviors, for example. + - Be careful about class semantics. Applying a **button class** to a **title** is not only + semantically wrong, it may also lead to migration issues and visual inconsistencies. + +The following sections describe tips to strip-down SCSS lines **when custom-code is the only way to +go**. + +.. _tutorials/scss_tips/browser_defaults: + +Browser defaults +================ + +By default, each browser renders content using a *user agent stylesheet*. To overcome +inconsistencies between browsers, some of these rules are overridden by `Bootstrap Reboot +`_. + +At this stage all "browser-specific-decoration" rules have been stripped away, but a big chunk of +rules defining basic layout information is maintained (or reinforced by *Reboot* for consistency +reasons). + +You can rely on these rules. + +.. example:: + + Applying `display: block;` to a `
` is normally not necessary. + + .. code-block:: css + + div.element { + display: block; + /* not needed 99% of the time */ + } + +.. example:: + + In this instance, you may opt to switching the HTML tag rather than adding a new CSS rule. + + .. code-block:: css + + span.element { + display: block; + /* replace with
instead + to get 'display: block' by default */ + } + +Here's a non-comprehensive list of default rules: + +.. list-table:: + :header-rows: 1 + + * - Tag / Attribute + - Defaults + * - `
`, `
`, `
`, `