When a component needed styling, we dropped a <style> block — and sometimes a <script> — right inside the component's HTML. It worked. It's also the wrong way to ship it.
We opened the page component: two files, customheaderlibs.html and customfooterlibs.html. I said clientlibs would one day plug in there.
<style> .cmp-callout { background:#0c0d10; border:1px solid #d32f2f; … } .cmp-callout__button { background:#d32f2f; color:#fff; … } </style> <div class="cmp-callout"> … Helpful <span>0</span> </div> <script> // a click counter, right here in the component </script>
Not in <head> — the page can paint before styles arrive.
Not before </body> — can block rendering of everything below.
CSS, JS and markup all in one file — nothing reusable.
clientlib-callout cq:ClientLibraryFolder ├── css │ └── callout.css ├── js │ └── callout.js ├── css.txt └── js.txt // folder property categories = [aeminsider.callout]
The name you reference it by. You never link the .css by path.
Which files go in, and in what order.
${clientlib.css @ categories='…'} — ask by category, AEM builds the URL.
<head> … </head> <body> … first Callout … <link href="/apps/…/callout.css"> <script src="/apps/…/callout.js"> … second Callout … // no duplicate </body>
${clientlib.css @ categories='aeminsider.callout'}
${clientlib.js @ categories='aeminsider.callout', async=true}
Works for admin on the author. On the publish server, /apps is restricted — the link breaks, the page loads unstyled.
A public copy of the compiled file. Anyone can open it. The source stays private under /apps.
Load A and AEM follows the whole chain. C, B and A — all three end up on the page.
Load A and you get B + A only, combined into one file. C is left out.
Every component file we've written ends in .html — but it isn't HTML. It's HTL (a.k.a. Sightly), AEM's template language: data-sly-use, data-sly-list, data-sly-test, the ${…} expressions and their contexts — the language behind everything we've built.