# How to Configure Theme Bundling and Offline Asset Distribution in Quarkdown HTML

> Learn to configure theme bundling and offline asset distribution in Quarkdown HTML. Bundle all dependencies into a self-contained build directory for offline rendering.

- Repository: [Giorgio Garofalo/quarkdown](https://github.com/iamgio/quarkdown)
- Tags: how-to-guide
- Published: 2026-04-29

---

**Quarkdown HTML bundles every runtime dependency—including third-party libraries and theme assets—into a self-contained `build/install/` directory via Gradle tasks defined in `quarkdown-html/build.gradle.kts`, enabling completely offline document rendering.**

Quarkdown HTML generates standalone documents that require no internet connectivity by compiling SCSS themes, bundling JavaScript dependencies, and assembling all assets into a local distribution. When you configure theme bundling and offline asset distribution in Quarkdown HTML, you control how custom styles and external libraries are packaged alongside the compiled output, ensuring self-contained deployments.

## The Gradle-Driven Build Pipeline

The entire bundling process is orchestrated through `quarkdown-html/build.gradle.kts`, which defines specialized tasks for theme compilation, asset collection, and library distribution. Each stage transforms source files into the final offline-ready structure under `build/install/`.

### SCSS Compilation and Theme Discovery

The build begins by discovering and compiling theme sources located in `src/main/scss/`. The **`discoverThemeNames`** function (lines 32-38) scans the directory structure for `layout/`, `color/`, and `locale/` subdirectories, excluding partials (files prefixed with `_`) to identify valid theme entries.

The **`compileSass`** task (lines 25-33) then compiles all discovered SCSS files—global styles, layout variants, color schemes, and locale-specific formatting—into flat CSS files. These compiled styles serve as the foundation for the theme assembly process.

### Asset Assembly via Manifest Processing

Once SCSS compilation completes, the **`assembleThemes`** task (lines 42-93) reorganizes the flat CSS output into a hierarchical directory structure at `build/install/theme/<kind>/<name>/`. This task also processes optional theme manifests: any `<theme>.json` file placed alongside an SCSS source can declare an `exports` array listing additional assets (fonts, images, or other static files) that must ship with the theme.

The **`readThemeExports`** helper (lines 22-26) validates these manifests at build time, failing early if declared files are missing from `node_modules` or source directories. This ensures that every theme package is complete before the distribution is finalized.

### Third-Party Library Bundling

Runtime JavaScript dependencies are handled by two distinct tasks. The **`bundleHighlightJs`** task (lines 78-103) creates a single IIFE bundle ([`highlightjs.min.js`](https://github.com/iamgio/quarkdown/blob/main/highlightjs.min.js)) from the modular highlight.js npm package, as the standard distribution lacks a pre-bundled browser version.

The **`bundleThirdParty`** task (lines 95-115) processes the `librariesToBundle` list—an array of **`LibrarySpec`** objects defined around lines 58-95. Each `LibrarySpec` specifies the target folder name, the source path within `node_modules`, and the specific files to copy into `build/install/lib/<target>/`. The TypeScript bundler marks these libraries as external (`--external:<target>`), ensuring that [`quarkdown.min.js`](https://github.com/iamgio/quarkdown/blob/main/quarkdown.min.js) references the runtime files rather than inlining them.

## Adding Custom Themes with Asset Exports

Creating a new theme requires placing SCSS source files in the appropriate category directory and optionally declaring additional assets through a JSON manifest.

1. **Create the SCSS source** in the appropriate subdirectory:

   ```scss
   // quarkdown-html/src/main/scss/color/sunset.scss
   $primary-color: #ff6b35;
   $secondary-color: #f7c59f;
   // ... additional variable definitions
   ```

2. **(Optional) Declare exported assets** by creating a manifest with the same base name:

   ```json
   {
     "exports": [
       "node_modules/@fontsource/roboto/files/roboto-latin-400-normal.woff2"
     ]
   }
   ```

3. **Execute the build** to generate the distribution:

   ```bash
   ./gradlew clean installDist
   ```

After compilation, the theme appears at [`build/install/theme/color/sunset/sunset.css`](https://github.com/iamgio/quarkdown/blob/main/build/install/theme/color/sunset/sunset.css), with exported fonts or images copied into the same directory. The HTML runtime loads these assets relative to the theme path without network requests.

## Bundling External JavaScript Libraries

To include additional runtime dependencies such as clipboard.js or other npm packages, extend the library specifications and install the package.

First, install the dependency via npm:

```bash
npm install clipboard --save

```

Then add a `LibrarySpec` entry in `build.gradle.kts`:

```kotlin
LibrarySpec(
    "clipboard",
    "clipboard/dist",
    listOf("clipboard.min.js")
),

```

Reference the library from the TypeScript entry point ([`quarkdown-html/src/main/typescript/index.ts`](https://github.com/iamgio/quarkdown/blob/main/quarkdown-html/src/main/typescript/index.ts)):

```typescript
import "clipboard";

```

The build system automatically copies the specified files into `build/install/lib/clipboard/` and configures the bundler to treat the import as an external dependency. The final `installDist` output includes the library in the `lib/` directory alongside [`quarkdown.min.js`](https://github.com/iamgio/quarkdown/blob/main/quarkdown.min.js) and all theme assets.

## Customizing Existing Theme Exports

You can extend existing themes without modifying Gradle configuration by editing their JSON manifests. To add a custom SVG logo to a layout theme:

Update the manifest at [`src/main/scss/layout/mylayout.json`](https://github.com/iamgio/quarkdown/blob/main/src/main/scss/layout/mylayout.json):

```json
{
  "exports": [
    "node_modules/@my-icons/logo.svg"
  ]
}

```

The `assembleThemes` task automatically validates the path and copies `logo.svg` into `build/install/theme/layout/mylayout/logo.svg` during the next build. This manifest-driven approach requires no changes to the core build logic, allowing per-theme customization of distributed assets.

## Summary

- **Gradle orchestrates the entire pipeline** through `quarkdown-html/build.gradle.kts`, handling SCSS compilation, theme assembly, and library bundling.
- **Themes are discovered automatically** from `src/main/scss/` subdirectories, with compilation handled by `tasks.compileSass` and assembly by `assembleThemes`.
- **Asset exports are manifest-driven**—JSON files declare additional resources that `readThemeExports` validates and `assembleThemes` packages into `build/install/theme/`.
- **Third-party libraries use `LibrarySpec`** definitions to copy files from `node_modules` into `build/install/lib/`, with the TypeScript bundler treating them as external runtime dependencies.
- **The final distribution** produced by `./gradlew installDist` is completely self-contained, loading all resources from local `lib/` and `theme/` directories without network connectivity.

## Frequently Asked Questions

### How does Quarkdown HTML handle the highlight.js dependency differently from other libraries?

Highlight.js ships as ES modules rather than pre-bundled browser code, so the **`bundleHighlightJs`** task (lines 78-103) uses esbuild to create a single [`highlightjs.min.js`](https://github.com/iamgio/quarkdown/blob/main/highlightjs.min.js) IIFE bundle specifically for the distribution. Other libraries listed in `librariesToBundle` are copied directly from `node_modules` without additional transformation.

### Where are theme assets loaded from when the HTML runs offline?

The HTML runtime uses **[`InstallDirectoryResolver.kt`](https://github.com/iamgio/quarkdown/blob/main/InstallDirectoryResolver.kt)** from the `quarkdown-install-layout-navigator` module to locate the `lib/` and `theme/` directories relative to the executed document. References to `lib/<name>/` and `theme/<kind>/<name>/` resolve to the local filesystem, ensuring all CSS, fonts, and JavaScript load without internet access.

### Can I bundle assets that are not located in node_modules?

Yes. The `exports` array in theme JSON manifests accepts relative paths from the project root. While the examples commonly reference `node_modules/`, you can specify paths to any file within the repository structure, such as `src/main/resources/custom-fonts/` or `assets/images/`, and `assembleThemes` will copy them into the theme directory.

### What happens if I declare an export that does not exist in the manifest?

The **`readThemeExports`** function validates all paths during the Gradle configuration phase. If a declared file is missing, the build fails immediately with an error indicating which theme manifest references the non-existent path, preventing incomplete or broken distributions from being generated.