Jump to content

Front end

Build system, asset pipeline, JavaScript/SCSS architecture and best practices.

MP-Core logo

Requirements

  • Node.js >=22 (Node 24 recommended)
  • npm >=10

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).

ScriptDescription
buildLint + production build (minified, optimised)
devLint + development build with source maps
watchDevelopment build with file monitoring
lintRun ESLint + Stylelint
eslint / eslint.fixJavaScript linting
stylelint / stylelint.fixCSS/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:

BundlePurpose
bootstrap.jsInitialisation of the Bootstrap framework
screen.jsMain frontend (sticky header, theme, etc.)
vue.jsVue.js 3 components (including Swiper integration)
navigationPrimary/Secondary/Tertiary.jsNavigation levels
print.jsPrint-specific styles
backend.jsTYPO3 backend styles
ckeditor.jsCKEditor RTE styles
 

Note: Swiper is included in the vue.js bundle – there is no separate swiper.js entry point.

 

Add a new entry

  1. Create a JS file in Build/Assets/Scripts/
  2. Register in vite.config.js
  3. Execute npm run watch
  4. 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 scrolling
  • toggleNavState(...) -- Open/closed state of the navigation
  • handleDropdownVisibility(element, showCb, hideCb) -- Bootstrap dropdown events
  • toggleAriaLabelAndTitle(element, openLabel, closeLabel) -- Toggling of accessible labels

Vue.js components

Located in Build/Assets/Scripts/components/:

ComponentDescription
TodoList.vueInteractive to-do list with localStorage, registered as a CType mpcore_todolist
GallerySwiper.vueSwiper-based gallery carousel for the gallery content element
SwiperSlider.vueGeneric 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

  1. Create .vue file in Build/Assets/Scripts/components/
  2. Register in code/Vue/vue-initialisation.js
  3. Build and via <f:asset.script> in Fluid (the vue.js entry point automatically mounts registered components).

SCSS architecture (ITCSS)

Levels from low to high specificity:

  1. Settings (Base/) -- Variables, fonts, colour palettes
  2. Tools (Mixins/) -- Functions, mixins (no CSS output)
  3. General -- Reset, Normalise (from Bootstrap)
  4. Elements (Elements/) -- Basic HTML elements
  5. Objects -- Layout patterns
  6. Components (Modules/) – Designed UI components
  7. 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 typePattern
Images in SCSSurl('../../Images/Icons/icon.png') (relative path)
Fonts@include font-face('Name', '../../Fonts/file', 400, normal, woff2)
Inline SVGsvg-load('../Images/Icons/arrow.svg')
Static filesBuild/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

ExtensionPathNotes
fluid_styled_contentResources/Extensions/fluid_styled_content/Private/In Bootstrap 5 style
FormResources/Extensions/form/Bootstrap forms + YAML configuration
NewsResources/Extensions/news/List, detail and category views
Indexed searchResources/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

ProblemSolution
404 error with fontsCheck ../ Segments from SCSS to Fonts/
Missing images in CSSPath in Assets/Images/
Bundle too largeImport only required Bootstrap components
Changes are not displayedClear browser and TYPO3 cache

Important: Never Resources/Public/ directly. Always edit in Build/Assets/ and run npm run build.

Further reading

Share page