Design system and constraints
Workspace UI pages run inside the Moltaro WebApp on the host’s own Vue, Pinia, Vuetify, vue-router, and vue-i18n instances. The design system is deliberately closed: an explicit import allowlist, a curated shared-component barrel, and a compiler that rejects any style able to escape the page. Workspace UI code is trusted workspace code compiled by Moltaro — the boundary is governance and review, not a hostile-code sandbox.
Allowed imports
Section titled “Allowed imports”The build accepts imports only from:
vue,pinia,vue-router,vue-i18n;- approved
vuetify/componentsandvuetifycomposable exports; @moltaro/ui(the barrel below) and the@moltaro/workspace-uihost SDK (pages, components, and stores);- local project-relative paths and the generated
@workspace-ui/*alias (project structure).
Everything else is a build error: Node built-ins, build-tool modules, WebApp
internals (@/*), remote URL modules, and unapproved npm packages. The
check covers static and supported dynamic import forms alike.
The framework modules are further restricted to specific named exports, so an allowed module does not mean every export is importable:
vue-router— onlyuseRoute. Navigate withuseNavigation(), notuseRouterorRouterLink.pinia— onlydefineStoreandstoreToRefs.vue-i18n— onlyuseI18n(see localization below).vue— reactivity, lifecycle, anddefineComponentare available, but rendering and app-creation APIs (h,render,createApp,Teleport,resolveDynamicComponent,cloneVNode,getCurrentInstance) are rejected; use templates and the host composables instead.
A disallowed named import fails the build with a specific WUI diagnostic that
names the offending symbol.
Shared components: @moltaro/ui
Section titled “Shared components: @moltaro/ui”| Component | Purpose |
|---|---|
AppCardHeader | Standard card header with title, subtitle, eyebrow, and action row |
AppEmptyState | Icon-plus-title empty or error state with an optional actions slot |
ConfirmDialog | Confirmation dialog before destructive or irreversible actions |
DetailHeaderPanel | Record detail header: eyebrow, title, number label, busy state, actions |
FormDialog | Dialog shell for short forms with the shared header and footer contract |
MoltaroGridBase | Shared AG Grid wrapper with Moltaro column and rendering conventions |
OperationalPageToolbar | Toolbar for operational console pages |
OperationalUtilityDrawer | Right-side utility drawer for filters, sorting, and columns |
PropertyItem | Read-only label/value fact row for detail surfaces |
UtilityDrawerSection | Collapsible content section inside a utility drawer |
UtilityDrawerSectionTitle | Title row for a utility drawer section |
The barrel also exports the TypeScript types these components use, such as
MoltaroGridColumnDefinition and MoltaroGridRowClickedEvent. Every export
is part of the host contract; other WebApp components are unreachable.
Page layouts
Section titled “Page layouts”Each manifest page declares one host-owned Layout; a missing or unknown
value is a build error.
- Default — the normal document layout: the Moltaro navigation shell plus a container that grows with content and scrolls at document level. Use it for forms, detail pages, reports, and vertically flowing surfaces.
- Fullscreen — the Data Explorer-style application layout: the same shell plus a root that fills the content viewport with no document-level scrolling; the page owns its internal scroll areas. It never hides Moltaro navigation and never calls the browser Fullscreen API.
Both layouts expose the same Vue/Vuetify context; responsive behavior stays
page-owned via useDisplay(), breakpoint props, and media or container
queries. A page can read its resolved layout but cannot replace the shell.
Styling rules
Section titled “Styling rules”Pages may use <style scoped> blocks and static style/:style bindings:
colors, typography, spacing, custom properties, flex/grid, media/container
queries, animations, transitions, and pseudo-classes/elements — alongside
Vuetify theme tokens, props, and utility classes. Inline style/:style is
accepted only on native HTML elements; to style a Vuetify component use its
props, classes, and theme tokens, or wrap it in a native element.
The compiler namespaces every style to its page: a deterministic opaque
scope id is rendered as a data-workspace-ui-scope attribute, every
compiled selector is rewritten to match only inside that scope, and keyframe
names are namespaced. Vuetify overlays teleport to a page-owned overlay
target carrying the same scope, so they never touch core Moltaro overlays.
Rejected at build time:
<style>withoutscoped, standalone or global CSS imports,:global, and selectors rooted athtml,body,:root,#app, or shell elements;@import, remote stylesheets or scripts,@font-face, and custom fonts;v-html, runtime template compilation, dynamic<component>resolution, runtime style or stylesheet injection, and a secondcreateApp;- direct host globals —
window,document,localStorage,sessionStorage,globalThis,self,top,parent— useuseDisplay()and container queries for responsive behavior, host composables for state, and the Moltaro/external API clients instead of rawfetch.
Framework and toolchain ownership
Section titled “Framework and toolchain ownership”Moltaro owns the framework and toolchain versions. Builds run against an offline toolchain packaged with the installation; there are no arbitrary npm dependencies or third-party build plugins. After a Moltaro update a rebuild may be required, and a breaking host-contract change may require source edits — see Build, publish, and upgrade.
Each build also runs an ESLint quality gate based on eslint-config-vuetify,
and lint errors fail the build. Author source to those conventions — attribute
ordering, self-closing components, and import ordering — so a functionally
correct page also passes the gate; every diagnostic carries the rule id that
flagged it.
Theming
Section titled “Theming”useMoltaroContext().theme exposes a computed { name, isDark }, and
Vuetify tokens such as rgb(var(--v-theme-primary)) resolve against the
active workspace theme, so scoped styles stay correct in light and dark.
Localization and accessibility
Section titled “Localization and accessibility”Project source owns locale JSON for en, uk, de, pl, and es; the
build requires matching key sets, messages register under a project
namespace, and the active WebApp locale selects the page locale. Render text
through useI18n() — call it with no arguments and destructure only t, te,
and tm (the $t template global and the options/scope overloads are
rejected). Read the active locale from useMoltaroContext().locale or
useFormatting().locale, not from useI18n(). Keep Vuetify inputs labeled and
page roots semantic, and give interactive elements stable data-testid values.
Example: generated Entity details page
Section titled “Example: generated Entity details page”A trimmed excerpt of the Entity details page produced by the UI Studio generator:
<script setup lang="ts">import { AppCardHeader, DetailHeaderPanel, PropertyItem } from '@moltaro/ui'import { useOrderDetailsStore } from '@workspace-ui/stores/orderDetailsStore'import { VCard, VCardText } from 'vuetify/components'
const store = useOrderDetailsStore()</script><template> <main data-testid="order-details-page"> <DetailHeaderPanel :busy="store.loading" eyebrow="Order" :number-label="store.record?.Number" :title="store.record?.DisplayName" /> <v-card v-for="group in groups" :key="group.key" border variant="flat"> <AppCardHeader density="compact" :title="group.title" /> <v-card-text> <PropertyItem v-for="field in group.fields" :key="field.key" :label="field.label" :value="displayValue(field)" /> </v-card-text> </v-card> </main></template>