
Technology stack
- Vite 8 – Build tool with HMR
- Vue.js 3.5 – Interactive components (TodoList, GallerySwiper, SwiperSlider)
- Bootstrap 5.3 – UI framework
- Sass 1.99 – CSS preprocessing (Modern Compiler API)
- PostCSS – preset-env, pxtorem
- ESLint 10 / Stylelint 17 – Code quality
- Swiper 12 – Touch slider (integrated via Vue components)
- Jarallax 3 – Parallax scrolling
Quick Start
cd Build
npm ci
npm run watch # Auto-rebuild on file changes
In an mpc monorepo with DDEV running, you can also build from the website’s root directory: ddev mp-core-build (equivalent to npm run build inside libs/mp-core/Build/).
The output is in Resources/Public/ (JavaScripts, StyleSheets, Fonts, Icons, Images, Favicons, BackendLayouts).
| Script | Description |
|---|---|
build | Lint + production build (minified, optimised) |
dev | Lint + development build with source maps |
watch | Development build with file monitoring |
lint | Run ESLint + Stylelint |
eslint / eslint.fix | JavaScript linting |
stylelint / stylelint.fix | CSS/SCSS linting |
Clean up the build: rm -rf node_modules Resources/Public && npm ci && npm run build
Project structure
Build directory
Build/
├── Assets/
│ ├── Fonts/ # Web fonts (WOFF2)
│ ├── Images/ # Source images, Icons/
│ ├── Scripts/ # JavaScript/Vue
│ │ ├── code/ # Feature modules
│ │ │ ├── Utils/ # Shared utilities (domUtils.js, …)
│ │ │ ├── Vue/ # vue-initialisation.js (component registry)
│ │ │ └── Navigation/ # Primary / Secondary / Tertiary
│ │ └── components/ # Vue SFCs
│ ├── Scss/ # SCSS (ITCSS layers)
│ │ ├── Base/ # Variables, fonts
│ │ ├── Elements/ # Base elements
│ │ ├── Mixins/ # SCSS mixins
│ │ ├── Modules/ # UI components
│ │ ├── Templates/ # Layout helpers
│ │ └── Extensions/ # TYPO3 extension overrides
│ └── Static/ # Copied as-is (BackendLayouts, Favicons)
├── vite.config.js
├── eslint.config.js
├── stylelint.config.js
└── postcss.config.js
Resources directory
Resources/
├── Private/ # Fluid templates (not web-accessible)
│ ├── Backend/, Language/, Layouts/, Partials/, Templates/
├── Extensions/ # Extension template overrides
│ ├── fluid_styled_content/, form/, indexed_search/, news/
└── Public/ # Compiled assets (web-accessible)
├── Fonts/, Icons/, Images/, JavaScripts/, StyleSheets/, Favicons/
Vite entry points
Defined in Build/vite.config.js:
| Bundle | Purpose |
|---|---|
bootstrap.js | Initialisation of the Bootstrap framework |
screen.js | Main frontend (sticky header, theme, etc.) |
vue.js | Vue.js 3 components (including Swiper integration) |
navigationPrimary/Secondary/Tertiary.js | Navigation levels |
print.js | Print-specific styles |
backend.js | TYPO3 backend styles |
ckeditor.js | CKEditor RTE styles |
Note: Swiper is included in the
vue.jsbundle – there is no separateswiper.jsentry point.
Add a new entry
- Create a JS file in
Build/Assets/Scripts/ - Register in
vite.config.js - Execute
npm run watch - Integrate into Fluid:
<f:asset.script identifier="myfeature" src="EXT:mp_core/Resources/Public/JavaScripts/myfeature.js" />
<f:asset.css identifier="myfeature" href="EXT:mp_core/Resources/Public/StyleSheets/myfeature.css" />
JavaScript architecture
Function modules (Build/Assets/Scripts/code/)
Core: main.js, i18n.js, i18nLinkHelper.js
UI: jarallax.js, modalGallery.js, openAccordionAndTabs.js, pagination.js, sticky.js, totop.js
Navigation: nav-toggle.js, Navigation/Primary/navigation.js, Navigation/Secondary/navigation.js, Navigation/Tertiary/navigation.js
Layout: moveHeaderDate.js, moveMeta.js, theme.js
Shared utility functions (code/Utils/domUtils.js)
debounce(func, wait)-- Performance-optimised handling of resizing and scrollingtoggleNavState(...)-- Open/closed state of the navigationhandleDropdownVisibility(element, showCb, hideCb)-- Bootstrap dropdown eventstoggleAriaLabelAndTitle(element, openLabel, closeLabel)-- Toggling of accessible labels
Vue.js components
Located in Build/Assets/Scripts/components/:
| Component | Description |
|---|---|
TodoList.vue | Interactive to-do list with localStorage, registered as a CType mpcore_todolist |
GallerySwiper.vue | Swiper-based gallery carousel for the gallery content element |
SwiperSlider.vue | Generic Swiper slider for container slider elements |
Component registration is performed in code/Vue/vue-initialisation.js.
Vue mounts on elements with data-container="vue" and data-component="ComponentName" (see VueComponents.typoscript and templates for content elements). Optional data-* attributes are passed via props (e.g. data-card-title in TodoList).
Creating a new component
- Create
.vuefile inBuild/Assets/Scripts/components/ - Register in
code/Vue/vue-initialisation.js - Build and via
<f:asset.script>in Fluid (thevue.jsentry point automatically mounts registered components).
SCSS architecture (ITCSS)
Levels from low to high specificity:
- Settings (
Base/) -- Variables, fonts, colour palettes - Tools (
Mixins/) -- Functions, mixins (no CSS output) - General -- Reset, Normalise (from Bootstrap)
- Elements (
Elements/) -- Basic HTML elements - Objects -- Layout patterns
- Components (
Modules/) – Designed UI components - Utility functions -- Utility classes
Bootstrap customisation
- Light theme:
Build/Assets/Scss/Base/Bootstrap/_custom-variables.scss - Dark theme:
Build/Assets/Scss/Base/Bootstrap/_custom-variables-dark.scss
Asset Management
| Asset type | Pattern |
|---|---|
| Images in SCSS | url('../../Images/Icons/icon.png') (relative path) |
| Fonts | @include font-face('Name', '../../Fonts/file', 400, normal, woff2) |
| Inline SVG | svg-load('../Images/Icons/arrow.svg') |
| Static files | Build/Assets/Static/ -> copied to Resources/Public/ |
Template integration
Fluid
<f:asset.css identifier="screen" href="EXT:mp_core/Resources/Public/StyleSheets/screen.css" />
<f:asset.script identifier="screen" src="EXT:mp_core/Resources/Public/JavaScripts/screen.js" />
TypoScript
page {
includeCSS.screen = EXT:mp_core/Resources/Public/StyleSheets/screen.css
includeJSFooter.screen = EXT:mp_core/Resources/Public/JavaScripts/screen.js
}
Priority of template paths: Higher numbers take precedence over lower ones (0 = core, 10 = extension, 20+ = project).
Replacements through extensions
| Extension | Path | Notes |
|---|---|---|
| fluid_styled_content | Resources/Extensions/fluid_styled_content/Private/ | In Bootstrap 5 style |
| Form | Resources/Extensions/form/ | Bootstrap forms + YAML configuration |
| News | Resources/Extensions/news/ | List, detail and category views |
| Indexed search | Resources/Extensions/indexed_search/ | Bootstrap search results |
Best practices
JavaScript: Modular code in code/, common patterns in Utils/, debounce on resize/scroll, event delegation, ARIA labels, lint before commit.
SCSS: Observe ITCSS levels, CSS variables for theming, logical properties for RTL, maximum of 3 nesting levels, mobile-first min-width queries.
Vue.js: Single-file components, scoped styles, prop validation, Composition API for complex logic.
Troubleshooting
| Problem | Solution |
|---|---|
| 404 error with fonts | Check ../ Segments from SCSS to Fonts/ |
| Missing images in CSS | Path in Assets/Images/ |
| Bundle too large | Import only required Bootstrap components |
| Changes are not displayed | Clear browser and TYPO3 cache |
Important: Never Resources/Public/ directly. Always edit in Build/Assets/ and run npm run build.
Further reading
- Favicons -- Favicon assets and Fluid partial (not with
output.html) - configuration – Site Sets, TypoScript, TCA
- Vite | Vue.js | Bootstrap 5 | ITCSS