Documentation Index

Fetch the complete documentation index at: https://docs.unqork.io/llms.txt

Use this file to discover all available pages before exploring further.

Embedded UI Navigation Guards and URL Isolation Reference

Prev Next

When an Unqork module runs inside Embedded UI, the module and the host page compete for control of the browser's address bar. The module's routing system expects to own the URL and update it as end-users navigate through module screens. Without intervention, those URL changes would push the host page to a different address, breaking the host application's routing and browser history. The navigation guard system resolves this conflict by intercepting URL change attempts from the module and keeping the host page's address bar stable throughout the session.

This reference covers how navigation guards work, how link interception operates, and how Shadow DOM and CSS isolation function in Embedded UI.

Why Navigation Guards Are Needed

Angular's internal routing system assumes it owns the browser's URL bar. In embedded mode, URL mutations must be suppressed so the host page's address remains unchanged. Without these guards, Angular would change the host page URL every time the embedded module navigates internally, breaking the host application.

$browser Decorator

The $browser service controls how Angular reads and sets the browser URL. In embed mode, this service is modified so URL changes from Angular routing never reach the host page's address bar.

Field: $browser.url() | Type: Angular service decorator

The $browser service is decorated during angular.module('unqorkEmbed').config(). All SET calls are silently ignored. GET calls pass through normally.

flowchart LR
    A["Angular calls $browser.url(newUrl)"] --> B{"Embed mode?"}
    B -->|"Yes"| C["No-op (URL not changed)"]
    B -->|"No"| D["Normal URL update"]

A click event listener on document intercepts <a> tag clicks originating from inside the Shadow DOM. Links outside the Shadow DOM (in the host page) continue to work normally.

When an end-user clicks a link inside the embedded module:

  1. The default navigation is prevented.
  2. A unqork-embed-navigate CustomEvent is dispatched on window with the href in event.detail.

The listener also blocks navigations where the href attribute starts with views/ or contains undefined—patterns that indicate Angular template paths or unresolved route expressions that would navigate the host page to an invalid URL if not intercepted.

Shadow DOM and CSS Isolation

Shadow DOM creates a private boundary around the embedded module's DOM and CSS. Styles from the host page cannot reach inside, and styles from the module cannot leak out.

Shadow DOM Structure

<unqork-app>                          ← Custom element (light DOM)
  #shadow-root (open)                 ← Shadow boundary
    <link rel="stylesheet" href="..."> ← Injected CSS
    <style>...</style>                 ← Scoped overrides
    <div id="centauri-root">           ← Angular bootstrap target
      <div ui-view>                    ← UI-Router viewport
        ...module content...
      </div>
    </div>

The outermost <unqork-app> element is a placeholder in the host page's regular DOM. The shadow root attached to it creates a boundary: styles and DOM from the host page cannot reach inside, and styles from the module cannot affect the host page. Stylesheets are injected at the top of the shadow root so all styling scopes to the module. Below the stylesheets, the module's content tree mounts and renders module screens as end-users navigate.

CSS Loading

Styles are loaded into the Shadow DOM from two sources:

Source endpoint Description
Platform styles /fbu/uapi/styles Returns the environment's base CSS, including Unqork platform styles.
Customer CSS Environment-specific Returns the customer-branded stylesheet for that environment.

Both are fetched and injected as <link> or <style> elements inside the Shadow DOM. They do not affect the host page.

Scoped Overrides

Certain styles are overridden for the embed context. For example, the workflow loader uses position: absolute instead of position: fixed to stay contained in the custom element boundary.

Limitations

Some browser behaviors cannot be fully controlled by the navigation guard system. Review these limitations before building integrations that depend on browser history or fixed-position layouts.

Limitation Description
position: fixed elements styled position: fixed inside Shadow DOM are relative to the viewport, not the shadow host. Modals and loaders must use position: absolute to stay in the embedded module's boundary.
Font-face declarations @font-face rules from the host page do not cross the Shadow DOM boundary. Fonts must be re-declared inside the shadow or loaded globally on the host page.
Global event listeners window.addEventListener is shared across the shadow boundary. events registered on window inside the embedded module are visible to the host page.
Browser back and forward buttons Navigation guards block URL changes, so browser history is not updated. The back and forward buttons affect the host page history, not the embedded module.
Deep linking The embedded module always starts fresh. Deep linking into a specific module state is not supported.

Changelog

Date Change
2026-06-15 Renamed "Link Interception" to "Document-Level Link Interception"; added step-by-step explanation of link click behavior and CustomEvent dispatch.
2026-06-10 Expanded introduction paragraph to clarify the URL conflict between the embedded module and host page; added explanatory paragraph following the Shadow DOM structure code block.
2026-06-09 Editorial pass: renamed heading "The Problem" to "Why Navigation Guards Are Needed"; added introductory descriptions to $browser Decorator, Shadow DOM and CSS Isolation, and Limitations sections; expanded table descriptions for non-experts.
2026-04-21 Initial publication.