---
name: orblio-template-designer
description: Erstellt vollständige, responsive Webseiten-Designs (Themes, Block-Komponenten und Sub-Templates) für das Orblio CMS. Aktiviere diesen Skill, wenn ein Orblio-Theme, ein Orblio-Template, eine Block-Komponente oder eine neue Webseite/Landingpage für Orblio gebaut, angepasst oder gestylt werden soll.
version: 1.0
---

# Orblio Template Designer — KI-Skill

> **Wofür:** Diese Datei ist eine vollständige, eigenständige Anleitung, mit der eine KI
> (ChatGPT, Claude, Gemini …) korrekte Orblio-Templates erzeugen kann. Gib der KI diese
> Datei zusammen mit deiner Design-Anforderung. Der Output sind fertige `.vue`-Dateien in
> der Orblio-Ordnerstruktur.

Du baust **Webseiten-Designs für das Orblio CMS** — ein mehrschichtiges, Gutenberg-artiges
Template-System. Dein Output sind `.vue`-Template-Dateien in einer festen Ordnerstruktur.
Designqualität (Farbe, Typografie, Layout, UX, A11y) ist genauso Pflicht wie die **exakte
Einhaltung der Orblio-Datenstruktur**. Ein optisch perfektes Template, das die Verdrahtung
verletzt, ist wertlos — es rendert nicht.

## Arbeitsweise (immer in dieser Reihenfolge)

1. **Analysiere zuerst.** Liegt bereits ein Template-Ordner / eine `theme.vue` / Beispiel-
   Komponenten vor? Dann lies sie und übernimm Token-Namen, Klassen-Präfixe, Breakpoints und
   Stil **exakt**. Erfinde keine neue Konvention neben einer bestehenden.
2. **Kläre den Scope — bei neuem Template zuerst FRAGEN, dann bauen.** Sammle fehlende
   Angaben in einer Runde ab (nicht raten, nicht mit Platzhaltern starten):
   - **Template-Name** (= Ordnername unter `templates/`)
   - **Designstil/Stimmung** (verspielt/seriös/editorial; Referenz-Websites, falls vorhanden)
   - **Farbwelt** (Brand-Farben/CI vorhanden?)
   - **Typografie** (gewünschte Fonts oder freie Wahl)
   - **Benötigte Block-Komponenten** (konkrete Liste)
   - **Navigations-Struktur** (welche Nav-IDs: main, Footer-Spalten, CTAs?)
   - **Sprachen** (mehrsprachig → Langswitcher-Platzierung), Logo/Assets vorhanden?
3. **Design-Basis festlegen.** Erzeuge pro Projekt eine frische, semantische Farbpalette als
   CSS-Tokens (`--c-*`). 60-30-10-Logik, HSL-basierte Tonleitern, kein reines `#000`/`#fff`.
4. **Implementiere** strikt nach den Regeln unten.
5. **Liefere vollständige, download-fertige Dateien** — keine Fragmente, keine `…`-Auslassungen.

## Ordnerstruktur eines Templates (verbindlich)

```
templates/<TEMPLATEFOLDER>/
├── components/              Block-Elemente (component1.vue, component2.vue, …)
├── files/                   Icons für Templates
│   ├── default.svg          Standard-Datei-Icon
│   ├── doc.svg  pdf.svg  ppt.svg  xls.svg  zip.svg
│   └── font/                (optional) eingebundene Schriften
├── dynamiclists.vue         Vorlagen für "Dynamische Liste"-Block
├── langswitcher.vue         Sprachumschalter
├── lightbox.vue             Lightbox / Galerie
├── navigation.vue           Navigation (ID-basiert)
├── search.vue               Seitensuche
└── theme.vue                Theme-Wrapper (Header/Main/Footer)
```

## Datei-Anatomie (jede `.vue`-Templatedatei)

Drei Blöcke, immer in dieser Reihenfolge:

```html
<config>
    { /* JSON — Theme vs. Block unterscheiden */ }
</config>
<script>
orblio.NAME = Vue.defineComponent({
    props: [[orblio_inject_props]],   /* vom System ersetzt — NIE selbst ausfüllen */
    template: ` … `,
    /* data / computed / methods / mounted nach Bedarf */
});
</script>
<style>
    /* Nested CSS mit & — KEIN SASS/SCSS */
</style>
```

- `NAME` = Dateiname ohne Endung (`theme`, `navigation`, `component5`, …). Bei Blöcken
  registriert das System unter `orblio.componentN`.
- **System-Platzhalter** (wörtlich stehen lassen, das System ersetzt sie):
  `[[orblio_inject_props]]`, `[[orblio_template_contents]]`, `[[orblio_template_mounted]]`,
  `[[orblio_editorbutton]]`.

### `<config>` — zwei Varianten

**Theme** (`theme.vue`):
```json
{ "title": "Mein Theme", "author": "...", "lastupdate": "2026-05-24" }
```

**Block-Komponente** (`components/componentN.vue`):
```json
{
    "name": {"default": "Hero", "en": "Hero"},
    "description": {"default": "Titel, Text, Bild"},
    "author": "...",
    "date": "2026-05-24",
    "dbKeys": { /* deklariert die DB-Struktur — siehe Content-Elemente */ }
}
```
`langswitcher`/`search`/`navigation` nutzen die einfache Block-Form **ohne** `dbKeys`.
`lightbox` und `dynamiclists` haben **keinen** `<config>`-Block.

## Harte Regeln (nicht verhandelbar)

1. **Kommentare nur `/* */`** — niemals `//`, weder in JS noch in CSS. Im Zweifel gar keine.
2. **Nested CSS mit `&`-Selektor, KEIN SASS/SCSS.** Keine `$variablen`, keine `@mixin`/`@include`.
   Nur natives nested CSS + CSS-Custom-Properties (`--token`).
3. **Vue 3 Options API** via `Vue.defineComponent({...})`. KEIN `<script setup>`, keine
   `import`/`export`, keine Composition-API (`ref`, `reactive`, `computed()` als Imports).
   Methoden als `function`-Ausdrücke (`mounted: function(){…}`).
4. **Zentrale Elemente sind unveränderlich.** `vtext`, `htmltext`, `oimage`, `ovideo`, `ofolder`,
   `repeatable`, `repeatcontents`, `fromdatabase` werden exakt wie unten verwendet. Nur bei
   `repeatable` und `fromdatabase` bestimmst du den Slot-Inhalt frei.
5. **`vtext` für alle designfix gestalteten Texte, `htmltext` nur für freien Fliesstext.**
   Überschriften, Eyebrows, Labels, Card-/Button-Beschriftungen, Stat-Zahlen → immer `vtext`.
   `htmltext` nur, wo der User bewusst frei formatieren und Links/Buttons setzen soll. Im Zweifel `vtext`.
6. **Links & Buttons nie in Block-Komponenten hardcoden.** CTAs müssen CMS-pflegbar sein
   (per `htmltext` gesetzt, oder Linktext via `vtext` + Ziel-URL über eigenen `dbKeys`-Key,
   gebunden mit `:href`). Reine Funktions-Buttons (Burger, Suche, Slider-Pfeile) gehören ins Theme.
7. **`props: [[orblio_inject_props]]`** in Block-/Theme-Komponenten nie selbst befüllen
   (Ausnahme: `navigation` mit eigenem `props`-Objekt).
8. **`val` bei einfachen Elementen = Config-`dbKeys`-Schlüssel, identisch.** Bei Containern ist
   `val` ein frei gewählter Order-Key (Konvention `_`-Präfix, z. B. `_order`, `_fdb`, `_rcorder`).
9. **`$root`-Vertrag respektieren.** Nur dokumentierte Felder lesen/schreiben. Server nur über `$req()`.
10. **Mobile-first & responsive**: jede Komponente muss bis 360 px sauber funktionieren.

## Naming-Konventionen

- **System/Theme/Sub-Templates**: Präfix `orb_` (`.orb_header`, `.orb_nav_main`, `.orb_search`,
  `.orb_lang`, `.orblb-*` für Lightbox).
- **Block-Komponenten**: numerischer Präfix `c{N}_` passend zur Dateinummer
  (`component5.vue` → `.c5_section`). So bleiben Block-Styles kollisionsfrei.
- CSS-Tokens projektweit im `theme.vue` `<style>` unter `:root`; Komponenten konsumieren mit
  Fallback: `var(--r-md, 10px)`.

---

# Content-Elemente — vollständige API

Die **einzige** erlaubte Art, CMS-pflegbare Inhalte in einer Block-Komponente auszugeben.

## Grundprinzip: `db`, `val`, `dbKeys`

- Jede Block-Komponente bekommt über `[[orblio_inject_props]]` ein `db`-Objekt. Alle Elemente
  erhalten `:db="db"`.
- `val` zeigt auf den Schlüssel in `db`, den das Element besetzt.
- Der `<config>`-Block deklariert über `dbKeys` die Schlüssel und ihren **Typ**.

**val-Regel:** Einfache Elemente → `val` identisch mit `dbKeys`-Schlüssel. Container → `val`
ist ein frei gewählter Order-Key (`_`-Präfix).

**dbKeys-Typen:** `"text"`→`vtext` · `"htmltext"`→`htmltext` · `"image"`→`oimage` ·
`"video"`→`ovideo` · `"folder"`→`ofolder` · `"repeatcontent"`→`repeatcontents`.

## Einfache Elemente

```html
<vtext :db="db" val="titel"></vtext>                       <!-- Config: "titel": "text" -->
<htmltext :db="db" val="body"></htmltext>                  <!-- Config: "body": "htmltext" -->
<oimage :db="db" val="bild"></oimage>                      <!-- Config: "bild": "image" -->
<ovideo :db="db" val="clip" w="100%" :autoplay="false"></ovideo>  <!-- "clip": "video" -->
<ofolder :db="db" val="downloads"></ofolder>              <!-- "downloads": "folder" -->
```

**Styling-Verträge:**
- `vtext` → reiner Textknoten; über Eltern-Klasse stylen.
- `htmltext` → Editor.js-Blöcke `.ce-block`; stylen via `& h2 / & p / & ul / & li / & a / & strong`.
- `oimage` → `article > .focus-image-container > img`. Immer Wrapper mit `aspect-ratio` setzen.
- `ovideo` → `.orb_videoout > video`. Immer Wrapper mit `aspect-ratio`.
- `ofolder` → Dateiliste mit `files/`-Icons.

## Container-Elemente

### `repeatable` — wiederholbarer Block nach festem Mini-Template
```html
<repeatable :count="'infinite'" elm="div" cls="c5_rep_inner"
    :basekeys="['text', 'image']" :db="db" val="_order"
    v-slot="{ index, keys }">
    <div class="c5_rep_text"><htmltext :db="db" :val="keys.text"></htmltext></div>
    <div class="c5_rep_img"><oimage :db="db" :val="keys.image"></oimage></div>
</repeatable>
```
Config: `"repeatable": { "text": "htmltext", "image": "image" }`
Mechanik: Items in `db[val]` als Index-Array. Pro Index N ist der echte DB-Key
`basekey + (N > 0 ? N : '')` (Item 1 → `text`, Item 2 → `text2`). `keys` ist `{ basekey: echterKey }`;
darum innen **`:val="keys.text"`** (mit Doppelpunkt).

### `repeatcontents` — gemischte, frei sortierbare Blöcke
```html
<repeatcontents :count="'infinite'" elm="div"
    :typesettings="{ 'htmltext': 'rctext', 'oimage': 'rcimage', 'ovideo': 'rcvideo' }"
    :db="db" val="_rcorder"></repeatcontents>
```
Config: `"repeatcontent1": "repeatcontent"`. **Kein `v-slot`**. `:typesettings` mappt Typ → CSS-Klasse.

### `fromdatabase` — Daten aus einer Sammlung
```html
<fromdatabase :count="50" elm="div" cls="c6_inner"
    :basekeys="['title', 'image']" :db="db" val="_fdb"
    v-slot="{ index, keys }">
    <h3>{{ keys.title }}</h3>
    <img :src="keys.image" />
</fromdatabase>
```
Config: `"fromdatabase": { "text": "title", "image": "image" }` (Richtung **Typ → Feldname**).
Unterschied zu `repeatable`: `keys` liefert **bereits aufgelöste Werte** — roh ausgeben,
NICHT durch `oimage`/`htmltext` führen. `htmltext`-Felder im Slot per `v-html`.

## editMode-Inject (nur wenn nötig)
```js
inject: { editMode: { from: 'editMode', default: () => () => Vue.ref(false) } },
computed: { isEditing: function() { const v = this.editMode()?.value; return v && typeof v === 'object'; } }
```

---

# Theme & Sub-Templates — Verträge

## System-Platzhalter
| Platzhalter | Ort | Bedeutung |
|-------------|-----|-----------|
| `[[orblio_inject_props]]` | `props:` | System-Props. Nie selbst befüllen (Ausnahme: `navigation`). |
| `[[orblio_template_contents]]` | `<main>` in `theme.vue` | Hier rendert das System die Blöcke. |
| `[[orblio_template_mounted]]` | `mounted()` in `theme.vue` | System-Mount-Logik. |
| `[[orblio_editorbutton]]` | Sub-Templates | Editier-Button im Edit-Modus, nach dem Wurzelelement. |

## `$root`-Zustandsvertrag (nur diese Felder lesen/schreiben)
| Zugriff | Zweck |
|---------|-------|
| `$root.page` (r/w) | Aktuelle Seiten-URL. Schreiben = Navigation (SPA). `'/'` = Home. |
| `$root.url` (r) | Basis-URL für `$req`. |
| `$root.site` (r) | Seiten-Datenobjekt; `site.nav[id]`, `site.navpages`. |
| `$root.contentLang` (r/w) | Aktuelle Inhaltssprache. |
| `$root.contentLangs` (r) | Map verfügbarer Sprachen. |
| `$root.$req({...})` | Server-Request. Auch `this.$req(...)`. |

**`$req`-Form:**
```js
this.$root.$req({
    data: { url: this.$root.url, type: 'ui', win: 'search', q: query },
    success: function(r) { /* … */ },
    error:   function(r) { /* … */ }
});
```
`type`: `'ui'` (Admin), `'public'` (Gäste), `'page'` (Seiten-Daten).

## `theme.vue` — Pflicht-Skelett
```html
<config>{ "title": "...", "author": "...", "lastupdate": "YYYY-MM-DD" }</config>
<script>
orblio.theme = Vue.defineComponent({
    props: [[orblio_inject_props]],
    template: `
        <div class="orb_site">
            <header class="orb_header" :class="{scrolled: headerScrolled, open: mobileNav}">
                <!-- Logo, <navigation id="main"/>, <search/>, CTAs, Burger -->
            </header>
            <main class="orb_main">[[orblio_template_contents]]</main>
            <footer class="orb_footer">
                <!-- <navigation id="footer_*"/>, <langswitcher/> -->
            </footer>
            <lightbox/>   <!-- MUSS vorhanden sein, sonst sind Lightbox-Links tot -->
        </div>
    `,
    data: function(){ return { mobileNav: false, headerScrolled: false } },
    watch: { '$root.page': function(){ this.mobileNav = false; } },
    mounted: function(){
        var self = this;
        window.addEventListener('scroll', function(){ self.headerScrolled = window.scrollY > 10; }, { passive: true });
        [[orblio_template_mounted]]
    }
});
</script>
<style> /* @font-face, :root-Tokens, Reset, globale Typo, Buttons, Utilities, Header/Footer */ </style>
```
Das `theme.vue` `<style>` ist der Ort für `:root`-Tokens, Reset, globale Typo, Button-System
(`.orb_btn*`), Utilities (`.orb_container`), Header/Footer. **Pro Navigations-ID** eigene
`.orb_nav_<id>`-Styles bereitstellen.

## Sub-Templates — DOM-Verträge (Script-Logik aus vorhandenen Skeletten unverändert übernehmen)

**`navigation.vue`** — eigenes `props: { db: {}, id: { type: String, default: 'main' } }`.
Kern-Logik (`navItems`, `navPages`, `itemLabel`, `itemHref`, `handleClick`, `isActive`,
`itemKey`, `onEditorButton`) unangetastet lassen; erweitern erlaubt, kürzen nicht.
```
nav.orb_nav.orb_nav_<id>
└─ ul.orb_nav_list
   └─ li.orb_nav_item            (.active, .has_sub)
      ├─ a > span
      └─ ul.orb_nav_sub
         └─ li.orb_nav_subitem   (.active) > a
```

**`search.vue`** — Debounce 300 ms, `$req` mit `type:'ui', win:'search', q`.
```
.orb_search > .orb_search_wrap ( .orb_search_icon | input.orb_search_input | .orb_search_clear )
.orb_search_results > a.orb_search_hit ( .orb_search_hit_title | .orb_search_hit_sub | .orb_search_hit_badge )
.orb_search_empty
```

**`langswitcher.vue`** — rendert nur bei > 1 Sprache; liest `$root.contentLangs`, setzt `$root.contentLang`.
```
.orb_lang > button.orb_lang_btn ( .orb_lang_current | .orb_lang_arrow )
.orb_lang_dropdown > .orb_lang_opt (.active) ( .orb_lang_name | .orb_lang_code )
```

**`lightbox.vue`** — kein `<config>`. Rendert per `<teleport to="body">`, hört auf
`a[data-link-open="lightbox"]`. Nur aktiv, wenn `theme.vue` `<lightbox/>` einbindet. Präfix `orblb-`.
```
.orblb-overlay ( .orblb-close .orblb-prev .orblb-next )
.orblb-stage ( .orblb-img | .orblb-video | .orblb-pdf | .orblb-iframe | .orblb-loader | .orblb-empty )
.orblb-bar ( .orblb-caption | .orblb-counter )
```
Transition-Name `orblb-fade`; hoher `z-index` (~99999).

**`dynamiclists.vue`** — KEINE Vue-Komponente, sondern ein JS-Array:
```js
const DynamicListTemplates = [
  {
    id: 'card-grid',
    name: 'Kachel-Raster',
    description: 'Raster mit Bild, Titel und Beschreibung',
    placeholders: { data1: 'Bild-URL', data2: 'Titel', data3: 'Beschreibung' },
    itemHtml: '<div class="dl-card">…{{data1}}…{{data2}}…{{data3}}…</div>',
    wrapperTag: 'div',
    wrapperClass: 'dl-card-grid'
  }
];
```
Präfix-Konvention `dl-`. Neue Listentypen = neuer Array-Eintrag + passende `dl-*`-Styles.

---

## Scaffold-Workflow (neue Webseite)

1. **Palette + Tokens** in `theme.vue :root` (`--c-*`, `--r-*`, `--s-*`, `--shadow-*`, `--ease`, `--header-h`).
2. **`theme.vue`** — Header, `<main>[[orblio_template_contents]]</main>`, Footer, `<lightbox/>`.
3. **Sub-Templates** — Script-Logik unverändert; nur Markup-Klassen + CSS gestalten.
4. **Block-Set** in `components/`: sinnvoller Standardsatz (Hero, Feature-Grid, Text+Bild,
   Testimonials, Logo-Strip, CTA, Galerie, FAQ/Accordion, Kontakt). Jeder Block: `c{N}_`-Präfix,
   korrekte `dbKeys`, responsive, A11y.
5. **`files/`** — sechs Standard-Icons.
6. **Übersicht** am Ende: Liste aller erzeugten Dateien + welche Navigations-IDs der User pflegen muss.

## Design-Anspruch

- Nicht "nach KI" aussehen: 60-30-10-Farblogik, HSL-Tonleitern, kein reines `#000`/`#fff`,
  definierte Interaction-States.
- Responsive mit klaren Breakpoints (1100 / 900 / 768 / 480 px), `clamp()` für fluide Typo.
- A11y: WCAG-Kontraste (4.5:1), Fokus-States, semantisches HTML, `aria-label` an Icon-Buttons.
- SEO: genau ein `<h1>` je Seite, saubere Heading-Hierarchie, sprechende `alt`-Texte via `oimage`.
- Politur: großzügiger Whitespace, weiche Schatten, sanfte Transitions (`var(--ease)`),
  Hover-Microinteractions.

## Anti-Patterns (verboten)

```
✗ // Kommentare (nur /* */)          ✗ <script setup> / import / export / ref() / reactive()
✗ fetch()/axios (nur this.$req())     ✗ props: [[orblio_inject_props]] selbst füllen
✗ val frei erfinden bei einfachen Elementen (muss = dbKeys-Key sein)
✗ zentrale Elemente umschreiben        ✗ htmltext für Überschriften (dort vtext)
✗ hardcoded <a href>/<button> mit fixem Text/URL in Block-Komponenten
✗ SASS ($color, @mixin, @include)      ✗ reines #000/#fff als Fläche/Text
✗ feste px-Layouts ohne Mobile-Fallback
```
