--- url: /guide/getting-started.md description: >- Set up @cocoar/vue-ui in a Vue 3 project: install, import fonts and styles, use components, toggle dark mode, and register the overlay plugin. --- # Getting Started Set up the Cocoar Design System in your Vue 3 project in a few steps. ## 1. Install ```bash pnpm add @cocoar/vue-ui ``` ## 2. Import Fonts & Styles Import fonts and styles in your app's entry point. Fonts are self-hosted via `@fontsource` — no external CDN needed. ```ts // main.ts import '@cocoar/vue-ui/fonts'; // Poppins + Inter (self-hosted) import '@cocoar/vue-ui/styles'; // Design tokens + component styles ``` ::: info Bring your own fonts? The font import is optional. If you prefer a CDN or custom fonts, skip `@cocoar/vue-ui/fonts` and load them yourself. Components fall back to system fonts gracefully. ::: ## 3. Use Components Import components directly — no global registration required. Tree-shaking is automatic. ```vue ``` ## 4. Dark Mode Toggle dark mode by adding the `.dark-mode` class to the root element. All design tokens and components adapt automatically. ```ts document.documentElement.classList.toggle('dark-mode', isDark); ``` ## 5. Overlay System For components that render overlays (Dialog, Toast, Popover, Tooltip), register the plugin once: ```ts // main.ts import { createApp } from 'vue'; import { CoarOverlayPlugin } from '@cocoar/vue-ui'; createApp(App) .use(CoarOverlayPlugin) .mount('#app'); ``` And add the overlay host to your root layout: ```vue ``` ## Date/Time Components The date and time pickers use the [Temporal API](https://tc39.es/proposal-temporal/docs/) via `@js-temporal/polyfill`, which is included as a dependency of `@cocoar/vue-ui`. No extra install needed. When native Temporal support reaches all browsers, the polyfill can be dropped in a future major release. ## Additional Packages Optional packages for extended functionality: ```bash pnpm add @cocoar/vue-localization # i18n & timezone pnpm add @cocoar/vue-data-grid # AG Grid wrapper pnpm add @cocoar/vue-markdown # Markdown viewer ``` --- --- url: /guide/error-handling.md description: >- Error handling patterns in Cocoar UI: graceful fallbacks, overlay Promise rejections, error toasts, and handling failures in application code. --- # Error Handling Cocoar UI components are designed to fail gracefully. Internal errors are either caught and recovered silently, or surfaced to the user through controlled feedback mechanisms. This guide explains the patterns used throughout the library and how to handle errors in your own application code. ## Library Philosophy: Fail Gracefully Components never throw unhandled exceptions into your application. Instead they follow one of two patterns: * **Silent fallback** — return a safe default (`null`, `'UTC'`, `false`) and continue * **User feedback** — surface the error visibly via a Toast or state change ## Overlay Promises `CoarDialog` and `CoarPopconfirm` return Promises. Always handle the rejection case: ```ts import { useDialog } from '@cocoar/vue-ui'; const dialog = useDialog(); // ✅ Always add .catch() dialog.confirm({ title: 'Delete item', message: 'This cannot be undone.', }) .then((confirmed) => { if (confirmed) deleteItem(); }) .catch(() => { // Dialog was closed unexpectedly (e.g. overlay destroyed before user responded) }); ``` With async/await: ```ts try { const confirmed = await dialog.confirm({ title: 'Delete item', message: '...' }); if (confirmed) await deleteItem(); } catch { // Handle unexpected close } ``` ::: tip Popconfirm `CoarPopconfirm` emits `@confirmed` and `@cancelled` events — no Promise handling needed there. Use it for simple inline confirmations, and reserve `useDialog()` for programmatic flows where error handling is more important. ::: ## Toast for Error Feedback Use `useToast().error()` to surface errors to the user. Error toasts are persistent by default (duration `0`) — they stay until the user dismisses them, which is appropriate for errors that require attention. ```ts import { useToast } from '@cocoar/vue-ui'; const toast = useToast(); async function saveData() { try { await api.save(payload); toast.success('Saved successfully'); } catch (err) { toast.error('Save failed', { message: err instanceof Error ? err.message : 'Please try again.', }); } } ``` ```ts // With a retry action toast.error('Connection lost', { message: 'Could not reach the server.', action: { label: 'Retry', callback: () => saveData(), }, }); ``` ## Date and Time Parsing Date parsing functions return `null` on failure instead of throwing. Always null-check the result before using it: ```ts import { coarParsePlainDate } from '@cocoar/vue-ui'; const date = coarParsePlainDate(userInput); if (date === null) { // Input was invalid — show validation error toast.error('Invalid date format'); return; } // date is a Temporal.PlainDate — safe to use processDate(date); ``` The date picker components handle this internally — invalid input simply doesn't update the model value. Your `v-model` will remain `null` until the user enters a valid date. ## Timezone Fallbacks Timezone utilities default to `'UTC'` when the browser API fails or the timezone identifier is unrecognised. This keeps date/time components functional even in restricted environments: ```ts import { useTimezone } from '@cocoar/vue-localization'; const { timezone } = useTimezone(); // Always a valid IANA identifier — 'UTC' as last resort ``` ## Async Operations in Overlays When loading data inside a Dialog or Popover, manage loading and error states yourself: ```vue ``` ## Pattern Summary | Situation | Recommended pattern | |-----------|---------------------| | Dialog/Popconfirm result | `.then().catch()` or `try/await/catch` | | API call in component | `try/catch` + `toast.error()` | | Date input validation | Null-check return value of parse functions | | Non-recoverable error | `toast.error()` with persistent duration (default) | | Recoverable error | `toast.error()` with `action: { label: 'Retry', callback }` | | Silent failures OK | Rely on library defaults (`null`, `'UTC'`, `false`) | --- --- url: /guide/theming.md description: >- Theme Cocoar with CSS custom properties: set five oklch base colors and all shade scales for light and dark mode recalculate automatically. --- # Theming Cocoar uses an **oklch-based** color system. You set a few base colors, and the entire palette — including all shades for light and dark mode — is auto-calculated. ## Quick Start Override the CSS custom properties on `:root` to match your brand: ```css :root { --coar-accent: #1183CD; /* Your brand color → primary buttons, links, focus rings */ } ``` That's it. All accent shades (50–900), in both light and dark mode, recalculate from this single value. ## Customizable Base Colors | Variable | Default | Purpose | |----------|---------|---------| | `--coar-accent` | `#1183CD` | Brand/accent color — primary buttons, active states, links | | `--coar-success` | `#1e8f48` | Success states — confirmations, positive feedback | | `--coar-error` | `#d63b3b` | Error states — validation errors, destructive actions | | `--coar-warning` | `#cc821f` | Warning states — caution, attention needed | | `--coar-info` | `#5e6b84` | Info states — neutral informational context | ### Example: Red Brand ```css :root { --coar-accent: #C41E3A; /* Red brand */ --coar-error: #8B0000; /* Darker red so errors are distinguishable */ } ``` ### Example: Purple Brand ```css :root { --coar-accent: #7C3AED; } ``` ## How It Works Each base color generates a 10-step shade scale using **oklch relative color syntax**: ```css /* You set this: */ --coar-accent: #1183CD; /* The library calculates these: */ --coar-color-accent-50: oklch(from var(--coar-accent) 0.97 0.012 h); /* lightest */ --coar-color-accent-100: oklch(from var(--coar-accent) 0.92 0.035 h); --coar-color-accent-200: oklch(from var(--coar-accent) 0.84 0.075 h); /* ... */ --coar-color-accent-500: var(--coar-accent); /* = your exact color */ /* ... */ --coar-color-accent-900: oklch(from var(--coar-accent) 0.31 0.095 h); /* darkest */ ``` The `h` (hue) is extracted from your color. Lightness and chroma follow a designed curve that keeps shades vibrant instead of washed out. `accent-500` is always your exact brand color. This works because **oklch is perceptually uniform** — unlike HSL, a lightness of 0.5 in oklch looks equally "medium" for blue, red, and yellow. ## Fine-Tuning Individual Shades If an auto-calculated shade doesn't look right for your specific color, override it: ```css :root { --coar-accent: #FF6600; /* Auto-calculated 50 too warm? Override just that one: */ --coar-color-accent-50: #FFF5EB; } ``` ## Dark Mode Dark mode shades are calculated from the same base variables — no need to set anything extra. The library uses a separate lightness/chroma curve designed for dark backgrounds: * Low numbers (50–200): dark with a subtle color tint * Mid range (300–500): vibrant and saturated * High numbers (600–900): lighter for text on dark backgrounds The primary button color (`accent-500`) stays identical in both modes. ## Browser Support The oklch color system requires: * Chrome 119+ * Firefox 128+ * Safari 18+ This covers all modern browsers. For older browsers, consider providing hex fallbacks for your specific brand color. --- --- url: /guide/migration.md description: >- Migration guide for Cocoar UI 2.11: date/time pickers move onto CoarFormField, the clear button becomes opt-in, and two CSS tokens are renamed. --- # Migrating to 2.11 The 2.11 release unifies the whole input family onto one internal shell (`CoarInputFrame`) and a single field-padding token. **Visually almost nothing changes** — padding and sizing values were preserved — and the public props and slots of the text / number / password / select family are unchanged. For most apps the migration is **one search-and-replace or nothing at all**. The checklist below is ordered by how likely it is to affect you. ## TL;DR | If you… | …then you need to | | --- | --- | | Use the **date / time pickers** with a `label`, `hint`, or `error="message"` | Wrap them in [`CoarFormField`](/components/form-field) (see below) — a consistency fix that brings them in line with every other input. **The only real code change.** | | Rely on the **auto clear ✕** on text / number / password / date inputs | Add the `clearable` prop where you want it. | | **Override `--coar-input-padding-x`** in your own CSS | Rename it to `--coar-field-padding-x`. | | **Use `--coar-spacing-2xs` / `-xxl` / `-xxxl`** in your own CSS | Switch `2xs → xxs`; pick another step for `xxl` / `xxxl`. | | None of the above | **Nothing — you're done.** | *** ## 1. Date & time pickers move onto `CoarFormField` This is really a **consistency fix**, not a feature removal. The date/time pickers were the only inputs that rendered their *own* label and below-field message instead of delegating to [`CoarFormField`](/components/form-field) like every other field — an oversight from when they were first built. They now follow the same pattern as the rest of the family, which also **fixes** a latent bug where a `CoarFormField`-wrapped picker didn't pick up the error border. Concretely: `CoarPlainDatePicker`, `CoarPlainDateTimePicker` and `CoarZonedDateTimePicker` no longer render their own label, required asterisk, or below-field hint/error message, and they no longer take `label` / `hint` props. Their `error` prop is now a **`boolean`** (it flips the red border + `aria-invalid`), matching `CoarTextInput` and the rest of the field family. Wrap them in [`CoarFormField`](/components/form-field) — exactly like every other input — to get the label, the required `*`, validation messages and the inline status icon. **Before:** ```vue ``` **After:** ```vue ``` Notes: * `error` on the **picker** is now a boolean — pass `!!errorMessage` (or any boolean). The human-readable message lives on `CoarFormField`. * New `id` prop on each picker (explicit input id; otherwise taken from the wrapping `CoarFormField`, otherwise auto-generated) for parity with the other inputs. ## 2. `clearable` is now opt-in (defaults to `false`) `CoarTextInput`, `CoarPasswordInput`, `CoarNumberInput` and the three date/time pickers previously defaulted `clearable` to `true`. They now default to `false`, in line with the library rule that **every boolean prop defaults `false`** (and matching `CoarSelect` / `CoarMultiSelect`, which were already `false`). Nothing errors — the inline clear ✕ simply no longer appears unless you ask for it. **Before** (✕ shown automatically): ```vue ``` **After** (add `clearable` where you want the ✕): ```vue ``` `@cocoar/vue-data-grid` is unaffected — its cell editors already pass `clearable` explicitly. ## 3. Renamed / removed CSS tokens These only affect you if you **override Cocoar design tokens** in your own stylesheet. If you consume the components as-is, skip this section. ### Renamed | Old | New | | --- | --- | | `--coar-input-padding-x` | `--coar-field-padding-x` | `--coar-field-padding-x` (`12px`) is the single source of truth for form-field horizontal padding. It is intentionally **off** the spacing scale and decoupled from it, so tuning `--coar-spacing-*` no longer moves field padding. Each control size scales it by `--coar-component-{xs,s,m,l}-scale`. ### Removed | Removed | Use instead | | --- | --- | | `--coar-spacing-2xs` (was a duplicate of `xxs`, both `2px`) | `--coar-spacing-xxs` | | `--coar-spacing-xxl` (`48px`, unused in the library) | a remaining step, e.g. `--coar-spacing-xl` (`32px`) | | `--coar-spacing-xxxl` (`64px`, unused in the library) | a remaining step | The spacing scale is now: `3xs 1 · xxs 2 · xs 4 · s 8 · m 16 · l 24 · xl 32`. *** ## What did **not** change * **Public props & slots** of `CoarTextInput`, `CoarPasswordInput`, `CoarNumberInput`, `CoarSelect`, `CoarMultiSelect`, `CoarTagSelect` — including the `prefix` / `suffix` / `leading` / `trailing` slots. The move onto the internal `CoarInputFrame` shell is invisible to consumers. * **Visual output** — padding, radius and sizing values were preserved. Fields may differ by a sub-pixel at most; nothing reflows. * **Toggle, listbox and segmented controls** — `CoarCheckbox`, `CoarSwitch`, `CoarRadioGroup`, `CoarListbox`, `CoarDualListbox`, `CoarSegmentedControl` got token-consistency polish only, no API change. (`CoarSwitch` and `CoarRadioGroup` additionally **gain** an `xs` size — additive.) --- --- url: /guide/migration-page-builder-3.md description: >- Migration guide for @cocoar/vue-page-builder 3.0: four PageConfig concepts removed, several names disambiguated, and documents migrated to schemaVersion 6. --- # Migrating Page Builder to 3.0 This release is confined to `@cocoar/vue-page-builder`. Every other package is untouched. ::: warning The package is back under Preview Page Builder shipped as GA in 2.17. That was an oversight — the authoring model is still moving, as this release shows. From 3.0 it carries the **Preview** badge again: expect the public API, `PageConfig` and the document schema to keep changing in minor releases, and pin a version if you depend on them. Documents stay safe either way — every schema change ships a migration that runs on ingest. ::: It removes four `PageConfig` concepts and renames several more. The removals all answer the same question — *who does this protect, from whom?* — and the renames all fix one word covering two things. The reasoning behind each is on the [Authoring contract](/components/page-builder/authoring-contract) page. **Your documents migrate themselves.** One document field changed (`repeat.props.source`), and it is renamed on ingest like every earlier schema migration. Nothing to do by hand. ## TL;DR | If your `PageConfig` sets… | …then | | --- | --- | | `fields` | Rename to **`dataContract`**. | | `elements` | Rename to **`elementTypes`**. | | `availableStates` | Remove. Pass the state through `runtimeContext` and declare the field's `allowedValues` — see [§3](#_3-view-state-becomes-ordinary-context). | | `previewFixtures` | Remove. Bind your own sample to `previewContext` — see [§4](#_4-preview-fixtures-become-host-chrome). | | `stylePresets` | Remove. Styling is `NodeStyle`, `CoarTheme` and `visual-markup` — see [§1](#_1-style-presets-are-gone). | | `requiredNodes` | Remove. Enforce it in your publication endpoint — see [§2](#_2-required-nodes-are-gone). | | None of these | Check the [rename table](#_6-renamed-api) — you may still import a renamed symbol. | *** ## 1. Style presets are gone Removed: `config.stylePresets`, `node.stylePreset`, the `PageStylePreset` type, `findStylePreset()` and `isSafeStylePreset()`. They let a host register named CSS classes for the author to pick by id. The justification was that a page author must not put CSS into a page they do not own — but a page author *does* own the realm their page renders in, so the restriction protected nobody. The feature was also never finished: the Editor canvas never applied the class, so picking a preset changed nothing until you switched to the Preview tab. **What to use instead.** Styling has three channels and always did: | Want | Use | | --- | --- | | Per-node appearance | `NodeStyle` — surface, typography, layout, box | | Brand colours, radii, fonts | `CoarTheme` via `CoarThemeScope` / `previewTheme` | | Free-form decoration | The `visual-markup` element (sealed iframe, free CSS) | A leftover `stylePreset` key in a stored document is **reported as an authoring warning, never stripped**. The renderer ignores it and emits no class from it. ## 2. Required nodes are gone Removed: `config.requiredNodes` (with `lockVisibility`, `lockStyle`, `parentId` and `maxIndex`). It pinned a node as present, placed and visually untouchable. Besides the ownership argument above, it did not work: a node carrying both locks still disappeared when the container **above** it was hidden, and `validatePageDocument()` reported the document as valid. **What to use instead.** Enforce it where activation happens. Your publish endpoint already validates the document before a revision goes live; a check there cannot be bypassed from the browser, which was never true of the config flag. ```ts // in the publish endpoint, on the document about to become active if (!containsVisibleNode(document, 'legal-notice')) { return reject('The legal notice must stay on the page.'); } ``` The builder no longer withholds delete, move or the drag grip from any node. ## 3. View state becomes ordinary context Removed: `config.availableStates`, ``'s `viewState` prop, ``'s `previewState` prop, `visibleWhen.source: 'state'`, and `page.viewState` in the code scope. The host's "which screen is this right now" was a second mechanism for something `runtimeContext` already carried. Note that **Page State is untouched**: `definePageState`, `page.state` and `source: 'state'` *bindings* all keep working — they mean the page author's own shared data, and losing the name collision is part of the point. **Before** ```ts const config: PageConfig = { availableStates: [{ id: 'prompt', label: 'Prompt' }, { id: 'expired', label: 'Expired' }], }; ``` ```vue ``` **After** ```ts const config: PageConfig = { contextFields: [ { path: 'runtime.viewState', type: 'string', allowedValues: ['prompt', 'expired'] }, ], }; ``` ```vue ``` `allowedValues` is new: a context field that declares it gets a **dropdown** in the condition editor instead of a free-text box — which is what `availableStates` used to provide, now available to every enumerable field. Documents using `visibleWhen: { source: 'state', … }` must move to `{ source: 'context', path: 'runtime.viewState', operator: 'equals', value: … }`, or express the condition in Element Code. ## 4. Preview fixtures become host chrome Removed: `config.previewFixtures` and the `PagePreviewFixture` type. A fixture was a named bundle of `{ context, state, locale, viewport }` plus a dropdown in the builder toolbar — but the host already owns `previewContext` and `previewLocale`. The only thing the config added was the builder drawing the picker, and the host is the one who knows what "empty" or "50 items" means for its own data. ```vue ``` The preview now runs when the host supplies the inputs its own `config` declares (`contextFields`, `locales`), and otherwise says so rather than rendering against invented data. ## 5. Auth presets are gone Removed: `createAuthPageConfig()` and `createAuthPageDocument()`. The package ships nothing auth-specific. An IDP owns its own `PageConfig` and starting documents; `apps/playground/src/views/auth-customization/` in this repository is a worked example of all four slots. ## 6. Renamed API Mechanical, and a search-and-replace covers all of them. | Before | After | | --- | --- | | `config.fields` | `config.dataContract` | | `config.elements` | `config.elementTypes` | | `PAGE_ELEMENTS_KEY` | `PAGE_ELEMENT_TYPES_KEY` | | `useSchemaValidation()` | `useAuthoringFindings()` — returns `{ findings, byNodeId }`, not `{ issues, … }` | | `ValidationIssue` | `AuthoringFinding` | | `IssueSeverity` | `FindingSeverity` | | `@validation` event | `@findings` | | `PageVisualFont.source` | `PageVisualFont.src` | | `repeat.props.source` | `repeat.props.contextPath` *(auto-migrated)* | Each pair existed because one word covered two things: `fields` was both the DTO contract and the live values, `elements` was both the type registry and this page's nodes, `validation` covered field rules, the activation contract **and** the builder's authoring hints, and `source` was an enum, a context path and a data URL at once. `binding.source` keeps the name — it is the dominant meaning. ## 7. Documents move to `schemaVersion: 6` The only document change is the repeat rename, applied by `migrateRepeatContextPath` on the same ingest path as every earlier migration — identity-preserving, idempotent, and skipped when the new key is already there. It runs on `v-model` assignment, on the initial value and on the JSON tab's Apply, so a stored v5 document opens and renders unchanged. Persist the version as-is; a document saved by 3.0 comes back stamped `6`. ## Also worth knowing Not breaking, but new in the same release: * **`@findings`** mirrors the builder's authoring findings to the host, so a save button can grey out on errors. `useAuthoringFindings()` is exported for the same check outside a mounted builder. * **`previewInitialValues`** starts the embedded preview from host values, merged over the authored `defaultValue`s — the edit-form case, and the case where a default is computed per tenant. * **The page is exactly its host container.** Size values on the page root are dropped and the root offers no size fields; the container owns the box. Its contract is that the container must have a determinable height — for `body`, `html, body { height: 100% }`. A document that set a root size is told so. * **Quick Properties resolve per breakpoint.** They showed the base value while the canvas rendered the resolved one; an inherited value now also names the override it came from. --- --- url: /guide/changelog.md description: >- Full version history of the Cocoar Design System packages, listing added features, changes, and fixes for each release. --- # Changelog All notable changes to the Cocoar Design System (Vue) will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Versions are calculated automatically by [GitVersion](https://gitversion.net/). *** ## 3.0.0 **Page Builder: a complete authoring platform, fewer concepts, unambiguous names — and back under Preview.** A major confined to `@cocoar/vue-page-builder` and its immediate neighbours. Nothing has been published since 2.19, so this release carries both the customer-authoring platform (isolated scripting, runtime bindings, reusable compositions) and the consolidation that followed it: four `PageConfig` concepts removed because they restricted a page author inside a realm they own or duplicated something the host already passed, plus several names disambiguated where one word covered two things. The package shipped as GA in 2.17 by oversight; it now carries the Preview badge until the authoring model settles, so expect the public API, `PageConfig` and the document schema to keep moving in minor releases. Documents stay safe: every schema change ships a migration that runs on ingest. See [Migrating Page Builder to 3.0](https://docs.cocoar.dev/cocoar-ui-vue/guide/migration-page-builder-3). ### Added * **`@cocoar/vue-page-builder` — isolated browser scripting.** Page State, Page Root Code, per-element compute/action code and async host calls run in one SES-hardened Web Worker session per rendered page. Tenant code has no ambient DOM, `window`, `fetch` or filesystem access; a host exposes explicit, structured-clone-safe capability facades with `definePageRuntimeHost()` and grants them per page and runtime definition. Reactive dependency tracking recalculates only definitions affected by changed fields, state, repeater context, viewport or host resources. Constrained Monaco templates keep function signatures and element identity locked while exposing element-specific IntelliSense. * **`@cocoar/vue-page-builder` — generic runtime bindings and authored state.** Supported properties, action arguments and visibility rules can read allow-listed host context, Page State, named form fields/selections, repeater item/index or sandbox expressions. The page owns its initial mutable state; element code may use ordinary local variables without persisting them. The code path computes immutable property snapshots before applying the final result, so repeated assignments do not cause repeated DOM writes. * **`@cocoar/vue-page-builder` — reusable, versioned compositions.** `PageCompositionRepository` is the host-owned persistence boundary for immutable subtree definitions. Repository summaries appear in a searchable **Compositions** library and can be dragged through the normal Tree/Canvas drop pipeline. Instances materialize as ordinary nodes, pin an exact version, support explicit version changes, update-to-latest, detach and host navigation through `open-composition`, and retain nested origin chains. `compilePageCompositions()` strips authoring metadata, so runtime documents need neither a repository nor a wrapper element. Separate Pages/Compositions applications use `composition-management="consume"`; compact tools may use inline definition management. * **`@cocoar/vue-page-builder` — general action arguments.** Every action-capable registry element shares optional `actionValues`, `actionValueField` and `actionValue` properties. JSON-safe static values and per-key dynamic bindings reach the handler with deterministic precedence: form values, then `actionValues`, then the legacy dynamic `actionValue`. Buttons, links and consumer elements therefore use one action contract. * **`@cocoar/vue-page-builder` — repeaters, selections and feedback zones.** The generic `repeat` element renders an allow-listed host array, exposes current item/index to descendants and can publish selected-key arrays under a configured name. The generic `feedback` element places form errors, status, loading or authored messages inside the visual layout. These primitives replace proprietary Auth provider/scope elements. * **`@cocoar/vue-page-builder` — sandboxed visual markup.** The optional `visual-markup` element renders a bounded declarative document with host-owned values, font registrations and navigation/action policy; arbitrary HTML, DOM scripts and page-global CSS are not accepted. * **`@cocoar/vue-page-builder` — responsive styling, translations and application theming.** Documents author mobile-first styles plus Phone, Tablet and Desktop overrides, safe modern viewport units (including `dvh`, `svh` and `lvh`), breakpoint visibility and centralized key-based translations. `CoarThemeScope` and the Builder's `previewTheme` apply the resolved application brand to runtime and canvas without restyling the administration chrome. * **`@findings` event and exported `useAuthoringFindings()`.** The builder's authoring findings reach the host, so a save button can grey out on errors or a host can render its own issue list. Outside a mounted builder, the composable answers the same question. * **`previewInitialValues`.** The embedded preview starts from host values, merged over the authored `defaultValue`s exactly as at runtime — the edit-form case, and the case where a default is computed per tenant. * **`PageContextField.allowedValues`.** A context field with a closed value set is authored with a dropdown in the condition editor, which is what makes a host state, tier or status authorable without a second mechanism for it. * **Authoring contract documentation.** Every field of the node grammar, the surface that writes it, every named exception with its cost, and the open gaps. ### Changed * **`@cocoar/vue-page-builder` — editor layout and authoring flow.** Outline and compact Properties now share the resizable left inspector, the Canvas stays central, and a searchable, independently collapsible element library sits on the right. Contract **Fields** appear first, followed by Containers, Elements and Compositions. Common properties remain quick controls; Page/Element Code is the authoritative escape hatch for the complete registered property surface. * **`@cocoar/vue-page-builder` — localization uses stable keys.** Localizable element properties reference a page translation catalogue edited in one Translations tab. Legacy embedded localized objects remain readable, while the properties inspector no longer displays them as `[object Object]`. * **`@cocoar/vue-script-editor` — constrained authoring is visually quieter and more usable.** Locked scaffolding markers can be hidden, protected lines are deemphasized without a dominant background, editor sizing is stable, and PageBuilder code dialogs use a wide viewport-relative layout. * **`@cocoar/vue-ui` — scoped application themes.** New `CoarThemeScope` applies typed theme primitives and light/dark/auto mode to a subtree, enabling an embedded preview and the production page to share the same brand contract. * **`config.fields` → `config.dataContract`.** `fields` named both the DTO contract and the live values (`page.fields`). The contract takes the new name; the values keep the one that reads correctly in code. * **`config.elements` → `config.elementTypes`, `PAGE_ELEMENTS_KEY` → `PAGE_ELEMENT_TYPES_KEY`.** The registry says which element *kinds* exist; `page.elements` is this page's nodes. * **`useSchemaValidation()` → `useAuthoringFindings()`** (returning `{ findings, byNodeId }`), **`ValidationIssue` → `AuthoringFinding`**, **`IssueSeverity` → `FindingSeverity`**, **`@validation` → `@findings`**. "Validation" covered a node's field rules, the activation contract and the builder's authoring hints; the first two keep the word. * **`repeat.props.source` → `props.contextPath`** and **`PageVisualFont.source` → `src`.** `source` was an enum, a context path and a data URL at once. `binding.source` keeps it. * **Schema v6.** The repeat rename is applied by `migrateRepeatContextPath` on every ingest path — identity-preserving, idempotent, skipped when the new key is present. Stored documents open and render unchanged. * **The page is exactly its host container.** Size values on the page root are dropped and the root offers no size fields; the host container owns the box and must have a determinable height. A document that set a root size is told so. * **Quick Properties resolve per breakpoint.** They showed the base value while the canvas rendered the resolved one. An inherited value now also names the override it came from. ### Removed * **`config.stylePresets`, `node.stylePreset`, `PageStylePreset`, `findStylePreset()`, `isSafeStylePreset()`.** Host-registered CSS classes the author picked by id. A page author owns the realm their page renders in, so the restriction protected nobody — and the Editor canvas never applied the class, so picking a preset changed nothing until you switched to the Preview tab. Styling remains `NodeStyle`, `CoarTheme` and the `visual-markup` element. A leftover `stylePreset` key is reported as an authoring warning, never stripped. * **`config.requiredNodes`** (with `lockVisibility`, `lockStyle`, `parentId`, `maxIndex`). Besides the same ownership argument, it did not hold: a node carrying both locks still vanished when the container above it was hidden, with `validatePageDocument()` reporting the document as valid. Guarantees of this kind belong in the publication endpoint, where they cannot be bypassed from the browser. * **`config.availableStates`, ``'s `viewState`, ``'s `previewState`, `visibleWhen.source: 'state'`, `page.viewState`.** The host's view state was a second mechanism for something `runtimeContext` already carried — and `source: 'state'` meant Page State in a binding but view state in a condition. Page State (`definePageState`, `page.state`, `source: 'state'` bindings) is untouched and now unambiguous. * **`config.previewFixtures` and `PagePreviewFixture`.** A fixture bundled `{ context, state, locale, viewport }` plus a builder-drawn dropdown, but the host already owns `previewContext` and `previewLocale`. The preview now runs when the host supplies the inputs its own config declares, and says so when it cannot. * **`createAuthPageConfig()` and `createAuthPageDocument()`.** The package ships nothing auth-specific; an IDP owns its own config and starting documents. ### Fixed * **`@cocoar/vue-page-builder` — packaged Worker execution in real Vite consumers.** The runtime Worker is emitted through a dedicated package subpath, remains outside Vite dependency pre-bundling where required, resolves correctly below non-root base paths and works in development optimization and production builds. Release CI now installs the packed PageBuilder and its peers into a separate Vite consumer before publishing; the packed-consumer matrix covers Linux and Windows. * **`@cocoar/vue-page-builder` — Auth integration correctness.** Runtime actions receive current form values plus explicit arguments, validation/errors/loading render in authored locations, conditional controls react without polling every expression, modern viewport lengths survive CSS sanitization, and generic external-provider/consent collections work without consumer-only element implementations. * **Monaco JSON diagnostics are documented at the consumer boundary.** PageBuilder uses JavaScript and JSON language services; Vite hosts must route `json` to Monaco's `JsonWorker`. Routing it to the generic editor worker caused `Missing requestHandler or method: doValidation`, `findDocumentColors` and `getFoldingRanges` messages even though the sandbox itself was healthy. * **`@cocoar/vue-page-builder` — the page root is a border box.** "The page is exactly its host container" only held while the page had no padding of its own: `.pb-page` was content-box, so an authored padding was added to `width: 100%` and the page came out wider than the container it fills. Invisible until the host is narrow, and then a horizontal scrollbar on a login page at 320px. *** ## 2.19.0 This release brings the Vue calendar's view hierarchy and interaction model in line with the newer Cocoar iOS calendar while keeping the web package deliberately presentation-agnostic. The flat `CalendarBuilder` remains the single integration surface: applications choose their own create/edit UI, persistence and recurring-series scope flows through callbacks and occurrence provenance. ### Added * **`@cocoar/vue-calendar` — iOS-style Year / Month / Day / Agenda hierarchy.** New public `CoarYearView`, `CoarContinuousMonthView` and `CoarMonthListView` components join the existing time-grid and agenda surfaces. The shell groups Compact, Stacked, Details and List under Month, groups One day and Multi-day under Day, and retains fixed Week and Work week views as useful web additions. The old `legacyMonth` idea is intentionally absent. * **Continuously scrolling Month.** Month sections render only their required four to six weeks, materialize and preload adjacent months, preserve the active month while density changes, and extend in either direction without an unbounded DOM. Compact, Stacked and Details use content-aware row heights; Month List places the selected-day list below the mini calendar in narrow containers and beside it when space allows. * **Responsive Multi-day view.** New fluent setters `dayMode('single' | 'multiDay')`, `dayColumnCount(...)` and `dayColumnMinWidth(...)` derive one to seven complete day columns from the calendar's actual container width. `api.setDayMode(...)` and `api.setMonthDensity(...)` provide the imperative counterparts. * **Calendar presentation controls.** `monthDensity(...)` selects Compact / Stacked / Details, `shadeWeekends(...)` controls the weekend tint, event metadata can render assignee avatars, and event foreground contrast is selected from the event colour. The default view set now follows the iOS hierarchy; Timeline remains opt-in. * **Public layout helpers.** `responsiveDayColumnCount`, `contentAwareCascadeFrames` and `eventTextColor` are exported from the core subpath for consumers building compatible custom surfaces. ### Changed * **Create and edit stay host-owned.** `onDateClick`, `onTimeClick`, `onEventClick`, `onEventDoubleClick`, hover handlers and the native DOM anchor form the complete Fluent API integration seam. The library does not force a modal or overlay, so consumers can choose a popover, dialog, side panel or routed editor. Recurring occurrences retain series id, recurrence id and source provenance for This occurrence / This and following / Entire series persistence flows. * **Month and time-grid overlaps follow the available content.** Same-day items no longer disappear after two entries in Details mode; every item remains reachable. Timed overlaps use content-aware cascading and width allocation, while responsive headers and view controls wrap against the calendar container instead of overflowing narrow host layouts. * **Month-cell drops preserve timed-event intent.** Dropping a timed event onto a month date moves it by the display-date delta while retaining its local time, duration and per-endpoint source zones. ### Fixed * **Selected-day hover contrast and shape.** Month List and Agenda keep the selected day on its accent background while hovering, so the white label remains readable. The Year view applies the same rule to its current-day marker. Month List now also derives its day-cell radius from the Cocoar button-radius token instead of using an out-of-scale hardcoded radius. * **Continuous-month separators and today marker.** Every month begins with a consistent top separator even when the previous section has no rendered day above it, and the current-day marker is no longer clipped at the section edge. * **Subpath declaration packaging.** The `recurrence` and `recurrence-rrule-temporal` exports now point at the declaration files Vite actually emits, so TypeScript consumers resolve both subpaths from the packed npm artifact. ## 2.18.0 ### Added * **`@cocoar/vue-ui` — `CoarNotice` for compact status messages and application banners.** The new component supports `info`, `success`, `warning`, `error`, `neutral` and `accent` variants with matching default icons, plus `placement="inline" | "banner"`. Inline notices render as compact bordered callouts; banners sit flush below an application header without making themselves sticky. Optional `label`, icon override, single-line inline `truncate`, long-form `#details` popover and right-aligned `#cta` slot cover short operational messages without turning the entire notice into a link. Banners always wrap, and actions remain independently interactive. New documentation compares `CoarNotice` with the richer, page-content-oriented `CoarNote`. * **`@cocoar/vue-ui` — `CoarCheckboxGroup` with real collection models.** Child `CoarCheckbox value="…"` controls can now project into either an ordered `string[]` or an explicit `Record` via `v-model`; `modelType` selects the empty-model shape and otherwise follows the supplied model. Array output follows checkbox registration order, object output includes every registered key with a true/false value, and external model changes update the children. The group owns `name`, orientation, size, disabled and error state, provides `role="group"` semantics, and integrates with `CoarFormField` labels and messages. * **Semantic `subtlest` status backgrounds.** Success, error, warning and info now expose `--coar-background-semantic-*-subtlest` tokens for lightweight status surfaces in light, dark and theme-less modes. The theme editor exposes the new tokens as first-class semantic overrides. ### Changed * **`@cocoar/vue-ui` — `CoarFormField` controls label layout consistently.** New `layout="stacked" | "inline"` and `labelPosition="before" | "after"` props support all four combinations for every child control. The label and status indicator move as one cluster, while the status popover trigger remains outside the native `