import{_ as a}from"./plugin-vue_export-helper-DlAUqK2U.js";import{c as l,a as e,b as i,d as t,e as d,f as r,r as o,o as p}from"./app-UjmrCZP8.js";const h={};function c(k,s){const n=o("RouteLink");return p(),l("div",null,[s[2]||(s[2]=e(`

@ocelot-social/ui - Projektdokumentation

Dieses Dokument dient als zentrale Planungs- und Statusübersicht für das UI-Library Subprojekt. Es ermöglicht das Pausieren und Wiederaufnehmen der Arbeit zu jedem Zeitpunkt.


Inhaltsverzeichnis

Schnellzugriff (Status)

AbschnittBeschreibung
FortschrittVisuelle Fortschrittsanzeige
Aktueller StandWas zuletzt erledigt wurde
MeilensteinePhasen 0-5 mit Checklisten

Nach Thema

VISION

#Abschnitt
1Projektziel & Vision

TECHNISCHE GRUNDLAGEN

#Abschnitt
2Tech-Stack
3Build & Distribution
4Icon-Architektur
5Design-Token & Branding

PROZESSE & QUALITÄT

#Abschnitt
6CI/CD & Release
7Dokumentation & DX
8Migrationsstrategie
9Dokumentationsstrategie
10Kompatibilitätstests

REFERENZ & HISTORIE

#Abschnitt
11Entscheidungen
12Arbeitsprotokoll
13Komponenten-Katalog
14Ressourcen & Links
15Dokumentationsstrategie (Details)

ABGRENZUNGEN

#Abschnitt
16Library vs. Webapp
16aWebapp ↔ Maintenance Code-Sharing
16bDaten-Entkopplung (ViewModel/Mapper)
17Externe Abhängigkeiten
18Kompatibilitätstests (Details)
19Komplexitätsanalyse

Wie dieses Dokument verwendet wird

Zum Fortsetzen der Arbeit:

"Lass uns am @ocelot-social/ui Projekt weiterarbeiten" (packages/ui)

Nach jeder Session aktualisieren:


Fortschritt

Gesamtprojekt

Phase 0: ██████████ 100% (6/6 Aufgaben) ✅
Phase 1: ██████████ 100% (6/6 Aufgaben) ✅
Phase 2: ██████████ 100% (26/26 Aufgaben) ✅
Phase 3: ██████████ 100% (24/24 Aufgaben) ✅ - Webapp-Integration komplett
Phase 4: █████████░  85% (23/27 Aufgaben) - Tier 1 ✅, Tier A ✅, Infra ✅, OsBadge ✅, ds-grid ✅, ds-table→HTML ✅, OsNumber ✅, OsModal ✅, ds-radio→HTML ✅ | Tier B ✅, OcelotInput ✅, OcelotSelect ✅, OsMenu ✅ | 0 ds-* Tags verbleibend
Phase 5: ███░░░░░░░  29% (2/7 Aufgaben) - Maintenance-App entkoppelt ✅
───────────────────────────────────────
Gesamt:  █████████░  91% (87/96 Aufgaben)

Katalogisierung (Details in KATALOG.md)

Webapp:     ██████████ 100% (139 Komponenten erfasst)
Styleguide: ██████████ 100% (38 Komponenten erfasst)
Analyse:    ██████████ 100% (Button, Modal, Menu detailiert)

OsButton Migration (Phase 3) ✅

Scope gesamt:     133 <os-button> Tags in 79 Webapp-Dateien
├─ Migriert:       133 Buttons (100%) ✅
├─ <base-button>:    0 verbleibend in Templates
├─ <ds-button>:      0 verbleibend in Templates
└─ Cleanup:        Snapshots/Tests müssen aktualisiert werden

OsButton Features:
├─ variant:     ✅ primary, secondary, danger, warning, success, info, default
├─ appearance:  ✅ filled, outline, ghost
├─ size:        ✅ sm, md, lg, xl
├─ disabled:    ✅ mit hover/active-Override (nur as="button")
├─ icon:        ✅ slot-basiert (icon-system-agnostisch)
├─ circle:      ✅ rounded-full, größenabhängig (p-1.5 bis p-3)
├─ loading:     ✅ animated SVG spinner, aria-busy (Milestone 4b)
└─ as:          ✅ polymorphes Rendering (button/a/NuxtLink/RouterLink)

as-Prop Migration:  15 <nuxt-link>/<a>-Wrapper in 15 Webapp-Dateien → as="nuxt-link"/as="a"

OsIcon (Phase 4)

OsIcon Features:
├─ name:       ✅ System-Icon per Name (check, close, plus)
├─ icon:       ✅ Custom Vue-Komponente (hat Vorrang vor name)
├─ size:       ✅ xs, sm, md, lg, xl, 2xl (em-basiert)
├─ a11y:       ✅ decorative (default) / semantic (mit aria-label)
├─ color:      ✅ fill-current (erbt von Parent)
└─ svg-plugin: ✅ vite-svg-icon (SVG → Vue Component via ?icon)

System-Icons:
├─ check.svg   (Checkmark)
├─ close.svg   (Close/X)
└─ plus.svg    (Plus/Add)

Ocelot-Icons (separates Entry-Point):
└─ 82 Icons (Feature-Icons + Kategorie-Icons aus Webapp migriert)

OsSpinner:
├─ size:       ✅ xs, sm, md, lg, xl, 2xl (em-basiert)
├─ color:      ✅ currentColor (erbt von Parent)
├─ a11y:       ✅ role="status", aria-label="Loading" (customizable)
├─ decorative: ✅ aria-hidden="true" suppresses role/aria-label
├─ os-button:  ✅ OsButton nutzt OsSpinner als Komponente (decorative)
├─ vue-compat: ✅ h() Render-Function mit isVue2
└─ webapp:     ✅ 4 Spinner migriert (DsSpinner + LoadingSpinner → OsSpinner)

BaseCard → OsCard Webapp-Migration: ✅
├─ ~30 Webapp-Dateien: <base-card> → <os-card> (lokale Imports)
├─ 3 Template-Dateien: #imageColumn/#topMenu Slots → inline Layout mit --columns CSS
├─ 16 Spec-Dateien: wrapper.classes('base-card') → wrapper.classes('os-card')
├─ 4 Story-Dateien: <base-card> → <os-card> mit Import
├─ 12 Cypress E2E-Dateien: .base-card → .os-card Selektoren
├─ 2 Cypress-Dateien: .hero-image → .os-card__hero-image
├─ BaseCard.vue Komponente gelöscht
├─ base-components.js Plugin gelöscht (keine Base*.vue mehr)
├─ nuxt.config.js, maintenance config, testSetup.js bereinigt
├─ main.scss: .os-card Regeln (title, ds-section, hero-image, --columns Layout)
├─ CSS Fixes: Tailwind p-6 Override (!important), outline statt border (highlight),
│  child selectors → descendant selectors (hero-image content wrapper)
├─ ContributionForm: Media-Query Selektoren auf .os-card__content korrigiert
├─ ProfileList: .profile-list.os-card Spezifität erhöht (0,3,0 vs 0,2,0)
└─ 0 <base-card> Template-Nutzungen verbleibend

DsSpinner/LoadingSpinner → OsSpinner Webapp-Migration: ✅
├─ ImageUploader.vue: LoadingSpinner → OsSpinner (size="lg")
├─ pages/profile: ds-spinner → os-spinner (size="lg")
├─ pages/groups: ds-spinner → os-spinner (size="lg")
├─ pages/admin: ds-spinner → os-spinner (size="xl") + ApolloQuery→apollo Option
├─ LoadingSpinner Komponente gelöscht
├─ ds-space centered → div+padding (Bugfix in 3 Seiten)
├─ Admin: ApolloQuery→$apollo.loading (Spinner war wg. SSR-Prefetch unsichtbar)
└─ infinite-loading: OsSpinner im spinner-Slot (index, profile, groups)

BaseIcon → OsIcon Webapp-Migration: ✅
├─ 131 <base-icon> in 70+ Dateien → <os-icon :icon="...">
├─ 82 SVGs in ocelot/icons/svgs/ (inkl. 17 Kategorie-Icons)
├─ vite-svg-icon Plugin erweitert (rect, circle, polygon, polyline, ellipse, line)
├─ Kategorie-Icons: DB-String → toCamelCase() → ocelotIcons Lookup
├─ Jest Mocks: @ocelot-social/ui/ocelot für ocelotIcons in Tests
├─ Tests aktualisiert: 911/939 Tests bestanden (3 pre-existing failures)
└─ 0 base-icon/BaseIcon Referenzen verbleibend

Tier A ds-* → Plain HTML + CSS: ✅
├─ 10 triviale Vue-Wrapper-Komponenten durch HTML-Elemente + CSS-Klassen ersetzt
├─ ~450 Nutzungen in ~90 Dateien migriert
├─ _ds-compat.scss: Utility-Klassen für Margins (.ds-mb-*, .ds-mt-*, .ds-my-*),
│  Flex (.ds-flex, .ds-flex-item, .ds-flex-gap-*), Centered (.ds-space-centered)
├─ ds-flex/ds-flex-item: JavaScript window.innerWidth → CSS @media Queries
│  (kein Layout-Shift bei SSR, bessere Performance)
├─ system.css bleibt geladen — bestehende CSS-Klassen funktionieren weiter
├─ Verbleibend: 8 ds-* Komponenten (Tier B Rest: 1 einfache, Tier C: 6 komplexe → UI-Library)
└─ 0 Tier-A ds-* Komponenten-Tags verbleibend

ds-number → OsNumber (UI-Library): ✅
├─ OsNumber Komponente: h() Render-Function, requestAnimationFrame Animation, inheritAttrs: false
├─ Props: count (required), label (optional), animated (optional)
├─ Animation: 1500ms ease-out, watch(count) re-animiert, SSR-safe (onMounted)
├─ Styling: tabular-nums + min-width für stabile Breite, --color-text-soft Label-Farbe
├─ ds-number + CountTo: 5 Dateien → <os-number> (UserTeaserPopover, TabNavigation, admin, profile, groups)
├─ vue-count-to Dependency entfernt, CountTo.vue gelöscht
├─ CSS-Variable: --color-text-soft in requiredCssVariables + ocelot-ui-variables.scss
├─ 11 Unit-Tests, 5 Stories, 5 Visual Tests + 1 Keyboard Test
└─ 0 ds-number/CountTo Nutzungen verbleibend

ds-chip + ds-tag → OsBadge (UI-Library): ✅
├─ OsBadge Komponente: CVA-Varianten, h() Render-Function, inheritAttrs: false
├─ Props: variant (default/primary/danger), size (sm/md/lg), shape (pill/square)
├─ Types: BadgeVariant, BadgeSize, BadgeShape (+ BadgeVariants)
├─ ds-chip: 20 Nutzungen in 5 Dateien → <os-badge> (Formzähler + Gruppen-Metadaten)
├─ ds-tag: 3 Nutzungen in 3 Dateien → <os-badge shape="square"> (Category, Hashtag)
├─ ARIA: role="status" aria-live="polite" auf Form-Zähler (11 Stellen in 2 Dateien)
├─ CSS-Variable: --color-default + --color-default-contrast (beide in requiredCssVariables)
├─ 18 Unit-Tests, 6 Stories, 5 Visual Tests + 1 Keyboard Test
└─ 0 ds-chip/ds-tag Nutzungen verbleibend

Aktueller Stand

Letzte Aktualisierung: 2026-03-28 (Session 35)

Aktuelle Phase: Phase 5 gestartet — Maintenance-App entkoppelt ✅ | Weitere Phase 5 Aufgaben ausstehend

Zuletzt abgeschlossen (Session 35 - Maintenance-App Entkopplung):

Zuvor abgeschlossen (Session 34 - Architektur-Entscheidungen):

Zuvor abgeschlossen (Session 33 - ds-radio → native HTML):

Zuvor abgeschlossen (Session 32 - OsNumber: ds-number + CountTo → OsNumber):

Zuvor abgeschlossen (Session 31 - ds-table → Plain HTML):

Zuvor abgeschlossen (Session 30 - OsBadge Code-Review Fixes):

Zuvor abgeschlossen (Session 29 - OsBadge: ds-chip + ds-tag Migration):

Zuvor abgeschlossen (Session 27 - Tier A: ds- Komponenten → Plain HTML):*

Verbleibende ds- Komponenten: ✅ ALLE MIGRIERT (0 ds- Tags in Webapp)**

Zuvor abgeschlossen (Session 26 - CodeRabbit Review Fixes):

Zuvor abgeschlossen (Session 25 - BaseCard → OsCard Webapp-Migration):

Zuvor abgeschlossen (Session 24 - OsSpinner Webapp-Migration + Refactoring):

Zuvor abgeschlossen (Session 23 - OsSpinner Komponente):

Zuvor abgeschlossen (Session 22 - BaseIcon → OsIcon Webapp-Migration):

Zuvor abgeschlossen (Session 21 - OsIcon Komponente, System-Icons, Ocelot-Umbenennung):

Zuvor abgeschlossen (Session 20 - as-Prop + nuxt-link Migration):

Zuvor erledigt (auf master gemergt):

Abgeschlossene Phasen:

Zuvor abgeschlossen (Sessions 11-19 — Details im Arbeitsprotokoll §12):

Nächste Schritte:

Manuelle Setup-Aufgaben (außerhalb Code):


Meilensteine

Phase 0: Analyse & Katalogisierung ✅

Phase 1: Vue 2.7 Upgrade ✅

Phase 2: Projekt-Setup ✅

Phase 3: Webapp-Integration (Validierung)

Ziel: OsButton in der Webapp einbinden, ohne visuelle oder funktionale Änderungen.

Ansatz: Integration First - Library einbinden, dann schrittweise OsButton ersetzen, beginnend mit einfachsten Stellen.

Milestone 1: Library-Einbindung

Milestone 2: Erste Integration (Minimaler Aufwand)

Milestone 3: Schrittweise Erweiterung

Milestone 4a: Weitere Buttons migrieren (14 ohne neue Props)

Milestone 4b: OsButton Props erweitern

Milestone 4c: Buttons mit icon/circle/loading migrieren ✅ ABGESCHLOSSEN

Button-Komponenten (Wrapper):

Navigation & Menus:

Editor:

Filter & Input:

Chat:

Forms & Auth:

Modals:

Features:

Filter-Menüs:

Pages:

Milestone 5: Validierung & Dokumentation

Einsatzstellen-Übersicht:

KategorieButtonsStatus
✅ Migriert (gesamt)13379 Dateien
<base-button> verbleibend0Nur BaseButton.vue Definition + Test-Dateien
<ds-button> verbleibend0Alle ersetzt
Gesamt133100% erledigt

Details siehe KATALOG.md (vollständige Tracking-Tabellen)

Erfolgskriterien:

KriteriumPrüfung
Visuell identischManueller Screenshot-Vergleich
Funktional identischClick, Disabled funktionieren
Keine RegressionWebapp Unit-Tests bestehen

Visuelle Validierung (OsButton vs Original):

Jeder migrierte Button muss manuell geprüft werden: Normal, Hover, Focus, Active, Disabled.

DateiButtonPropsValidiert
components/Group/GroupForm.vueCanceldefault
components/Group/GroupMember.vueRemove Memberappearance="outline" variant="primary" size="sm"
components/CommentCard/CommentCard.vueShow more/lessappearance="ghost" variant="primary" size="sm"
components/UserTeaser/UserTeaserPopover.vueOpen Profilevariant="primary"
components/DonationInfo/DonationInfo.vueDonate Nowsize="sm" variant="primary"
components/Map/MapStylesButtons.vueMap Styles:appearance dynamisch + custom CSS
components/Embed/EmbedComponent.vueCancelappearance="outline" variant="danger" + custom CSS
components/Embed/EmbedComponent.vuePlay Nowvariant="primary" + custom CSS
pages/terms-and-conditions-confirm.vueRead T&Cappearance="outline" variant="primary"
pages/terms-and-conditions-confirm.vueSavevariant="primary" + disabled
pages/settings/privacy.vueSavevariant="primary" + disabled
pages/settings/notifications.vueCheck Allappearance="outline" variant="primary" + disabled
pages/settings/notifications.vueUncheck Allappearance="outline" variant="primary" + disabled
pages/settings/notifications.vueSavevariant="primary" + disabled
pages/settings/embeds.vueAllow Allappearance="outline" variant="primary" + disabled
pages/settings/embeds.vueDeny Allvariant="primary" + disabled

Validierung abgeschlossen: 16/16 (100%) ✅

Nach Abschluss aller Validierungen:

Phase 4: Komponenten-Migration

Tier 1: Kern-Komponenten (UI-Library)

Tier A: Triviale ds- Wrapper → Plain HTML + CSS* ✅

Tier B: Einfache ds- → Plain HTML / UI-Library*

Tier 2: Layout & Feedback (UI-Library)

Tier 3: Navigation (UI-Library)

Tier 4: Spezial-Komponenten

Infrastruktur

Hinweis: ds-heading, ds-text, ds-tag wurden zu Plain HTML migriert (Tier A). OsHeading/OsText/OsTag als UI-Library-Komponenten sind daher nicht mehr geplant.

Phase 5: Finalisierung & Entkopplung


VISION

1. Projektziel & Vision

Kurzbeschreibung: Neue Vue 3 Komponentenbibliothek aufbauen, die später die Vue 2 Komponenten in der Webapp ersetzen soll.

Hintergrund:

Vision: Ein stark definiertes und flexibles Design-System, das den Branding-Anforderungen von ocelot.social gerecht wird und eine saubere, schrittweise Migration von Vue 2 nach Vue 3 ermöglicht.

Geplanter Ansatz: Migration vorbereiten - schrittweise neue Komponenten in Vue 3 entwickeln, die das bestehende Design-System respektieren und flexible Branding-Optionen bieten.


TECHNISCHE GRUNDLAGEN

2. Tech-Stack

KomponenteEntscheidungNotizen
FrameworkVue 3 + ViteSchnellstes Setup, modernes Tooling
Build-ToolViteSchnelles HMR, einfache Konfiguration
DokumentationStorybook 10Komponenten-Dokumentation mit Vue 3 + Vite
StylingTailwind CSSMit CSS Custom Properties für Branding
TestingVitestVite-nativ, Jest-kompatible API
Paket-Name@ocelot-social/uiUnter ocelot-social npm Org
Komponenten-PrefixOsOsButton, OsCard, etc.
Vue 2 Kompatibilitätvue-demiLibrary funktioniert in Vue 2 und Vue 3
Varianten-SystemCVAclass-variance-authority für typsichere Prop-Varianten
Klassen-Mergecn()clsx + tailwind-merge für Klassen-Kombination
Lintingeslint-config-it4cEnthält: TypeScript, Vue, Prettier, weitere Regeln
Releaserelease-pleaseAutomatische Versionen und Changelogs
IconsHybrid-ArchitekturSystem-Icons in Library, Feature-Icons in App (siehe §4)
Browser-SupportModern onlyChrome, Firefox, Safari, Edge (letzte 2 Versionen)
SSRJaNuxt-kompatibel
Dark ModeJa, von Anfang anAlle Komponenten mit Light/Dark Varianten
LizenzApache 2.0Permissiv mit Patent-Schutz
RepositoryMonorepoOcelot-Social/packages/ui/

Konventionen

AspektEntscheidungNotizen
Vue API<script setup>Composition API mit script setup (erfordert Vue 2.7+)
SpracheEnglischCode, Comments, Dokumentation
Package ManagernpmBereits im Projekt verwendet
Test Coverage100%Vollständige Testabdeckung
DateinamenPascalCaseOsButton.vue, OsCard.vue
i18nNur PropsKeine Default-Texte in Komponenten
BreakpointsTailwind Standardsm:640, md:768, lg:1024, xl:1280, 2xl:1536
Size PropsTailwind-Skalasm, md, lg, xl (komponentenspezifisch)
Rounded PropsTailwind-Skala (vollständig)none, sm, md, lg, xl, 2xl, 3xl, full
Shadow PropsTailwind-Skala (vollständig)none, sm, md, lg, xl, 2xl
Variant PropsSemantisch (vollständig)primary, secondary, danger, warning, success, info
Dark ModeTailwind CSS-KlassenVia dark: Prefix, kein "inverse" Prop
Prop-VollständigkeitAlle oder keineWenn Komponente einen Prop unterstützt, dann die gesamte Skala
CSS VariablesKeine Defaults in LibraryWebapp definiert Default-Branding, spezialisierte Brandings überschreiben
TypeScriptstrict: trueStrikte Typisierung

3. Build & Distribution

Dual-Build Strategie

Die Library bietet zwei Nutzungsmöglichkeiten:

@ocelot-social/ui
├── dist/
│   ├── index.js              # Vue Komponenten
│   ├── style.css             # Vorkompilierte Styles (für Nicht-Tailwind)
│   └── tailwind.preset.js    # Tailwind Preset (für Tailwind-Nutzer)

Nutzung MIT Tailwind

// tailwind.config.js
import ocelotPreset from '@ocelot-social/ui/tailwind.preset'
export default { presets: [ocelotPreset] }

// main.js
import { OsButton } from '@ocelot-social/ui'

Nutzung OHNE Tailwind

// main.js
import { OsButton } from '@ocelot-social/ui'
import '@ocelot-social/ui/style.css'

Branding (funktioniert für beide)

/* branding.css */
:root {
  --color-primary: rgb(110, 139, 135);
  --button-primary-bg: var(--color-secondary);
}

Beide Varianten nutzen CSS Custom Properties - Branding ist identisch.

Webapp-Integration (Entwicklung)

Lokale Entwicklung mit Nuxt Alias:

// webapp/nuxt.config.js
export default {
  alias: {
    '@ocelot-social/ui': process.env.LOCAL_UI
      ? path.resolve(__dirname, '../packages/ui/src')
      : '@ocelot-social/ui'
  }
}

Entwickler startet mit:

LOCAL_UI=true yarn dev

Release-Check: Bei Ocelot-Release wird geprüft, ob packages/ui unreleased Änderungen hat:

# Commits seit letztem ui-Tag?
git log --oneline $(git describe --tags --match "ui-v*" --abbrev=0)..HEAD -- packages/ui/

→ Wenn Output vorhanden: UI muss zuerst released werden.

Webapp-Aufgaben (TODO für Ocelot-Webapp)

Die folgenden Aufgaben müssen in der Webapp umgesetzt werden, nicht in der UI-Library:

AufgabeBeschreibungStatus
Branding-ValidierungTest/Workflow der validateCssVariables() aufruft und sicherstellt, dass das Default-Branding alle von der Library geforderten CSS-Variablen definiert⏳ Offen

Beispiel-Implementierung (Webapp):

// webapp/tests/branding.spec.ts
import { validateCssVariables, requiredCssVariables } from '@ocelot-social/ui/tailwind.preset'

describe('Default Branding', () => {
  it('defines all required CSS variables', () => {
    // Load default branding CSS
    // ...

    const styles = getComputedStyle(document.documentElement)
    const missing: string[] = []

    for (const variable of requiredCssVariables) {
      const value = styles.getPropertyValue(variable).trim()
      if (!value) {
        missing.push(variable)
      }
    }

    expect(missing).toEqual([])
  })
})

4. Icon-Architektur

Entscheidung: Hybrid-Ansatz

Die Library verwendet eine Hybrid-Architektur für Icons:

┌─────────────────────────────────────────────────────────────┐
│  @ocelot-social/ui (Library)                                │
├─────────────────────────────────────────────────────────────┤
│  src/components/OsIcon/icons/svgs/  # System-Icons          │
│  ├── check.svg           # Bestätigung, Checkboxen          │
│  ├── close.svg           # Modal, Chip, Dialoge             │
│  └── plus.svg            # Hinzufügen, Erstellen            │
│                                                             │
│  src/ocelot/icons/svgs/             # Ocelot-Icons          │
│  └── angle-down.svg      # Dropdown-Pfeil                   │
│                                                             │
│  (Weitere System-Icons werden bei Bedarf ergänzt)           │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│  Webapp / Konsumierendes Projekt                            │
├─────────────────────────────────────────────────────────────┤
│  assets/icons/           # Feature-Icons (beliebig viele)   │
│  ├── user.svg            │
│  ├── bell.svg            │
│  ├── heart.svg           │
│  ├── settings.svg        │
│  └── ...                 │
└─────────────────────────────────────────────────────────────┘

Begründung

  1. Library muss standalone funktionieren - Ein OsModal braucht einen Close-Button ohne zusätzliche Konfiguration
  2. 616 Icons sind zu viel - Der Styleguide hat 616 Icons, die meisten werden nie gebraucht
  3. Feature-Icons gehören zur App - Icons wie user, bell, heart sind Business-Logik
  4. Branding-Flexibilität - Verschiedene Ocelot-Instanzen können unterschiedliche Icon-Sets verwenden

System-Icons (in Library enthalten) ✅

IconVerwendungStatus
checkBestätigung, Checkboxen✅ implementiert
closeModal, Chip, Dialoge✅ implementiert
plusHinzufügen, Erstellen✅ implementiert

Geplant (bei Bedarf ergänzen):

IconVerwendung in Komponenten
chevron-downOsSelect, OsDropdown, OsAccordion
chevron-upOsSelect, OsAccordion
barsOsPage (mobile menu)
searchOsInput (search variant)

API-Design (implementiert)

// OsIcon — System-Icon per Name
<OsIcon name="close" />
<OsIcon name="check" size="lg" />

// OsIcon — Custom Vue-Komponente (hat Vorrang vor name)
<OsIcon :icon="UserIcon" />

// OsIcon — Semantic (mit aria-label)
<OsIcon name="close" aria-label="Schließen" />

// OsButton — Icon über #icon Slot (icon-system-agnostisch)
<OsButton variant="primary">
  <template #icon><OsIcon name="check" /></template>
  Speichern
</OsButton>

SVG-Loading (vite-svg-icon Plugin)

// SVGs werden via ?icon Query als Vue-Komponenten geladen
import CheckIcon from './check.svg?icon'

// Plugin extrahiert viewBox + <path> und transformiert zu:
// h('svg', { viewBox, ... }, [h('path', { d })])

Ocelot-Icons (separates Entry-Point)

// Dynamisches Loading via import.meta.glob
import { ocelotIcons } from '@ocelot-social/ui/ocelot'

// Filename → PascalCase: angle-down.svg → IconAngleDown
// Returns Record<string, () => VNode>

Aktuelle Icon-Statistik

QuelleAnzahlStatus
Styleguide (_all)616Nicht übernehmen (FontAwesome 4 komplett)
Webapp (svgs)238Feature-Icons, bleiben in Webapp
Library (system)3✅ check, close, plus
Ocelot-Icons82✅ Feature-Icons + Kategorie-Icons (separates Entry-Point)

5. Design-Token & Branding

3-Stufen Token-System

┌─────────────────────────────────────────────────────────┐
│  1. BASE TOKENS (Rohwerte)                              │
│     --color-green: rgb(23, 181, 63);                    │
│     --color-teal: rgb(110, 139, 135);                   │
│     --space-small: 16px;                                │
└────────────────────────┬────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│  2. SEMANTIC TOKENS (Bedeutung)                         │
│     --color-primary: var(--color-green);                │
│     --color-secondary: var(--color-teal);               │
│     --text-color-base: var(--color-neutral-20);         │
└────────────────────────┬────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│  3. COMPONENT TOKENS (Komponenten-spezifisch)           │
│     --button-primary-bg: var(--color-primary);          │
│     --button-primary-text: var(--color-primary-inverse);│
│     --card-bg: var(--background-color-base);            │
└─────────────────────────────────────────────────────────┘

Branding-Flexibilität

Jedes Branding kann auf jeder Ebene eingreifen:

/* Standard */
:root {
  --button-primary-bg: var(--color-primary);
}

/* Yunite-Branding: Button nutzt Secondary statt Primary */
:root {
  --button-primary-bg: var(--color-secondary);
}

Kompatibilität mit bestehendem System

CVA + Tailwind + CSS-Variablen

Wie CVA funktioniert:

CVA (Class Variance Authority) mappt Props typsicher auf Tailwind-Klassen:

// button.variants.ts
export const buttonVariants = cva(
  'inline-flex items-center font-medium',  // Basis-Klassen
  {
    variants: {
      variant: {
        primary: 'bg-[var(--color-primary)] text-[var(--color-primary-contrast)]',
        danger: 'bg-[var(--color-danger)] text-[var(--color-danger-contrast)]',
      },
      size: {
        sm: 'h-8 px-3 text-sm',
        md: 'h-10 px-4 text-base',
      },
    },
    defaultVariants: { variant: 'primary', size: 'md' },
  }
)

// Aufruf:
buttonVariants({ variant: 'primary', size: 'sm' })
// → 'inline-flex items-center font-medium bg-[var(--color-primary)] ... h-8 px-3 text-sm'

Was ist via Branding überschreibbar?

EbeneÜberschreibbarBeispiel
Farben✅ Ja (CSS-Variablen)--color-primary, --color-danger
Hover-Farben✅ Ja (CSS-Variablen)--color-primary-hover
Kontrast✅ Ja (CSS-Variablen)--color-primary-contrast
Größen❌ Nein (Tailwind-Skala)h-10, px-4, text-base
Abstände❌ Nein (Tailwind-Skala)rounded-md, gap-2
Einzelne Instanz✅ Ja (class-Prop)<OsButton class="h-12" />

Branding-Beispiel:

/* branding/default.css */
:root {
  --color-primary: rgb(23, 181, 63);
  --color-primary-contrast: rgb(255, 255, 255);
  --color-primary-hover: rgb(18, 140, 49);
}

/* branding/yunite.css (überschreibt) */
:root {
  --color-primary: rgb(110, 139, 135);
  --color-primary-contrast: rgb(255, 255, 255);
  --color-primary-hover: rgb(90, 115, 112);
}

Die Rolle von cn() (clsx + tailwind-merge):

import { clsx } from 'clsx'
import { twMerge } from 'tailwind-merge'

export function cn(...inputs) {
  return twMerge(clsx(inputs))
}

// Beispiel: Custom class überschreibt CVA-Werte
cn('h-10 px-4', 'h-12 px-8')  // → 'h-12 px-8' (letzte gewinnt)

Architektur-Übersicht:

┌─────────────────────────────────────────────────────────────┐
│  CSS-Variablen (Branding-überschreibbar)                    │
│  --color-primary, --color-danger, --color-*-hover, etc.     │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│  CVA-Varianten (nutzen CSS-Variablen)                       │
│  bg-[var(--color-primary)], text-[var(--color-contrast)]    │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│  Tailwind-Klassen (feste Skala, konsistent)                 │
│  h-8, h-10, h-12, px-3, px-4, rounded-md, text-sm           │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│  cn() + class-Prop (Escape-Hatch für Einzelfälle)           │
│  <OsButton class="h-20 rounded-full" />                     │
└─────────────────────────────────────────────────────────────┘

Designentscheidung:


PROZESSE & QUALITÄT

6. CI/CD & Release

Release-Workflow (release-please)

Manifest-Modus für Monorepo:

┌─────────────────────────────────────────────────────────────────┐
│                    RELEASE WORKFLOW                             │
├─────────────────────────────────────────────────────────────────┤
│  1. Commit mit Conventional Commits Format                      │
│  2. release-please erstellt Release-PR                          │
│  3. PR merge → Version bump + Changelog                         │
│  4. Automatisch: npm publish + Storybook deploy                  │
└─────────────────────────────────────────────────────────────────┘

Konfigurationsdateien:

GitHub Workflows

WorkflowTriggerBeschreibung
LintPush/PReslint-config-it4c (TypeScript + Vue + Prettier)
TestPush/PRVitest Unit-Tests
BuildPush/PRVite Build verifizieren
ReleasePush to mainrelease-please PR erstellen
PublishRelease creatednpm publish + Storybook deploy

Qualitätssicherung bei PRs

PR erstellt

┌─────────────┐
│   Lint      │──→ eslint-config-it4c (TS + Vue + Prettier)
├─────────────┤
│   Tests     │──→ Vitest
├─────────────┤
│   Build     │──→ Vite
└─────────────┘

Alle grün → Merge erlaubt

Automatische Deployments

EventAktion
Release erstelltnpm publish
Release erstelltStorybook build + deploy auf Server

Storybook Deploy (Webhook):

  1. GitHub sendet Webhook bei Release-Event
  2. Server empfängt Webhook
  3. Server führt scripts/deploy-storybook.sh aus (Teil des Repos)
  4. Script: git pull → npm ci → storybook build → copy to webroot

GitHub Workflows (vollständige Liste)

WorkflowTriggerToolBeschreibung
lintPush/PReslint-config-it4cCode-Stil prüfen
typecheckPush/PRTypeScriptTyp-Fehler finden
testPush/PRVitestUnit-Tests
test-a11yPush/PRaxe-coreAccessibility-Tests
test-visualPush/PRPlaywrightVisual Regression Screenshots
buildPush/PRViteBuild verifizieren
build-storybookPush/PRStorybookDokumentation bauen
size-checkPush/PRsize-limitBundle-Größe prüfen
releasePush mainrelease-pleaseRelease-PR erstellen
publishReleasenpmAuf npm veröffentlichen
deploy-docsReleaseWebhookStorybook auf Server deployen
check-ui-releaseOcelot ReleaseGitPrüft unreleased UI-Änderungen

Erweiterte Qualitätssicherung

MaßnahmeToolBeschreibung
Visual RegressionPlaywrightScreenshot-Vergleiche bei Änderungen
Accessibility@axe-core/playwrightA11y-Prüfung integriert in Visual Tests
Bundle Sizesize-limitWarnung wenn Library-Größe Schwellwert überschreitet

Migrations-Absicherung

Feature Parity Checklist (pro Komponente):

┌─────────────────────────────────────────────────────────┐
│  KOMPONENTE: [Name]                                     │
├─────────────────────────────────────────────────────────┤
│  [ ] Alle Props der alten Komponente übernommen         │
│  [ ] Alle Events identisch                              │
│  [ ] Alle Slots vorhanden                               │
│  [ ] Default-Werte identisch                            │
│  [ ] Visuell identisch (Screenshot-Vergleich)           │
│  [ ] A11y mindestens gleich gut                         │
│  [ ] Dokumentation vollständig                          │
└─────────────────────────────────────────────────────────┘

Deprecation Warnings: Nach erfolgreicher Migration einer Komponente werden in der Vue 2 Webapp Warnungen eingebaut:

// In alter Komponente
if (process.env.NODE_ENV === 'development') {
  console.warn('[DEPRECATED] Button: Bitte @ocelot-social/ui verwenden')
}

7. Dokumentation & DX

Storybook als Komponenten-Dokumentation

Die Komponenten werden über Storybook dokumentiert und auf einer öffentlichen Webseite für Entwickler zugänglich gemacht.

Features für Entwickler:

Hosting:

Workflow:

Komponente entwickeln → Storybook Story schreiben → Build → Deploy auf Server

8. Migrationsstrategie

Grundprinzipien

┌─────────────────────────────────────────────────────────────────┐
│                    MIGRATIONSSTRATEGIE                          │
├─────────────────────────────────────────────────────────────────┤
│  1. Library vollständig in Vue 3 schreiben                      │
│  2. vue-demi für Vue 2 Kompatibilität                           │
│  3. Komponente für Komponente migrieren                         │
│  4. Jede Komponente: vollständig getestet + in Storybook         │
│  5. Nach Migration: Duplikate entfernen, Varianten konsolidieren│
└─────────────────────────────────────────────────────────────────┘

Ablauf pro Komponente

┌──────────────┐    ┌──────────────┐    ┌──────────────┐    ┌──────────────┐    ┌──────────────┐
│   ANALYSE    │ →  │    SPEC      │ →  │   DEVELOP    │ →  │     QA       │ →  │  INTEGRATE   │
├──────────────┤    ├──────────────┤    ├──────────────┤    ├──────────────┤    ├──────────────┤
│ Bestehende   │    │ Props        │    │ Vue 3 Code   │    │ Alle Tests   │    │ In Vue 2     │
│ Varianten    │    │ Varianten    │    │ Unit Tests   │    │ grün         │    │ Projekt      │
│ identifiz.   │    │ Zustände     │    │ Storybook     │    │ Visual Regr. │    │ einbinden    │
│ Duplikate    │    │ A11y         │    │ Stories      │    │ A11y Check   │    │              │
│ finden       │    │ Tokens       │    │              │    │ Review       │    │ Alte Komp.   │
│              │    │              │    │              │    │              │    │ entfernen    │
└──────────────┘    └──────────────┘    └──────────────┘    └──────────────┘    └──────────────┘

Komponenten-Protokoll

Pro Komponente wird eine Status-Datei im Komponenten-Ordner geführt (Colocation):

packages/ui/src/components/OsButton/
├── OsButton.vue
├── OsButton.spec.ts
├── button.variants.ts
├── index.ts
└── STATUS.md          ← Status-Datei hier

Inhalt:

Begründung für Colocation:

Qualitätsanforderungen pro Komponente

AnforderungBeschreibung
Vollständig getestetUnit-Tests für alle Props, Varianten, Edge-Cases
Storybook StoriesAlle Varianten und Zustände dokumentiert
Detaillierte SpecProps, Events, Slots, A11y vor Implementierung definiert
Token-basiertNutzt ausschließlich Design Tokens, keine hardcoded Werte
Vue 2 kompatibelFunktioniert via vue-demi im bestehenden Projekt
A11y geprüftaxe-core Tests bestanden
Visual RegressionScreenshot-Baseline erstellt
Feature ParityCheckliste abgehakt (alle Props, Events, Slots)

Konsolidierungsziele

Bei der Migration werden:


9. Dokumentationsstrategie

Siehe ausführliche Version in §15 Dokumentationsstrategie (Details). Kurzzusammenfassung: Generierte Docs (vue-component-meta) + Manuell (Storybook).


10. Kompatibilitätstests

Vue 2/3 × Tailwind/CSS Testmatrix. Details siehe §18.

Kurzfassung:


REFERENZ & HISTORIE

11. Entscheidungen

73 Entscheidungen in 9 Kategorien

Vision & Ziele

#FrageEntscheidungBegründung
1HauptzielMigration vorbereitenSchrittweise Vue 2 → Vue 3

Tech-Stack

#FrageEntscheidungBegründung
2FrameworkVue 3 + ViteModern, schnell, gute DX
3DokumentationStorybook 10Storybook 1.0-beta hängt mit Tailwind v4 + Node 25, Storybook funktioniert sofort
4StylingTailwind + CSS VariablesModern + Branding-kompatibel
6TestingVitestVite-nativ, Jest-kompatibel
9Vue 2 Kompatibilitätvue-demiLibrary funktioniert in beiden Vue-Versionen
16Lintingeslint-config-it4cTypeScript + Vue + Prettier + weitere Regeln
34Vue API<script setup>Composition API mit script setup
36Package ManagernpmBereits im Projekt verwendet
41TypeScriptstrict: trueStrikte Typisierung
43Vue 2 MinimumVue 2.7Erforderlich für <script setup> Support
69Varianten-SystemCVA (class-variance-authority)Typsichere Props, Composable, DX wie shadcn/ui
70Klassen-Utilitycn() (clsx + tailwind-merge)Bedingte Klassen + Tailwind-Deduplizierung
71Komponenten-StatusSTATUS.md im Komponenten-OrdnerColocated mit Code, nicht in separatem docs/

Build & Distribution

#FrageEntscheidungBegründung
8Paket-Formatnpm LibraryWiederverwendbar
25Paket-Name@ocelot-social/uiUnter ocelot-social npm Org
31Build-StrategieDual-BuildTailwind Preset + Vorkompilierte CSS
32LizenzApache 2.0Permissiv mit Patent-Schutz
33RepositoryMonorepoOcelot-Social/packages/ui/
42Ordnerstrukturpackages/uiMonorepo-Standard, erweiterbar
44Dev-LinkingNuxt AliasLOCAL_UI=true für lokale Library

CI/CD & Release

#FrageEntscheidungBegründung
15Release-Toolrelease-please (Manifest)Monorepo-kompatibel, nur packages/ui Änderungen
17CI WorkflowsLint, Test, BuildQualitätssicherung bei jedem PR
18npm PublishAutomatisch bei ReleaseNach release-please PR merge
19Doku-DeployAutomatisch bei Releasestorybook build + deploy
45Release-CheckGit-basiertPrüft unreleased UI-Änderungen vor Ocelot-Release
46Storybook DeployWebhook + ScriptServer zieht und baut bei Release
50GitHub Workflows12 WorkflowsVollständige CI/CD Pipeline

Testing & Qualität

#FrageEntscheidungBegründung
20Visual RegressionPlaywright ScreenshotsVisueller Vergleich bei Änderungen
21Accessibility@axe-core/playwrightA11y integriert in Visual Tests
22Bundle Sizesize-limitWarnung bei Größenüberschreitung
23Feature ParityCheckliste pro KomponenteSicherstellen dass alle Features migriert
37Test Coverage100%Vollständige Testabdeckung
57Kompatibilitätstests4er-Matrix + CIVue 2/3 × Tailwind/CSS (siehe §18)

Design-System & Branding

#FrageEntscheidungBegründung
5Token-System3-Stufen (Base/Semantic/Component)Maximale Branding-Flexibilität
29Dark ModeJa, von Anfang anAlle Komponenten mit Light/Dark
30IconsHybrid-ArchitekturSystem-Icons in Library, Feature-Icons in App
51Icon-ArchitekturHybrid~10 System-Icons in Library, Rest in App (siehe §4)
59Size PropsTailwind-Skala (sm, md, lg, xl)Komponentenspezifisch, OsButton nutzt sm-xl
60Rounded PropsTailwind-Skala (none, sm, md, lg, xl, 2xl, 3xl, full)Konsistenz mit Tailwind border-radius
61Shadow PropsTailwind-Skala (none, sm, md, lg, xl, 2xl)Konsistenz mit Tailwind box-shadow
62Variant PropsSemantisch (primary, secondary, danger, warning, success, info)Übliche UI-Farbvarianten
63Dark Mode HandlingCSS-Klassen (dark: Prefix)Standard-Tailwind-Pattern, keine "inverse" Props
64Prop-VollständigkeitAlle Werte einer SkalaKonsistente API, keine Teilmengen pro Komponente
65CSS Variable DefaultsKeine Defaults in LibraryWebapp definiert Branding, Library ist design-agnostisch
66Branding-HierarchieWebapp → Spezialisiertes BrandingDefault-Branding in Webapp, Overrides pro Instanz
67Variable-ValidierungRuntime-Check in DevelopmentvalidateCssVariables() warnt bei fehlenden Variablen
68Branding-Test (Webapp)CI-Test in WebappWebapp testet, dass Default-Branding alle Library-Variablen definiert
72Webapp ↔ Maintenance SharingWebapp als Source of TruthKein separates "shared" Package, maintenance importiert aus webapp/ (siehe §16a)
73Daten-EntkopplungViewModel/Mapper PatternKomponenten kennen nur ViewModels, Mapper transformieren API-Daten (siehe §16b)

Komponenten-API & Konventionen

#FrageEntscheidungBegründung
26Komponenten-PrefixOsOsButton, OsCard, etc.
27Browser-SupportModern onlyLetzte 2 Versionen von Chrome, Firefox, Safari, Edge
28SSRJaNuxt-kompatibel
35SpracheEnglischCode, Comments, Docs
38DateinamenPascalCaseOsButton.vue, OsCard.vue
39i18nNur PropsKeine Default-Texte in Komponenten
40BreakpointsTailwind Standardsm, md, lg, xl, 2xl
55Komponenten-AbgrenzungEntscheidungsbaumLibrary: präsentational, Webapp: Business-Logik (siehe §16)

Dokumentation

#FrageEntscheidungBegründung
13Doku-HostingEigener ServerÖffentlich zugängliche Komponenten-Doku
14Doku-ZugangÖffentlichFür alle Entwickler frei zugänglich
47Komponenten-ProtokollSTATUS.md im Komponenten-OrdnerColocated: src/components/OsButton/STATUS.md
52Docs-Generierungvue-component-metaKomponenten-Tabelle aus Code generiert
53Docs CI-CheckGitHub WorkflowPrüft JSDoc-Coverage und README-Aktualität
54Nach MigrationARCHITECTURE.mdPROJEKT.md → ARCHITECTURE.md, KATALOG.md archivieren

Migration & Prozess

#FrageEntscheidungBegründung
7Komponenten-ReihenfolgeNach BedarfFlexibel, Token-System zuerst
10Migrations-AblaufKomponente für KomponenteKontrolliert, schrittweise
11Vor-AnalyseVollständige AnalyseDuplikate/Probleme vor Migration identifizieren
12SpezifikationDetailliert vor ImplementierungProps, Varianten, A11y vorher definieren
24Deprecation WarningsConsole WarningsHinweise in alter Codebase
48KatalogisierungKATALOG.mdUnterbrechbar, Webapp + Styleguide
49FortschrittBerechenbarPro Phase und Gesamt
56Externe Abhängigkeiten✅ Gelösteslint-config-it4c v0.8.0 ist modular
58KomplexitätsanalyseDokumentiert in §19Risikofaktoren, Parallelisierbarkeit, Aufwandstreiber

12. Arbeitsprotokoll

DatumBeschreibungErgebnis
2026-02-04ProjektstartOrdner und Planungsdokument erstellt
2026-02-04Tech-Stack EntscheidungenAlle Kernentscheidungen getroffen
2026-02-04Migrationsstrategievue-demi, Komponente-für-Komponente, Analyse-First
2026-02-04DokumentationKomponenten-Doku auf eigenem Server, öffentlich zugänglich
2026-02-04CI/CD & Releaserelease-please, GitHub Workflows, automatisches npm publish
2026-02-04Erweiterte QAVisual Regression, A11y Tests, Bundle Size Check
2026-02-04Migrations-AbsicherungFeature Parity Checklist, Deprecation Warnings
2026-02-04Naming & Paket@ocelot-social/ui, Os-Prefix
2026-02-04PlattformModern Browsers, SSR-kompatibel, Dark Mode
2026-02-04Build & IconsDual-Build (Tailwind + CSS), Hybrid-Architektur (~10 System-Icons)
2026-02-04OrganisatorischApache 2.0, bleibt im Monorepo
2026-02-04Konventionenscript setup, Englisch, npm, 100% Coverage, PascalCase
2026-02-04WeitereNur Props (kein i18n), Tailwind Breakpoints, strict TS
2026-02-04Ordnerstrukturpackages/ui statt styleguide-vue3
2026-02-04KonfliktanalyseVue 2.7 Upgrade als Voraussetzung für script setup
2026-02-04Webapp-IntegrationNuxt Alias für lokale Entwicklung, Git-basierter Release-Check
2026-02-04ProzesseQA-Schritt pro Komponente, Komponenten-Protokoll, KATALOG.md
2026-02-04FortschrittBerechenbar für Gesamt und Einzelschritte
2026-02-04Phase 1 abgeschlossenVue 2.7 Upgrade erfolgreich, alle Tests bestanden
2026-02-04Icon-ArchitekturHybrid-Ansatz: ~10 System-Icons in Library, Feature-Icons in App
2026-02-04DokumentationsstrategieHybrid: Generiert (vue-component-meta) + Manuell, CI-geprüft
2026-02-04Abgrenzung Library/WebappEntscheidungsbaum + Checkliste für Komponenten-Zuordnung
2026-02-04Externe Abhängigkeiteslint-config-it4c blockiert Linting-Setup, Workaround dokumentiert
2026-02-04Kompatibilitätstests4er-Matrix (Vue 2/3 × Tailwind/CSS), Example Apps, Playwright E2E
2026-02-07Storybook statt HistoireHistoire 1.0-beta hängt mit Tailwind v4 + Node 25 (kein Output, 100% CPU). Storybook 10 funktioniert sofort.
2026-02-07Storybook Greyscale-ThemeKomponenten in Graustufen - verdeutlicht, dass Farben von der App kommen, nicht von der Library.
2026-02-07CSS Token-SystemrequiredCssVariables mit 18 Farb-Variablen (6 Farben × 3 Werte) befüllt.
2026-02-07Storybook Workflowui-storybook.yml für Build + Artifact Upload.
2026-02-07Docker SetupDockerfile (dev + prod), ui-docker.yml Workflow, docker-compose Services.
2026-02-07Storybook Build FixviteFinal entfernt vite-plugin-dts und build-css für Storybook-Build. Stories aus Coverage ausgeschlossen.
2026-02-07Docs-Check Scriptscripts/check-docs.ts prüft: Story-Existenz, JSDoc für Props, Varianten-Abdeckung. ui-docs.yml Workflow.
2026-02-04Phasen umbenannt0.5→1, 1→2, 2→3, 3→4, 4→5 (nur ganzzahlige Phasen)
2026-02-04Dokument-Konsolidierung§13 Zahlen korrigiert, §14 Link entfernt, §16 Reihenfolge, Terminologie vereinheitlicht
2026-02-04Komplexitätsanalyse§20 hinzugefügt: Risikofaktoren, Parallelisierbarkeit, Aufwandstreiber pro Komponente
2026-02-04Phase 2 gestartetVite + Vue 3 Projekt initialisiert, vue-demi, Vitest, Package-Struktur
2026-02-04Build-Systemvite.config.ts mit Library-Mode, vite-plugin-dts für Types, vite-tsconfig-paths
2026-02-04TestingVitest in vite.config.ts integriert, Plugin-Tests geschrieben
2026-02-04DokumentationREADME.md mit Installation und Usage (Tree-Shaking vs Plugin)
2026-02-04Tailwind-KonventionenSize, Rounded, Shadow, Variant - vollständige Skalen, Dark Mode via CSS
2026-02-04Tailwind v4 Setup@tailwindcss/vite Plugin, Dual-Build (style.css + tailwind.preset)
2026-02-04Prop-Typessrc/types.d.ts mit Size, Rounded, Shadow, Variant
2026-02-04Branding-ArchitekturKeine Defaults in Library, Webapp definiert Branding, validateCssVariables()
2026-02-07ESLint Setupeslint-config-it4c v0.8.0 eingerichtet (Vue 3, Vitest, Prettier)
2026-02-07GitHub Workflowsui-lint.yml, ui-test.yml (100% Coverage), ui-build.yml (Build + Verify)
2026-02-07.tool-versionsNode 25.5.0 zentral definiert, Workflows nutzen node-version-file
2026-02-07Vue 2/3 Matrixvue2 + @vitejs/plugin-vue2, CI Matrix-Tests, .npmrc legacy-peer-deps
2026-02-07Vue 2 Test-Strategie geändertInline-Matrix entfernt (Peer-Dependency-Konflikte), Vue 2 wird via Example Apps getestet
2026-02-07Example Apps erstelltvue2-app und vue3-app mit Vitest, ui-compatibility.yml Workflow
2026-02-07ESLint ExamplesEigene eslint.config.ts + prettier.config.mjs pro Example App, Lint im Compatibility-Workflow
2026-02-07Type Assertionsas unknown as Plugin für CI-Kompatibilität bei verlinkten Packages
2026-02-07Package-Validierungpublint + arethetypeswrong, separate .d.cts für CJS, afterBuild Hook in vite-plugin-dts
2026-02-074er Example Apps Matrixvue3-tailwind, vue3-css, vue2-tailwind, vue2-css; Workflow mit Matrix-Strategie
2026-02-07Release & Publishrelease-please Manifest-Config, ui-release.yml Workflow mit npm publish
2026-02-07CONTRIBUTING.mdEntwickler-Leitfaden mit Setup, Komponenten-Erstellung, Code-Standards, Testing, Commits
2026-02-07DependabotKonfiguration für UI-Package und alle 4 Example Apps hinzugefügt
2026-02-07CSS-BuildTailwind CLI im closeBundle Hook für style.css Generierung
2026-02-07CVA Setupclass-variance-authority, clsx, tailwind-merge als Dependencies
2026-02-07cn() UtilityTailwind-Klassen-Merge Funktion (clsx + tailwind-merge) mit Tests
2026-02-07OsButtonErste Komponente mit CVA-Varianten (variant, size, fullWidth) implementiert
2026-02-07ESLint Fixesvue/max-attributes-per-line off (Prettier), import-x/no-relative-parent-imports off
2026-02-07STATUS.md KonventionKomponenten-Status als STATUS.md im Komponenten-Ordner (Colocation statt docs/)
2026-02-07OsButton STATUS.mdStatus-Datei erstellt mit Feature-Übersicht, fehlenden Features, Test-Coverage, Architektur
2026-02-07CVA-Dokumentation§5 erweitert: CVA + Tailwind + CSS-Variablen Architektur, Branding-Überschreibbarkeit
2026-02-07Accessibility Testsvitest-axe mit axe-core, Custom TypeScript-Declarations, OsButton a11y-Tests
2026-02-07Completeness Checkcheck-docs.ts → check-completeness.ts, docs:check → verify, ui-docs.yml → ui-verify.yml
2026-02-07Visual Regression TestsPlaywright mit colocated Tests (*.visual.spec.ts), ui-visual.yml Workflow
2026-02-07Colocated Screenshotsscreenshots/ im Komponenten-Ordner statt separatem e2e/
2026-02-07Visual Test Coverageverify prüft ob alle Stories Visual Tests haben (--kebab-case URL)
2026-02-07Phase 2 abgeschlossenAlle 26 Aufgaben erledigt, bereit für Phase 3
2026-02-08Keyboard Testsdescribe('keyboard accessibility') mit 4 Tests, verify prüft Existenz
2026-02-08ESLint Pluginseslint-plugin-storybook installiert, flat/recommended Config
2026-02-08A11y Konsolidierungvitest-axe → @axe-core/playwright, checkA11y() in Visual Tests
2026-02-08Greyscale Theme WCAGFarben für 4.5:1 Kontrast angepasst (warning, info, secondary)
2026-02-08Test-Vereinfachung*.a11y.spec.ts entfernt, A11y via checkA11y() in Visual Tests
2026-02-08Completeness Updateverify prüft: Story, Visual, checkA11y(), Keyboard, Varianten
2026-02-08Phase 2 KonsolidierungDependencies aufgeräumt (vitest-axe, axe-core entfernt)
2026-02-08Projekt-Optimierungsrc/test/setup.ts entfernt, @storybook/vue3 entfernt, README.md fix
2026-02-08Package Updatessize-limit 12.0.0, eslint-plugin-jsdoc 62.5.4, vite-tsconfig-paths 6.1.0
2026-02-08TODO: eslint-config-it4cMuss auf ESLint 10 aktualisiert werden (aktuell inkompatibel)
2026-02-08Phase 3: vue-demi Integrationvue-demi zur Webapp hinzugefügt, Webpack-Alias für Vue 2.7 Kompatibilität
2026-02-08Phase 3: Webpack-Alias@ocelot-social/ui$ und style.css$ Aliase für yarn-linked Package
2026-02-08Phase 3: isVue2 RenderOsButton mit isVue2 Check: Vue 2 attrs-Objekt, Vue 3 flat props
2026-02-08Phase 3: CSS-SpezifitätUI-Library CSS nach Styleguide laden (styleguide.js Plugin)
2026-02-08Phase 3: Jest vue-demiCustom Mock (__mocks__/@ocelot-social/ui.js) mit Module._load Patch, defineComponent von Vue.default, vueDemiSetup.js, 979 Tests ✅
2026-02-08Phase 3: Button-StylesVariants angepasst: font-semibold, rounded, box-shadow, h-[37.5px]
2026-02-08Phase 3: Erste IntegrationUserTeaserPopover.vue verwendet <os-button>
2026-02-08Phase 3: Visueller TestManueller Vergleich OsButton vs ds-button erfolgreich ✅
2026-02-08Phase 3: v8 ignoreVue 2 Branch in OsButton mit /* v8 ignore */ für 100% Coverage in Vitest
2026-02-08Phase 3: Docker Buildui-library Stage in Dockerfile + Dockerfile.maintenance, COPY --from=ui-library
2026-02-08Phase 3: CI-FixRelativer Pfad file:../packages/ui statt absolut für yarn install außerhalb Docker
2026-02-08Phase 3: Storybook FixTypeScript-Fehler in Stories behoben (default aus args entfernt)
2026-02-08Phase 3: attrs/listenersOsButton forwarded jetzt attrs + $listeners für Vue 2 (getCurrentInstance)
2026-02-08Phase 3: Jest Mock erweitertAlle Composition API Funktionen (computed, ref, watch, etc.) im Mock
2026-02-08Phase 3: 15 Buttons migriertGroupForm, EmbedComponent, DonationInfo, CommentCard, MapStylesButtons, GroupMember, embeds, notifications, privacy, terms-and-conditions-confirm
2026-02-08Phase 3: Test-Updatesprivacy.spec.js Selektoren, notifications Snapshot, DonationInfo.spec.js
2026-02-08OsButton: appearance PropNeue appearance Prop: filled (default), outline, ghost - ermöglicht base-button Stile
2026-02-08OsButton: xs SizeExakte Pixel-Werte für base-button --small: h-26px, px-8px, text-12px, rounded-5px
2026-02-08OsButton: outline primaryGrüner Rahmen + grüner Text + hellgrüner Hintergrund-Tint (rgba(25,122,49,0.18))
2026-02-08OsButton: ghost primaryTransparenter Hintergrund, grüner Text, Hover füllt grün, Active dunkler
2026-02-08OsButton: Focus Stylefocus:outline-dashed focus:outline-1 statt ring (wie base-button)
2026-02-08OsButton: Active Stateactive:bg-[var(--color-VARIANT-hover)] für dunkleren Hintergrund beim Drücken
2026-02-08Visuelle ValidierungTracking-Tabelle in PROJEKT.md für manuelle Button-Vergleiche (4/16 validiert)
2026-02-08Storybook Grayscale ThemeVollständige CSS-Variablen: default, active-states, contrast-inverse
2026-02-08Tailwind Source Filter@import "tailwindcss" source(none) - verhindert Markdown-Scanning
2026-02-08Button Variants KonsistenzAlle 21 compound variants mit korrekten active-states (--color-*-active)
2026-02-08CSS-Variablen erweitert--color-secondary/warning/success/info-active in ocelot-ui-variables.scss
2026-02-08Story Dokumentation"Medium (37.5px)" → "Medium (36px)" korrigiert
2026-02-08Playwright ToleranzmaxDiffPixelRatio: 0.03 für Cross-Platform Font-Rendering
2026-02-09Disabled-Styles korrigiertCSS-Variablen --color-disabled, filled: grauer Hintergrund statt opacity
2026-02-09terms-and-conditions-confirmRead T&C Button → appearance="outline" variant="primary"
2026-02-09Visuelle Validierung10/16 Buttons validiert (terms-and-conditions-confirm.vue abgeschlossen)
2026-02-09Disabled:active/hover FixCSS-Regeln in index.css mit höherer Spezifität für sofortige disabled-Darstellung
2026-02-09notifications.vueCheck All + Uncheck All → appearance="outline" variant="primary"
2026-02-09Visuelle Validierung14/16 Buttons validiert (notifications.vue abgeschlossen)
2026-02-09embeds.vueAllow All → appearance="outline" variant="primary"
2026-02-09Disabled Border-FixCSS-Regeln in index.css: border-style: solid + border-width: 0.8px bei :disabled
2026-02-09Visuelle Validierung abgeschlossen16/16 Buttons validiert (100%) ✅ Milestone 5 erfolgreich
2026-02-09Button-Analyse erweitert14 weitere Buttons identifiziert (ohne icon/circle/loading) → Scope: 16/35
2026-02-09Scope auf ~90 erweitert~60 weitere Buttons mit icon/circle/loading identifiziert
2026-02-09Milestone 4a: 8 ButtonsDisableModal, DeleteUserModal, ReleaseModal, ContributionForm, EnterNonce, MySomethingList, ImageUploader (2x)
2026-02-09ImageUploader CSS-Fixposition: absolute !important für crop-confirm (überschreibt OsButton relative)
2026-02-09§16a hinzugefügtWebapp ↔ Maintenance Code-Sharing: Webapp als Source of Truth (Entscheidung #69)
2026-02-09§16b hinzugefügtDaten-Entkopplung: ViewModel/Mapper Pattern für API-agnostische Komponenten (Entscheidung #70)
2026-02-09NotificationMenu.vue2 Buttons migriert (ghost primary), padding-top Fix für vertical-align Unterschied
2026-02-09Milestone 4a abgeschlossen6 weitere Buttons migriert: donations.vue (Save), profile/_id/_slug.vue (Unblock, Unmute), badges.vue (Remove), notifications/index.vue (Mark All Read), ReportRow.vue (More Details)
2026-02-10Wasserfarben-FarbschemaGreyscale-Theme → Aquarell-Farben (Ultramarin, Dioxazin-Violett, Alizarin, Ocker, Viridian, Cöruleum), WCAG AA konform
2026-02-10Stories konsolidiertPrimary/Secondary/Danger/Default entfernt → AllVariants; AllSizes/AllAppearances/Disabled/FullWidth zeigen alle 7 Varianten
2026-02-10Appearance: Filled/Outline/GhostEinzelne Stories umbenannt und mit allen 7 Varianten erweitert
2026-02-10Playground-StoryInteraktive Controls (argTypes nur in Playground, nicht global)
2026-02-10Einheitlicher Borderborder-[0.8px] border-solid border-transparent als Base-Klasse für alle Appearances
2026-02-10WCAG 2.4.7 FixDefault-Variante: focus:outline-nonefocus:outline-dashed focus:outline-current
2026-02-10Keyboard A11y TestPlaywright-Test fokussiert alle Buttons und prüft outlineStyle !== 'none'
2026-02-10data-appearance AttributOsButton rendert data-appearance auf <button>; CSS-Selektoren nutzen [data-appearance="filled"] statt escaped Tailwind-Klassen
2026-02-10Code-Review FixesUnit-Tests: spezifischere Assertions (Compound-Variant-Logik), Trailing Spaces in Testnamen, ESLint restrict-template-expressions Fix
2026-02-10CSS-Linting@eslint/css + tailwind-csstree für Tailwind v4 Custom Syntax; excludeCSS() Helper verhindert JS-Regel-Konflikte; Regeln: no-empty-blocks, no-duplicate-imports, no-invalid-at-rules
2026-02-10CI-Workflow-Trigger9 UI-Workflows von on: push auf push+pull_request mit Branch-Filter (master) und Path-Filter (packages/ui/** + Workflow-Datei) umgestellt
2026-02-10custom-class entferntcustom-class Prop (entfernt aus OsButton) → class Attribut in notifications.vue, MapStylesButtons.vue, EmbedComponent.vue (4 Stellen); Snapshot aktualisiert
2026-02-10Vue 3 Template-Fixthis.$t()$t() in CommentCard.vue (this im Template in Vue 3 nicht verfügbar)
2026-02-11Icon-Slot implementiertBenannter #icon Slot für OsButton, slot-basiert statt Icon-Prop (icon-system-agnostisch)
2026-02-11Icon-Wrapper KlassenTailwind-Utility-Klassen direkt auf <span>: inline-flex items-center shrink-0 h-[1.2em] [&>svg]:h-full [&>svg]:w-auto [&>svg]:fill-current
2026-02-11VNode Text-ErkennunghasText prüft VNode-Children auf sichtbaren Inhalt; whitespace-only → icon-only Verhalten
2026-02-11Gap & Margin Logikgap-2 bei Icon+Text, -ml-1 bei Icon, -ml-1 -mr-1 bei Icon-Only (optischer Ausgleich)
2026-02-114 neue StoriesIcon, IconOnly, IconSizes, IconAppearances mit Inline-SVG Komponenten (CheckIcon, CloseIcon, PlusIcon)
2026-02-11Playground erweitertReaktiver Icon-Selektor (none/check/close/plus) + Label-Text-Control via computed()
2026-02-11Storybook: components OptionFunktionale Komponenten müssen in components registriert werden, nicht in setup() return
2026-02-11Storybook: CSS nicht in index.cssStorybook lädt eigene storybook.css, nicht src/styles/index.css → Utility-Klassen direkt verwenden
2026-02-11SVG-Targeting[&>svg] statt [&>*] für Icon-Sizing (BaseIcon rendert <span><svg>, Wrapper-Span darf nicht beeinflusst werden)
2026-02-11my-email-address migriertSave-Button: <os-button variant="primary"> mit <template #icon><base-icon name="check" /></template>
2026-02-11Code-OptimierungICON_CLASS Konstante extrahiert, iconMargin Variable, vereinfachte hasText-Logik (kein Symbol.for)
2026-02-11Größenabhängiger Gapgap-1 (4px) für xs/sm, gap-2 (8px) für md/lg/xl bei Icon+Text
2026-02-11Größenabhängiger MarginKein negativer Icon-Margin bei xs/sm (voller Padding-Abstand zur Button-Grenze)
2026-02-11DisableModal.vueConfirm-Button migriert: danger filled icon="exclamation-circle"variant="danger" + #icon Slot
2026-02-11DeleteUserModal.vueConfirm-Button migriert: identisches Pattern wie DisableModal
2026-02-11CtaUnblockAuthor.vueButton migriert: filled icon="arrow-right"variant="primary" + #icon Slot, OsButton importiert
2026-02-11LocationSelect.vueIcon-only Close-Button migriert: ghost size="small" icon="close"variant="primary" appearance="ghost" size="sm" + aria-label
2026-02-11CategoriesSelect.vuev-for Buttons migriert: dynamisches :icon#icon Slot, :filled:appearance, CSS .base-buttonbutton
2026-02-11profile/_id/_slug.vueChat-Button migriert: icon="chat-bubble"variant="primary" appearance="outline" full-width + #icon Slot
2026-02-11verify.vue korrigiertKein Button vorhanden (Eintrag aus Milestone-Liste entfernt)
2026-02-11PaginationButtons.vue2 circle icon-only Buttons migriert: outline primary circle + #icon Slot + aria-label
2026-02-11OsButton: circle Propcircle Prop: rounded-full p-0 + größenabhängige Breiten (CIRCLE_WIDTHS Map)
2026-02-11OsButton: loading PropAnimierter SVG-Spinner mit aria-busy="true", Button auto-disabled bei loading
2026-02-11Spinner-ArchitekturBeide Animationen (rotate + dash) auf <circle> Element; SVG ist statischer Container; Chrome-Compositing-Bug-Workaround
2026-02-11Spinner-ZentrierungIcon-Buttons: Spinner über Icon (translate-basiert, overflow:visible); Text-Buttons: Spinner im Button-Container (inset-0 m-auto)
2026-02-11animations.cssKeyframes os-spinner-dash + os-spinner-rotate in separate CSS-Datei ausgelagert
2026-02-11min-width pro Größemin-w-[26px]/min-w-[36px]/min-w-12/min-w-14 in button.variants.ts (verhindert zu kleine leere Buttons)
2026-02-11Code-OptimierungOsButton ~250→207 Zeilen: buttonData geteilt, SPINNER_PX vereinfacht, redundante cn() entfernt, getCurrentInstance nur Vue 2
2026-02-115 neue Unit-Testsdefault type, data-appearance, min-w, icon-only loading, circle gap-1; gesamt: 76 Tests
2026-02-11Milestone 4b abgeschlossenicon ✅, circle ✅, loading ✅ — alle OsButton-Props implementiert
2026-02-11Milestone 4c: 59 ButtonsChat (2), AddChatRoomByUserSearch (1), CommentCard (1), CommentForm (2), ComponentSlider (2), ContributionForm (1), DeleteData (1), EmbedComponent (1), FilterMenu (1), HeaderButton (2), CategoriesFilter (2), OrderByFilter (2), EventsByFilter (2), FollowingFilter (3), GroupButton (1), ConfirmModal (2), ReportModal (2), Password/Change (1), PasswordReset/Request (1), PasswordReset/ChangePassword (1), Registration/Signup (1), ReleaseModal (1), ImageUploader (2), CreateInvitation (1), Invitation (2), ProfileList (1), ReportRow (1), MySomethingList (3), ActionButton (1), pages/index (2), profile/add-post (1), post/blur-toggle (1), groups/slug (3), settings/index (1), admin/users (2), blocked-users (1), data-download (1), muted-users (1), groups/index (1), enter-nonce (1)
2026-02-11type="submit" PatternOsButton hat type="button" als Default; alle Form-Submit-Buttons brauchen explizit type="submit"
2026-02-11!!errors PatternformErrors ist ein Objekt, nicht Boolean; OsButton disabled Prop erwartet Boolean → !!formErrors nötig
2026-02-11CSS-Selector Pattern.base-button> button oder button; Position/Dimensions brauchen !important für Tailwind-Override
2026-02-11Disabled border-colorOutline disabled border von var(--color-disabled) auf var(--color-disabled-border,#e5e3e8) mit Fallback
2026-02-11Phase 3 abgeschlossen132 <os-button> Tags in 78 Dateien, 0 <base-button> in Templates verbleibend
2026-02-11Password/Change.vue Fix!!errors für disabled-Prop (formErrors ist Objekt)
2026-02-11CommentForm.vue Fixtype="submit" fehlte + !!errors für disabled-Prop
2026-02-11GroupForm.vue ds-buttonLetzter <ds-button> in Webapp → <os-button> mit #icon Slot migriert
2026-02-11OsButton.spec.ts TS-Fixsize aus Object.entries als Union Type gecastet (\`as 'sm'
2026-02-11Coverage 100%v8 ignore start/stop für Vue 2 Branch, v8 ignore next für defensive \`
2026-02-11Scope: 133 Buttons133 <os-button> Tags in 79 Dateien, 0 <base-button> + 0 <ds-button> verbleibend
2026-02-12data-variant AttributOsButton rendert data-variant auf <button> (konsistent mit data-appearance), ermöglicht CSS-Selektoren wie button[data-variant="danger"]
2026-02-12notifications.spec.jsTest-API korrigiert: wrapper.find() (Vue Test Utils) → screen.getByText() (Testing Library), button.disabled statt button.attributes('disabled')
2026-02-12FilterMenu Regressionsbugappearance="ghost" war hardcoded statt dynamisch; filterActive Computed Property existierte aber war nicht genutzt → :appearance="filterActive ? 'filled' : 'ghost'"
2026-02-12FilterMenu.spec.jsTest von CSS-Klasse --filled auf data-appearance="filled" Attribut-Selektor umgestellt
2026-02-12CtaUnblockAuthor.vueTypo require: truerequired: true (Vue ignorierte die Prop-Validierung)
2026-02-12LocationSelect.vue Fixesevent.target.valuethis.currentValue (Button hat kein value), @click.native@click (Vue 3), aria-label via i18n
2026-02-12i18n: actions.clearNeuer Key in allen 9 Sprachdateien: en=Clear, de=Zurücksetzen, fr=Effacer, es=Borrar, it=Cancella, nl=Wissen, pl=Wyczyść, pt=Limpar, ru=Очистить
2026-02-12OsButton JSDocSlot-Dokumentation (@slot default, @slot icon) für vue-component-meta/Storybook autodocs
2026-02-12OsButton xs entferntisSmall von ['xs', 'sm'].includes(size) auf size === 'sm' vereinfacht (xs ist kein gültiger Size-Wert)
2026-02-12Strikte Typisierungtype Size = NonNullable<ButtonVariants['size']>, Record<Size, ...> für CIRCLE_WIDTHS + SPINNER_PX; props.size!(props.size ?? 'md') as Size
2026-02-12animations.cssStylelint-konforme Formatierung: eine Deklaration pro Zeile, Leerzeilen zwischen Keyframe-Stufen
2026-02-12OsButton RefactoringvueAttrs() Helper für Vue 2/3 Attribut-Handling, Einmal-Variablen durch cn() ersetzt, children inline; 77 Tests, 100% Coverage
2026-02-12CSS @import Reihenfolge@import "./animations.css" vor @source-Direktiven verschoben (CSS-Spec: @import vor anderen At-Rules)
2026-02-12CustomButton CleanupisEmpty aus data() entfernt — reine Utility-Funktion braucht keine Vue-Reaktivität
2026-02-12notifications.spec.jsDoppelten beforeEach konsolidiert; wrapper von Modulebene in describe-Block verschoben
2026-02-12Style-ScopingMenuLegend.vue: <style scoped> hinzugefügt; ReportModal + DeleteUserModal: CSS-Selektoren mit Komponenten-Prefix
2026-02-12data-test SelektorenLocationSelect (clear-location-button) + HashtagsFilter (clear-search-button): spezifischere Test-Selektoren
2026-02-12Vue 3 Compat FixesFollowButton: .native entfernt; FilterMenu: slot/slot-scope<template #default>; HashtagsFilter: this.$t()$t()
2026-02-12A11y: aria-labelGroupContentMenu icon-only Button: $t('group.contentMenu.menuButton'); PaginationButtons: $t('pagination.previous/next')
2026-02-12i18n Keyspagination.previous/next + group.contentMenu.menuButton in allen 9 Sprachdateien angelegt
2026-02-12Modal KonsistenzDisableModal + DeleteUserModal: appearance="filled" + :loading="loading" auf Danger-Buttons
2026-02-12Loading Statemy-email-address/index.vue: loadingData hinzugefügt + finally Block für Reset
2026-02-12MapButton #icon SlotIcon von Default-Slot in <template #icon> verschoben (konsistent mit allen anderen Buttons)
2026-02-12Dead Code entferntMySomethingList.vue: .icon-button CSS-Klasse (nach Migration nicht mehr verwendet)
2026-02-12Button-Wrapper-Analyse15 OsButton-Wrapper klassifiziert: 4 Smart (Apollo/Vuex), 4 Presentational, 7 Borderline; GroupButton + MapButton als Inline-Kandidaten identifiziert
2026-02-12compat/ KonzeptSeparates Verzeichnis für temporäre Migrations-Wrapper (nicht von check-completeness.ts erfasst); BaseIcon als erster Kandidat (131 Nutzungen)
2026-02-13data-test Selektoren~10 Komponenten: data-test Attribute für robuste Test-Selektoren (unmute-btn, unblock-btn, follow-btn, join-leave-btn, login-btn, load-all-connections-btn, content-menu-button)
2026-02-13Cypress Selektoren4 Step-Definitions: .user-content-menu button / .content-menu button[data-test="content-menu-button"]
2026-02-13Spec-SelektorenFollowList, FollowButton, LoginButton, CtaJoinLeaveGroup, ReportRow, muted-users, blocked-users: generische button[data-test="..."]
2026-02-13A11y: aria-label~15 icon-only Buttons: aria-label hinzugefügt (admin/users, AddChatRoom, EmbedComponent, groups, profile, CustomButton, HeaderMenu, ImageUploader, ContentMenu, HeaderButton, InviteButton, LoginButton, blocked/muted-users)
2026-02-13Zustandsabhängiges aria-labelpost/_id/_slug: $t(blurred ? 'post.sensitiveContent.show' : 'post.sensitiveContent.hide')
2026-02-13ComponentSlider aria-labelInterpoliertes Label: $t('component-slider.step', { current: index + 1, total: ... })
2026-02-13i18n Keys (6 neue)actions.search, actions.close, actions.menu, site.navigation, post.sensitiveContent.show/hide, component-slider.step in allen 9 Sprachdateien
2026-02-13Loading-State FixesDisableModal + ReleaseModal: finally { this.loading = false } für Reset
2026-02-13BugfixesChangePassword: !!errors; Password/Change: disabled aus data() entfernt + 2 tote Tests; MenuBar: unbenutztes ref entfernt
2026-02-13Button-PropsGroupForm cancel: variant="default" appearance="filled"; donations/LoginForm/EnterNonce: appearance="filled" ergänzt
2026-02-13CSS-SelektorenLoginForm: .login-form button.login-form button[type='submit']; pages/index: redundante Klasse auf BaseIcon entfernt
2026-02-13JoinLeaveButton.native von @mouseenter/@mouseleave entfernt (Vue 3 Kompatibilität)
2026-02-13MySomethingList:title + :aria-label auf Edit/Delete-Buttons (Tooltip beibehalten neben Accessibility)
2026-02-13OsButton Klasseos-button CSS-Klasse auf Button-Element für Branding-Kompatibilität (#9211)
2026-02-14ESLint Config Updateeslint-config-it4c v0.11.2: Flat Config, path alias #src, CSS-Linting, security/detect-non-literal-fs-filename, n/no-sync, n/shebang (#9233)
2026-02-14check-completenessParallelisiertes File-Reading, breitere Regex für Keyboard-Tests, Playground-Tests ignoriert
2026-02-14Release v0.0.2@ocelot-social/ui v0.0.2 veröffentlicht
2026-02-14as PropPolymorphe OsButton-Komponente: as Prop für dynamischen Tag/Komponente (button, a, nuxt-link, router-link); moderner Standard (Headless UI, Radix Vue)
2026-02-14Naming: tag → astagas umbenannt nach Recherche moderner UI-Libraries (Headless UI, Radix Vue, Chakra UI, PrimeVue nutzen as)
2026-02-14Disabled nur für buttondisabled/type/loading nur bei as="button" (Links haben kein natives disabled); aria-disabled/tabindex Logik entfernt
2026-02-14Polymorphic StoryNeue Story Polymorphic mit Varianten, Icons, disabled-Vergleich; Playground mit as-Selektor (button/a)
2026-02-14nuxt-link Migration15 <nuxt-link>/<a>-Wrapper → as="nuxt-link"/as="a" in 15 Webapp-Dateien; invalides HTML (<button> in <a>) eliminiert
2026-02-14CustomButton konsolidiertv-if/v-else für <a>/<nuxt-link> Wrapper → einzelner <os-button :as="linkTag" v-bind="linkProps"> mit Computed Properties
2026-02-14NotificationMenu konsolidiert2 separate Buttons (kein Badge / mit Badge) zu einem zusammengeführt — counter-icon zeigt bei count=0 kein Badge
2026-02-14CSS-Selektor Fixpages/index.vue: button.post-add-button-top/bottom.post-add-button-top/bottom (nuxt-link rendert <a>, nicht <button>)
2026-02-14Profil-Spacingprofile/_id/_slug.vue: v-if auf ds-grid-item (kein leerer Abstand), symmetrisches Padding $space-x-small
2026-02-15OsIcon KomponenteNeue Komponente: name (System-Icon), icon (Custom Component), size (xs-2xl); Vue 2/3 via vue-demi h()
2026-02-15vite-svg-icon PluginCustom Vite Plugin: extrahiert viewBox + <path> aus SVG, transformiert zu Vue h()-Aufruf via ?icon Query
2026-02-15System-Icons3 SVG-Icons in Library: check, close, plus (viewBox 0 0 32 32, stroke-basiert); SYSTEM_ICONS Registry + SystemIconName Type
2026-02-15Icon-GrößenCVA-Varianten: xs(0.75em), sm(0.875em), md(1.2em default), lg(1.5em), xl(2em), 2xl(2.5em) — em-basiert für Kontext-Skalierung
2026-02-15Icon A11yDecorative: aria-hidden="true" (default); Semantic: role="img" + aria-label wenn Label-Prop vorhanden
2026-02-15OsIcon in OsButtonOsButton nutzt OsIcon statt inline SVG-Elemente für Icon-Slot-Rendering
2026-02-15Ocelot-IconsSeparates Entry-Point (ocelot.mjs): dynamisches Icon-Loading via import.meta.glob('**/*.svg', { query: '?icon' })
2026-02-15webapp → ocelotsrc/webapp/src/ocelot/ umbenannt; Stories, Tests, Exports angepasst; konsistentes Naming
2026-02-15OsIcon Tests211 Zeilen Unit-Tests (Rendering, Sizes, A11y, CSS, System-Icons); Visual Tests mit checkA11y(); Keyboard A11y; 100% Coverage
2026-02-15OsButton StoriesBereinigt: Inline-SVG-Komponenten durch OsIcon ersetzt; WithAriaLabel-Story entfernt; InheritColor-Story vereinfacht
2026-02-15check-completenessErweitert für ocelot/ Verzeichnis; unterstützt OsIcon-Patterns
2026-02-15svg-icon.d.tsTypeScript-Deklaration für ?icon Import-Query (Component-Typ)
2026-02-15BaseIcon → OsIcon Migration131 <base-icon> in 70+ Dateien → <os-icon :icon="icons.xxx">, 0 BaseIcon verbleibend
2026-02-1582 Ocelot-IconsVon 1 auf 82 Icons: Feature-Icons + 17 Kategorie-Icons aus Webapp kopiert
2026-02-15vite-svg-icon erweitertUnterstützt jetzt <rect>, <circle>, <polygon>, <polyline>, <ellipse>, <line> (war path-only)
2026-02-15SVG-MinifizierungAlle 21 neuen SVGs auf Single-Line minifiziert (Multiline brach JS-String-Literale im Plugin)
2026-02-15Kategorie-IconsDB-String → toCamelCase()ocelotIcons[key] Lookup in Category, CategoriesFilter, CategoriesSelect, admin/categories
2026-02-15MenuLegend.vue FixlegendItems von data()computed (data() läuft vor created(), this.icons undefined)
2026-02-15HeaderMenu.vue FixMap-Button Icon-Größe: size="xl" + negative Margin (OsIcon md=1.2em vs BaseIcon --large=2.2em)
2026-02-15ShowPassword.vue Fix:data-test="iconName" entfernt (Icon ist jetzt Render-Function statt String)
2026-02-15Test-Updates (8 Specs)Category, ProfileAvatar, CounterIcon, ReportRow, ActionButton, ComponentSlider, ShowPassword, LoginForm: BaseIcon → OsIcon + ocelotIcons
2026-02-158 Snapshots gelöschtStale Snapshot-Dateien entfernt nach BaseIcon → OsIcon Migration
2026-02-15CSS Migration.base-icon.os-icon in main.scss und Category/index.vue
2026-02-15Jest Mock ocelottest/__mocks__/@ocelot-social/ui/ocelot.js für ocelotIcons in Jest-Umgebung
2026-02-18OsSpinner KomponenteNeue Komponente: size (xs-2xl, em-basiert), currentColor, role="status", aria-label; Vue 2/3 via h() + isVue2
2026-02-18OsSpinner Decorativearia-hidden="true" unterdrückt role/aria-label; OsButton nutzt OsSpinner als Komponente (decorative)
2026-02-18ButtonSize TypeButtonSize (sm/md/lg/xl) exportiert aus button.variants.ts; types.d.ts Kommentar: Size ist Vokabular, nicht Pflicht
2026-02-18OsSpinner Webapp-Migration4 Stellen migriert: ImageUploader, profile, groups, admin; LoadingSpinner gelöscht
2026-02-18ds-space centered Bugfix<ds-space centered><div style="text-align:center;padding:48px 0"> in 3 Seiten (Styleguide-Bug)
2026-02-18Admin Spinner Fix<ApolloQuery>apollo-Option + $apollo.loading; SSR-Prefetch verhinderte Loading-State im Client
2026-02-18filterStatistics Fixdelete data.__typename → Destructuring { __typename, ...rest } (keine Mutation des Originalobjekts)
2026-02-18infinite-loading Spinner-SlotOsSpinner im spinner-Slot von vue-infinite-loading in 3 Seiten (index, profile, groups); einheitliches Spinner-Design
2026-02-19BaseCard → OsCard Migration~30 Webapp-Dateien: <base-card><os-card> mit lokalen Imports; CSS-Fixes für Tailwind p-6 Override, outline highlight, child→descendant selectors
2026-02-19#imageColumn/#topMenu inlineLoginForm, RegistrationSlider, password-reset: BaseCard-Slots → inline Layout mit .os-card.--columns CSS in main.scss
2026-02-19Tests & Stories migriert16 Spec-Dateien, 4 Story-Dateien, 12+2 Cypress E2E Step-Definitions: base-card → os-card Selektoren
2026-02-19BaseCard gelöschtBaseCard.vue Komponente + base-components.js Plugin entfernt; nuxt.config, maintenance config, testSetup bereinigt
2026-02-19CSS-FixesContributionForm Media-Query Selektoren, ProfileList Spezifität, InternalPage $space-small, OsCard highlight outline-1 Tests
2026-02-19Code-QualitySocialMedia Props typisiert, LoginForm querySelector, redundante client-only entfernt, NotificationsTable optional chaining, HashtagsFilter doppeltes Mounting
2026-02-19Review Fixes (Session 26)Cypress Kind-Kombinator .os-card > .title, OsCard.spec.ts Bitmask-Assertion fix, Maintenance-App Abhängigkeiten als pre-existing bewertet
2026-02-19Tier A Migration (Session 27)10 triviale ds-* Vue-Wrapper → Plain HTML + CSS-Klassen; _ds-compat.scss Utility-Klassen; ~90 Dateien geändert
2026-02-19ds-section/placeholder/tag/list19 Nutzungen in 13 Dateien → HTML-Elemente mit bestehenden CSS-Klassen aus system.css
2026-02-19ds-container14 Nutzungen in 12 Dateien → <div class="ds-container ds-container-{width}">
2026-02-19ds-heading31 Nutzungen in 25 Dateien → <h1-h4 class="ds-heading ds-heading-h{n}"> mit soft/no-margin/align
2026-02-19ds-text80 Nutzungen in 42 Dateien → <p/div class="ds-text ds-text-{color} ds-text-size-{size}">
2026-02-19ds-space139 Nutzungen in 55+ Dateien → <div class="ds-mb-{size}"> / <div class="ds-my-{size}">; neue Utility-Klassen in _ds-compat.scss
2026-02-19ds-flex/ds-flex-item103 Nutzungen in 29 Dateien → Plain HTML + CSS @media Queries; JS window.innerWidth → CSS Media Queries; gap statt negative-margin/padding
2026-02-19HTML-Validierung Bugfix<p> mit Block-Level-Kindern → <div> in DateTimeRange.vue und verify.vue
2026-02-19Test-FixEmpty.spec.js: attributes().marginclasses().toContain('ds-my-xxx-small')
2026-02-19Review Fixes (Session 28)~20 CodeRabbit Review-Kommentare für Tier A PR bearbeitet; Bugfixes, A11y, i18n, Scoping
2026-02-19Heading-SemantikComponentSlider h1→h3, SearchHeading h1→h5 (ds-heading size-Prop wurde falsch zu h1 migriert)
2026-02-19A11y FixesLabel-for Mismatch (RegistrationSlideEmail),   in Aside entfernt, link.id→link.url Key
2026-02-19i18nlogout.vue "Logging out..." → $t() mit 9 Sprachen; maintenance alt-Text → $t()
2026-02-19CSS Fixes.buttons Klasse ergänzt (GroupForm), display:flex auf .ds-space-centered, 33%→33.333%, inline-style→Utilities
2026-02-19ScopingSignup.vue, maintenance/index.vue, post/create/_type.vue, post/edit/_id.vue: unscoped → scoped Style-Blöcke
2026-02-19Migration-ArtefakteRedundante Wrapper-Divs, tote Attribute (margin="large"), Span→P Block-Level Regression
2026-02-20OsBadge Komponente (Session 29)Neue Komponente: CVA-Varianten (variant, size, shape), h() Render-Function, 16 Tests, 6 Stories
2026-02-20ds-chip → OsBadge20 Nutzungen in 5 Dateien: GroupTeaser, GroupMember, GroupForm, ContributionForm, groups/_slug
2026-02-20ds-tag → OsBadge3 Nutzungen in 3 Dateien: Category (shape="square"), Hashtag (shape="square"), PostTeaser (CSS)
2026-02-20OsBadge Featuresshape-Prop (pill/square), w-fit (Flex-Container-Fix), inline-flex items-center (Icon-Zentrierung)
2026-02-20CSS-Variable--color-default + --color-default-contrast für neutralen Badge-Hintergrund
2026-02-20Code-Review Fixes (Session 30)--color-default-contrast in requiredCssVariables, doppelte --color-default Deklaration entfernt
2026-02-20Type SafetyBadgeVariant Typ exportiert, PlaygroundArgs typisiert, redundante Ternäre entfernt
2026-02-20Layout-KonsistenzGroupForm float:right → Flexbox align-self:flex-end, Inline-Style → Tailwind in Stories
2026-02-20ARIA Live Regionsrole="status"/aria-live="polite" auf 11 Form-Badges (WCAG 4.1.3), live Prop → Standard-Attribute
2026-02-20ds-grid → CSS Grid10 Dateien migriert: ds-grid/ds-grid-item → native CSS Grid + Klassen
2026-02-20ds-table → Plain HTML (Session 31)7 Dateien: <ds-table> → native <table> + CSS-Klassen (.ds-table, .ds-table-col, etc.)
2026-02-20Table-CSS_ds-compat.scss erweitert: .ds-table-wrap, .ds-table, .ds-table-col, .ds-table-head-col, bordered, condensed, alignment
2026-02-20fields() entferntComputed Properties fields()/tableFields() aus 7 Dateien entfernt — Labels direkt in <th>
2026-02-20Scope-Objekte entferntscope.row Zugriffe → direkte Iteration-Variable (user, tag, member, report)
2026-02-20OsNumber Komponente (Session 32)Neue Komponente: h() Render-Function, requestAnimationFrame Animation (1500ms ease-out), count (required), label, animated Props
2026-02-20ds-number + CountTo → OsNumber5 Dateien: UserTeaserPopover, TabNavigation, admin/index, profile/_slug, groups/_slug
2026-02-20Animation-Stabilitättabular-nums + min-width: Nch für stabile Breite während Count-up Animation
2026-02-20CountTo.vue gelöschtvue-count-to Dependency entfernt, followedByCountStartValue/membersCountStartValue Pattern entfernt
2026-02-20CSS-Variable --color-text-softNeuer Contract-Eintrag in tailwind.preset.ts + ocelot-ui-variables.scss (Label-Farbe)
2026-02-20Admin-Label uppercase.admin-stats__item .os-number-label { text-transform: uppercase } per CSS statt neuem Prop
2026-03-13OsModal KomponenteNeue Tier 2 Komponente: h() Render-Function, Vue 2/3 via vue-demi, Focus-Trap, Body Scroll-Lock, ESC-Key, Backdrop-Click, A11y (role=dialog, aria-modal, aria-labelledby/aria-label), 37 Unit-Tests, 5 Visual Tests, 100% Coverage
2026-03-13OsModal Featuresopen Prop (v-model:open), title, cancelLabel/confirmLabel, ariaLabel Fallback, footer Scoped-Slot ({confirm, cancel}), Scroll-Fade (top gradient), tabindex=0 auf scrollbarem Content
2026-03-13OsModal Eventsupdate:open, confirm, cancel, close (mit Typ: 'confirm'/'cancel'/'close'/'backdrop'), opened
2026-03-13OsModal Vue 2 Compatattrs Forwarding in beiden Vue 2 Branches (closed + open), eventProps() Helper, $listeners Forwarding, $createElement für Icons
2026-03-13Modal Webapp-IntegrationConfirmModal + ReportModal nutzen OsModal; Vuex Modal Store entfernt; Modals inline gerendert
2026-03-13Modal Bugfixesz-index Stacking Context Fix (PostTeaser/GroupTeaser), Callback-Promise Propagation (ReportList, MySomethingList), Group Leave Authorization Fix ($nextTick), Cypress .ds-modal → .os-modal
2026-03-13Modal A11yscrollable-region-focusable Fix (tabindex=0), aria-label Fallback wenn kein Title, body overflow save/restore
2026-03-14ds-form entkoppeltNeues formValidation Mixin (async-validator): provide/subscribe Pattern, formData/formSchema/formErrors, handleInput/handleInputValid Callbacks; vuelidate komplett entfernt
2026-03-1418 Formulare migriertCommentForm, ContributionForm, EnterNonce, GroupForm, Password/Change, PasswordReset (2), Registration (5), Signup, MySomethingList, donations, admin/users, settings (3)
2026-03-20formValidation FixhandleInput() vor $validateForm() aufrufen (Reihenfolge-Bug: handleInput überschrieb handleInputValid bei synchronem async-validator Callback)
2026-03-23ds-input → OcelotInputNeue Webapp-Komponente OcelotInput.vue: vereint DsInput + FormItem + InputLabel + InputError in einer Datei. 23 Vue-Dateien migriert mit lokalen Imports (tree-shakeable). formValidation Mixin voll kompatibel. dot-prop Abhängigkeit durch inline getNestedValue() ersetzt. 28 Test-Suites, 210 Tests ✅, 7 Snapshots aktualisiert.
2026-03-23OcelotInput: ds-icon → os-iconDsIcon durch OsIcon + resolveIcon() ersetzt. at.svg, envelope.svg, paperclip.svg zu Ocelot-Icons hinzugefügt. Ocelot-Icons Visual Snapshot aktualisiert.
2026-03-23ds-select → OcelotSelectNeue Webapp-Komponente OcelotSelect.vue: vereint DsSelect + inputMixin + multiinputMixin (~420 Zeilen). Form-Validation entfernt (von keinem Consumer genutzt). DsChip→OsBadge, DsSpinner→OsSpinner, DsIcon→OsIcon. vue-click-outside durch inline document.addEventListener ersetzt. 3 Dateien migriert, 16 Tests ✅.
2026-03-23ds-menu → OsMenu/OsMenuItemNeue packages/ui Komponenten: h() Render, vue-demi, provide/inject, dropdown Prop für Popup-Variante. CSS in src/styles/index.css (integriert in style.css Build). 17 Nutzungen in 11 Dateien migriert. Action-Menüs nutzen link-tag default 'a' statt router-link. router-link Stub global in testSetup.js. Vite closeBundle Hook: ui.css in style.css gemergt. 273 UI-Tests, 108 Webapp-Tests ✅. 0 ds- Komponenten-Tags verbleibend in Webapp.*
2026-03-28Maintenance-App entkoppelt (Session 35)Eigenständiges Nuxt 4-Projekt unter maintenance/. Vue 3, SSR off, nuxt generate → statisches HTML. Abhängigkeiten: @ocelot-social/ui (OsButton, OsIcon, OsCard), @nuxtjs/i18n v10 (11 Sprachen), floating-vue (VDropdown für LocaleSwitch), Tailwind CSS v4. Eigene LocaleSwitch-Komponente (OsButton ghost/circle + OsIcon language + VDropdown). Eigene Locale-Dateien, Branding-CSS, Docker + nginx. Tests: vitest + @nuxt/test-utils. Kein Vuex/Apollo/v-tooltip — vollständig von Webapp entkoppelt. Validiert packages/ui als echten Shared Layer.

13. Komponenten-Katalog

`,322)),i("blockquote",null,[i("p",null,[i("strong",null,[s[1]||(s[1]=t("Detaillierte Katalogisierung in separater Datei: ")),d(n,{to:"/packages/ui/KATALOG.html"},{default:r(()=>s[0]||(s[0]=[t("KATALOG.md")])),_:1})])])]),s[3]||(s[3]=e(`

Zusammenfassung (aus KATALOG.md)

QuelleGesamtStatus
Webapp139✅ Katalogisiert
Styleguide38✅ Katalogisiert — 23 in Webapp genutzt

Styleguide-Migration:

StatusKomponenten
✅ UI-LibraryOsButton, OsIcon, OsSpinner, OsCard, OsBadge, OsNumber, OsModal (7)
✅ → Plain HTMLSection, Placeholder, List, ListItem, Container, Heading, Text, Space, Flex, FlexItem, Grid, GridItem, Table, Radio (14) — Tier A/B
✅ → UI-LibraryChip, Tag → OsBadge (2), Number → OsNumber (1) — Tier B
✅ ds-form entkoppeltForm-Validierung → formValidation Mixin (async-validator), vuelidate entfernt
✅ ds-input → OcelotInputWebapp-Komponente (23 Dateien), lokale Imports, FormItem/InputLabel/InputError vereint
✅ ds-select → OcelotSelectWebapp-Komponente (3 Dateien), lokale Imports, click-outside inline, DsChip→OsBadge
✅ → OsMenu/OsMenuItemMenu, MenuItem (17 Nutzungen → packages/ui, dropdown Prop, eigene CSS)
⬜ Nicht genutztCode, CopyField, FormItem, InputError, InputLabel, Page, PageTitle, Logo, Avatar, TableCol, TableHeadCol (11)

Projekt:

Tracking:

Dokumentation:


15. Dokumentationsstrategie (Details)

Übersicht

┌─────────────────────────────────────────────────────────────┐
│  GENERIERT (aus Code) - Single Source of Truth              │
├─────────────────────────────────────────────────────────────┤
│  • README.md Komponenten-Tabelle                            │
│  • Props/Events/Slots Dokumentation                         │
│  • TypeScript-Typen                                         │
└─────────────────────────────────────────────────────────────┘
                            +
┌─────────────────────────────────────────────────────────────┐
│  MANUELL                                                    │
├─────────────────────────────────────────────────────────────┤
│  • README.md Installation, Quick Start, Theming             │
│  • Storybook Stories (interaktive Beispiele)                 │
│  • ARCHITECTURE.md (Entscheidungen)                         │
│  • Best Practices, Patterns                                 │
└─────────────────────────────────────────────────────────────┘

README.md Struktur

# @ocelot-social/ui

## Übersicht
- Was ist die Library?
- Link zu Storybook (Live-Dokumentation)

## Installation
- npm install
- Peer Dependencies (vue, vue-demi)

## Quick Start
- Minimal-Beispiel (Import + Nutzung)
- Mit Tailwind / Ohne Tailwind

## Theming / Branding
- CSS Custom Properties überschreiben
- Beispiel-Branding

## Icon-System
- System-Icons (enthalten)
- Eigene Icons registrieren

## Vue 2 / Vue 3
- vue-demi Erklärung
- Kompatibilitätshinweise

## Komponenten                    ← GENERIERT
- Tabelle aller Komponenten
- Link zu Storybook für Details

## Contributing
- Link zu CONTRIBUTING.md

## License
- Apache 2.0

Generierung mit vue-component-meta

Tool: vue-component-meta (offizielles Vue-Tool)

// scripts/generate-docs.ts
import { createComponentMetaChecker } from 'vue-component-meta'

// Extrahiert aus .vue Dateien:
{
  name: "OsButton",
  description: "Primary button component for user actions",
  props: [
    {
      name: "variant",
      type: "'primary' | 'secondary' | 'danger'",
      default: "'primary'",
      required: false,
      description: "Visual style variant"
    }
  ],
  events: [{ name: "click", type: "(event: MouseEvent) => void" }],
  slots: [{ name: "default", description: "Button content" }]
}

Generierte Komponenten-Tabelle:

| Komponente | Beschreibung | Props | Events | Slots |
|------------|--------------|-------|--------|-------|
| OsButton | Primary button for actions | 8 | 1 | 2 |
| OsCard | Container with header/footer | 5 | 0 | 3 |
| ... | ... | ... | ... | ... |

CI-Workflow: docs-check

# .github/workflows/docs-check.yml
name: Documentation Check

on: [push, pull_request]

jobs:
  docs:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Generate docs from components
        run: npm run docs:generate

      - name: Check for undocumented props
        run: npm run docs:check-coverage
        # Fails if props/events/slots missing JSDoc

      - name: Verify README is up-to-date
        run: npm run docs:verify
        # Compares generated table with README
        # Fails if differences found

Scripts in package.json:

{
  "scripts": {
    "docs:generate": "tsx scripts/generate-docs.ts",
    "docs:check-coverage": "tsx scripts/check-doc-coverage.ts",
    "docs:verify": "tsx scripts/verify-readme.ts",
    "docs:update": "npm run docs:generate && npm run docs:inject-readme"
  }
}

ESLint-Regeln für Dokumentation

// eslint.config.js
{
  rules: {
    // Erzwingt JSDoc für exportierte Funktionen/Komponenten
    "jsdoc/require-jsdoc": ["error", {
      require: {
        FunctionDeclaration: true,
        ClassDeclaration: true
      }
    }],
    "jsdoc/require-description": "error",
    "jsdoc/require-param-description": "error"
  }
}

Dokumentations-Phasen

PhaseDokumentationStatus
Phase 2README.md Grundgerüst (Installation, Setup)Manuell
Phase 2CONTRIBUTING.mdManuell
Phase 2docs:generate Script einrichtenAutomatisiert
Phase 2CI docs-check WorkflowAutomatisiert
Phase 4Storybook Stories pro KomponenteManuell
Phase 4JSDoc in KomponentenIm Code
Phase 4README Komponenten-TabelleGeneriert
Phase 4README FinalisierungManuell
Phase 5ARCHITECTURE.md (aus PROJEKT.md)Manuell

Nach der Migration: Datei-Transformation

Während Migration:
├── PROJEKT.md          # Planungs- und Statusdokument
├── KATALOG.md          # Komponenten-Tracking

Nach Phase 5 (Migration abgeschlossen):
├── README.md           # Nutzer-Dokumentation (teilweise generiert)
├── CONTRIBUTING.md     # Beitragende
├── CHANGELOG.md        # Automatisch via release-please
├── ARCHITECTURE.md     # Entscheidungen aus PROJEKT.md §2, §4, §5, §11, §15, §16
├── src/components/*/STATUS.md  # Komponenten-Status (colocated, werden beibehalten)
└── docs/
    └── archive/        # Historische Referenz (optional)
        ├── PROJEKT.md
        └── KATALOG.md

Was wird übernommen nach ARCHITECTURE.md:

Was wird archiviert/gelöscht:


ABGRENZUNGEN

16. Library vs. Webapp

Grundprinzip

┌─────────────────────────────────────────────────────────────┐
│  @ocelot-social/ui (Library)                                │
├─────────────────────────────────────────────────────────────┤
│  • Rein präsentational                                      │
│  • Keine Business-Logik                                     │
│  • Keine API-Calls                                          │
│  • Kein App-State                                           │
│  • Wiederverwendbar in jedem Vue-Projekt                    │
└─────────────────────────────────────────────────────────────┘

                            │ nutzt
┌─────────────────────────────────────────────────────────────┐
│  Webapp (Ocelot)                                            │
├─────────────────────────────────────────────────────────────┤
│  • Business-Logik (GraphQL, Auth, etc.)                     │
│  • App-State (Vuex/Pinia)                                   │
│  • i18n Texte                                               │
│  • Routing-Logik                                            │
│  • Ocelot-spezifische Features                              │
└─────────────────────────────────────────────────────────────┘

Entscheidungs-Checkliste

KriteriumLibrary ✅Webapp ✅
Business-LogikKeineHat GraphQL/API-Calls
App-StateKein Vuex/PiniaBraucht Store
i18nNur via PropsNutzt $t() direkt
RoutingNur via Props (to)Nutzt $router direkt
WiederverwendbarIn jedem Vue-ProjektNur in Ocelot
AbhängigkeitenNur Vue + vue-demiOcelot-spezifisch
StylingDesign-TokensApp-spezifische Styles
DatenErhält via PropsFetcht selbst

Entscheidungsbaum

Komponente X


┌─────────────────────────────────────┐
│ Hat sie Business-Logik?             │
│ (API-Calls, Mutations, Auth-Check)  │
└─────────────────────────────────────┘

    ├── JA ──► WEBAPP

    ▼ NEIN
┌─────────────────────────────────────┐
│ Braucht sie App-State?              │
│ (Vuex, Pinia, globaler State)       │
└─────────────────────────────────────┘

    ├── JA ──► WEBAPP

    ▼ NEIN
┌─────────────────────────────────────┐
│ Nutzt sie $t() oder $router direkt? │
└─────────────────────────────────────┘

    ├── JA ──► WEBAPP (oder refactoren)

    ▼ NEIN
┌─────────────────────────────────────┐
│ Ist sie generisch wiederverwendbar? │
│ (Könnte in anderem Projekt helfen)  │
└─────────────────────────────────────┘

    ├── NEIN ──► WEBAPP

    ▼ JA

    ══► LIBRARY

Quantitative Regel

Wenn ≥2 Kriterien auf "Webapp" zeigen → WebappWenn alle Kriterien auf "Library" zeigen → Library

Konkrete Beispiele

KomponenteEntscheidungBegründung
OsButtonLibraryRein präsentational, keine Logik
OsModalLibraryUI-Container, Logik via Events
OsInputLibraryGenerisches Form-Element
OsAvatarLibraryNur Bild + Fallback-Initialen
OsCardLibraryLayout-Container
OsDropdownLibraryPopover-Mechanik
FollowButtonWebappGraphQL Mutation, User-State
PostTeaserWebappOcelot-Datenstruktur, Links
CommentFormWebappAPI-Call, Auth-Check
ConfirmModalWebappNutzt OsModal + Callbacks
LoginFormWebappAuth-Logik, Routing, i18n
UserAvatarWebappNutzt OsAvatar + User-Daten
NotificationMenuWebappGraphQL, Store, i18n

Composition Pattern für Grenzfälle

Wenn eine Komponente UI- und Business-Anteile hat: Aufteilen

┌─────────────────────────────────────────────────────────────┐
│  LIBRARY: OsModal                                           │
│  - Overlay, Animation, Backdrop                             │
│  - close/confirm Events                                     │
│  - Slots für Content                                        │
│  - Props: title, confirmLabel, cancelLabel                  │
└─────────────────────────────────────────────────────────────┘

                        │ nutzt
┌─────────────────────────────────────────────────────────────┐
│  WEBAPP: DeleteUserModal                                    │
│  - Wraps OsModal                                            │
│  - GraphQL Mutation                                         │
│  - i18n Texte via $t()                                      │
│  - Redirect nach Löschung                                   │
└─────────────────────────────────────────────────────────────┘

Beispiel-Code:

<!-- Webapp: DeleteUserModal.vue -->
<template>
  <OsModal
    :title="$t('user.delete.title')"
    :confirm-label="$t('common.delete')"
    confirm-variant="danger"
    @confirm="handleDelete"
    @cancel="$emit('close')"
  >
    <p>{{ $t('user.delete.warning', { name: user.name }) }}</p>
  </OsModal>
</template>

<script setup>
import { OsModal } from '@ocelot-social/ui'
import { useDeleteUserMutation } from '~/graphql/mutations'
import { useRouter } from 'vue-router'

const props = defineProps(['user'])
const emit = defineEmits(['close'])
const router = useRouter()
const { mutate: deleteUser } = useDeleteUserMutation()

const handleDelete = async () => {
  await deleteUser({ id: props.user.id })
  emit('close')
  router.push('/')
}
</script>

Checkliste bei neuer Komponente

Vor dem Erstellen einer Komponente diese Fragen beantworten:

[ ] Wo gehört die Komponente hin? (Entscheidungsbaum durchlaufen)
[ ] Falls Library: Sind alle Texte via Props?
[ ] Falls Library: Keine direkten Store/Router Imports?
[ ] Falls Webapp: Welche Library-Komponenten werden genutzt?
[ ] Falls Grenzfall: Kann sie aufgeteilt werden?

16a. Webapp ↔ Maintenance Code-Sharing

Problemstellung (historisch)

Die Webapp und Maintenance-App waren verschachtelt (webapp/maintenance/). Die Frage war, wie geteilte Business-Komponenten organisiert werden sollten.

Umgesetzte Lösung: Vollständige Entkopplung ✅ (Session 35, 2026-03-28)

Die Maintenance-App ist eine rein statische Wartungsseite ohne Business-Logik. Daher wurde statt Option C (Webapp als Source) die einfachste Lösung gewählt: Vollständige Entkopplung ohne Webapp-Imports.

┌─────────────────────────────────────────────────────────────┐
│  @ocelot-social/ui                                          │
│  ─────────────────                                          │
│  • OsButton, OsIcon, OsCard, OsModal, OsBadge, OsNumber,   │
│    OsSpinner, OsMenu, OsMenuItem                            │
│  • Rein präsentational, keine Abhängigkeiten                │
├─────────────────────────────────────────────────────────────┤
│  webapp/                                                    │
│  ───────                                                    │
│  • Nuxt 2 (Vue 2.7), importiert @ocelot-social/ui          │
│  • Alle Business-Komponenten + GraphQL + Vuex              │
│  • OcelotInput, OcelotSelect (lokale Webapp-Komponenten)   │
├─────────────────────────────────────────────────────────────┤
│  maintenance/                    ← NEU: eigenständig        │
│  ────────────                                               │
│  • Nuxt 4 (Vue 3), importiert @ocelot-social/ui            │
│  • Keine Imports aus webapp/ — vollständig unabhängig       │
│  • Eigene LocaleSwitch (OsButton + OsIcon + floating-vue)  │
│  • @nuxtjs/i18n v10, eigene Locale-Dateien                 │
│  • Tailwind CSS v4, Docker + nginx                          │
└─────────────────────────────────────────────────────────────┘

Kriterien für Entwickler

FrageAntwort
Wo suche ich eine UI-Komponente?@ocelot-social/ui (packages/ui)
Wo suche ich eine Business-Komponente?webapp/components/
Wo erstelle ich maintenance-spezifische Komponenten?maintenance/app/components/
Teilen Webapp und Maintenance Code?Nein — nur @ocelot-social/ui als Shared Layer

Vorteile der Entkopplung

  1. Keine Webapp-Abhängigkeit: Maintenance kann unabhängig gebaut und deployed werden
  2. Validiert packages/ui: Erster externer Consumer — beweist Library-Unabhängigkeit
  3. Moderner Stack: Vue 3 + Nuxt 4 + Tailwind v4 (Webapp noch Vue 2.7 + Nuxt 2)
  4. Einfacher Build: nuxt generate → statisches HTML, keine API-Abhängigkeiten

Spätere Evolution (optional)

Wenn klare Patterns entstehen, können Domain-Packages extrahiert werden:

Phase 1 (jetzt):  Webapp ist Source of Truth
Phase 2 (später): Patterns identifizieren
Phase 3 (später): @ocelot-social/auth, @ocelot-social/posts, etc.

Entscheidung

#DatumEntscheidung
722026-02-09Webapp als Source of Truth für geteilte Business-Komponenten

16b. Daten-Entkopplung (ViewModel/Mapper Pattern)

Problemstellung

Komponenten sind oft direkt an API/GraphQL-Strukturen gekoppelt:

<!-- ❌ Tight Coupling -->
<UserCard :user="graphqlResponse.User" />

// Komponente kennt GraphQL-Struktur
props.user.avatar.url
props.user._followedByCurrentUserCount  // Underscore?!
props.user.__typename                   // Leaked!

Probleme:

Lösung: ViewModel + Mapper Pattern

┌─────────────────────────────────────────────────────────────┐
│  GraphQL / API Layer                                        │
│  • Queries & Mutations                                      │
│  • Generated Types (graphql-codegen)                        │
└─────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│  Mappers (einziger Ort der API-Struktur kennt)              │
│  • toUserCardViewModel(graphqlUser) → UserCardViewModel     │
│  • toPostTeaserViewModel(graphqlPost) → PostTeaserViewModel │
└─────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│  ViewModels (was die UI braucht)                            │
│  • UserCardViewModel { displayName, avatarUrl, ... }        │
│  • PostTeaserViewModel { title, excerpt, ... }              │
└─────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│  Presentational Components (kennen nur ViewModels)          │
│  • <UserCard :user="UserCardViewModel" />                   │
│  • <PostTeaser :post="PostTeaserViewModel" />               │
└─────────────────────────────────────────────────────────────┘

Implementierung

1. ViewModels definieren:

// types/viewModels.ts
export interface UserCardViewModel {
  id: string
  displayName: string
  avatarUrl: string | null
  followerCount: number
  isFollowedByMe: boolean
}

export interface PostTeaserViewModel {
  id: string
  title: string
  excerpt: string
  authorName: string
  authorAvatarUrl: string | null
  createdAt: Date
  commentCount: number
  canEdit: boolean
}

2. Mapper-Funktionen:

// mappers/userMapper.ts
import type { UserCardViewModel } from '~/types/viewModels'
import type { UserGraphQL } from '~/graphql/types'

export function toUserCardViewModel(
  user: UserGraphQL,
  currentUserId?: string
): UserCardViewModel {
  return {
    id: user.id,
    displayName: user.name || user.slug || 'Anonymous',
    avatarUrl: user.avatar?.url ?? null,
    followerCount: user._followedByCurrentUserCount ?? 0,
    isFollowedByMe: user.followedByCurrentUser ?? false,
  }
}

3. Komponenten nutzen nur ViewModels:

<!-- components/UserCard.vue -->
<script setup lang="ts">
import type { UserCardViewModel } from '~/types/viewModels'

// Komponente kennt NUR das ViewModel, nicht GraphQL
defineProps<{
  user: UserCardViewModel
}>()
</script>

4. Composables kapseln Mapping:

// composables/useUser.ts
import { computed } from 'vue'
import { useQuery } from '@vue/apollo-composable'
import { GET_USER } from '~/graphql/queries'
import { toUserCardViewModel } from '~/mappers/userMapper'
import type { UserCardViewModel } from '~/types/viewModels'

export function useUser(userId: string) {
  const { result, loading, error } = useQuery(GET_USER, { id: userId })

  const user = computed<UserCardViewModel | null>(() => {
    if (!result.value?.User) return null
    return toUserCardViewModel(result.value.User)
  })

  return { user, loading, error }
}

Ordnerstruktur

webapp/
├── graphql/
│   ├── queries/
│   ├── mutations/
│   └── types/              # Generated by graphql-codegen
├── types/
│   └── viewModels.ts       # UI-spezifische Interfaces
├── mappers/
│   ├── userMapper.ts
│   ├── postMapper.ts
│   └── index.ts
├── lib/
│   └── composables/
│       ├── useAuth.ts
│       ├── useUser.ts      # Gibt ViewModel zurück
│       └── usePost.ts
├── components/             # Presentational (nur ViewModels)
│   ├── UserCard.vue
│   └── PostTeaser.vue
└── pages/                  # Nutzen Composables
    └── users/[id].vue

Vorteile

AspektOhne MapperMit Mapper
API-ÄnderungAlle Komponenten anpassenNur Mapper anpassen
TestingMock GraphQL ResponseEinfaches ViewModel-Objekt
WiederverwendungKomponente an API gebundenKomponente API-agnostisch
TypeScriptKomplexe/generierte TypesKlare, einfache Interfaces
webapp ↔ maintenanceVerschiedene StrukturenGleiche ViewModels

Regeln

┌─────────────────────────────────────────────────────────────┐
│  Regel 1: Komponenten definieren was sie BRAUCHEN           │
│           (ViewModel), nicht was die API LIEFERT            │
├─────────────────────────────────────────────────────────────┤
│  Regel 2: Mapper sind der EINZIGE Ort der API kennt         │
│           API-Änderung = nur Mapper ändern                  │
├─────────────────────────────────────────────────────────────┤
│  Regel 3: Composables kapseln Fetching + Mapping            │
│           useUser() gibt UserCardViewModel zurück           │
├─────────────────────────────────────────────────────────────┤
│  Regel 4: Presentational Components sind API-agnostisch     │
│           Einfach testbar, wiederverwendbar                 │
└─────────────────────────────────────────────────────────────┘

Entscheidung

#DatumEntscheidung
732026-02-09ViewModel/Mapper Pattern für Daten-Entkopplung

17. Externe Abhängigkeiten

Übersicht

AbhängigkeitStatusBeschreibung
eslint-config-it4c⚠️ ESLint 10 ausstehendv0.8.0 eingebunden, ESLint 10 inkompatibel

eslint-config-it4c

Status: Funktional, ESLint 10 Update ausstehend

TODO: eslint-config-it4c muss für ESLint 10 aktualisiert werden (aktuell inkompatibel wegen @typescript-eslint/utils).

Lösung: Das Paket wurde in Version 0.8.0 modularisiert und unterstützt jetzt ESLint Flat Config.

Aktuelle Nutzung in @ocelot-social/ui:

// eslint.config.ts
import config, { vue3, vitest } from 'eslint-config-it4c'

export default [
  ...config,    // Base + TypeScript + Prettier + weitere
  ...vue3,      // Vue 3 Regeln
  ...vitest,    // Vitest Test-Regeln
  // Projekt-spezifische Overrides...
]

Projekt-spezifische Anpassungen:


18. Kompatibilitätstests (Details)

Testmatrix

Die Library muss in 4 Kombinationen funktionieren:

                    │  Tailwind         │  CSS (vorkompiliert)
────────────────────┼───────────────────┼──────────────────────
Vue 3.4+            │  ✓ Muss testen    │  ✓ Muss testen
Vue 2.7             │  ✓ Muss testen    │  ✓ Muss testen

Werkzeuge & Strategien

1. vue-demi (Code-Ebene)

Abstrahiert Vue 2/3 API-Unterschiede:

// Komponente importiert von vue-demi, nicht vue
import { ref, computed, defineComponent } from 'vue-demi'

2. Unit Tests mit Vitest

Hauptpaket testet nur mit Vue 3 (Entwicklungsumgebung). Vue 2 Kompatibilität wird via vue-demi gewährleistet und in Example Apps getestet.

Begründung: Inline Vue 2/3 Matrix verursacht Peer-Dependency-Konflikte.

3. Example Apps (4 Kombinationen) - Hauptstrategie für Vue 2 Tests

packages/ui/
├── examples/
│   ├── vue3-tailwind/     # Vite + Vue 3 + Tailwind Preset
│   │   ├── package.json
│   │   ├── vite.config.ts
│   │   ├── tailwind.config.js  # Nutzt @ocelot-social/ui/tailwind.preset
│   │   └── src/App.vue         # Importiert alle Komponenten
│   │
│   ├── vue3-css/          # Vite + Vue 3 + style.css
│   │   ├── package.json
│   │   ├── vite.config.ts
│   │   └── src/
│   │       ├── main.ts         # import '@ocelot-social/ui/style.css'
│   │       └── App.vue
│   │
│   ├── vue2-tailwind/     # Vue CLI / Nuxt 2 + Tailwind
│   │   └── ...
│   │
│   └── vue2-css/          # Vue CLI / Nuxt 2 + style.css
│       └── ...

Jede Example App:

4. Playwright E2E Tests

// e2e/compatibility.spec.ts
import { test, expect } from '@playwright/test'

const examples = [
  { name: 'vue3-tailwind', port: 3001 },
  { name: 'vue3-css', port: 3002 },
  { name: 'vue2-tailwind', port: 3003 },
  { name: 'vue2-css', port: 3004 },
]

for (const example of examples) {
  test.describe(example.name, () => {
    test('all components render', async ({ page }) => {
      await page.goto(\`http://localhost:\${example.port}\`)

      // Prüfe dass alle Komponenten sichtbar sind
      await expect(page.locator('[data-testid="os-button"]')).toBeVisible()
      await expect(page.locator('[data-testid="os-card"]')).toBeVisible()
      await expect(page.locator('[data-testid="os-modal"]')).toBeVisible()
    })

    test('styles are applied correctly', async ({ page }) => {
      await page.goto(\`http://localhost:\${example.port}\`)
      const button = page.locator('[data-testid="os-button-primary"]')

      // Prüfe dass CSS korrekt angewendet wird
      await expect(button).toHaveCSS('background-color', /rgb/)
    })

    test('visual regression', async ({ page }) => {
      await page.goto(\`http://localhost:\${example.port}\`)
      await expect(page).toHaveScreenshot(\`\${example.name}.png\`)
    })
  })
}

5. Package Validation

ToolZweck
publintPrüft package.json auf Export-Fehler
arethetypeswrongPrüft TypeScript-Typen für alle Entry Points
{
  "scripts": {
    "check:exports": "publint && attw --pack .",
    "prepublishOnly": "npm run check:exports"
  }
}

6. package.json Exports (korrekte Struktur)

{
  "name": "@ocelot-social/ui",
  "type": "module",
  "exports": {
    ".": {
      "import": "./dist/index.mjs",
      "require": "./dist/index.cjs",
      "types": "./dist/index.d.ts"
    },
    "./style.css": "./dist/style.css",
    "./tailwind.preset": {
      "import": "./dist/tailwind.preset.mjs",
      "require": "./dist/tailwind.preset.cjs",
      "types": "./dist/tailwind.preset.d.ts"
    }
  },
  "peerDependencies": {
    "vue": "^2.7.0 || ^3.0.0"
  },
  "peerDependenciesMeta": {
    "tailwindcss": {
      "optional": true
    }
  }
}

CI Workflow: Compatibility Matrix

# .github/workflows/compatibility.yml
name: Compatibility Tests

on: [push, pull_request]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
      - run: npm ci
      - run: npm run build
      - uses: actions/upload-artifact@v4
        with:
          name: dist
          path: packages/ui/dist

  test-unit:
    needs: build
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        vue-version: ['2.7', '3.4']
    steps:
      - uses: actions/checkout@v4
      - uses: actions/download-artifact@v4
        with:
          name: dist
          path: packages/ui/dist
      - run: npm ci
      - run: npm test
        env:
          VUE_VERSION: \${{ matrix.vue-version }}

  test-e2e:
    needs: build
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        example: ['vue3-tailwind', 'vue3-css', 'vue2-tailwind', 'vue2-css']
    steps:
      - uses: actions/checkout@v4
      - uses: actions/download-artifact@v4
        with:
          name: dist
          path: packages/ui/dist

      - name: Setup example app
        working-directory: packages/ui/examples/\${{ matrix.example }}
        run: |
          npm ci
          npm run build

      - name: Start preview server
        working-directory: packages/ui/examples/\${{ matrix.example }}
        run: npm run preview &

      - name: Wait for server
        run: npx wait-on http://localhost:4173 --timeout 30000

      - name: Run Playwright tests
        working-directory: packages/ui
        run: npx playwright test --grep "\${{ matrix.example }}"

      - uses: actions/upload-artifact@v4
        if: failure()
        with:
          name: playwright-report-\${{ matrix.example }}
          path: packages/ui/playwright-report/

  validate-package:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/download-artifact@v4
        with:
          name: dist
          path: packages/ui/dist
      - run: npm ci
      - name: Validate exports
        run: |
          npx publint packages/ui
          npx @arethetypeswrong/cli packages/ui

Werkzeug-Übersicht

WerkzeugZweckPhase
vue-demiVue 2/3 API-Kompatibilität im CodePhase 2
Vitest + MatrixUnit Tests für Vue 2.7 und Vue 3.4Phase 2
Example Apps (4x)Echte Projekte für jede KombinationPhase 2
PlaywrightE2E + Visual Regression für alle 4Phase 2
publintPackage.json Export-ValidierungPhase 2
arethetypeswrongTypeScript Entry Points CheckPhase 2
pkg-pr-newPreview-Releases in PRs (optional)Optional

Checkliste für neue Komponenten

[ ] Unit Tests laufen mit VUE_VERSION=2.7
[ ] Unit Tests laufen mit VUE_VERSION=3.4
[ ] Komponente in allen 4 Example Apps hinzugefügt
[ ] E2E Tests für Komponente geschrieben
[ ] Visual Regression Baseline erstellt
[ ] Keine Vue 3-only APIs verwendet (oder via vue-demi abstrahiert)

19. Komplexitätsanalyse

Umfang nach Phasen

PhaseAufgabenKomplexitätAbhängigkeiten
Phase 0: Analyse6 Tasks✅ Erledigt-
Phase 1: Vue 2.76 Tasks✅ Erledigt-
Phase 2: Setup26 TasksMittel⚠️ eslint-config-it4c (extern)
Phase 3: Tokens6 TasksNiedrigKeine externen
Phase 4: Migration15 KomponentenHochPro Komponente: Spec→Dev→Test→Integrate
Phase 5: Finalisierung7 TasksNiedrigAlle vorherigen Phasen

Bekannte Risikofaktoren

RisikoBeschreibungAuswirkungMitigation
eslint-config-it4cExternes Projekt muss zuerst modularisiert werdenBlockiert Linting-Setup in Phase 2Temporäre lokale ESLint-Config als Workaround
vue-demi KompatibilitätUnbekannte Edge-Cases bei Vue 2/3 Dual-SupportUnerwartete Bugs bei IntegrationFrühzeitig in Example Apps testen
Visual Regression BaselinesKönnen bei Design-Änderungen viel Nacharbeit erfordernZusätzlicher Aufwand bei ÄnderungenBaselines erst nach Design-Freeze erstellen
Feature-ParityAlte Komponenten haben undokumentierte VerhaltensweisenRegressions bei MigrationGründliche Analyse vor Implementierung
Tailwind + CSS Dual-BuildKomplexe Build-KonfigurationBuild-Fehler, InkonsistenzenFrüh beide Varianten parallel testen

Parallelisierbarkeit

PhaseParallelisierbarDetails
Phase 2TeilweiseDie meisten Tasks sind sequentiell (Setup-Reihenfolge wichtig)
Phase 3NeinToken-Ebenen bauen aufeinander auf (Base → Semantic → Component)
Phase 4Ja (nach Tier 1)Tier 2/3 Komponenten können parallel entwickelt werden
Phase 5TeilweiseDokumentation kann parallel zur letzten Integration

Parallelisierbare Aufgaben in Phase 2:

Sequentielle Abhängigkeiten in Phase 2:

  1. Vite + Vue 3 Projekt → vue-demi → Tailwind → Build-Pipeline
  2. Vitest → Tests → Visual Regression
  3. Package-Struktur → release-please → npm Publish Workflow

Aufwandstreiber pro Komponente (Phase 4)

Jede Komponente durchläuft:

┌─────────────┐   ┌─────────────┐   ┌─────────────┐   ┌─────────────┐   ┌─────────────┐
│  ANALYSE    │ → │    SPEC     │ → │   DEVELOP   │ → │     QA      │ → │  INTEGRATE  │
├─────────────┤   ├─────────────┤   ├─────────────┤   ├─────────────┤   ├─────────────┤
│ Bestehende  │   │ Props       │   │ Vue 3 Code  │   │ Unit Tests  │   │ In Webapp   │
│ Varianten   │   │ Events      │   │ TypeScript  │   │ Vue 2 Tests │   │ einbinden   │
│ analysieren │   │ Slots       │   │ Tailwind    │   │ A11y Tests  │   │             │
│             │   │ Tokens      │   │ Storybook    │   │ Visual Reg. │   │ Alte Komp.  │
│             │   │ A11y        │   │ Stories     │   │ 4 Examples  │   │ entfernen   │
└─────────────┘   └─────────────┘   └─────────────┘   └─────────────┘   └─────────────┘

Aufwand variiert stark nach Komponente:

KomponenteKomplexitätGrund
OsIconNiedrigEinfache Wrapper-Komponente
OsSpinnerNiedrigNur Animation + Größen
OsButtonHochViele Varianten, Link-Support, States
OsCardNiedrigEinfaches Layout
OsModalHoch ✅Focus-Trap, Scroll-Lock, Body Overflow Save/Restore, Vue 2/3 h() Compat, A11y (dialog, aria-modal, aria-labelledby), ESC-Key, Backdrop-Click, Scrollable Content tabindex
OsDropdownHochPositioning, Click-Outside, Hover-States
OsInputMittelValidierung, States, Icons
OsAvatarNiedrigBild + Fallback
`,158))])}const m=a(h,[["render",c],["__file","PROJEKT.html.vue"]]),b=JSON.parse('{"path":"/packages/ui/PROJEKT.html","title":"@ocelot-social/ui - Projektdokumentation","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Inhaltsverzeichnis","slug":"inhaltsverzeichnis","link":"#inhaltsverzeichnis","children":[{"level":3,"title":"Schnellzugriff (Status)","slug":"schnellzugriff-status","link":"#schnellzugriff-status","children":[]},{"level":3,"title":"Nach Thema","slug":"nach-thema","link":"#nach-thema","children":[]},{"level":3,"title":"Wie dieses Dokument verwendet wird","slug":"wie-dieses-dokument-verwendet-wird","link":"#wie-dieses-dokument-verwendet-wird","children":[]}]},{"level":2,"title":"Fortschritt","slug":"fortschritt","link":"#fortschritt","children":[{"level":3,"title":"Gesamtprojekt","slug":"gesamtprojekt","link":"#gesamtprojekt","children":[]},{"level":3,"title":"Katalogisierung (Details in KATALOG.md)","slug":"katalogisierung-details-in-katalog-md","link":"#katalogisierung-details-in-katalog-md","children":[]},{"level":3,"title":"OsButton Migration (Phase 3) ✅","slug":"osbutton-migration-phase-3-✅","link":"#osbutton-migration-phase-3-✅","children":[]},{"level":3,"title":"OsIcon (Phase 4)","slug":"osicon-phase-4","link":"#osicon-phase-4","children":[]}]},{"level":2,"title":"Aktueller Stand","slug":"aktueller-stand","link":"#aktueller-stand","children":[]},{"level":2,"title":"Meilensteine","slug":"meilensteine","link":"#meilensteine","children":[{"level":3,"title":"Phase 0: Analyse & Katalogisierung ✅","slug":"phase-0-analyse-katalogisierung-✅","link":"#phase-0-analyse-katalogisierung-✅","children":[]},{"level":3,"title":"Phase 1: Vue 2.7 Upgrade ✅","slug":"phase-1-vue-2-7-upgrade-✅","link":"#phase-1-vue-2-7-upgrade-✅","children":[]},{"level":3,"title":"Phase 2: Projekt-Setup ✅","slug":"phase-2-projekt-setup-✅","link":"#phase-2-projekt-setup-✅","children":[]},{"level":3,"title":"Phase 3: Webapp-Integration (Validierung)","slug":"phase-3-webapp-integration-validierung","link":"#phase-3-webapp-integration-validierung","children":[]},{"level":3,"title":"Phase 4: Komponenten-Migration","slug":"phase-4-komponenten-migration","link":"#phase-4-komponenten-migration","children":[]},{"level":3,"title":"Phase 5: Finalisierung & Entkopplung","slug":"phase-5-finalisierung-entkopplung","link":"#phase-5-finalisierung-entkopplung","children":[]}]},{"level":2,"title":"1. Projektziel & Vision","slug":"_1-projektziel-vision","link":"#_1-projektziel-vision","children":[]},{"level":2,"title":"2. Tech-Stack","slug":"_2-tech-stack","link":"#_2-tech-stack","children":[{"level":3,"title":"Konventionen","slug":"konventionen","link":"#konventionen","children":[]}]},{"level":2,"title":"3. Build & Distribution","slug":"_3-build-distribution","link":"#_3-build-distribution","children":[{"level":3,"title":"Dual-Build Strategie","slug":"dual-build-strategie","link":"#dual-build-strategie","children":[]},{"level":3,"title":"Nutzung MIT Tailwind","slug":"nutzung-mit-tailwind","link":"#nutzung-mit-tailwind","children":[]},{"level":3,"title":"Nutzung OHNE Tailwind","slug":"nutzung-ohne-tailwind","link":"#nutzung-ohne-tailwind","children":[]},{"level":3,"title":"Branding (funktioniert für beide)","slug":"branding-funktioniert-fur-beide","link":"#branding-funktioniert-fur-beide","children":[]},{"level":3,"title":"Webapp-Integration (Entwicklung)","slug":"webapp-integration-entwicklung","link":"#webapp-integration-entwicklung","children":[]},{"level":3,"title":"Webapp-Aufgaben (TODO für Ocelot-Webapp)","slug":"webapp-aufgaben-todo-fur-ocelot-webapp","link":"#webapp-aufgaben-todo-fur-ocelot-webapp","children":[]}]},{"level":2,"title":"4. Icon-Architektur","slug":"_4-icon-architektur","link":"#_4-icon-architektur","children":[{"level":3,"title":"Entscheidung: Hybrid-Ansatz","slug":"entscheidung-hybrid-ansatz","link":"#entscheidung-hybrid-ansatz","children":[]},{"level":3,"title":"Begründung","slug":"begrundung","link":"#begrundung","children":[]},{"level":3,"title":"System-Icons (in Library enthalten) ✅","slug":"system-icons-in-library-enthalten-✅","link":"#system-icons-in-library-enthalten-✅","children":[]},{"level":3,"title":"API-Design (implementiert)","slug":"api-design-implementiert","link":"#api-design-implementiert","children":[]},{"level":3,"title":"SVG-Loading (vite-svg-icon Plugin)","slug":"svg-loading-vite-svg-icon-plugin","link":"#svg-loading-vite-svg-icon-plugin","children":[]},{"level":3,"title":"Ocelot-Icons (separates Entry-Point)","slug":"ocelot-icons-separates-entry-point","link":"#ocelot-icons-separates-entry-point","children":[]},{"level":3,"title":"Aktuelle Icon-Statistik","slug":"aktuelle-icon-statistik","link":"#aktuelle-icon-statistik","children":[]}]},{"level":2,"title":"5. Design-Token & Branding","slug":"_5-design-token-branding","link":"#_5-design-token-branding","children":[{"level":3,"title":"3-Stufen Token-System","slug":"_3-stufen-token-system","link":"#_3-stufen-token-system","children":[]},{"level":3,"title":"Branding-Flexibilität","slug":"branding-flexibilitat","link":"#branding-flexibilitat","children":[]},{"level":3,"title":"Kompatibilität mit bestehendem System","slug":"kompatibilitat-mit-bestehendem-system","link":"#kompatibilitat-mit-bestehendem-system","children":[]},{"level":3,"title":"CVA + Tailwind + CSS-Variablen","slug":"cva-tailwind-css-variablen","link":"#cva-tailwind-css-variablen","children":[]}]},{"level":2,"title":"6. CI/CD & Release","slug":"_6-ci-cd-release","link":"#_6-ci-cd-release","children":[{"level":3,"title":"Release-Workflow (release-please)","slug":"release-workflow-release-please","link":"#release-workflow-release-please","children":[]},{"level":3,"title":"GitHub Workflows","slug":"github-workflows","link":"#github-workflows","children":[]},{"level":3,"title":"Qualitätssicherung bei PRs","slug":"qualitatssicherung-bei-prs","link":"#qualitatssicherung-bei-prs","children":[]},{"level":3,"title":"Automatische Deployments","slug":"automatische-deployments","link":"#automatische-deployments","children":[]},{"level":3,"title":"GitHub Workflows (vollständige Liste)","slug":"github-workflows-vollstandige-liste","link":"#github-workflows-vollstandige-liste","children":[]},{"level":3,"title":"Erweiterte Qualitätssicherung","slug":"erweiterte-qualitatssicherung","link":"#erweiterte-qualitatssicherung","children":[]},{"level":3,"title":"Migrations-Absicherung","slug":"migrations-absicherung","link":"#migrations-absicherung","children":[]}]},{"level":2,"title":"7. Dokumentation & DX","slug":"_7-dokumentation-dx","link":"#_7-dokumentation-dx","children":[{"level":3,"title":"Storybook als Komponenten-Dokumentation","slug":"storybook-als-komponenten-dokumentation","link":"#storybook-als-komponenten-dokumentation","children":[]}]},{"level":2,"title":"8. Migrationsstrategie","slug":"_8-migrationsstrategie","link":"#_8-migrationsstrategie","children":[{"level":3,"title":"Grundprinzipien","slug":"grundprinzipien","link":"#grundprinzipien","children":[]},{"level":3,"title":"Ablauf pro Komponente","slug":"ablauf-pro-komponente","link":"#ablauf-pro-komponente","children":[]},{"level":3,"title":"Komponenten-Protokoll","slug":"komponenten-protokoll","link":"#komponenten-protokoll","children":[]},{"level":3,"title":"Qualitätsanforderungen pro Komponente","slug":"qualitatsanforderungen-pro-komponente","link":"#qualitatsanforderungen-pro-komponente","children":[]},{"level":3,"title":"Konsolidierungsziele","slug":"konsolidierungsziele","link":"#konsolidierungsziele","children":[]}]},{"level":2,"title":"9. Dokumentationsstrategie","slug":"_9-dokumentationsstrategie","link":"#_9-dokumentationsstrategie","children":[]},{"level":2,"title":"10. Kompatibilitätstests","slug":"_10-kompatibilitatstests","link":"#_10-kompatibilitatstests","children":[]},{"level":2,"title":"11. Entscheidungen","slug":"_11-entscheidungen","link":"#_11-entscheidungen","children":[{"level":3,"title":"Vision & Ziele","slug":"vision-ziele","link":"#vision-ziele","children":[]},{"level":3,"title":"Tech-Stack","slug":"tech-stack","link":"#tech-stack","children":[]},{"level":3,"title":"Build & Distribution","slug":"build-distribution","link":"#build-distribution","children":[]},{"level":3,"title":"CI/CD & Release","slug":"ci-cd-release","link":"#ci-cd-release","children":[]},{"level":3,"title":"Testing & Qualität","slug":"testing-qualitat","link":"#testing-qualitat","children":[]},{"level":3,"title":"Design-System & Branding","slug":"design-system-branding","link":"#design-system-branding","children":[]},{"level":3,"title":"Komponenten-API & Konventionen","slug":"komponenten-api-konventionen","link":"#komponenten-api-konventionen","children":[]},{"level":3,"title":"Dokumentation","slug":"dokumentation","link":"#dokumentation","children":[]},{"level":3,"title":"Migration & Prozess","slug":"migration-prozess","link":"#migration-prozess","children":[]}]},{"level":2,"title":"12. Arbeitsprotokoll","slug":"_12-arbeitsprotokoll","link":"#_12-arbeitsprotokoll","children":[]},{"level":2,"title":"13. Komponenten-Katalog","slug":"_13-komponenten-katalog","link":"#_13-komponenten-katalog","children":[{"level":3,"title":"Zusammenfassung (aus KATALOG.md)","slug":"zusammenfassung-aus-katalog-md","link":"#zusammenfassung-aus-katalog-md","children":[]}]},{"level":2,"title":"14. Ressourcen & Links","slug":"_14-ressourcen-links","link":"#_14-ressourcen-links","children":[]},{"level":2,"title":"15. Dokumentationsstrategie (Details)","slug":"_15-dokumentationsstrategie-details","link":"#_15-dokumentationsstrategie-details","children":[{"level":3,"title":"Übersicht","slug":"ubersicht","link":"#ubersicht","children":[]},{"level":3,"title":"README.md Struktur","slug":"readme-md-struktur","link":"#readme-md-struktur","children":[]},{"level":3,"title":"Generierung mit vue-component-meta","slug":"generierung-mit-vue-component-meta","link":"#generierung-mit-vue-component-meta","children":[]},{"level":3,"title":"CI-Workflow: docs-check","slug":"ci-workflow-docs-check","link":"#ci-workflow-docs-check","children":[]},{"level":3,"title":"ESLint-Regeln für Dokumentation","slug":"eslint-regeln-fur-dokumentation","link":"#eslint-regeln-fur-dokumentation","children":[]},{"level":3,"title":"Dokumentations-Phasen","slug":"dokumentations-phasen","link":"#dokumentations-phasen","children":[]},{"level":3,"title":"Nach der Migration: Datei-Transformation","slug":"nach-der-migration-datei-transformation","link":"#nach-der-migration-datei-transformation","children":[]}]},{"level":2,"title":"16. Library vs. Webapp","slug":"_16-library-vs-webapp","link":"#_16-library-vs-webapp","children":[{"level":3,"title":"Grundprinzip","slug":"grundprinzip","link":"#grundprinzip","children":[]},{"level":3,"title":"Entscheidungs-Checkliste","slug":"entscheidungs-checkliste","link":"#entscheidungs-checkliste","children":[]},{"level":3,"title":"Entscheidungsbaum","slug":"entscheidungsbaum","link":"#entscheidungsbaum","children":[]},{"level":3,"title":"Quantitative Regel","slug":"quantitative-regel","link":"#quantitative-regel","children":[]},{"level":3,"title":"Konkrete Beispiele","slug":"konkrete-beispiele","link":"#konkrete-beispiele","children":[]},{"level":3,"title":"Composition Pattern für Grenzfälle","slug":"composition-pattern-fur-grenzfalle","link":"#composition-pattern-fur-grenzfalle","children":[]},{"level":3,"title":"Checkliste bei neuer Komponente","slug":"checkliste-bei-neuer-komponente","link":"#checkliste-bei-neuer-komponente","children":[]}]},{"level":2,"title":"16a. Webapp ↔ Maintenance Code-Sharing","slug":"_16a-webapp-↔-maintenance-code-sharing","link":"#_16a-webapp-↔-maintenance-code-sharing","children":[{"level":3,"title":"Problemstellung (historisch)","slug":"problemstellung-historisch","link":"#problemstellung-historisch","children":[]},{"level":3,"title":"Umgesetzte Lösung: Vollständige Entkopplung ✅ (Session 35, 2026-03-28)","slug":"umgesetzte-losung-vollstandige-entkopplung-✅-session-35-2026-03-28","link":"#umgesetzte-losung-vollstandige-entkopplung-✅-session-35-2026-03-28","children":[]},{"level":3,"title":"Kriterien für Entwickler","slug":"kriterien-fur-entwickler","link":"#kriterien-fur-entwickler","children":[]},{"level":3,"title":"Vorteile der Entkopplung","slug":"vorteile-der-entkopplung","link":"#vorteile-der-entkopplung","children":[]},{"level":3,"title":"Spätere Evolution (optional)","slug":"spatere-evolution-optional","link":"#spatere-evolution-optional","children":[]},{"level":3,"title":"Entscheidung","slug":"entscheidung","link":"#entscheidung","children":[]}]},{"level":2,"title":"16b. Daten-Entkopplung (ViewModel/Mapper Pattern)","slug":"_16b-daten-entkopplung-viewmodel-mapper-pattern","link":"#_16b-daten-entkopplung-viewmodel-mapper-pattern","children":[{"level":3,"title":"Problemstellung","slug":"problemstellung","link":"#problemstellung","children":[]},{"level":3,"title":"Lösung: ViewModel + Mapper Pattern","slug":"losung-viewmodel-mapper-pattern","link":"#losung-viewmodel-mapper-pattern","children":[]},{"level":3,"title":"Implementierung","slug":"implementierung","link":"#implementierung","children":[]},{"level":3,"title":"Ordnerstruktur","slug":"ordnerstruktur","link":"#ordnerstruktur","children":[]},{"level":3,"title":"Vorteile","slug":"vorteile","link":"#vorteile","children":[]},{"level":3,"title":"Regeln","slug":"regeln","link":"#regeln","children":[]},{"level":3,"title":"Entscheidung","slug":"entscheidung-1","link":"#entscheidung-1","children":[]}]},{"level":2,"title":"17. Externe Abhängigkeiten","slug":"_17-externe-abhangigkeiten","link":"#_17-externe-abhangigkeiten","children":[{"level":3,"title":"Übersicht","slug":"ubersicht-1","link":"#ubersicht-1","children":[]},{"level":3,"title":"eslint-config-it4c","slug":"eslint-config-it4c","link":"#eslint-config-it4c","children":[]}]},{"level":2,"title":"18. Kompatibilitätstests (Details)","slug":"_18-kompatibilitatstests-details","link":"#_18-kompatibilitatstests-details","children":[{"level":3,"title":"Testmatrix","slug":"testmatrix","link":"#testmatrix","children":[]},{"level":3,"title":"Werkzeuge & Strategien","slug":"werkzeuge-strategien","link":"#werkzeuge-strategien","children":[]},{"level":3,"title":"CI Workflow: Compatibility Matrix","slug":"ci-workflow-compatibility-matrix","link":"#ci-workflow-compatibility-matrix","children":[]},{"level":3,"title":"Werkzeug-Übersicht","slug":"werkzeug-ubersicht","link":"#werkzeug-ubersicht","children":[]},{"level":3,"title":"Checkliste für neue Komponenten","slug":"checkliste-fur-neue-komponenten","link":"#checkliste-fur-neue-komponenten","children":[]}]},{"level":2,"title":"19. Komplexitätsanalyse","slug":"_19-komplexitatsanalyse","link":"#_19-komplexitatsanalyse","children":[{"level":3,"title":"Umfang nach Phasen","slug":"umfang-nach-phasen","link":"#umfang-nach-phasen","children":[]},{"level":3,"title":"Bekannte Risikofaktoren","slug":"bekannte-risikofaktoren","link":"#bekannte-risikofaktoren","children":[]},{"level":3,"title":"Parallelisierbarkeit","slug":"parallelisierbarkeit","link":"#parallelisierbarkeit","children":[]},{"level":3,"title":"Aufwandstreiber pro Komponente (Phase 4)","slug":"aufwandstreiber-pro-komponente-phase-4","link":"#aufwandstreiber-pro-komponente-phase-4","children":[]}]}],"git":{"createdTime":1775422481000,"updatedTime":1775422481000,"contributors":[{"name":"dependabot[bot]","email":"49699333+dependabot[bot]@users.noreply.github.com","commits":1}]},"readingTime":{"minutes":55.56,"words":16667},"filePathRelative":"packages/ui/PROJEKT.md","localizedDate":"April 5, 2026","excerpt":"\\n
\\n

Dieses Dokument dient als zentrale Planungs- und Statusübersicht für das UI-Library Subprojekt.\\nEs ermöglicht das Pausieren und Wiederaufnehmen der Arbeit zu jedem Zeitpunkt.

\\n
\\n
\\n

Inhaltsverzeichnis

\\n

Schnellzugriff (Status)

\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n
AbschnittBeschreibung
FortschrittVisuelle Fortschrittsanzeige
Aktueller StandWas zuletzt erledigt wurde
MeilensteinePhasen 0-5 mit Checklisten
"}');export{m as comp,b as data};