Follow this guide to learn how Layers can be integrated with different UI frameworks across different platforms.
This guide covers how to load Layers themes and styles into an application or library. It draws on real integrations: vortex-desktop (Qt desktop), vortex-android (Kotlin via JNI), and vortex-web (JavaScript via WebAssembly).
There is no single canonical pattern. Pick the one that matches your platform and how much control you want over loading.
Layers has a single global controller, accessed via lController (a macro for LController::instance()). Two calls cover the typical lifecycle, loading your styles and then resolving links once:
#include <Layers/lcontroller.h>
lController.include("/path/to/styles"); // load a directory of JSON styles
lController.root_style()->resolve_links(); // resolve every L: link once
After this, your UI code reads resolved values via LAttribute::as<T>(). Reads follow the links resolved above. To make a value change without re-resolving the tree (a theme switch, say), re-point a shared node's attributes and everything linked to it follows (the indirection-node pattern in the JSON guide).
When you load styles from more than one library, pass a namespace so their top-level names can't collide; the library's styles then live under that node and references resolve within it first, falling back to the global root:
lController.include("/styles/vortex", "Vortex"); // Box -> Vortex/Box, ...
lController.include("/styles/app"); // app styles, un-namespaced
See Namespacing style sets in the JSON guide for the reference rules (bare within a library, qualified across, mounts kept global).
If you compile your styles into your binary with LayersResourceCompiler (recommended for C++ libraries; see Pattern A), you'll use lController.include_internal() instead of include().
Use when: Your styles ship inside a C++ library and should work once that library is linked. No app-side init call required.
Example: VortexWidgets (the widgets library inside vortex-desktop).
styles/ directory in your library..lrc (Layers Resource Config) file.LayersResourceCompiler to generate styles.cpp.Initializer instance whose constructor calls the generated initResources_styles_*() function and lController.include_internal().styles.cpp and the initializer into your library.The static instance constructs before main() runs, so styles are available the moment the library is loaded.
widgets/styles/styles_vortex.lrc:
{
"/styles/vortex": [
"vbutton.json",
"vdialog.json",
"..."
]
}
widgets/CMakeLists.txt (snippet):
set(STYLES_LRC "${CMAKE_CURRENT_SOURCE_DIR}/styles/styles_vortex.lrc")
set(STYLES_CPP "${CMAKE_CURRENT_BINARY_DIR}/styles.cpp")
add_custom_command(
OUTPUT ${STYLES_CPP}
COMMAND $<TARGET_FILE:Layers::LayersResourceCompiler>
${STYLES_LRC} ${STYLES_CPP}
DEPENDS ${STYLES_LRC}
VERBATIM
)
add_library(VortexWidgets SHARED ${WIDGETS_SOURCES} ${STYLES_CPP})
widgets/src/vortex_widgets_init.cpp:
#include <VortexWidgets/vortex_widgets_init.h>
#include <Layers/lcontroller.h>
#include <Layers/lresources.h>
namespace Layers::Resources {
extern void initResources_styles_vortex();
}
using namespace Vortex;
Initializer_WIDGETS::Initializer_WIDGETS()
{
Layers::Resources::initResources_styles_vortex();
lController.include_internal("/styles/vortex");
} static Initializer_WIDGETS vortex_widgets_initializer;
Use when: Your application wants explicit control over when styles are loaded, typically because it needs to combine bundled assets with files in user directories.
Example: the application layer in vortex-desktop, sitting on top of a compiled widgets library.
Widget styles are still compiled into a library via LayersResourceCompiler and loaded by that library's own static initializer (same as Pattern A). What differs is that additional style sets (and any user styles) are loaded explicitly at app startup, and links are resolved once everything is in:
void App::init() {
// Bundled + user-directory styles (paths from resources / settings)
lController.include(bundled_styles_path);
for (const auto& dir : user_style_dirs)
lController.include(dir);
// Optional user styles the app lets users toggle on top
lController.load_user_styles(user_styles_path);
// Resolve every L: link once, after all styles are in
lController.root_style()->resolve_links();
}
Paths come from Qt resources for bundled assets and from QSettings for user-added directories. Anything the app layers on top of Layers (theming, for instance) is data the app owns and swaps behind a mount node (see the JSON guide); it is not part of this loading step.
resolve_links()Use when: Your application is not built around a C++ main: Android with JNI, the web via WebAssembly, or any host that fetches assets at runtime.
Examples: vortex-android, vortex-web.
You expose a small bridge from the host language to Layers. The bridge underneath is just calls into lController:
bool load_styles_from_path(const std::string& path) {
try { lController.include(path); return true; }
catch (...) { return false; }
}
void resolve_styles() {
lController.root_style()->resolve_links(); // once, after all styles are loaded
}
The host language is responsible for getting JSON onto a path Layers can read:
LayersBridge.loadStylesFromPath(path) and LayersBridge.resolveStyles().Module.loadStylesFromPath('/styles') and Module.resolveStyles() are called.Anything built on top of the styles (theming, string tables) is data the host feeds and swaps behind a mount node itself (see the indirection-node pattern in the JSON guide); Layers just resolves the styles.
After loading, the host queries Layers per UI element for resolved attribute values. vortex-android and vortex-web both expose helpers along the lines of:
resolveBoxStyle(stylePath, states) -> { fill, cornerRadii, borderThickness, ... }
resolveTextColor(stylePath, states) -> color string
getAttributeString(stylePath, attrName) -> string
The host then renders with native widgets (Compose on Android, DOM on web) using those values.
| Pattern | Build-time embedding | Host language | Best for |
|---|---|---|---|
| A: Static init | Yes (LRC) | C++ | Libraries shipping a fixed style set |
| B: Runtime load | Yes for widget styles (LRC) | C++ | Apps merging bundled + user-supplied content |
| C: Pure runtime | No | Any (via bridge) | Mobile, web, or any non-C++ frontend |
Patterns can be combined. vortex-desktop uses LRC-compiled widget styles (Pattern A-style for its widgets library) but drives theme + user-style loading from the application (Pattern B).
Layers also supports user styles: extra style sets that users can toggle on top of developer styles to override the UI. The relevant API is lController.load_user_styles() and lController.toggle_custom_style().
This feature is experimental. No shipping project uses it yet, and the authoring format may still change. See the User styles section of the JSON guide for the format; the two controller methods above are the entry points.