Conventions

13 minute read

Philosophy for Conventions

Philosophy of AEM.Design is to empower all developers (FED/BED) to be able to achive more without having to learn a lot more initialy. This means that focus changes to providing a simplified and spcific experience that allows gradual learning of AEM and framework features. As more knowledge is gained and existing strucutres are understood developers can start update strucutres as needed.

Major principles that should be considred as you start learning

  • Emphases on Content - ensure that as much structure that required is converted into content so that its changable post deployment, this is following Davids #1 Rule.
  • Abstract AEM Knoweldge - generalise and abstract AEM specific knowledge into patterns that other developers can use without immidiatly needing to understand underlying principles.
  • Component Grouping and Naming - components should be named in the relation to their content and AEM purpouse, and not in relation to their end usage, see more details in Component Grouping and Naming.
  • Component Variants and Badges - ability to represent different structures of component content without having to resort to using CSS to hide available content, see more details in Component Variants and Badges.
  • Tags Usage - tags in AEM are specificaly made to provide a mechanims to create taxonomy of keywords that allow linking of content together. This capability is very usefull to represent Design Language keywords that privide styling nomenclature.

Component Grouping and Naming

All components used by authors without exception must be located in their designated component group folders.

components
    layout (component group)
        article

This structure will ensure that there components are orgnanised and easily found, additionlay grouping provides a segmentation structure that will allow component numbers scale without creating a mess in one folder.

Following is the list of name that should be used for making component groups

Group Name Description
Analytics analytics components for analytics use
Media media assets related component, galleries, asset lists, videos etc
Content content primary components used to store authored content, content from these components is used by other components for display
Cloud Services cloudservices components for cloud services
Layout layout provide layout ability, grouping and structuring of content, contain layout and style related content
List list find pages and show their data in lists, that contain layout and style related content
Details details used to create and display custom metadata for different page types
Widgets widgets used to provide client-side functionality, integrating 3rd party services
Commerce commerce used for eComponents, shopping carts, checkouts, product listing etc
Common common used for shared components that are available to authors
Forms forms used to contain all the components that are used in forms
Template template used to store all the available Template components that contain HTML page structures
Workflow workflow steps that appear in workflow editor

Groups are targeted to be meaning full to Authors, when creating new groups consider how to explain component groups to authors.

Component Variants and Badges

Component Variants are used as a pattern to output diffrent HTML templates for the same comonent content.

Component Variants

Every component will have unique fields that are used by authors for content input. Usually these fields will be tied to a specific visual representation of that content. There are scenarios where you need to be able to output diffrent HTML strucutre of a component data, to achieve Component Variant pattern is used.

Component Variants Entity Diagram

Each component should have at least one default Variant and component template should provide a content driven approach to loading new Variants see HTL Comonent Template for example. This allows authors to choose from a dropdown list which variant they require.

Additionally Component Variants concepts is applied as a convention to allow a component to control display of its content in related components. This enables to abstract all of the ways that content of a component that can be presented in the component that has the content. This pattern removes dependecies of one component on structure of another component.

Component Badges

If a component needs to leverage content of another component it used Sling selectors to call component with a specific badge name. This further allows asbtracting all of the structure into content and create an Author controlled presentation pattern.

Component Structure

All component must follow consistent patterns that help everyone to understand consistency across components. Following is the conventions that are used for components

Node Purpouse Usercase Extension
_cq_design_dalog Authoring dialog that will be used to configure component mandatory xml
_cq_design Primary Authoring fo interacting with component properties mandatory xml
.content.xml component authoring metadata mandatory xml
_cq_editConfig.xml component authoring configuration for Editor UI mandatory xml
README.md readme describing the component which is viewed in the Components Experience in AEM mandatory md
variant.default. default template for display mandatory jsp,html
component. default boostrap of component mandatory jsp,html
badge.default. default badge to be used by other components for representing current component content optional jsp,html

Component Client Libs Namespace Convention

Components that have specific clientlibs should have following client libs assigned if they will be loaded by clientlibs in template footer and headers.

Paths of component namespace should be following the location of the component path eg .components... with repeating for each subfolder leading up to component folder.

Namespace Purpose
.components.author specifies that this component library should be loaded into global authoring clientlibs
.components.publish specifies that this component library should be loaded into global publish clientlibs
.components. specific group for the component to be used for specific usage such as component loading its own client libs

Component Versioning

Initialy when component is written its acceptable to have the component code to be located under its folder path.

components
    layout
        article
            <component structure>

Where possible initial component structure should be located in the v1 subfolder, this will also ensure that evolution of component will require marginally less effort.

components
    layout
        article ( sling:resourceSuperType → v1/article )
            v1
                <component structure>

As component is evolves into a versions a subfolders under the component path should be used to separate component code and component path should be pointed to the specific version all existing references of component should be using.

components
    layout
        article ( sling:resourceSuperType → v2/article )
            v1
                article
            v2
                article

This will ensure that all existing pages will continue working as expected.

Component Client Libs Modules

Component generic client libs should to be located in the component folder

components
    layout
        article
            clientlibs (version agnostic client libs)
                css/
                js/
                css.txt
                js.txt
            v1
                article 
                clientlibs (version specific client libs) 
                <component structure>
            v2
                article
                clientlibs (version specific client libs) 
                <component structure>

JSP Component Template

For ease of understanding component code following sections should be used as reference

LABEL Description
** COMPONENT DEPENDECIES ** used for describing dependecies
** COMPONENT DECLARATIONS **  
** COMPONENT LOGIC **  
** COMPONENT TEMPLATING **  

Following is a sample for component bootrstap, please remove annotations when using as template.

*** COMPONENT DEPENDECIES ***
<%@ page import="com.google.common.base.Throwables" %>
<%@ include file="/apps/aemdesign/global/global.jsp" %>
<%@ include file="/apps/aemdesign/global/images.jsp" %>
<%@ include file="/apps/aemdesign/global/components.jsp" %>
<%
*** COMPONENT DECLARATIONS ***
 
    final String DEFAULT_ARIA_ROLE = "article";
 
    // {
    //   1 required - property name,
    //   2 required - default value,
    //   3 optional - name of component attribute to add value into
    //   4 optional - canonical name of class for handling multivalues, String or Tag
    // }
    Object[][] componentFields = {
        {FIELD_ARIA_ROLE,DEFAULT_ARIA_ROLE, DEFAULT_ARIA_ROLE_ATTRIBUTE},
        {FIELD_VARIANT, DEFAULT_VARIANT},
    };
 
    ComponentProperties componentProperties = getComponentProperties(
            pageContext,
            componentFields,
            DEFAULT_FIELDS_STYLE,
            DEFAULT_FIELDS_ACCESSIBILITY);
 
*** COMPONENT LOGIC ***
    componentProperties.put(DEFAULT_BACKGROUND_IMAGE_NODE_NAME,getBackgroundImageRenditions(pageContext));
 
%>
*** COMPONENT TEMPLATING ***
<c:set var="componentProperties" value="<%= componentProperties %>"/>
<%@ include file="/apps/aemdesign/global/component-background.jsp" %>
<c:choose>
    <c:when test="${componentProperties.variant eq DEFAULT_VARIANT}">
        <%@ include file="variant.default.jsp" %>
    </c:when>
    <c:otherwise>
        <%@ include file="variant.default.jsp" %>
    </c:otherwise>
</c:choose>
<%@include file="/apps/aemdesign/global/component-badge.jsp" %>

HTL Component Template

Following is a sample for component bootrstap

<sly data-sly-use.component="design.aem.models.v2.layout.Article"
     data-sly-use.variant="${component.componentProperties.variantTemplate}"
     data-sly-use.background="aemdesign/global/templates/component-background.html"
     data-sly-use.info="aemdesign/global/templates/component-info.html">
 
    <!--/* print component background */-->
    <sly data-sly-call="${background.variant @ componentId=component.componentProperties.componentId, assets=component.componentProperties.bgimage.componentBackgroundAssets}"></sly>
 
    <!--/* print component variant */-->
    <sly data-sly-call="${variant.variant @ componentProperties=component.componentProperties}"></sly>
 
    <!--/* print component info in edit mode */-->
    <sly data-sly-call="${info.variant @ componentProperties=component.componentProperties, component=component}"></sly>
 
</sly>

HTL Component WCMUsePojo Class

Following is a reference for component Class that will be backing each HTL component

package design.aem.models.v2.details;
 
import com.adobe.cq.sightly.WCMUsePojo;
import design.aem.components.ComponentProperties;
import design.aem.utils.components.TagUtil;
import com.day.cq.i18n.I18n;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
public class PageDetails extends WCMUsePojo {
 
    private static final Logger LOGGER = LoggerFactory.getLogger(PageDetails.class);
 
    private ComponentProperties componentProperties = null;
    public ComponentProperties getComponentProperties() {
        return this.componentProperties;
    }
     
    @Override
    public void activate() throws Exception {
         
        com.day.cq.i18n.I18n _i18n = new I18n(getRequest());
 
        //COMPONENT STYLES
        // {
        //   1 required - property name,
        //   2 required - default value,
        //   3 optional - name of component attribute to add value into
        //   4 optional - canonical name of class for handling multivalues, String or Tag
        // }
        Object[][] componentFields = {
            {FIELD_VARIANT, DEFAULT_VARIANT},
        };
        componentProperties = ComponentsUtil.getComponentProperties(
            this,
            componentFields,
            DEFAULT_FIELDS_STYLE,
            DEFAULT_FIELDS_ACCESSIBILITY,
            DEFAULT_FIELDS_ANALYTICS
        );
         
    }
}

ClientLibs Convention - JavaScript

When writing component specific API code in Java script pelase ensure you encapsulating namespacing that does not direclty depend on external dependecies.

JavaScript for all Component JavaScript code must be divided into:

  1. functions.js - component functions API, has all of the required static utility functions that will be used by component behaviours. These should only be functions and should not be binding to elements of the page on load events.
  2. behaviour.js - component binding API, has all of the load events and binding to HTML, uses functions to help with structure and reusable config as required. This should be the main file that demonstrates how component is being boudn to the code.

JS modules should be located as following

components
    layout
        article
            clientlibs (version agnostic client libs)
                css/
                js/
                    functions.js
                    behaviour.js
                css.txt
                js.txt

Following is an example of a function used for component.

//search - functions
window.AEMDESIGN = window.AEMDESIGN || {"jQuery":{}};
window.AEMDESIGN.components = AEMDESIGN.components || {};
window.AEMDESIGN.components.search = AEMDESIGN.components.search || {};
 
(function ($, _, ko, log, utils, ns, window, undefined) { //NOSONAR convention for wrapping all modules
 
    "use strict";
    var _version = "0.1";
 
    ns.version = function () {
        return _version;
    };
 
    ns.init = function($el) {
        //INIT CODE
    };
 
})(AEMDESIGN.jQuery,_,ko, AEMDESIGN.log, AEMDESIGN.utils, AEMDESIGN.components.search, this);

Following is an example of a behaviours used for component.

//search - behaviour
window.AEMDESIGN = window.AEMDESIGN || {"jQuery":{}};
window.AEMDESIGN.components = AEMDESIGN.components || {};
window.AEMDESIGN.components.search = AEMDESIGN.components.search || {};
 
(function ($, _, ko, utils, log, search, window, undefined) { //NOSONAR convention for wrapping all modules
 
    $(document).ready(function () {
 
        $("[data-modules*='search']").each(function() {
 
            var base = $(this);
 
            search.init(base);
 
 
        });
 
 
    });
 
})(AEMDESIGN.jQuery, _, ko, AEMDESIGN.utils, AEMDESIGN.log, AEMDESIGN.components.search, this); //pass in additional dependencies

ClientLibs Convention - CSS

When writing CSS ensure that component Styles are added under component namespace. Here is an example

[component] .<component name> {
}

Dialog Conventions

Common Tabs for all component - use shared dialogs

<styleTab
    jcr:primaryType="nt:unstructured"
    jcr:title="Style"
    path="/apps/aemdesign/global/dialog/touch/style/content/items/styleTab"
    resourceType="granite/ui/components/foundation/section"
    sling:resourceType="granite/ui/components/coral/foundation/include"/>

Use Tags as source for your Dropdowns - will ensure that content can be updated later

<cancelinheritparent
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/select"
    fieldLabel="Cancel Inherit Parent"
    name="./cancelInheritParent"
    emptyOption="{Boolean}true"
    value="">
    <datasource
        jcr:primaryType="nt:unstructured"
        sling:resourceType="aemdesign/components/coral/datasources/tags"
        variant="valuelist"
        path="/content/cq:tags/${(empty tenant.id and empty tenantId) ? 'aemdesign' : (empty tenant.id ? tenantId : tenant.id )}/component-dialog/common/true-false"/>
</cancelinheritparent>

Don’t use Checkbox fields in dialogs - use Dropdowns so that its clear what if use has made a selection and what defauls are

<cancelinheritparent
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/select"
    fieldLabel="Cancel Inherit Parent"
    name="./cancelInheritParent"
    emptyOption="{Boolean}true"
    value="">
...
</cancelinheritparent>

Common Component Field Spec - define component fields in an array to use with component helper function ComponentsUtil.getComponentProperties

Object[][] componentFields = {
    {"author", pageAuthorFullName},
    {"authorUrl", pageAuthorEmail},
    {FIELD_VARIANT, DEFAULT_VARIANT}
};

Common Component Field Load - this will collect and construct all fields with defaults and select Design/Policy config as defaults

componentProperties = ComponentsUtil.getComponentProperties(
    this,
    componentFields,
    DEFAULT_FIELDS_STYLE,
    DEFAULT_FIELDS_ACCESSIBILITY);

Common Component Field Presets - use globaly defined fields spect to collect content from shared tabls in dialog

For Styles Tabs and Common Fields

//COMPONENT STYLES
// {
//   1 required - property name,
//   2 required - default value,
//   3 optional - name of component attribute to add value into
//   4 optional - canonical name of class for handling multivalues, String or Tag
// }
public static final Object[][] DEFAULT_FIELDS_STYLE = {
    {FIELD_STYLE_COMPONENT_ID, "", "id"},
    {FIELD_STYLE_COMPONENT_THEME, new String[]{}, "class", Tag.class.getCanonicalName()},
    {FIELD_STYLE_COMPONENT_MODIFIERS, new String[]{}, "class", Tag.class.getCanonicalName()},
    {FIELD_STYLE_COMPONENT_MODULES, new String[]{}, "data-modules", Tag.class.getCanonicalName()},
    {FIELD_STYLE_COMPONENT_CHEVRON, new String[]{}, "class", Tag.class.getCanonicalName()},
    {FIELD_STYLE_COMPONENT_ICON, new String[]{}, "class", Tag.class.getCanonicalName()},
    {FIELD_STYLE_COMPONENT_POSITIONX, "", "x"},
    {FIELD_STYLE_COMPONENT_POSITIONY, "", "y"},
    {FIELD_STYLE_COMPONENT_WIDTH, "", "width"},
    {FIELD_STYLE_COMPONENT_HEIGHT, "", "height"},
    {FIELD_STYLE_COMPONENT_SITETHEMECATEGORY, ""},
    {FIELD_STYLE_COMPONENT_SITETHEMECOLOR, ""},
    {FIELD_STYLE_COMPONENT_SITETITLECOLOR, ""},
    {FIELD_STYLE_COMPONENT_BOOLEANATTR, new String[]{}, " ", Tag.class.getCanonicalName()}, //#3" " =do not store content in data attributes
    {FIELD_STYLE_COMPONENT_ANIMATION_ENABLED, false},
    {FIELD_STYLE_COMPONENT_ANIMATION_NAME, StringUtils.EMPTY, "data-aos"},
    {FIELD_STYLE_COMPONENT_ANIMATION_ONCE, StringUtils.EMPTY, "data-aos-once"},
    {FIELD_STYLE_COMPONENT_ANIMATION_EASING, StringUtils.EMPTY, "data-aos-easing"},
    {FIELD_STYLE_COMPONENT_ANIMATION_DELAY, StringUtils.EMPTY, "data-aos-delay"},
    {FIELD_STYLE_COMPONENT_ANIMATION_DURATION, StringUtils.EMPTY, "data-aos-duration"},
};

Dialog Layout

Consistent dialog patern allow ease of understanding and experience of developing, using and training components to Authors.

Dialog Layout

Component Artifacts Alignment

All components must have matching artifact in related artifacts. This is to ensure consistency and completness of component delivery.

Component Artifacts Alignment

Using Tags for Styles

One of the main objectives of AEM.Design is to provide a consistent way of representing Design Languages as a taxonomy of Tags. This provide a mechanism to create Tags taxonomy by designers and then applied to components by authors.

Following image demonstrates shared Style dialog that is applied to all components.

Shared Dialog - Styles

Following table describes each available option. Each of the options is defined in a way to be able to easily apply to Design Systems and so that it can be easily understood by Authors.

Field Type Description
ID text generates an Id attribute for a component
Theme tags used to apply style one or combination of nested components
Modifiers tags used to apply design system tweaks to components
Module tags used to apply behaviour to component
Chevron tags used to apply Chevron to a component
Icon tags used to apply Icon to the component
Boolean Attributes tags used to apply metadata attributes
Positions text used by Modules for placement of component in parent eg Tooltips
Size text used to add width and height attributes to component

All text fields provide direct input that is added to component wrapper. All Tag fields allow selecting multiple tags for each filed which enables flexibility when applying styles.

Technically a component wrapper tag is trying to create following footprint and it automated by using shared dialogs. This is reference output of share libraries to provide a consistent wrapper for all components

<div component id="${componentProperties.id}" 
    class="${componentProperties.name} ${componentProperties.theme} ${componentProperties.modifier} ${componentProperties.chevron} ${componentProperties.icon}"
    style="width:${componentProperties.width};height:${componentProperties.height};" 
    x="${componentProperties.x}" y="${componentProperties.y}"
    data-modules="${componentProperties.modules}"
    ${componentProperties.booleanAttributes}></div>

Furthermore using Tags allows storing Style related content in a central place which can be update by authors, using tags also has a benefit of translations and ability to change the actual value of tag class by updating tag entry.

Tags Authoring

These updates to tag authoring UI enable updating and adding values that correspond to CSS names in authoring UI.

Style Tags Content Generator

Compose project contains all of the CSS, JS and Tag YAML files that describe design being developed. This subproject is compiled and installed into AEM as a standalone package. Tag YAML files is used to specify design language mapping to CSS classes and during compiling Tags content is generated.

A custom Content Generator is used to generate Tags from YAML file similar to flowing

content/_cq_tags/aemdesign/component-style-icon: &component-style-icon
  feed/atom:
    flat: true
    prefix: 'fa fa-rss'
    title: 'Atom'

  feed/rss:
    flat: true
    prefix: 'fa fa-rss'
    title: 'RSS'

  social:
    prefixes:
      - facebook
      - facebook-f
      - instagram
      - linkedin
      - linkedin-in
      - pinterest-p
      - tumblr
      - tumblr-square
      - twitter
      - youtube
    valueFormat: fab fa-%%prefix%%
    title: '%%prefix_normalised%%'

Reference Tag YAML files can be found in your Componse project and will be located in the Compose content-generator/config/core folder if you have generated using the AEM.Design Archetype.

Tags:

Categories:

Updated:

Leave a Comment