25 KiB
Boilerplate-Inventur: UI-Layer & App-Shell
Faktische Bestandsaufnahme des Bereichs "UI-Layer & App-Shell" der Boilerplate
(/Users/raffaelsheikh/Documents/GitHub/boilerplate). Stand: Read-only-Inventur,
keine Aenderungen vorgenommen.
Inventarisierte Pakete/Pfade:
packages/ui, packages/ui-app, packages/ui-mock, packages/ui-subframe,
packages/view-models (Vertrags-Fundament), apps/web, apps/storybook.
TL;DR — Reale Layer-Trennung
Die Layer-Trennung ist noch nicht voll aktiviert. Sie ist als bewusst
strukturierte "Adapter-/Vertrags-Grenze" angelegt, waehrend die echten
Laufzeit-Komponenten weiterhin in apps/web/app/ leben.
@toolctrl/view-models— reine TypeScript-Typen + kleine Helfer: View-Model- Statusmodell (VMState), Shell-Registry-Typen (Sidebar/Breadcrumb/Mobile/ Portal), i18n-Key-Validierung. Kein React.@toolctrl/ui-app— App-Shell-Registry-Factories + headlessAppShell-Stub (rendert nurdata-*-Attribute, kein echtes Layout). Konsumiert view-models.@toolctrl/ui-mock— Fixture-/Mock-Generator fuer Hot-Swap- und Visual-Tests (erzeugt statisches HTML + serialisierbares Modul aus einemMockPage).@toolctrl/ui-subframe— leerer Platzhalter (createSubframePlaceholder()liefert{ status: "empty" }). Subframe-Adapter ist reserviert, nicht gebaut.@toolctrl/ui— source-only Adapter-Metadaten (keine React-Komponenten). Beschreibt nur stabile Namen + Verweise auf die echten Implementierungen inapps/web/app/.apps/web/app/— echte Laufzeit: App-Shell (shadcn/Radix), Sidebar-Konfig, RR7-Routing, ~707.tsx-Komponenten, Theme/Dark-Mode, i18n.apps/storybook— Storybook 8 (react-vite) ueber diepackages/ui-Stories.
Verbindung von Vertrag → echter Code laeuft ueber stabile data-ui-id-IDs
(packages/.../application/ui/uiRegistry.ts) und die maschinenlesbare
Inventur in docs/ui-inventory/ (von Tests gegen das Dateisystem geprueft).
Paket: @toolctrl/view-models
Zweck: Vertrags-Fundament fuer View-Model → UI. Reine Typen + winzige
Validierungs-/Konstruktor-Helfer, ohne React-Abhaengigkeit. Bildet die
gemeinsame Sprache, gegen die ui-app, ui-mock und die echte App komponieren.
Zentrale Datei: /Users/raffaelsheikh/Documents/GitHub/boilerplate/packages/view-models/src/index.ts
package.json: /Users/raffaelsheikh/Documents/GitHub/boilerplate/packages/view-models/package.json (Name @toolctrl/view-models, Export . → ./src/index.ts)
Haupt-Exporte (Typen):
| Export | Bedeutung |
|---|---|
VMStatus |
"idle" | "loading" | "ready" | "empty" | "error" |
VMState<T> |
Diskriminierte Union ueber VMStatus; error traegt { messageKey; code? } (i18n-Key, nicht Klartext) |
ActionCallbacks<T> |
onCreate/onUpdate/onDelete/onRefresh — Standard-CRUD-Callback-Vertrag |
I18nLabel |
{ labelKey; descriptionKey? } — UI-Labels sind immer i18n-Keys |
RegistryGate |
{ permission?; flag? } — Sichtbarkeits-Gate (Permission/Feature-Flag) pro Registry-Item |
SidebarRegistryItem |
id/href/icon?/children? + I18nLabel + RegistryGate |
BreadcrumbRegistryItem |
id/href? + Label + Gate |
MobileRegistryItem |
id/href/priority? + Label + Gate |
PortalRegistryItem |
id/slot/order? + Label + Gate |
ShellRegistries |
{ sidebar?; breadcrumbs?; mobile?; portals? } — das Shell-Konfig-Aggregat |
Helfer-Funktionen: ready<T>(data), empty<T>(), assertI18nLabel(label)
(erzwingt Regex ^[a-z][a-z0-9]*(?:[.-][a-z0-9]+)*$ fuer Label- und Description-Keys).
HiveGrid-Relevanz: Hoch und querschnittlich. Das VMState-Statusmodell und
der i18n-Key-Zwang sind direkt uebernehmbar als Daten-/UI-Vertrag fuer alle
HiveGrid-Module (Listen/Detail/Editoren), und die ShellRegistries-Typen sind
die Grundlage fuer datengetriebene Navigation (App-Shell-Querschnitt).
Paket: @toolctrl/ui-app
Zweck: App-Shell-Schicht. Liefert Registry-Factories (sortiert/normalisiert
Sidebar/Breadcrumb/Mobile/Portal-Items) und einen headless AppShell-Stub.
Der Stub rendert kein echtes Layout — nur ein <div> mit data-ui-shell
und Zaehler-Attributen. Echte Shell lebt in apps/web.
Zentrale Datei: /Users/raffaelsheikh/Documents/GitHub/boilerplate/packages/ui-app/src/index.ts
package.json: /Users/raffaelsheikh/Documents/GitHub/boilerplate/packages/ui-app/package.json (deps: @toolctrl/view-models, react; Test-Subpath-Exports fuer axe/a11y-Template)
Haupt-Exporte:
| Export | Art | Verhalten |
|---|---|---|
AppShellProps |
Typ | { registries?: ShellRegistries; children? } |
createSidebarRegistry(items) |
fn | Kopie der Items |
createBreadcrumbRegistry(items) |
fn | Kopie der Items |
createMobileRegistry(items) |
fn | sortiert nach priority (default 100) |
createPortalRegistry(items) |
fn | sortiert nach order (default 100) |
createShellRegistries(input) |
fn | aggregiert + normalisiert alle 4 Registries |
EmptyPage({ labelKey }) |
Komponente | Empty-State-Stub (data-page-state="empty") |
AppShell({ registries, children }) |
Komponente | headless Shell-Stub mit data-*-Zaehlern |
a11y-Test-Vertrag: /Users/raffaelsheikh/Documents/GitHub/boilerplate/packages/ui-app/src/test-utils/a11y-template.ts
exportiert generateA11yTest(Component, defaultProps, options) — kanonisches
axe-Test-Template (jede Komponente in ui-app/ui-subframe soll eine
co-lokierte <Name>.a11y.test.tsx mitliefern). axe-Setup unter test-setup/axe.ts.
HiveGrid-Relevanz: App-Shell-/Navigations-Querschnitt. Die Registry-
Factories sind die kanonische Art, HiveGrid-Sidebars/Breadcrumbs aus Daten zu
bauen (mit Priority/Order-Sortierung und Permission/Flag-Gating). generateA11yTest
ist als Compliance-/a11y-Querschnitt direkt nutzbar.
Paket: @toolctrl/ui-mock
Zweck: Hot-Swap-/Visual-Test-Fundament. Erzeugt aus einem deklarativen
MockPage-Deskriptor deterministisch (a) ein serialisierbares JS-Modul und
(b) statisches HTML inkl. eigenem Inline-CSS — als "Drop-in"-Ersatz fuer eine
echte Seite. Liefert stabile Hashes/Metriken fuer Visual-Snapshots.
Zentrale Datei: /Users/raffaelsheikh/Documents/GitHub/boilerplate/packages/ui-mock/src/index.ts
package.json: /Users/raffaelsheikh/Documents/GitHub/boilerplate/packages/ui-mock/package.json (dep: @toolctrl/view-models)
Visual-Baseline: /Users/raffaelsheikh/Documents/GitHub/boilerplate/packages/ui-mock/visual-baseline/
Haupt-Exporte:
| Export | Art | Verhalten |
|---|---|---|
MockPage<T> |
Typ | { id; route; title?/titleKey?; description?/descriptionKey?; state: VMState<T>; registries? } |
HotSwapDrillData |
Typ | { cards[]; activity[] } — Demo-Datenform |
MockVisualSnapshot |
Typ | htmlHash/moduleHash/stateHash (FNV-1a) + Byte-/Item-Metriken |
MockArtifacts |
Typ | { module; html; snapshot } |
createMockPage(page) |
fn | Identitaet (Typ-Anker) |
createMockRegistry(pages) |
fn | Map<route, MockPage> |
generateHotSwapMock(page) |
fn | erzeugt export const mockPage = ... Modul-String |
generateMockPageHtml(page) |
fn | volles statisches HTML (Sidebar/Breadcrumbs/Cards/Activity/State) |
createVisualSnapshot(page, artifacts?) |
fn | Snapshot mit stabilen Hashes |
generateMockArtifacts(page) |
fn | { module, html, snapshot } in einem Schritt |
createHotSwapDrillPage() |
fn | fertige Referenz-MockPage (/mock/phase-c/hot-swap) |
Enthaelt eine eingebaute Mini-Uebersetzungstabelle (defaultMockTranslations)
fuer die Mock-Texte (i18n-Keys → Klartext, deterministisch).
HiveGrid-Relevanz: Test-/QA-Querschnitt. Das Mock-Drop-in-Muster (deklarativer Deskriptor → deterministisches HTML + Hash) ist als Vorlage fuer HiveGrid-Visual- Regression und fuer das Bauen von Hot-Swap-/Whitelabel-Vorschauen verwendbar.
Paket: @toolctrl/ui-subframe
Zweck: Reservierte, leere Paket-Grenze fuer kuenftige Subframe-generierte UI. Bewusst leer, bis ein spaeterer Slice Import-Regeln + Review-Gates definiert.
Zentrale Datei: /Users/raffaelsheikh/Documents/GitHub/boilerplate/packages/ui-subframe/src/index.ts
uiSubframeRegistry = [] as constcreateSubframePlaceholder()→{ status: "empty" }
README: /Users/raffaelsheikh/Documents/GitHub/boilerplate/packages/ui-subframe/README.md
("Reserved package boundary for future Subframe-generated UI imports. Exports an empty registry.")
HiveGrid-Relevanz: Niedrig (Stub). Relevant nur als Marker dafuer, dass der
Subframe-Adapter-Layer der Boilerplate nicht gebaut ist — siehe MEMORY.md
("Subframe mandatory for all routes"). HiveGrid muesste diese Grenze selbst fuellen.
Paket: @toolctrl/ui
Zweck: Source-only Adapter-Metadaten ("category migration boundary").
Enthaelt keine React-Komponenten und importiert React nicht. Beschreibt
stabile Namen pro Kategorie und zeigt per source-Feld auf die echten
Implementierungen in apps/web/app/. Erlaubt Agenten/Tools, stabile Namen
anzusprechen, bevor Laufzeit-Imports migriert sind.
README: /Users/raffaelsheikh/Documents/GitHub/boilerplate/packages/ui/README.md
package.json: /Users/raffaelsheikh/Documents/GitHub/boilerplate/packages/ui/package.json
Migration-Doku: /Users/raffaelsheikh/Documents/GitHub/boilerplate/packages/ui/docs/migration.md
Subpath-Exporte (Kategorien): ., ./buttons, ./inputs, ./dialogs,
./tables, ./layout, ./navigation, ./feedback.
Zentrale Dateien:
| Datei | Inhalt |
|---|---|
src/index.ts |
Re-Exporte aller Kategorien |
src/types.ts |
uiAdapterCategories, UiAdapterDefinition, UiAdapterLifecycle (placeholder/available/deprecated), createEmptyAdapterRegistry, isUiAdapterCategory |
src/navigation.ts |
navigationAdapters (status available): AppSidebar, AdminSidebar, DocsSidebar, NavMain, NavSecondary, NavQuickLinks, NavActions, SidebarPrimitives — mit source-Pfaden in apps/web/app/... |
src/layout.ts |
layoutAdapters: ShadcnSidebarLayout, UiRegistry |
src/buttons.ts |
buttonAdapters (Button, buttonVariants) |
src/inputs.ts, dialogs.ts, tables.ts, feedback.ts |
derzeit leere Readonly-Registries (Placeholder bis Migrations-Phase) |
src/stories/*.stories.tsx |
~21 Storybook-Stories (Button, Input, Select, Dialog, DataTable, Table, Tabs, Toast, Tooltip, Popover, Card, Badge, Avatar, Checkbox, Radio, Switch, Textarea, Form, EmptyState, LoadingState, Sidebar) ueber _demo-primitives.tsx |
Alle source-Pfade aus navigation.ts/layout.ts (absolut):
/Users/raffaelsheikh/Documents/GitHub/boilerplate/apps/web/app/application/sidebar/AppSidebar.tsx,
.../AdminSidebar.tsx, .../DocsSidebar.ts,
.../components/layouts/sidebars/shadcn/nav-main.tsx, nav-secondary.tsx,
nav-quick-links.tsx, nav-actions.tsx,
.../components/ui/sidebar.tsx,
.../components/layouts/sidebars/shadcn/ShadcnSidebarLayout.tsx,
.../application/ui/uiRegistry.ts.
HiveGrid-Relevanz: Konventions-Querschnitt. Liefert die Taxonomie
(buttons/inputs/dialogs/tables/layout/navigation/feedback) und das Lifecycle-
Modell, an dem sich eine HiveGrid-Komponentenbibliothek ausrichten kann. Faktisch
ist es jedoch nur Metadaten — die echten Komponenten liegen in apps/web.
App: apps/web — echte App-Shell, Sidebar-Konfig, Routing
package.json: /Users/raffaelsheikh/Documents/GitHub/boilerplate/apps/web/package.json
Stack: React Router 7 (RR7), Vite, Tailwind v4, shadcn/Radix. SaasRock-aehnliche Struktur.
App-Shell / Layout
| Asset | Pfad | Was |
|---|---|---|
AppLayout |
apps/web/app/components/app/AppLayout.tsx |
Einstieg: SkipToContent + CommandPalette + ShadcnSidebarLayout; Prop layout: "app" | "admin" | "docs" |
ShadcnSidebarLayout |
apps/web/app/components/layouts/sidebars/shadcn/ShadcnSidebarLayout.tsx |
Authentifizierte Shell: SidebarProvider → ShadcnAppSidebar + SidebarInset (Header mit Breadcrumb/SidebarTrigger/NavActions, <main id="main-content">). Liest Tenant-themeConfig + Dark-Mode → injiziert --sidebar* CSS-Variablen. |
ShadcnAppSidebar |
apps/web/app/components/layouts/sidebars/shadcn/app-sidebar.tsx |
Waehlt je layout die Menue-Quelle: AppSidebar/AdminSidebar/DocsSidebar; wendet Permission-/Sichtbarkeits-Filter (getVisibleItem) + Active-State an; rendert NavMain/NavSecondary |
NavMain |
.../shadcn/nav-main.tsx |
Rendert SidebarGroupDto[] → shadcn SidebarMenu mit collapsible Untermenues; setzt data-ui-id-Attribute via getUiIdAttributes |
| weitere Nav | .../shadcn/nav-secondary.tsx, nav-quick-links.tsx, nav-actions.tsx, nav-user.tsx, team-switcher.tsx |
Sekundaer-Nav, Quick-Links, Header-Aktionen (Search/AI/Notifications/Feedback), User-/Team-Switcher |
Sidebar-Konfiguration (datengetrieben)
| Asset | Pfad | Was |
|---|---|---|
SideBarItem (Interface) |
apps/web/app/application/sidebar/SidebarItem.ts |
Item-Vertrag: id/title/path/icon/items/permission/adminOnly/tenantUserTypes/isCollapsible/isSecondary/isQuickLink/redirectTo/uiIdPattern/uiIdParams/... |
SidebarGroupDto |
apps/web/app/application/sidebar/SidebarGroupDto.ts |
{ id?; items: SideBarItem[]; title?; type?: "main"|"secondary"|"quick-link" } |
AppSidebar(props) |
apps/web/app/application/sidebar/AppSidebar.tsx |
Baut Tenant-App-Menue dynamisch aus entities, entityGroups, productAccess (Feature-Gating: crm-basic, email-marketing, workflow-engine, api, ai-credits); host-aware Pfade via appPath/tenantPath |
AdminSidebar(props) |
apps/web/app/application/sidebar/AdminSidebar.tsx |
Statisches Admin-Menue in Segmenten Manage/Build/Market/Settings; Permission-getaggt; Plugin-Routen via enabledAdminRouteItem/disabledPluginRouteSlugs ausblendbar |
DocsSidebar() |
apps/web/app/application/sidebar/DocsSidebar.ts |
Statischer Docs-Baum; vergibt stabile ui.nav.docs.*-IDs (withDocsSidebarIds) |
UI-Registry (stabile IDs / data-ui-id)
| Asset | Pfad | Was |
|---|---|---|
uiRegistry.ts |
apps/web/app/application/ui/uiRegistry.ts |
Single source of truth fuer stabile UI-IDs: UI_SURFACE_IDS, UI_ACTION_IDS, UI_NAV_IDS, UI_ID_PATTERNS; Helfer getSidebarSurfaceId(layout), uiIdSegment(value), getUiIdAttributes(item) (erzeugt data-ui-id / -pattern / -param-*) |
RR7-Routing-Konvention (app/routes)
| Asset | Pfad | Was |
|---|---|---|
| Route-Manifest | apps/web/app/routes.ts |
Nutzt Remix-v1-Flat-Folder-Convention ueber remixConfigRoutes + createRoutesFromFolders (NICHT die RR7-Default-Convention). Definiert defaultIgnoredRouteFilePatterns (*.test.*, *.server.*, *.css, dotfiles) und defaultDisabledPluginRouteFilePatterns (deaktivierbare Plugin-Routen) |
| Convention-Impl | apps/web/app/utils/compat/v1-route-convention.ts |
Kopie von remix-run/v1-compat-utils; Param-Prefix $, Escape [ ], Optional ( ), Module-Exts .js/.jsx/.ts/.tsx/.md/.mdx |
react-router.config.ts |
apps/web/react-router.config.ts |
RR7-App-Config |
| Routen-Tests | apps/web/app/routes.test.ts, apps/web/app/utils/compat/v1-route-convention.test.ts |
Verifizieren das Manifest |
Routen-Topologie (Auszug, apps/web/app/routes/, ~102 Eintraege):
- Tenant-Space:
app.$tenant.tsx(Layout) +app.$tenant/(dashboard, crm, email-marketing, notifications, settings, forms, g.$group, sites, internal, __portals, __widgets, __workflows, $entity.__autogenerated). - Admin-Space:
admin.tsx+admin/(accounts, analytics, entities, workflow-engine, api, feature-flags, navigation, portals, prompts, email-marketing, marketing-leads, knowledge-base, help-desk, onboarding, metrics, settings, audit-trails, events). - Docs:
docs/. Auth:auth.*,forgot-password.*,login-Flows. Fehlerseiten:401/403/404/500/$. - Hot-Swap-Demo (dev-only):
apps/web/app/routes/hot-swap-demo.tsx.
Shell-Verdrahtung: app.$tenant.tsx wrappt Tenant-Routen ueber
<AppLayout layout="app">; app-sidebar.tsx zieht die Menue-Daten ueber
useAppOrAdminData() (RR7 Loader-Daten) und ruft die passende Sidebar-Factory.
Theme / Dark-Mode
| Asset | Pfad | Was |
|---|---|---|
root.tsx |
apps/web/app/root.tsx |
Setzt dark-Klasse aus userSession.lightOrDarkMode (gated ueber DARK_MODE_IN_APP), theme-${rootData.theme} (+ -scaled); Actions toggleLightOrDarkMode, setTheme, setLocale (Persistenz in User-Session/DB) |
themes.css |
apps/web/app/themes.css (86 KB) |
Vordefinierte Themes als CSS-Variablen |
globals.css |
apps/web/app/globals.css |
Globale Tokens/Tailwind-Base |
Theme-Enum |
apps/web/app/application/enums/shared/Theme.ts |
LIGHT | DARK |
| Theme-Preview-Store | apps/web/app/stores/themePreviewStore.ts (via useThemePreviewStore) |
Live-Preview-Override im Shell-Layout |
parseThemeConfig / ThemeConfig |
apps/web/app/utils/theme/ThemeConfig.ts |
Parst Tenant-themeConfig (Farben inkl. darkColors, sidebar*-Farben) |
tailwind.config.ts |
boilerplate/tailwind.config.ts (Repo-Root) |
Tailwind-Setup |
Prisma-Bezug (Theme/Shell-State):
prisma/schema/accounts.prisma:17—User.lightOrDarkMode String?(Hell/Dunkel-Praeferenz pro User).prisma/schema/accounts.prisma:188—Tenant.themeConfig String?(Per-Tenant-Whitelabel-Theme als serialisierter JSON-String).prisma/schema/portals.prisma—Portal,PortalPage, ... (Portal-Feature; Bezug zuPortalRegistryItem/Admin-Portals-Nav).
i18n in der UI
| Asset | Pfad | Was |
|---|---|---|
| Web-i18n-Setup | apps/web/app/locale/ (i18n.ts, i18next.server.ts, i18next.global.server.ts, format.ts, date-fns.ts) |
i18next + RR7 SSR-Integration |
| Locale-Daten | apps/web/public/locales/{de,en}/*.json |
Namespaces: translations (legacy Bulk), common, errors, mail, settings, billing, crm, auth, emailMarketing |
| Shared-Paket | packages/i18n/src/index.ts (@toolctrl/i18n) |
defaultLocale="de", supportedLocales=["de","en"], i18nNamespaces, i18nConfig, getDirection/isRTL (RTL-Support vorbereitet), Re-Exporte formatNumber/Currency/Date/RelativeTime/List, formatIcu, createUseTranslate |
Sidebars/Registries verwenden durchgaengig i18n-Keys (t(...) bzw. labelKey),
nie Klartext — assertI18nLabel (view-models) erzwingt das auf Vertragsebene.
Komponenten-/Page-Bibliothek (real, in apps/web)
Insgesamt ~707 .tsx-Komponenten (ohne *.test.tsx/*.stories.tsx) unter
apps/web/app/components/. Verzeichnis-Highlights:
| Bereich | Pfad | Inhalt (Auszug) |
|---|---|---|
| Primitives (shadcn/Radix) | apps/web/app/components/ui/ |
button.tsx, input.tsx, select.tsx, dialog.tsx, sheet.tsx, drawer.tsx, popover.tsx, table.tsx, tabs.tsx, form.tsx, sidebar.tsx, command.tsx, toast.tsx/sonner.tsx, badge.tsx, card.tsx, calendar.tsx, chart.tsx, multi-select.tsx, stepper.tsx, pagination.tsx, ... |
| Tabellen | apps/web/app/components/ui/tables/ |
TableSimple.tsx, TableNew.tsx, TablePagination.tsx, GridPagination.tsx, ColumnSelector.tsx, CollapsibleRow.tsx, RowsLoadMoreCard.tsx, PreviewTableSimple.tsx |
| Formulare | apps/web/app/components/ui/forms/, .../input, .../selectors, .../editors |
FormGroup.tsx, InputGroup.tsx + reichhaltige Input-/Selector-/Editor-Komponenten |
| Modale/Dialoge | apps/web/app/components/ui/modals/, .../slideOvers |
Modal.tsx, ModalOrSlide.tsx, ConfirmDialog.tsx, ConfirmModal.tsx, ActionResultModal.tsx, ErrorModal.tsx, SuccessModal.tsx, OpenModal.tsx, ... |
| Empty/Loading | apps/web/app/components/ui/emptyState, .../loaders |
Empty-/Loading-States |
| Command-Palette | apps/web/app/components/ui/commandPalettes/CommandPalette |
Kommando-Palette (im Shell verdrahtet) |
| Entitaeten/Editoren | apps/web/app/components/entities, .../editors, .../json, .../pdf |
Entity-Builder-UI, Rich-Editoren, JSON-/PDF-Viewer |
| Weitere | aceternity, ai-elements, analytics, audit, brand, cookies, dashboard, images, motion-primitives, notifications, pages, public, recaptcha |
App-spezifische Komponenten-Cluster |
Hot-Swap-Aktivierung (Drop-in, real verdrahtet)
| Asset | Pfad | Was |
|---|---|---|
| Renderer | apps/web/app/components/ui/hot-swap/HotSwapPageRenderer.tsx |
Bruecke: MockPage-Deskriptor (@toolctrl/ui-mock) → RR7-JSX, nutzt createShellRegistries() aus @toolctrl/ui-app |
| Demo-Route | apps/web/app/routes/hot-swap-demo.tsx |
Rendert createHotSwapDrillPage(); gated auf NODE_ENV !== "production" |
| Renderer-Test | apps/web/app/components/ui/hot-swap/HotSwapPageRenderer.test.tsx |
Prueft Deskriptor → DOM (Sidebar/Breadcrumbs/Header/Cards/Activity) |
HiveGrid-Relevanz (apps/web gesamt): Sehr hoch. Dies ist der reale,
sofort wiederverwendbare Fundus: vollstaendige authentifizierte App-Shell
(App/Admin/Docs), datengetriebene Sidebar mit Permission-/Feature-Gating,
RR7-Flat-Folder-Routing inkl. Tenant- (app.$tenant) und Admin-Trennung,
Theme/Dark-Mode mit Per-Tenant-Whitelabel (Tenant.themeConfig), i18n (de/en),
und eine breite shadcn/Radix-Komponentenbibliothek (Tabellen/Forms/Modale).
Querschnittlich relevant fuer praktisch jedes HiveGrid-Modul.
App: apps/storybook
Zweck: Storybook-8-Workbench + Visual-Regression (lost-pixel) + a11y-Addon.
package.json: /Users/raffaelsheikh/Documents/GitHub/boilerplate/apps/storybook/package.json
(@storybook/react-vite 8.6, Addons: essentials/a11y/interactions/themes; lost-pixel 3.22; dep @toolctrl/i18n)
Zentrale Dateien:
| Asset | Pfad | Was |
|---|---|---|
| Storybook-Config | apps/storybook/.storybook/main.ts |
Stories aus apps/storybook/src/** und packages/{ui,ui-app,ui-subframe,ui-mock}/src/**/*.stories.*; Tailwind-v4-Vite-Plugin; Alias ~ → apps/web/app |
| i18n-Bootstrap | apps/storybook/.storybook/i18n.ts, preview.tsx, preview.css |
i18next + Theme-Decorator in Preview |
| Lost-Pixel | apps/storybook/lostpixel.config.ts, .lostpixel/ |
Visual-Regression-Config + Baselines |
| Test-Runner | apps/storybook/.storybook/test-runner.ts |
axe-playwright-basierte a11y-Checks im Test-Runner |
| Docs (MDX) | apps/storybook/src/Introduction.mdx, src/ThemeGuide.mdx |
Einfuehrung + Theme-Guide |
| Scripts | package.json |
storybook (dev :6006), build-storybook, test:storybook, visual-regression(:update) |
Status: Stories sind aktuell (~21) groesstenteils inline-styled Demos ueber
packages/ui/src/stories/_demo-primitives.tsx (CSS-Variablen-Tokens aus
preview.css); der "echte Tailwind-v4-Pipeline"-Pfad ist im main.ts als
Migrations-Vorbereitung schon vorhanden (Tailwind-Plugin no-op fuer heutige Stories).
HiveGrid-Relevanz: QA-/Design-Querschnitt. Direkt nutzbar als Visual-
Regression- und a11y-Gate fuer eine HiveGrid-Komponentenbibliothek; der Alias
auf apps/web/app erlaubt, echte App-Komponenten in Stories zu ziehen.
Maschinenlesbare UI-Inventur & Verifikations-Gates
Verbindet Vertrag ↔ echte Komponenten und wird von der Test-Suite geprueft.
| Asset | Pfad | Was |
|---|---|---|
| Komponenten-Inventur | docs/ui-inventory/components.json |
71 Eintraege (stableName/path/category/library/status); Schema docs/ui-inventory/schema.json |
| Surface-Map | docs/ui-inventory/surfaces.json (+ surfaces.schema.json, surface-map.md) |
Mapping stabiler data-ui-id → Quelldateien/Renderer |
| Inventur-Test | apps/web/app/application/ui/uiInventory.test.ts |
Prueft, dass Inventur-Pfade existieren, alle components/ui/*.tsx-Primitives gelistet sind und Registry-IDs in der Surface-Map stehen |
| Adapter-Test | apps/web/app/application/ui/uiAdapters.test.ts, uiRegistry.test.ts |
Prueft @toolctrl/ui-Adapter ↔ App-Registry-Konsistenz |
| Hot-Swap-Status | docs/ui-inventory/hot-swap-status.md |
Dokumentiert, was verdrahtet ist (ui-app/ui-mock aktiv, ui-subframe Platzhalter) + Aktivierungs-Checkliste |
| Boundary-Gate | scripts/verify-phase-c-boundaries.mjs (npm verify:phase-c-boundaries) |
Erzwingt, dass Aktivierung keine Laufzeit-Layer-Grenze verletzt |
| Hot-Swap-Verify | scripts/verify-ui-hot-swap.ts (npm verify:ui-hot-swap, test:hot-swap) |
Mock-getriebener Hot-Swap-Drill |
| Visual-Verify | scripts/verify-ui-mock-visual.ts (npm verify:ui-visual) |
Mock-Visual-Baseline |
HiveGrid-Relevanz: Governance-/Konventions-Querschnitt. Das Muster "stabile ID-Registry + maschinenlesbare Inventur + dateisystem-geprueftes Gate" ist direkt als HiveGrid-UI-Governance uebertragbar (Whitelabel/Hot-Swap-Faehigkeit mit hartem CI-Schutz der Layer-Grenzen).
Wichtige Vorbehalte (Anti-Erfindung)
@toolctrl/uienthaelt keine lauffaehigen Komponenten — nur Adapter-Metadaten, die aufapps/web/app/...verweisen. Echte Primitives =apps/web/app/components/ui/*.@toolctrl/ui-subframeist leer (Placeholder). Es gibt keinen gebauten Subframe-Adapter-Layer in der Boilerplate.- Der
AppShellin@toolctrl/ui-appist ein headless Stub (nurdata-*), nicht die echte sichtbare Shell. Sichtbare Shell =ShadcnSidebarLayout. - Hot-Swap ist nur an der dev-only Demo-Route (
hot-swap-demo.tsx) end-to-end aktiv; Produktiv-Routen rendern weiterhin direkt ausapps/web/app/components/. - Routing nutzt die Remix-v1-Flat-Folder-Convention (Compat-Layer), nicht die
RR7-Default-
routes.ts-Convention. - Es gibt keine Prisma-Modelle, die exklusiv zu den UI-Paketen gehoeren;
UI-relevant sind
User.lightOrDarkModeundTenant.themeConfig(accounts.prisma) sowiePortal*(portals.prisma).