Architecture
Parallax Designer follows a single-page, zero-router, composable-first architecture built with Vue 3 and TypeScript.
Design Principles
- Pure core layer — All engine logic, schema validation, constraints, defaults, and export generation live in
src/core/with zero Vue imports. This makes the core testable in isolation and reusable outside of Vue. - Composable state management — Reactive state is managed through Vue composables (
src/composables/) instead of a store library like Pinia or Vuex. - Single orchestrator —
App.vueis the top-level orchestrator connecting all composables and passing data to components. There is no router because the entire app is one view. - Component composition — Components are pure SFCs focused on rendering and user interaction, receiving data and callbacks from the composable layer.
Folder Structure
src/
├── core/ # Pure logic — no Vue dependency
│ ├── parallaxEngine.ts # requestAnimationFrame loop, transforms
│ ├── schema.ts # V3 validation + normalization
│ ├── projectDefaults.ts # Starter templates + 5 named presets
│ ├── projectConstraints.ts # Numeric min/max/step for every field
│ ├── backgroundStyle.ts # CSS background property composition
│ ├── exportRuntimeHtml.ts # Runtime HTML export planner + builder
│ ├── exportSnippet.ts # JSON helper utilities
│ ├── imageRefs.ts # idb:// URI helpers
│ ├── assetDb.ts # Dexie DB (assets + customPresets)
│ └── utils.ts # deepClone, makeId
│
├── composables/ # Stateful Vue integration
│ ├── useProjectState.ts # Central project ref, layer CRUD, z-index norm
│ ├── useProjectIO.ts # Export/import and custom presets
│ ├── useParallaxEngine.ts # Engine lifecycle, pointer tracking, FPS
│ ├── useLayerAssets.ts # IndexedDB images → object URLs
│ ├── useI18n.ts # Custom typed i18n
│ ├── useToast.ts # Global toast notifications
│ ├── useHelp.ts # Contextual help tooltip state
│ └── useHelpDock.ts # Help tooltip positioning
│
├── components/ # Vue SFCs
│ ├── ParallaxScene.vue # Fullscreen viewport + geometry guide
│ ├── OverlayPanel.vue # Draggable floating panel
│ ├── LayerList.vue # Sortable layer stack
│ ├── LayerEditor.vue # Geometry + motion editors
│ ├── LayerBasicEditor.vue # Name, image, background
│ ├── LayerBackgroundEditor.vue # Full CSS background UI
│ ├── SceneControls.vue # Scene parameters
│ ├── HarmonicsControls.vue # Float harmonics
│ ├── controls/ # Reusable form controls
│ ├── modals/ # Modal dialogs
│ └── project-settings/ # Settings panel groups
│
├── types/ # TypeScript type definitions
│ ├── parallax.ts # Core types (project, scene, layer, etc.)
│ └── geometryGuide.ts # Geometry guide types
│
├── i18n/ # Internationalization
│ ├── config.ts # Locale metadata + defaults
│ └── locales/ # EN, FR catalogs + registry
│
└── content/ # Static content
└── helpContent.ts # Help tooltip key mappingsData Flow
Component Hierarchy
Key Patterns
No External State Store
The app uses no Pinia, Vuex, or other state management library. All state lives in reactive ref and computed values inside composables. State is shared by calling the same composable (global singletons) or by passing refs between composables (factory pattern).
Result Pattern for Validation
Schema validation and import operations return Result<T, E> instead of throwing exceptions:
ts
type Result<T, E> = { ok: true; data: T } | { ok: false; error: E };This ensures all error paths are handled explicitly at the call site.
Z-Index Auto-Normalization
Layer add/remove/duplicate flows normalize zIndex values into a contiguous 1..N range. This avoids duplicate or missing stack ranks after structural edits.