Embedding patterns
Common integration patterns for @paprel/embed-accounting inside a partner app shell.
Host-owned routing and URL state
Paprel does not install a router. List components emit shared events through Shadow DOM so one delegated listener can synchronize your router, query parameters, and analytics:
container.addEventListener("paprel:resource-open", (event) => {
const { resource, id } = event.detail;
event.preventDefault();
router.push(`/${resource}/${id}`);
});
container.addEventListener("paprel:view-change", (event) => {
syncQueryParameters(event.detail.source.component, event.detail.state);
});
| Event | Purpose | Detail |
|---|---|---|
paprel:resource-open | Cancelable request for the host to open a resource | { version, source, resource, id } |
paprel:view-change | Complete collection state after search, filter, tab, sort, or pagination | { version, source, reason, state } |
paprel:operation-success | Host toast or analytics after a mutation | { version, source, action, message, resource? } |
Component-specific events remain available for local wiring. Prefer the shared events for application-wide integrations. See Host integration events.
Refresh and caching
Components expose imperative refresh() where account data is cached (paprel-chart-of-accounts, paprel-account-select, paprel-journal-list). Journal reads always hit the network on load.
Call refresh() after mutations your app performs outside the embed layer (e.g. API import jobs).
Permissions
Grant scopes on your App Connect client for the surfaces you mount. UI may hide actions based on permissions[] returned with the token; Core still enforces every write.
COA + journals v1:
accounting:account-list
accounting:journal-list
accounting:journal-add
Banking widgets (same package, accounting scope):
accounting:banking-connection-list
accounting:banking-transaction-list
accounting:banking-transaction-reconcile
accounting:match-policy-apply
Theming
Domain packages include their component styles. Override inherited tokens on a wrapper to apply your brand. The overview documents the default palette.
Components inherit typography and color from CSS custom properties — no shadow-DOM piercing required:
.paprel-shell {
--paprel-color-primary: #1c1c18;
--paprel-color-primary-text: #ffffff;
--paprel-color-primary-hover: #2c2c27;
--paprel-color-text: #1c1c18;
--paprel-color-muted: #78786f;
--paprel-color-border: #e8e8e2;
--paprel-color-border-subtle: #f0f0eb;
--paprel-color-surface: #ffffff;
--paprel-color-surface-muted: #f7f7f4;
--paprel-color-danger: #dc2626;
--paprel-font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
--paprel-font-size: 14px;
--paprel-radius: 14px;
--paprel-radius-sm: 10px;
}
<div class="paprel-shell">
<paprel-journal-list page="1"></paprel-journal-list>
</div>
System account names resolve through the embed i18n catalog synced with the Paprel workspace — set locale in configureAccounting to match your app language.
Tailwind and other CSS frameworks
Embed components use shadow DOM, so utility classes from your app's Tailwind build do not apply inside them automatically. Recommended approaches:
- CSS custom properties (default) — map your design tokens to
--paprel-*on.paprel-shell(works with any framework). - Tailwind on the host page — style layout, spacing, and wrappers around the custom elements; leave component internals to the package.
- Tailwind inside the package (advanced) — compile utilities at build time into Lit
cssblocks (e.g. Tailwind v4 + Vite). Possible for@paprel/embed-accountingitself, but styles must ship with the component — partners cannot inject runtime Tailwind classes across the shadow boundary.
For most integrations, option 1 keeps the smallest bundle and clearest separation between your shell and Paprel widget chrome.
AI agents on the same books
Embed UI targets human operators in your app. Paprel MCP targets AI clients — company-bound endpoint, OAuth discovery, scoped tools. Both sit on App Connect tokens and the same ledger. See MCP & AI and Paprel MCP for AI agents.