Understanding how to code for the Style System understanding-how-to-code-for-the-aem-style-system
In this video we’ll take a look at the anatomy of the CSS (or LESS) and JavaScript used to style Experience Manage’s Core Title Component using the Style System, as well as how these styles are applied to the HTML and DOM.
Understanding how to code for the Style System understanding-how-to-code-for-the-style-system
The provided AEM Package (technical-review.sites.style-system-1.0.0.zip) installs the example title style, sample policies for the We.Retail Layout Container and Title components, and a sample page.
The CSS the-css
The following is the LESS definition for the example style found at:
/apps/demo/sites/style-system/clientlib-example/components/titles/styles/example.less
For those that prefer CSS, below this code snippet is the CSS this LESS compiles into.
/* LESS */
.cmp-title--example {
.cmp-title {
text-align: center;
.cmp-title__text {
color: #EB212E;
font-weight: 600;
font-size: 5rem;
border-bottom: solid 1px #ddd;
padding-bottom: 0;
margin-bottom: .25rem
}
// Last Modified At element injected via JS
.cmp-title__last-modified-at {
color: #999;
font-size: 1.5rem;
font-style: italic;
font-weight: 200;
}
}
}
The above LESS is compiled natively by Experience Manager to the following CSS.
/* CSS */
.cmp-title--example .cmp-title {
text-align: center;
}
.cmp-title--example .cmp-title .cmp-title__text {
color: #EB212E;
font-weight: 600;
font-size: 5rem;
border-bottom: solid 1px #ddd;
padding-bottom: 0;
margin-bottom: 0.25rem;
}
.cmp-title--example .cmp-title .cmp-title__last-modified-at {
color: #999;
font-size: 1.5rem;
font-style: italic;
font-weight: 200;
}
The JavaScript example-javascript
The following JavaScript collects and injects the current page’s last modified date and time beneath the title text when the Example style is applied to the Title component.
The use of jQuery is optional, as well as the naming conventions used.
The following is the LESS definition for the example style found at:
/apps/demo/sites/style-system/clientlib-example/components/titles/styles/js/title.js
/**
* JavaScript supporting Styles may be re-used across multi Component Styles.
*
* For example:
* Many styles may require the Components Image (provided via an <img> element) to be set as the background-image.
* A single JavaScript function may be used to adjust the DOM for all styles that required this effect.
*
* JavaScript must react to the DOMNodeInserted event to handle style-switching in the Page Editor Authoring experience.
* JavaScript must also run on DOM ready to handle the initial page load rendering (AEM Publish).
* JavaScript must mark and check for elements as processed to avoid cyclic processing (ie. if the JavaScript inserts a DOM node of its own).
*/
jQuery(function ($) {
"use strict;"
moment.locale("en");
/**
* Method that injects p element, containing the current pages last modified date/time, under the title text.
*
* Similar to the CSS Style application, component HTML elements should be targeted via the BEM class names (as they define the stable API)
* and targeting "raw" elements (ex. "li", "a") should be avoided.
*/
function applyComponentStyles() {
$(".cmp-title--example").not("[data-styles-title-last-modified-processed]").each(function () {
var title = $(this).attr("data-styles-title-last-modified-processed", true),
url = Granite.HTTP.getPath() + ".model.json";
$.getJSON(url, function(data) {
var dateObject = moment(data['lastModifiedDate']),
titleText = title.find('.cmp-title__text');
titleText.after($("<p>").addClass("cmp-title__last-modified-at").text("Last modified " + dateObject.fromNow()));
});
});
}
// Handle DOM Ready event
applyComponentStyles();
// Apply Styles when a component is inserted into the DOM (ie. during Authoring)
$(".responsivegrid").bind("DOMNodeInserted", applyComponentStyles);
});
Development best practices development-best-practices
HTML best practices html-best-practices
- HTML (generated via HTL) should be as structurally semantic as possible; avoiding unnecessary grouping/nesting of elements.
- HTML elements should be addressable via BEM-style CSS classes.
Good - All elements in the component are addressable via BEM notation:
<!-- Good practice -->
<div class="cmp-list">
<ul class="cmp-list__item-group">
<li class="cmp-list__item">...</li>
</ul>
</div>
Bad - The list and list elements are only addressable by element name:
<!-- Bad practice -->
<div class="cmp-list">
<ul>
<li>...</li>
</ul>
</div>
-
It is better to expose more data and hide it than to expose too little data requiring future back-end development to expose it.
-
Implementing author-able content toggles can aid in keeping this HTML lean, whereby authors are able to select which content elements are written to the HTML. The can be especially important when writing images to the HTML that may not be used for all styles.
-
The exception to this rule is when expensive resources, for example, images, are exposed by default, as event images hidden by CSS are, in this case, unnecessarily fetched.
- Modern image components often will use JavaScript to select and load the most appropriate image for the use case (viewport).
-
CSS best practices css-best-practices
BLOCK--MODIFIER
is applied to the parent of the BLOCK
element.-
Use preprocessors such as LESS (supported by AEM natively) or SCSS (requires custom build system) to allow for clear CSS definition, and re-usability.
-
Keep selector weight/specificity uniform; This helps to avoid and resolve difficult-to-identify CSS cascade conflicts.
-
Organize each style into a discrete file.
- These files can be combined using LESS/SCSS
@imports
or if raw CSS is required, via HTML Client Library file inclusion, or custom front-end asset build systems.
- These files can be combined using LESS/SCSS
-
Avoid mixing many complex styles.
- The more styles that can be applied at a single time to a component, the greater the variety of permutations. This can become difficult to maintain/QA/ensure brand alignment.
-
Always use CSS classes (following BEM notation) to define CSS rules.
- If selecting elements without CSS classes (i.e. bare elements) is absolutely necessary, move them higher in the CSS definition to make it clear that they have lower specificity than any collisions with elements of that type that do have selectable CSS classes.
-
Avoid styling the
BLOCK--MODIFIER
directly as this is attached to the Responsive Grid. Changing the display of this element may affect the rendering and functionality of the Responsive Grid, so only style at this level when the intent is to change the Responsive Grid’s behavior. -
Apply style scope using
BLOCK--MODIFIER
. TheBLOCK__ELEMENT--MODIFIERS
can be used in the Component, but since theBLOCK
represents the Component, and the Component is what is styled, the Style is “defined” and scoped viaBLOCK--MODIFIER
.
Example CSS selector structure should be as follows:
In the case of nested components, the CSS selector depth for these nested Component elements will exceed the 3rd level selector. Repeat the same pattern for the nested component, but scoped by the parent Component’s BLOCK
. Or in other words, start the nested component’s BLOCK
at the 3rd level, and the nested Component’s ELEMENT
is at the 4th selector level.
JavaScript best practices javascript-best-practices
The best practices defined in this section pertain to “style-JavaScript”, or JavaScript specifically intended to manipulate the Component for stylistic, rather than functional purposes.
- Style-JavaScript should be used judiciously and is a minority use case.
- Style-JavaScript should be primarily used for manipulating the component’s DOM to support styling by CSS.
- Re-evaluate use of Javascript if components will appear many times on a page, and understand the computational/and re-draw cost.
- Re-evaluate use of Javascript if it pulls in new data/content asynchronously (via AJAX) when the component may appear many times on a page.
- Handle both Publish and Authoring experiences.
- Re-use style-Javascript when possible.
- For example, if multiple styles of a Component require it’s image to be moved to a background image, the style-JavaScript can be implemented once and attached to multiple
BLOCK--MODIFIERs
.
- For example, if multiple styles of a Component require it’s image to be moved to a background image, the style-JavaScript can be implemented once and attached to multiple
- Separate style-JavaScript from functional JavaScript when possible.
- Evaluate the cost of JavaScript vs. manifesting these DOM changes in the HTML directly via HTL.
- When a component that uses style-JavaScript requires server-side modification, evaluate if the JavaScript manipulation can be brought in at this time, and what the effects/ramifications are to performance and supportability of the component.
Performance considerations performance-considerations
- Style-JavaScript should be kept light and lean.
- To avoid flickering and unnecessary re-draws, initially hide the component via
BLOCK--MODIFIER BLOCK
, and show it when all DOM manipulations in the JavaScript are complete. - The performance of the style-JavaScript manipulations is akin to basic jQuery plug-ins that attach to and modify elements on DOMReady.
- Ensure requests are gzipped, and CSS and JavaScript are minified.