Developing CSS and JS for UIF
CSS approach
CSS Class naming: BEM with independent state classes
We use BEM naming conventions to minimise the need for nested CSS selectors which can cause specificity issues. Sometimes nesting is considered a necessary evil, but should be avoided as much as possible.
The BEM approach also helps to remove ambiguity around parent/child naming relationships, versus modifying classes.
When using the BEM approach, only go one deep in terms of your nested element separator syntax (__
). For example, consider the following DOM hierarchy:
Parent > Child > Child 2 > Child 3
The deepest child will have the following class name:
.parent__child-3 //Acceptable class name
There is no need to give it the following class name, as it becomes too verbose:
.parent__child__child-2__child-3 //Do not do this
The modifier always goes on the end of the class name, even for element/child classes.
.block–-modifier, .block__element--modifier
Abstracted components should be our default. If section-specific styles do end up being re-used significantly then those styles should be refactored into components.
In addition to BEM naming conventions, state classes can be used which do not necessarily follow BEM conventions. For example:
<div class="block__element has-state">
Some common states are things like .is-current
, .has-js
, .has-error
etc.
CSS organisation
Components are broken up into elements and blocks. Elements can't be broken up, while blocks can. This simpler demarcation avoids conflict around atomic naming conventions, where, for example, the difference between a molecule and an organism can become debatable for certain UI segments. This can result in inconsistency and confusion.
Separate SCSS files should be created for each block or element component. Use the underscore prefix for any file that doesn't get compiled directly (ie it is imported by other files to be compiled).
Styles that are not specific to a block or element component, should be added to the relevant base styles that exist in the base
styles directory. App/section-specific styles can be added to the apps
directory with the intent to move these later to the app in question. Bespoke styling like this should be an exception not a rule, as styles should be made into re-usable components as much as possible.
Any styles that are specific to the presentation of the UIF (documentation etc), which is not used on an actual production site, must be placed in the _debug.scss
file, and class names should include the string debug
to prevent ambiguity. These files are then only imported into the stylesheet used by the documentation pages. The term "debug" is used because it is the least likely term to be confused with any production site requirements.
JavaScript approach
Modules should use a global namespace so they can be used by other scripts, and their names should be prefixed with pf
to help make it clear they have been written specifically for Prodigy Finance (pf) and are not 3rd party scripts.
If the module makes use of jQuery, the file name should follow this pattern:
jquery.pfModuleName.js
Agnosticism and re-usability
JavaScript should be agnostic to the styling of the markup with which it interacts. Scripts should avoid applying styles directly to the HTML as much as possible, rather adding or removing CSS classes, thus allowing the CSS to decide what styling should be applied. If the styling changes, then one can more confidently update the CSS without worrying about styling dependencies squirreled away in some JavaScript file somewhere.
Also, effort should be made to construct modules so they are reusable in different contexts. Certain modules should also have their methods and properties constructed in such a way as to allow them to be called programatically from other scripts, and customised on the fly.
JavaScript hooks
Use a data-js
attribute for JavaScript identifiers on HTML elements. This keeps a clean distinction between what is used for JavaScript versus what is used for CSS styling. For example:
<div class="accordion" data-js="accordion">
In the above example, the class refers to the accordion styling only, while the data attribute refers to the JavaScript implementation of that accordion, regardless of styling.
Any other JavaScript-specific information that needs to be added to the HTML should also use `data` attributes, for example:
<div class="accordion" data-js="accordion" data-accordion-type="open-single">