Covers creation of AEM Client-Side Libraries or clientlibs to deploy and manage CSS and Javascript for an AEM Sites implementation. Integration with AEM's responsive grid and mobile emulator. aemfed module is used to accelerate front-end development.


This is Chapter 3 of the multi-part tutorial. Chapter 2 can be found here and an overview can be found here.

You can check out the finished code on GitHub or you can download the solution package:


Front-end Frameworks

Below are some of the front-end technologies used in the creation of the WKND site. The intention is to give you an idea of how 3rd party frameworks can easily be integrated into an AEM Site.

LESS - popular CSS pre-compiler that allows variables and other functionality. AEM client libraries natively support LESS compilation. Saas or other pre-compilers can be used but would need to be compiled outside of AEM.

jQuery - popular JavaScript feature-rich library for manipulating HTML

aemfed - npm module that leverages aemsync and BrowserSync to instantly deploy and reload changes to accelerate front-end development.


Client-Side Libraries Structure

Client-Side Libraries provides a mechanism to organize and manage CSS and JavaScript files necessary for an AEM Sites implementation. The basic goals for client libraries or clientlibs are:

  1. Store CSS/JS in small discrete files for easier development and maintenance
  2. Manage dependencies on 3rd party frameworks in an organized fashion
  3. Minimize the number of client-side requests by concatenating CSS/JS into one or two requests
  4. Minify CSS/JS that is delivered to optimize speed/performance of a site

More information about using Client-Side Libraries can be found here.

Client-Side Libraries provide many ways for organization, below is a convention used by the WKND site and WE.Retail sites and can be applied to most Sites implementations.

clientlib architecture
High Level Clientlib Architecture

Site Client-Side Libraries

We will now implement Client-Side libraries that contain styles and javascript that will be applied site wide. This includes some global styles and brand variables to create a consistent look and feel.

Creating a client library is similar to creating any other type of node in AEM. The basic structure includes:

[cq:ClientLibraryFolder] - jcr:primaryType
     - categories (String[])
     - embed (String[])
     - dependencies (String[])
     + css.txt (nt:file)
     + js.txt (nt:file)

There are a lot of nodes and files for the WKND site client libraries. To expedite the tutorial (and to avoid tedious steps) install the below package that includes several important client libraries for the WKND site. This will provide a base-line of styles for some of the foundational HTML elements. 

Client Libraries are considered code and thus are stored in the ui.apps module beneath /apps/wknd/clientlibs

  1. The AEM project archetype creates /apps/clientlibs/clientlib-base automatically. The below zip includes the source for the additional client libraries:

    • clientlib-author
    • clientlib-dependencies
    • clientlib-site
  2. Download

    Download and unzip directly inside your project on the file system beneath ui.appps/src/main/content/jcr_root/apps/wknd/clientlibs. (make sure to only copy the folders within the zip archive)

  3. In the ui.apps module there should now be a clientlibs folder beneath /apps/wknd. With 4 folders beneath the clientlibs folder:


    Below we will inspect the new clientlibraries of clientlib-dependencies, clientlib-site, and clientlib-author.

  4. clientlib-dependencies

    This clientlibrary embeds some common AEM javascript dependencies that would be used for integrating analytics, ContextHub, CSRF protection and other Adobe marketing cloud offerings. The version of jquery that ships with AEM (/libs/clientlibs/granite/jquery) will be used. You can download a newer version of jquery and create a new client library if necessary.


    Name Type Value Description
    (tutorial info only don't copy)
    jcr:primaryType Name cq:ClientLibraryFolder defines a client library
    allowProxy Boolean true exposes CSS/JS via /etc.clientlibs
    categories String[] wknd.dependencies
    category(s) that allows the clientlib to be referenced
    embed String[] jquery
    embeds jquery and several granite frameworks. Only a single version of jquery is actually embedded as both granite.jquery and cq.jquery are just wrappers.
    <?xml version="1.0" encoding="UTF-8"?>
    <jcr:root xmlns:cq="" xmlns:jcr=""


    The clientlib-dependencies typically consists mostly of Javascript. However since these dependencies are needed for features like ContextHub they will be included in the at the top of the page in the HTML head.

  5. clientlib-site

    This is the main client library for the WKND site. It includes site specific styles. It also includes two files named  variables.less and mixins.less that establish some leverage features of LESS to ensure consistency.

    Two files grid.less, main.less are inspected below in more detail.


    Name Type Value Description
    (tutorial info only don't copy)
    jcr:primaryType Name cq:ClientLibraryFolder defines a client library
    allowProxy Boolean true exposes CSS/JS via /etc.clientlibs
    categories String[]
    category(s) that allows the clientlib to be referenced

    main.less - /apps/wknd/clientlibs/clientlib-site/main.less

    This is the entry point for all of the LESS styles in the client library. At the top of the file variables.less and mixins.less are imported to be able to share with the rest of the files. At the bottom of main.less you can see the inclusion of component specific styles. Notice the organization folder structure for the component specific files.The main.less also includeds a file named grid.less that will be responsible for creating AEM's responsive grid. 

    /* WKND main.less */
    /* Import variables */
    @import "less/variables.less";
    /* Import mixins */
    @import "less/mixins.less";
    /* Import Responsive Grid */
    @import "less/grid.less";
    /* Custom Icons for WKND site */
    @import "less/wkndicons.less";
    /* Page Styles at the top */
    @import "components/page/page.less";
    /* Component Styles */
    @import "components/breadcrumb/breadcrumb.less";
    @import "components/contentfragment/contentfragment.less";
    @import "components/image/image.less";
    @import "components/text/text.less";
    @import "components/title/title.less";

    grid.less - /apps/wknd/clientlibs/clientlib-site/less/grid.less

    Generates the AEM responsive grid that will allow content authors to use all the capabilities of the AEM Layout Mode and the Layout Container. It uses a custom mixin found in grid_base.less to generate the grid. Notice that the Phone and Tablet breakpoints use variables found in the less/variables.less file. These breakpoints match the breakpoints defined in the empty template type: /conf/wknd/settings/wcm/template-types/empty-page/structure/jcr:content/cq:responsive/breakpoints. 

    grid_base.less - /apps/wknd/clientlibs/clientlib-site/less/grid_base.less

    grid_base.less is a file provided by AEM that has been copied into the WKND project's library for simplicity and portability between versions of AEM.


    The location of the grid_base LESS file provided by AEM has changed from 6.3 to 6.4. If referencing the file directly in your own project ensure that the correct location is used:

    • 6.3 Path to Responsive Grid: /etc/clientlibs/wcm/foundation/grid/grid_base.less
    • 6.4 Path to Responsive Grid: /libs/wcm/foundation/clientlibs/grid/grid_base.less

     * grid_base.less is a file provided by AEM. 
     * For portability between versions of AEM the file has been copied into WKND project's client library.
     * 6.3 Path to Responsive Grid: /etc/clientlibs/wcm/foundation/grid/grid_base.less
     * 6.4 Path to Responsive Grid:  /libs/wcm/foundation/clientlibs/grid/grid_base.less
    @import "grid_base.less";
    /* maximum amount of grid cells to be provided */
    @max_col: 12;
    /* default breakpoint */
    .aem-Grid {
        .generate-grid(default, @max_col);
        width: auto;
    /* phone breakpoint */
    @media (max-width: @screen-small) {
        .aem-Grid {
            .generate-grid(phone, @max_col);
    /* tablet breakpoint */
    @media (min-width: (@screen-small + 1)) and (max-width: @screen-large) {
        .aem-Grid {
            .generate-grid(tablet, @max_col);
    .aem-GridColumn {
        padding: 0 @gutter-padding;
    .responsivegrid.aem-GridColumn {
    	padding-left: 0;
    	padding-right: 0;

    Within clientlib-site you will also notice a folder named webfonts. This is a small clientlibrary that includes a call to Google webfonts. This has been split from the rest of clientlib-site in order to load this clientlibrary at the top of the CSS file. In the next section we will embed this clientlibrary as part of clientlib-base.

  6. clientlib-author

    As a best practice the code base for the Author environment should be nearly identical to the Publish environment. There are certain cases in which some extra CSS/JS is necessary to provide a better authoring experience. The WKND site has a fixed-header design and in the Editor this makes it difficult to select the Header component in edit mode. Therefore a small amount of CSS overrides will be stored in this client library and only loaded in the Author environment. This practice should be used sparingly and only when absolutely necessary.

    Name Type Value Description
    jcr:primaryType Name cq:ClientLibraryFolder defines a client library
    categories String[]
    category(s) that allows the clientlib to be referenced

Update clientlib-base

  1. clientlib-base

    This clientlibrary, which is automatically generated by the archetype, represents the base level of CSS and JavaScript needed for the WKND site to function. This library won't include any CSS/JS files directly, instead it will include multiple client libraries via the embed property. 

    Update the embed property to include two WKND specific client libraries. Include wknd.webfonts as the first embed. Append to the end of the String array.


    The order in which the embed array matters. It determines the order in which libraries are included. We want the webfonts to be loaded first and we want our site specific libraries to be loaded last to ensure we can override styles inherited from core components.

    Name Type Value Description
    jcr:primaryType Name cq:ClientLibraryFolder defines a client library
    allowProxy Boolean true exposes CSS/JS via /etc.clientlibs
    categories String[] wknd.base category(s) that allows the clientlib to be referenced
    embed String[]



    embeds the clientlibs by their respective category. Include any CoreComponent clientlibs here. Also including bootstrap but just the JS. LESS imports is used to include the rest of the bootstrap library. The category embeds the clientlib-site library. wknd.webfonts is split and embedded at the top so that the webfonts are correctly imported at the top of the generated CSS file.
    <?xml version="1.0" encoding="UTF-8"?>
    <jcr:root xmlns:cq="" xmlns:jcr=""


    Several Core Components come with client libraries that need to be included on the page in order for the component to function properly. For example see /apps/core/wcm/components/image/v2/image/clientlibs

    There are two client libraries in this folder one named editor and another named site. The editor clientlib is meant just for the author environment and typically included by the component dialog. The site clientlib is the one that needs to be included in the project's clientlib in order for the component to behave properly. For each Core Component you can view the to understand what each clientlib does: /apps/core/wcm/components/image/v2/image/

Implement Header Styles

Next we will implement some basic styles for the Header component. We want to make the header component behave as a "sticky" header and scroll with the page. We also want to resize the header to optimize for tablet/mobile views. LESS and JavaScript will be added to the clientlib-site library.

  1. Implement Page Scroll listener

    We will add a piece of javascript that will listen for a browser scroll and inject a CSS class into the body tag of the HTML page. We can then modify CSS based on this class change. We could have made this javascript specific to just the Header component, however we may want other scroll behavior across components. We will add this script with the global styles with the other page styles.

    Beneath /apps/wknd/clientlibs/clientlib-site/components/page add a new file named page.js

  2. Populate page.js with the following: 

    /* JS Snippet to trigger class change on page scroll */
    (function (element, $) {
        'use strict';
        var target = $(element),
            className = "scrolly",
            mobileBreakpoint = 992;
        if($(window).scrollTop() > 0) {
             scroll = $(window).scrollTop();
        if(scroll > 0 ) {
        } else {

    The above function will add a CSS class named "scrolly" to the body tag whenever the scroll position changes form 0. This javascript depends on jQuery being loaded. The clientlib-dependencies library will include jQuery on the page.

  3. Update /apps/wknd/clientlibs/clientlib-site/js.txt to include page.js. The js.txt and css.txt files in a client library act as a manifest for which files to be surfaced and the order in which they will be loaded. Simply adding a file in the client library is not enough to include it in library.

    Update js.txt:

  4.  Beneath /apps/wknd/clientlibs/clientlib-site/components create the following folder and file structure for the header styles:

            + header.less
                + default.less
  5. Populate header.less to include the default style for the Header component:

    /* WKND Header Styles */
    @import "styles/default.less";

    Populate default.less with the following: 

    /* WKND Header - default.less */
    .wknd-header {
    	z-index: 1030;
    	position: fixed;
    	top: 0;
    	padding-top: 45px;
    	.container {
    		max-width: 1116px;
    	 @media (max-width: @screen-medium) {
    	 	padding: 12px;
        	top: 0;
        	left: 0;
        	right: 0;
    		height: 60px;
    		position: absolute;
    	a.wknd-header-logo {
    		color: @text-color;
    	    font-weight: bolder;
    	    font-size: @font-size-xlarge;
    	    padding: 11px 12px;
    	    line-height: 28px;
    		height: 50px;
    		padding-left: 0;
    	    text-decoration: none;
    		text-align: left;
    		@media (max-width: @screen-medium) {
    	 		font-size: (@font-size-large + 8px);
    			margin-left: 0;
    			padding-top: 10px;
    			padding-left: @gutter-padding;
    	h1 {
    		float: left;
    	    margin-top: 0.25em;
    	    font-size: 1.5em;
    .scrolly {
    	.wknd-header {
    		@media (min-width: (@screen-medium + 1)) {
    			height: 80px;
    			background-color: @body-bg;
    			-webkit-box-shadow: 0px 8px 20px 0px rgba(0,0,0,0.26);
    			-moz-box-shadow: 0px 8px 20px 0px rgba(0,0,0,0.26);
    			box-shadow: 0px 8px 20px 0px rgba(0,0,0,0.26);

    Notice that at the bottom of the file we add a box-shadow to the header div based on the addition of the scrolly class name. The scrolly class name will be populated based on the JavaScript added earlier when the page scrolls.

  6. Lastly, update /apps/wknd/clientlibs/clientlib-site/main.less to include the header.less style: 

    /* main.less */
    /* Component Styles */
       @import "components/breadcrumb/breadcrumb.less";
       @import "components/contentfragment/contentfragment.less";
       /* add header.less */
       @import "components/header/header.less";
       @import "components/image/image.less";
       @import "components/layout-container/layout-container.less";
       @import "components/text/text.less";
       @import "components/title/title.less";

Adding Clientlibs to the Page

Next the clientlibs needed to be included via the base page. The Core Component page provides two files designed to include custom clientlibs in the html head for CSS and at the very bottom of the page for JavaScript. The AEM project archetype has already included the wknd.base client library.

Updates are needed to include wknd.dependencies and in the authoring environment

In the ui.apps module modify files beneath: /apps/wknd/components/structure/page

  1. Update /apps/wknd/components/structure/page/customheaderlibs.html

    This will override /apps/core/wcm/components/page/v2/page/customheaderlibs.html

    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <sly data-sly-use.clientLib="/libs/granite/sightly/templates/clientlib.html"
         data-sly-call="${clientlib.js @ categories='wknd.dependencies'}"/>
    <sly data-sly-use.clientLib="/libs/granite/sightly/templates/clientlib.html"
         data-sly-call="${clientlib.css @ categories='wknd.base'}"/>
    <!--/* Load authoring related client libraries */-->
    <sly data-sly-test="${wcmmode.preview || wcmmode.edit}" data-sly-call="${clientLib.all @ categories=''}"/>
    <!--/* Include Context Hub */-->
    <sly data-sly-resource="${'contexthub' @ resourceType='granite/contexthub/components/contexthub'}"/>

    This includes the CSS for the wknd.base client library and the JS for the wknd.dependencies client library. Typically it is a best practice to include JS at the bottom of the page. The wknd.dependencies JS is used for loading libraries that support targeted content like ContextHub and so in this case it is a best practice to load it at the top of the page. The author-specific client library is also loaded. Notice the wcmmode conditional, this means that only if the page is open in the Author editing environment will this client library be loaded. More information about the WCMMode can be found here.

    ContextHub is a JS based framework for personalization, it is loaded in the header. This tutorial does not cover implementing ContextHub but more information about ContextHub can be found here.

  2. View file customfooterlibs.html

    This will be included at the very bottom of the page and will include the JS for wknd.base client library. It will override: /apps/core/wcm/components/page/v2/page/customfooterlibs.html. No changes are needed as the AEM project archetype created this file already.

    <sly data-sly-use.clientlib="/libs/granite/sightly/templates/clientlib.html">
        <sly data-sly-call="${clientlib.js @ categories='wknd.base'}"/>
  3. Deploy to AEM

    Deploy the code base to add both client libraries and updated page component to the local AEM instance with Eclipse dev tools or with the following Maven command:

    $ mvn -PautoInstallPackage -Padobe-public clean install
  4. Verify the inclusion of client libraries

    Updating the base page component will update any pages created via the WKND Article Template. 

    1. Open up a page created from the Article Template: http://localhost:4502/editor.html/content/wknd/en/first-article.html. You should see the updated fonts and fixed width container.
    2. View the page as published by clicking the Page Properties menu icon (upper left, next to the sidebar icon) > "View as Published" button
    3. This will open a new tab with wcmmode=disabled added as a URL parameter. Viewing the page with wcmmode=disabled is a great way to debug/develop as there is a significant amount of Javascript for the Sites editor to support component authoring. http://localhost:4502/content/wknd/en/first-article.html?wcmmode=disabled
    4. Viewing the page source you should see following tags:


    /* Source of http://localhost:4502/content/wknd/first-article.html?wcmmode=disabled */
            <script type="text/javascript" src="/etc.clientlibs/wknd/clientlibs/clientlib-dependencies.js"></script>
            <link rel="stylesheet" href="/etc.clientlibs/wknd/clientlibs/clientlib-base.css" type="text/css">
                <script type="text/javascript" src="/etc.clientlibs/wknd/clientlibs/clientlib-base.js"></script>

    The main takeaway is that the CSS and Javascript is dynamically loaded from a path that starts with /etc.clientlibs. The client libraries are stored beneath /apps/wknd to make it easier from an organizational standpoint as it is in the same directory as our component code. It is critical on the publish side that the client libraries are not served from /apps as this path should be restricted for security reasons using the Dispatcher filter section. The allowProxy property of the client library ensures the CSS and JS are served from /etc.clientlibs. 

  5. While view the page with wcmmode=disabled check that the scrolling header is working when the browser is sized > 992px.


Debugging Client Libraries

With different methods of categories and embeds to include multiple client libraries it can be cumbersome to troubleshoot. AEM exposes several tools to help with this. One of the most important tools is Rebuild Client Libraries which will force AEM to re-compile any LESS files and generate the CSS. 

  • Dump Libs - Lists all of the client libraries registered in the AEM instance. <host>/libs/granite/ui/content/dumplibs.html
  • Test Output - allows a user to see the expected HTML output of clientlib includes based on category. <host>/libs/granite/ui/content/dumplibs.test.html
  • Libraries Dependencies validationhighlights any dependencies or embedded categories that cannot be found. <host>/libs/granite/ui/content/dumplibs.validate.html
  • Rebuild Client Libraries - allows a user to force AEM to rebuild all of the client libraries or invalidate the cache of client libraries. This tool is particularly effective when developing with LESS as this can force AEM to re-compile the generated CSS. In general it is more effective to Invalidate Caches and then perform a page refresh versus rebuilding all of the libraries. <host>/libs/granite/ui/content/dumplibs.rebuild.html



Developing with aemfed

aemfed is a open-source, commandline tool that can be used to speed up front-end development. It is powered by aemsync, Browsersync and Sling Log Tracer

At a high level aemfed listens to file changes and automatically syncs the to a running AEM instance. Based on the changes, a local browser will automatically refresh, thereby speeding up front-end development. It also is built to work with Sling Log tracer to automatically display any serverside errors directly in the terminal. Full documentation can be found here.


We will now walk through the steps of installing aemfed globally. For the latest and greatest instructions make sure to check the official aemfed documentation

  1. A key part of aemfed is the ability to relay errors directly to the command line. To enable this behavior we need to update the configuration for Sling Log Tracer. 

    Navigate to http://localhost:4502/system/console/configMgr and search for Apache Sling Log Tracer. Update the configuration to Enabled and Recording Servlet Enabled



    Apache Sling Log Tracer version 1.0.0 or newer should be used and should only be enabled in development and other non-production environments. 

  2. To install aemfed you will need npm installed available via the commandline:

    $ npm -help
    Usage: npm <command>
    where <command> is one of:
        access, adduser, audit, bin, bugs, c, cache, ci, cit,
        completion, config, create, ddp, dedupe, deprecate,
        dist-tag, docs, doctor, edit, explore, get, help,
        help-search, hook, i, init, install, install-test, it, link,
        list, ln, login, logout, ls, outdated, owner, pack, ping,
        prefix, profile, prune, publish, rb, rebuild, repo, restart,
        root, run, run-script, s, se, search, set, shrinkwrap, star,
        stars, start, stop, t, team, test, token, tst, un,
        uninstall, unpublish, unstar, up, update, v, version, view,

    If you do not have npm installed, the easiest way is to download and install Node.js

  3. Install aemfed globally with the following command:

    $ npm install aemfed --global
    # (aemfed is installed globally)
    $ aemfed -h
    Usage: aemfed [OPTIONS]
      -t targets           Default is http://admin:admin@localhost:4502
      -p proxy_port        Default is 3000
      -w path_to_watch     Default is current
      -e exclude_filter    Anymatch exclude filter; disabled by default
      -i sync_interval     Update interval in milliseconds; default is 100
      -o open_page         Browser page to be opened after successful launch; default is "false".
      -b browser           Browser where page should be opened in; this parameter is platform dependent; for example, Chrome is "google chrome" on OS X, "google-chrome" on Linux and "chrome" on Windows; default is "google chrome"
      -h                   Displays this screen
      -v                   Displays version of this package


    This will allow us to run aemfed from any directory. See the aemfed documentation for instructions on how to add it to a project that uses package.json

  4. From the command line navigate to the <src>/aem-guides-wknd directory. Run the following command to start aemfed against an aem instance running on localhost:4502:

    aemfed -t "http://admin:admin@localhost:4502" -e "**/*___jb_+(old|tmp)___" -w "ui.apps/src/main/content/jcr_root/"
    [Browsersync] Proxying: http://localhost:4502
    [Browsersync] Access URLs:
           Local: http://localhost:3000
              UI: http://localhost:3001
     UI External: http://localhost:3001
  5. Navigate using the proxy URL to the AEM page you have been working with:


    You should now see the same contents of AEM but proxied via port 3000.

  6. Update some of the .less files like header.less to see the changes automatically reflected in the browser


Authoring with Responsive Grid

At this point everything needed to leverage AEM's Responisve Grid and Layout features should be enabled. Try resizing a few components using different device widths to see this working.

  1. Add a text component and use the Layout Mode to shrink it to half the page width. Repeat with a second text component to get 2 components side by side.


    Currently the layout takes the full width of the page and it may be a little difficult to find the handles. In the next chapter we will correct this with some styles to the Layout Container. 


    Populate the Text Component with some text. 

  2. Add 3 Image components to the page. Next resize each of them so that the three Image components equal 1/3 of the page width and sit side by side.


    Populate the images with some pictures.

  3. While in Layout mode use the emulator to preview the page in Mobile views. Notice that the columns simply shrink, creating a layout that is not optimized for the screen device.


    Resize the components to make them stack vertically in Mobile view by expanding the width to full page width for each one.

  4. Upload relevant images to the DAM (optional)

    You can upload several images to AEM Assets that match the WKND lifestyle design. These images will be used later. Imagery is license-free from

    You can download and install the below package on your local AEM instance.



    The storing of DAM assets outside of AEM and in source control is rare. If you are following the tutorial on the Git repo we have added the DAM assets to the project. This is to allow someone to pick up the tutorial at any point but the storing of a lot of assets in source control is not a best practice.


If you get stuck or have additional questions make sure to check out the Experience League forums for AEM or view existing GitHub issues

Didn't find what you were looking for? Think you found an error? Please file a GitHub issue for the WKND project

Next Steps

Next part in the tutorial: Getting Started with AEM Sites Chapter 4 - Developing with the Style System

View the finished code on GitHub or download the finished package for this part of the tutorial: