Skip to content

Theme-Struktur

Jedes Theme unter _theme/ ist ein eigenstaendiges Nuxt-4-Projekt. Diese Seite zeigt die vollstaendige Ordner-Struktur am Beispiel _theme/vue-base/, erklaert jeden Bereich und zeigt Custom-Overrides fuer Plugin-Layouts und Widgets.

Top-Level eines Themes

_theme/vue-base/
├── app/                      # Nuxt App-Verzeichnis (Nuxt 4 Convention)
├── public/                   # Statische Assets (ueber Root-URL verfuegbar)
├── server/                   # Server-Side-Middleware, API-Routes
├── node_modules/             # npm-Dependencies
├── .nuxt/                    # Nuxt-Build-Output (git-ignored)
├── .output/                  # Production-Build-Output (git-ignored)
├── .env                      # Theme-spezifische Env-Variablen
├── .htaccess                 # Apache-Regeln (falls Theme ueber Apache geliefert wird)
├── nuxt.config.ts            # Nuxt-Konfiguration
├── package.json              # Dependencies, Build-Scripts
├── package-lock.json
├── tsconfig.json
├── app.cjs                   # Ggf. Legacy-Einstieg
├── deploy.sh                 # Ggf. Projekt-Deploy-Script
└── README.md

Der entscheidende Teil ist app/ (Nuxt App) — dort lebt der gesamte Theme-Code.

app/ — Nuxt-App

app/
├── app.vue                   # Root-Komponente (Layout-Wrapper)
├── components/               # Vue-Komponenten
│   ├── AppFooter.vue         # Frontend-Footer
│   ├── AppHeader.vue         # Frontend-Header
│   ├── AppMenuEditor.vue     # Menu-Editor-Frontend
│   ├── AppPagebuilder.vue    # Pagebuilder-Einstieg (laedt Rows → Cols → Widgets)
│   ├── admin/                # Ns* Admin-UI (NsModal, NsForm, NsTable, MediaManager, ...)
│   └── pagebuilder/          # Pagebuilder-Rendering
│       ├── PagebuilderRow.vue
│       ├── PagebuilderColumn.vue
│       └── PagebuilderWidget.vue
├── composables/              # Composables (useSeoHead, useSchemaOrg, useUserCenter, ...)
├── layouts/                  # Nuxt Layouts (admin.vue, admin-login.vue, default.vue)
├── middleware/               # Route-Middleware
├── pages/                    # File-based Routing
│   ├── admin/
│   │   └── [...slug].vue     # EINZIGE Admin-Route — kein index.vue!
│   └── [...slug].vue         # Frontend-Catch-all
├── plugins/                  # Nuxt-Plugins (siehe Tabelle unten)
├── stores/                   # Pinia-Stores (admin.ts, auth.ts, layout.ts)
├── utils/                    # Hilfs-Funktionen
└── widgets/                  # Pagebuilder-Widget-Registry (auto-generiert!)
    └── plugin-registry.js    # NICHT MANUELL EDITIEREN

Widget-Komponenten leben nicht hier

Pagebuilder-Widget-Komponenten (Testimonials, Banner, Shop-Listings, …) liegen nicht in components/widgets/, sondern unter _public/extensions/core/backend/{plugin}/widgets/{widget_template}/template/index.vue. Siehe Widgets › Widget-Anatomie.

Keine admin/index.vue

Das Admin-Routing ist ein einziger Catch-all-Route: app/pages/admin/[...slug].vue. Kein index.vue darunter — der Nuxt-Router wuerde sonst Konflikte erzeugen. Alle Admin-Plugins werden ueber import.meta.glob dynamisch in diese Route geladen.

components/admin/ — Backend-UI-Bausteine

Das Admin-UI nutzt einen gemeinsamen Satz wiederverwendbarer Komponenten mit Ns-Prefix:

KomponenteZweck
NsTableListen mit PrimeVue DataTable, Pagination, Suche
NsFormDynamische Formular-Rendering aus Plugin-Config
NsModalModals mit Sprach-Tabs, Bootstrap 5-basiert
NsSelectTomSelect-Wrapper fuer durchsuchbare Dropdowns
NsModuleListSub-Listen im Modal (show_module_list-Button)
NsTemplateLoader fuer template-Construct-Plugins
NsDetailDetail-Ansicht fuer content_construct=table
NsCustomContentRenderer fuer show_custom_content-Button
MediaManagerFile-Picker mit Tree-Navigation + Upload

Details zu diesen Komponenten: Skill php-backend-check (references/vue-components.md).

plugins/ — Nuxt-Plugins

PluginClient/ServerZweck
ab-tracking.client.tsclientA/B-Test-Impression/Conversion-Tracking
admin-css.client.tsclientCSS-Isolation fuer /admin (laedt nur dort Admin-Styles)
admin-i18n.tsbeidesAdmin-Uebersetzungen (laeuft auch in SSR, damit First-Paint lokalisiert ist)
ai-admin.client.tsclientAI-Feature-Injection in Admin-Formulare (data-aifeature-Attribute)
ckeditor.client.tsclientCKEditor 5 Setup fuer textarea-Felder mit Rich-Text
cookieconsent.client.tsclientCookie-Consent-Banner (4 Kategorien)
gsap-animations.client.tsclientGSAP-Scroll-Animationen, lazy-loaded
primevue.tsbeidesPrimeVue-Setup (Basis fuer NsTable DataTable)
widgets.client.tsclientPlugin-Widget-Registrierung (User-Center-Items etc.)

.client.ts-Suffix bedeutet: nur im Browser laufen, nicht in SSR. Plugins ohne Suffix (admin-i18n.ts, primevue.ts) laufen auch in SSR — das ist bei PrimeVue noetig fuer serverseitiges Markup der DataTable und bei i18n fuer lokalisiertes First-Paint.

stores/ — Pinia-Stores

StoreZweck
admin.tsAktiver Plugin, Navigation, systemVersion
auth.tsFrontend-Auth-Session (Login-Status, User-Profil)
layout.tsMobile-Menu-Status, Menu-Editor-State

Plugin-spezifische Stores (z. B. sw6Store.ts fuer Shopware-6-Cart/Customer/Config) leben nicht im Theme, sondern im jeweiligen Plugin unter _public/extensions/core/backend/{plugin}/widgets/.../. So bleibt der Store mitsamt seiner Logik gebuendelt beim Plugin und wird beim De-/Installieren automatisch mitgefuehrt.

public/ — Statische Assets

public/
├── backend/
│   ├── css/
│   │   ├── style.less            # Admin Light-Theme (Glassmorphism)
│   │   ├── style-darkmode.less   # Admin Dark-Theme
│   │   └── index.less            # LESS-Einstiegspunkt
│   └── js/
│       └── pagebuilder/           # Preview-Overlay-Scripts (postMessage)
├── css/                           # Frontend-spezifische Styles
├── fonts/                         # Font-Dateien
├── favicon.ico
└── robots.txt

Die Dateien hier liegen unter dem Root-URL-Pfad — public/favicon.ico/favicon.ico.

Admin-Styling-Details: Skill NewSemantics-Backend-Layout.

server/ — Server-Side

Nuxt-Server-Middleware und API-Routes. Im CMS aktuell leichtgewichtig — die meisten API-Calls gehen direkt an den PHP-Backend (/api/*), nicht an Nuxt-Server-Routes.

nuxt.config.ts

Die zentrale Nuxt-Konfiguration. Die wichtigsten Bereiche:

ts
export default defineNuxtConfig({
    // Nuxt-Module (@pinia/nuxt, @nuxt/image, @vueuse/nuxt, ...)
    modules: [ /* ... */ ],

    // Runtime-Config (Env-Variablen)
    runtimeConfig: {
        apiKey: process.env.NUXT_API_KEY,
        public: {
            apiBase: process.env.NUXT_PUBLIC_API_BASE,
            siteUrl: process.env.NUXT_PUBLIC_SITE_URL || '',
            theme:   process.env.NUXT_PUBLIC_THEME || 'vue-base',
        }
    },

    // Cross-Plugin-Aliases
    alias: {
        '#extensions':              resolve(process.cwd(), '../../_public/extensions/core/backend'),
        '#widgets/plugin-registry': resolve(process.cwd(), 'app/widgets/plugin-registry.js'),
    },

    // Vite + PurgeCSS-Safelist
    vite: {
        plugins: [
            purgecss({
                safelist: {
                    standard: [ /^swiper/, /^fa-/, /^ns-/, /* ... */ ],
                    greedy:   [ /^row_/, /^col_/, /^widget_/, /* ... */ ],
                    deep:     [ /data-animation/ ]
                }
            })
        ]
    }
})

Details zu Env-Variablen: Environment-Variablen. Details zu Aliases: Nuxt-Aliases.

.env

env
NUXT_API_KEY=123456
NUXT_PUBLIC_API_BASE=https://demo.new-semantics.com/api
NUXT_PUBLIC_SITE_URL=https://demo.new-semantics.com
NUXT_PUBLIC_THEME=vue-base

Projektspezifische Safelist-Erweiterungen kommen ebenfalls ins .env — siehe Environment-Variablen › PurgeCSS-Safelist.

package.json — Scripts

json
{
    "scripts": {
        "dev":      "nuxt dev",
        "build":    "nuxt build",
        "generate": "nuxt generate",
        "preview":  "nuxt preview"
    }
}

Jedes Theme hat seinen eigenen Build — npm install und npm run build geschehen im Theme-Ordner, nicht im Projekt-Root. Details: Deployment.

Custom-Overrides (Theme-spezifisch)

Themes koennen Plugin-Layouts und Widget-Templates uebersteuern, ohne den Plugin-Code anzufassen:

_theme/{theme}/app/custom/{plugin}/widgets/{widget_template}/template/index.vue

Zum Beispiel kann ein Projekt-Theme das categoryBannerNoLink-Widget ueberschreiben:

_theme/projekt01/app/custom/shop/widgets/categoryBannerNoLink/template/index.vue

Der vueRegistry-Rebuild (/admin/cache) bevorzugt diese Datei vor der Original-Version aus _public/extensions/core/backend/shop/widgets/categoryBannerNoLink/template/index.vue. Details: Widgets › Widget-Anatomie › Custom-Theme-Overrides.

Analog fuer Menu-Styling: jedes Theme kann ein eigenes public/css/menu.less haben, das ueber den compiled_css-Endpoint vom Menu-Editor geladen wird.

Theme-Varianten: vue-base vs. projekt01

Aspektvue-baseprojekt01
ZweckDefault/Produktiv-ThemeProjekt-Fork fuer Kunden-Customizing
app/Voll ausgestattetKopie + Anpassungen
nuxt.config.tsSystem-DefaultProjekt-Safelist, eigene Aliases
custom.less (via /admin/design)Standard-TokensProjekt-Tokens
Custom-Overrideskeineapp/custom/* uebersteuert Plugin-Widgets

Ein neuer Projekt-Fork kopiert vue-base/ vollstaendig und benennt den Ordner um — der Build-Prozess und alle Konventionen bleiben identisch.

Haeufige Fehler

app/pages/admin/index.vue angelegt

Die Nuxt-Router-Konvention bricht — catch-all plus index fuehrt zu kollidierenden Routes. Nur [...slug].vue nutzen.

.env nicht geladen

Nuxt laedt .env automatisch aus dem Theme-Root (nicht aus dem Projekt-Root). Env-Variablen muessen im jeweiligen Theme-Ordner stehen, nicht zentral.

Shared Code in _theme/base/ importiert, aber nicht aufgeloest

_theme/base/ ist kein npm-Package — es wird per relativer Pfad oder Nuxt-Alias eingebunden. Details zu Aliases: Nuxt-Aliases.

plugin-registry.js manuell editiert

Die Datei wird beim Cache-Rebuild aus allen Widget-Ordnern neu generiert. Aenderungen gehen verloren. Fuer Widget-Aenderungen immer das Widget-Template selbst editieren.

Theme-Wechsel ohne npm install

Jedes Theme hat eigene node_modules/. Ein Wechsel von vue-base zu projekt01 braucht npm install im neuen Ordner.

Siehe auch