Skip to content

UI Frameworks & Libraries

UI Libraries

A UI Library is a standalone bundle of CSS, JavaScript, and HTL templates that a UI Framework can pull in. Because Kestros keeps components separate from their views, libraries let you package and version front-end assets once and reuse them across many sites in the same instance.

Naming. These were previously called Vendor Libraries, and the code still uses that name throughout: a framework lists its libraries through the kes:vendorLibraries property, and library nodes live under /etc/vendor-libraries. This page uses UI Library throughout, but the JCR paths and property names below are the real, current ones.

What a UI Library is for

Every page and site in Kestros is configured with a UI Framework, which decides two things: what CSS and JavaScript is included on the page, and which HTL templates render at the component level. A UI Library is a self-contained unit that a framework can include to supply those assets.

Splitting front-end code into libraries keeps CSS, JavaScript, and HTL separated for multisite and microsite setups. You can create several libraries and assign them to independent sites managed within the same instance. As a rule of thumb, the more you push into UI Libraries, the less you have to manage inside any single framework, and the more reuse you get out of your code.

The simplest library

Before the versioning and packaging details, here is the smallest thing that counts as a UI Library: a single kes:VendorLibrary node with a css folder inside it. No versions/ folder, nothing else. This is a real one from the sample sites, a styleless library shipping a single stylesheet:

JCR structure
/etc/vendor-libraries/lorem-ipsum-site-vendor-library/   # kes:VendorLibrary
  css/                                                    # sling:OrderedFolder
    lorem-ipsum-sample-site-library.less
.content.xml
<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0"
    jcr:primaryType="kes:VendorLibrary"
    jcr:title="Lorem Ipsum Generic Library"
    jcr:description="Styleless library. Black text on a white background."
    documentationUrl="https://kestros.io"/>

That is a working library. Everything below, versioning, the css/js folder details, and filter.xml packaging, is refinement on top of this shape.

Versioning UI Libraries

A UI Library does not have to be versioned, but it can be. In a managed library each version is its own node, and several versions of the same library can exist at once. That gives you conflict-free upgrades: when you use a versioned front-end framework such as Tailwind, Bootstrap, Vue, or Svelte, you can stand up each version as its own node and avoid dependency clashes for legacy sites.

Example. A framework is built on Vue 1.x. A new feature needs Vue 2.x. Both versions can live side by side in the UI Libraries, each supporting its own components, until every component has migrated to the new version.

Location and structure

Project UI Libraries are created under /etc/vendor-libraries. For a managed (versioned) library the root node is a kes:ManagedVendorLibrary whose versions live beneath a versions folder; every version carries its own css and js folders:

JCR structure
/etc/vendor-libraries/my-ui-library/       # kes:ManagedVendorLibrary  (root)
  versions/
    1.0.0/                           # kes:VendorLibrary  (a version)
      css/                          # sling:OrderedFolder  (include=[...])
        styles.css
      js/                           # sling:OrderedFolder  (include=[...])
        scripts.js

The library root node

For a managed library, create the library folder and a .content.xml for its root node. The root is a kes:ManagedVendorLibrary:

Required property:

PropertyValue
jcr:primaryTypekes:ManagedVendorLibrary

Configurable properties (in .content.xml):

Req: Y = required, Rec = recommended, blank = optional
PropertyTypeReqDescription
jcr:titleStringTitle of the UI Library as it appears in the system
kes:libraryCodeStringShorthand code used for component-view folder structures; defaults to the node name if omitted
jcr:descriptionStringDescription of the UI Library
documentationUrlStringExternal URL for third-party documentation
fontAwesomeIconStringFont Awesome icon class shown for the library in the Site Admin UI
my-ui-library/.content.xml
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
          xmlns:jcr="http://www.jcp.org/jcr/1.0"
          xmlns:kes="http://kestros.io/kes/1.0"
          jcr:primaryType="kes:ManagedVendorLibrary"
          jcr:title="My UI Library"
          kes:libraryCode="my-ui-library"
          jcr:description="My custom UI Library"/>

Adding CSS and JavaScript

Assets live under a version node. Create a version folder whose name is a semantic version number (major.minor.patch), then add css and js folders inside it. Any CSS or JavaScript files go into the matching folder.

The version folder is itself a kes:VendorLibrary. Give it a .content.xml with its own properties:

PropertyTypeReqDescription
jcr:primaryTypeYMust be kes:VendorLibrary
jcr:titleStringDisplay name in the system view and authoring dialogs
jcr:descriptionStringDescription of the UI Library version
dependenciesString[]Other libraries this version needs, each entry node-name/minVersion:maxVersion (for example vendor-library/0.0.1:1.0.0)
documentationUrlStringExternal URL for third-party documentation
versions/1.0.0/.content.xml
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
          xmlns:jcr="http://www.jcp.org/jcr/1.0"
          xmlns:kes="http://kestros.io/kes/1.0"
          jcr:primaryType="kes:VendorLibrary"
          jcr:title="My UI Library"
          jcr:description="Description of my UI Library"/>

The source files sit in the JCR at /etc/vendor-libraries/my-ui-library/versions/1.0.0/css/styles.css and /etc/vendor-libraries/my-ui-library/versions/1.0.0/js/scripts.js.

Each of the css and js folders also needs its own .content.xml to set the compile order. Both are a sling:OrderedFolder with an include array:

PropertyTypeReqDescription
jcr:primaryTypeYMust be sling:OrderedFolder
includeString[]YLists the files in the folder to be compiled by the system, in order
versions/1.0.0/css/.content.xml
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
          xmlns:jcr="http://www.jcp.org/jcr/1.0"
          xmlns:kes="http://kestros.io/kes/1.0"
          jcr:primaryType="sling:OrderedFolder"
          include="[styles.css]"/>
versions/1.0.0/js/.content.xml
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
          xmlns:jcr="http://www.jcp.org/jcr/1.0"
          xmlns:kes="http://kestros.io/kes/1.0"
          jcr:primaryType="sling:OrderedFolder"
          include="[scripts.js]"/>

Referencing a library from a framework

A framework pulls in a library through its kes:vendorLibraries property. Each entry is the library’s JCR node name, not its library code (the two can differ):

  • Unversioned: [my-library]
  • Versioned: [my-versioned-library/0.0.1] (node name, a slash, then the version)

See UI Frameworks & Themes for how a framework declares its libraries and exposes them through a theme.

Packaging with filter.xml

The filter.xml defines the JCR paths the content package owns. It lives under the module's src/content/META-INF/vault/filter.xml (or src/main/resources/META-INF/vault/), and normally needs no changes:

filter.xml
<?xml version="1.0" encoding="UTF-8"?>
<workspaceFilter version="1.0">
  <filter root="/etc/vendor-libraries/my-ui-library"/>
</workspaceFilter>
The second filter root (/apps/kestros/commons/components, with the include pattern) is only needed when the library also ships component views under that path. A plain CSS/JS UI Library owns just its /etc/vendor-libraries/<lib> tree, so it needs only the first filter root. Do not copy the /apps/kestros/commons/components filter into a library that ships no views.
filter.xml: extra root, only if the library also ships component views
<filter root="/apps/kestros/commons/components">
  <include pattern="^/apps/kestros/commons/components(/.*/my-ui-library(/.*)?)?$"/>
</filter>
Installing a package replaces the content inside its filter. Nodes that were removed from the package are removed from the instance for any path the filter covers. Paths outside the filter includes are merged, not replaced. After a package install that changes component-view trees, rendered pages can return a transient 500 (Failed to include script) while the component-view cache is stale; redeploying kestros-site-building-core and letting it settle clears it.