@lilaquadrat/design-core
v0.2.0
Published
framework layer shared by every lilaquadrat STUDIO design: app bootstrap, router, stores, plugins and build tooling
Readme
@lilaquadrat/design-core
The framework half of a STUDIO design.
A design repository used to carry two things at once: the design (modules, partials, less, gallery fixtures) and the machinery that makes it an app (bootstrap, router, stores, plugins, models, build tooling). Only the first half differs between projects. This package is the second half, so a design update no longer means porting framework changes by hand into every project.
The design stays in the project. Everything else lives here.
What is in here
| path | what |
| --- | --- |
| main, client-entry, server-entry | app bootstrap for browser, hydration and SSR |
| routes, mixins/getRoutes, mixins/createRouter, mixins/hooks | routing, the auth and signup gate, scroll behaviour |
| mixins/loadComponents | module and partial registration, editor module list |
| stores/* | main, user, content, editor, files, cart, calls |
| plugins/* | auth, currency, events, filters, inview, replacer, resize, signupFlow, traceable, translations, youtube |
| libs/* | Models.class, ActionNotice |
| functions/* | payment provider factory, shopify and stripe providers, dom helpers |
| models/* | Address and Contact declarations |
| mixins/* | date, formatSize, getAnchor, hasSlotContent, replaceVariables, createCookieString, logger |
| views/* | content, editor, login, signup-account, download |
| partials/* | client-only, main-components, error, qrcode, action, mediadetection |
| translations/de | framework wording only - validation, lists, files, signup, order |
| vite, eslint, stylelint, cypress | build and test tooling |
What is NOT in here, on purpose
Modules, partials, assets/less (variables, mixins, fonts, theme), the gallery
(preview.view.vue, viewData/*, preview-controls, preview-colors,
previewTheme, paletteDerive), the contrast audit, the module registries
(modules.browser.ts, partials.browser.ts and the mail pair), config.ts,
staticData.ts and every module-shape interface.
Those are the design. They change per project, and a package version has no business deciding what they look like.
Source, not a build
The package exports .ts and .vue source. Two of the views carry scoped
less that uses the project's own variables and mixins, injected by vite's
globalVars. A prebuilt stylesheet would bake this repo's palette into every
project, so the consumer compiles the source instead:
optimizeDeps: { exclude: ['@lilaquadrat/studio', '@lilaquadrat/design-core'] }createViteConfig already sets that.
Using it in a design
vite
// vite.config.ts
import { fileURLToPath, URL } from 'node:url';
import { defineConfig } from 'vite';
import { createViteConfig } from '@lilaquadrat/design-core/vite';
import config from './config.ts';
export default defineConfig(createViteConfig({
config,
root : fileURLToPath(new URL('.', import.meta.url)),
lessDir: './src/assets/less',
}));entry
The project owns App.vue (global styles, editor wiring) and the module
registries, so it hands them to the package:
// src/client-entry.ts
import { createClientEntry } from '@lilaquadrat/design-core/client-entry';
import { dynamicRoutes, editorRoutes, createPreviewRoutes } from '@lilaquadrat/design-core/routes';
import App from './App.vue';
import PreviewView from '@/views/preview.view.vue';
import TestView from '@/views/test.view.vue';
import modules from './modules.browser';
import partials from './partials.browser';
import modulesMail from './modules.mail';
import partialsMail from './partials.mail';
createClientEntry({
components: {
root : App,
modules : modules.modules,
partials,
modulesMail : modulesMail.modules,
partialsMail,
},
routes: {
dynamicRoutes,
editorRoutes,
previewRoutes: createPreviewRoutes(PreviewView, TestView),
},
});render from @lilaquadrat/design-core/server-entry takes the same
components object through its options argument.
translations
The package ships the strings it renders itself. Module wording belongs to the design and is merged on top:
import { addMessages } from '@lilaquadrat/design-core/plugins/translations';
import de from '@/translations/de';
addMessages('de', de);eslint, stylelint, cypress
// eslint.config.js
export { default } from '@lilaquadrat/design-core/eslint';// cypress.config.ts
import { createCypressConfig } from '@lilaquadrat/design-core/cypress';
export default createCypressConfig();cy:parallel bin-packs specs by measured runtime. A project with its own spec
set overrides the table in cypress/durations.json.
SSR preflight
tooling/ssr-test renders a whole design through the real ssr build and
fails if anything in it throws, warns or comes back empty. It is the gate before
publishing.
It exists because the production renderer is forgiving in the worst way:
RenderClass.renderSingle collects renders with Promise.allSettled and only
console.errors a rejected one, so a page that blows up during SSR is published
as an empty shell and nobody notices.
Offline by construction - the two build artifacts are the only input, nothing talks to an api:
dist/server the published ssr build (yarn build-ssr)
dist/ssr-test the design's fixtures (yarn build:ssr-fixtures)// src/ssr-test.fixtures.ts - built with: vite build --ssr --mode ssr-test
import type { Content } from '@lilaquadrat/interfaces';
import modulesBrowser from './modules.browser';
import text from './views/viewData/text';
export const viewData: Record<string, Content> = { text };
export const modules = modulesBrowser.modules.map((single) => ({
name : single.name,
variants: (single.variants ?? []).map((variant) => ({ key: variant.key })),
}));// test/ssr.test.mjs - run with: node --test test/*.test.mjs
import { runSsrSuite } from '@lilaquadrat/design-core/ssr-test';
const fixtures = await import('../dist/ssr-test/ssr-test.fixtures.js');
await runSsrSuite({ fixtures });Three passes:
| pass | what it renders |
| --- | --- |
| viewData | every gallery fixture as its own site, at its own url |
| empty modules | every registered module with nothing but a type and a uuid, then once per registered variant |
| empty mail modules | the same for the mail registry, rendered at the mail url |
Every render is asserted to resolve, to log no console.error and no
[Vue warn], to produce the content module, to return parseable initialState,
and to fill in the html template's placeholders. The context it passes is the
one RenderClass.createGenericContext builds, so what the suite exercises is
the contract the renderer actually uses.
Options:
allowedConsole- regexes for output the project expects offline (a module that needs a structure declaration or a payment provider from the backend). Reported as a diagnostic instead of failing.markers- root class overrides for modules whose class does not follow from their type.variants: false- render each module once instead of once per variant.paths- where the build output lives, if not the defaults above.
A module that renders nothing for empty data is fine and is reported, not failed. A module that throws on empty data is the thing this suite is for.
Migrating an existing design
Keep the old import paths working with a one line shim per file, then delete the shims per project whenever convenient:
// src/stores/main.store.ts
export * from '@lilaquadrat/design-core/stores/main.store';
export { default } from '@lilaquadrat/design-core/stores/main.store';The subpaths mirror the layout a design already had, so the shim is always the same two lines and no component import has to change.
The one seam that matters
The package never imports a design component. main-components.partial.vue and
error.partial.vue render lila-content-module by its global component
name, resolved at runtime from the registry the project passed in. That keeps
the dependency one-directional: design depends on core, core never on design.
Known state
yarn type-check reports the errors this code already had inside the design
repo - Models.class, main.store, action.partial, the shopify provider and
Contact.model. They were not introduced by the extraction and are unchanged
from their origin. One extra error in user.store is an artifact of this
repository's own dev tree carrying two copies of @lilaquadrat/interfaces; a
consuming project with a single hoisted copy does not see it.

