> For AI agents: the complete documentation index is available at https://nhanbe.id.vn/llms.txt, the full documentation bundle is available at https://nhanbe.id.vn/llms-full.txt.

# Client Libraries

## Structure

```text
/apps/<site>/clientlibs/
└── clientlib-base/
    ├── .content.xml          # cq:ClientLibraryFolder
    ├── css.txt               # CSS inclusion order
    ├── js.txt                # JS inclusion order
    ├── css/
    │   ├── reset.css
    │   ├── variables.css
    │   └── base.css
    └── js/
        ├── utils.js
        └── main.js
```

`.content.xml`:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:cq="http://www.day.com/jcr/cq/1.0"
          xmlns:jcr="http://www.jcp.org/jcr/1.0"
          jcr:primaryType="cq:ClientLibraryFolder"
          categories="[<site>.base]"
          allowProxy="{Boolean}true"/>
```

| Property       | Purpose                                                         |
| -------------- | --------------------------------------------------------------- |
| `categories`   | names the category, included in HTL using this string           |
| `allowProxy`   | serves via `/etc.clientlibs/` — **always enable**, even on-prem |
| `dependencies` | loads other categories first (separate requests)                |
| `embed`        | merges other categories into this bundle (fewer requests)       |

## `css.txt` / `js.txt`

`css.txt`:

```text
#base=css
reset.css
variables.css
base.css
```

`js.txt`:

```text
#base=js
utils.js
main.js
```

File order = concatenation order. `#base=` sets the target folder prefix. Unlisted files are omitted from the bundle.

## Including in HTL

```html
<sly data-sly-use.clientlib="/libs/granite/sightly/templates/clientlib.html">
  <!-- CSS in <head> -->
  <sly data-sly-call="${clientlib.css @ categories='<site>.base'}"/>

  <!-- JS before </body> -->
  <sly data-sly-call="${clientlib.js @ categories='<site>.base'}"/>

  <!-- Both -->
  <sly data-sly-call="${clientlib.all @ categories='<site>.base'}"/>
</sly>
```

Multiple categories use an array: `categories=['<site>.base', '<site>.components']`.

## Dependencies vs Embed

`dependencies` = keeps separate requests to ensure load order. `embed` = combines into 1 request. Use embed for the site's final bundle, dependencies for shared libs requiring independent caching.

**dependencies:**

```xml
<jcr:root
  jcr:primaryType="cq:ClientLibraryFolder"
  categories="[<site>.components]"
  dependencies="[<site>.base]"
  allowProxy="{Boolean}true"/>
```

→ 2 separate requests: `&lt;site&gt;.base` → `&lt;site&gt;.components`

**embed:**

```xml
<jcr:root
  jcr:primaryType="cq:ClientLibraryFolder"
  categories="[<site>.site]"
  embed="[<site>.base,<site>.components]"
  allowProxy="{Boolean}true"/>
```

→ 1 request: `&lt;site&gt;.site` (contains both base + components)

## Component clientlib

```text
/apps/<site>/components/hero/
├── .content.xml
├── hero.html
├── _cq_dialog/.content.xml
└── clientlibs/
    ├── .content.xml
    ├── css.txt
    ├── js.txt
    ├── css/hero.css
    └── js/hero.js
```

The `.content.xml` of a component clientlib is similar, differing only in `categories`. Multiple components can use the **same category** (`&lt;site&gt;.components`) to bundle together.

## `ui.frontend` Module

SCSS/TS builds outside AEM, output copies to `ui.apps/.../clientlibs/`. Standard flow:

1. `npm run build` inside `ui.frontend/`
2. Output copied to `ui.apps/src/main/content/jcr_root/apps/&lt;site&gt;/clientlibs/`
3. Deploy Maven package into AEM

Dev loop: `aem-clientlib-generator` (standard archetype) or `aemsync`.

## Naming Conventions

| Category                    | Content                      | Load                        |
| --------------------------- | ---------------------------- | --------------------------- |
| `&lt;site&gt;.base`         | reset/fonts/variables/global | every page                  |
| `&lt;site&gt;.components`   | per-component CSS/JS         | every page                  |
| `&lt;site&gt;.site`         | embeds base + components     | every page (single include) |
| `&lt;site&gt;.dependencies` | third-party libs             | site dependency             |

## Debugging

- `?debugClientLibs=true` — view unminified libs loading on page
- `/libs/granite/ui/content/dumplibs.rebuild.html` — invalidate cache + rebuild (use when changes do not reflect)
- Check direct proxy file: `http://localhost:4502/etc.clientlibs/<site>/clientlibs/clientlib-base.css`

| Symptom                      | Check                              | Fix                                                          |
| ---------------------------- | ---------------------------------- | ------------------------------------------------------------ |
| CSS/JS not loading           | category string in HTL             | match `categories` in `.content.xml`                         |
| 404 on `/etc.clientlibs/...` | `allowProxy`                       | set `{Boolean}true` + deploy                                 |
| Deployed but changes missing | browser/dispatcher cache + rebuild | invalidate + rebuild libs                                    |
| Incorrect load order         | `dependencies`/`embed`             | move shared libs to dependencies; bundle app libs with embed |
| JS file does not execute     | `js.txt` + path + `#base`          | add file to `js.txt`, verify location                        |

- [Templates & Policies](https://nhanbe.id.vn/aem/foundation/templates-and-policies.md)
