Skip to content

Component Basics

Grundlagen der Component-Entwicklung für Webmate Studio.

Component-Struktur

Jede Component ist ein Verzeichnis mit diesen Dateien:

components/
└── MyComponent/
    ├── component.html       # Erforderlich: HTML Template
    ├── islands/             # Optional: Interactive JavaScript
    │   └── my-component.js
    └── assets/              # Optional: Bilder, Fonts, etc.
        └── image.jpg

Minimale Component

Die einfachste mögliche Component:

Verzeichnis: components/SimpleText/

component.html:

html
<div class="simple-text">
  <p class="text-gray-600">Hello World</p>
</div>

Das ist alles! Keine Build-Config, kein Framework-Overhead, nur HTML.

component.html

Das HTML-Template mit deinem Markup:

Basis-Struktur

html
<div class="component-wrapper">
  <h2 class="text-2xl font-bold text-gray-900">{{title}}</h2>
  <p class="text-gray-600">{{description}}</p>
</div>

<style>
  /* Optional: Custom CSS */
  .component-wrapper {
    /* Deine Styles hier */
  }
</style>

Props verwenden

Props werden über wm dev verwaltet und mit interpoliert:

html
<h1>{{title}}</h1>
<p>{{subtitle}}</p>
<img src="{{imageUrl}}" alt="{{imageAlt}}">

Mehr dazu: Props

Conditionals

Elemente bedingt anzeigen mit wm:if:

html
<img wm:if="showImage" src="{{imageUrl}}" alt="Hero">
<div wm:if="!showImage" class="placeholder"></div>

Mehr dazu: Conditionals

Loops

Listen rendern mit wm:for:

html
<ul>
  <li wm:for="item in items">
    {{item.name}}
  </li>
</ul>

Mehr dazu: Loops

Keine Script Tags!

Nicht so:

html
<div id="my-component"></div>

<script>
  // Funktioniert nicht!
  document.getElementById('my-component').innerHTML = 'Hello';
</script>

Sondern so:

Verwende Islands für JavaScript:

html
<!-- component.html -->
<div data-island="my-component">
  <!-- Island übernimmt hier -->
</div>
javascript
// islands/my-component.js
export default class MyComponentIsland {
  constructor(element) {
    element.innerHTML = 'Hello';
  }
}

Warum keine Scripts?

  • Sicherheit: Scripts können nicht sanitized werden
  • SSR: Server-side Rendering führt <script> nicht aus
  • Islands: Proper Lifecycle-Management
  • Performance: Islands laden nur bei Bedarf

Tailwind CSS verwenden

Alle Components haben Zugriff auf Tailwind Utility Classes:

Layout

html
<div class="container mx-auto px-4 py-8">
  <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
    <div class="col-span-1">Column 1</div>
    <div class="col-span-1">Column 2</div>
    <div class="col-span-1">Column 3</div>
  </div>
</div>

Typography

html
<h1 class="text-4xl font-bold text-gray-900 mb-4">Heading 1</h1>
<h2 class="text-3xl font-semibold text-gray-800 mb-3">Heading 2</h2>
<p class="text-base text-gray-600 leading-relaxed">Body text with good line height.</p>
<a href="#" class="text-blue-600 hover:text-blue-800 underline">Link</a>

Colors

html
<!-- Standard colors -->
<div class="bg-blue-600 text-white">Blue background</div>
<div class="bg-red-500 text-white">Red background</div>
<div class="bg-green-600 text-white">Green background</div>

<!-- Design Tokens (konfiguriert im CMS) -->
<div class="bg-primary text-white">Primary color</div>
<div class="bg-secondary text-white">Secondary color</div>

Design Tokens

text-primary, bg-primary, border-primary verwenden Farben aus den CMS-Einstellungen. Admins können diese ändern, ohne Components anzupassen.

Mehr: Design Tokens

Spacing

html
<!-- Padding -->
<div class="p-4">Padding all sides (1rem)</div>
<div class="px-6 py-4">Padding horizontal & vertical</div>
<div class="pt-8 pb-4">Padding top & bottom</div>

<!-- Margin -->
<div class="m-4">Margin all sides</div>
<div class="mt-8 mb-4">Margin top & bottom</div>
<div class="mx-auto">Center with auto margins</div>

<!-- Gaps (for flex/grid) -->
<div class="flex gap-4">
  <div>Item 1</div>
  <div>Item 2</div>
</div>

Responsive Design

html
<div class="
  w-full              <!-- Mobile: full width -->
  md:w-1/2            <!-- Tablet: 50% width -->
  lg:w-1/3            <!-- Desktop: 33% width -->
  xl:w-1/4            <!-- Large: 25% width -->
">
  Responsive width
</div>

<div class="
  text-sm             <!-- Mobile: small text -->
  md:text-base        <!-- Tablet: normal text -->
  lg:text-lg          <!-- Desktop: large text -->
">
  Responsive text
</div>

Hover, Focus, Active States

html
<button class="
  bg-blue-600
  hover:bg-blue-700        <!-- Darker on hover -->
  active:bg-blue-800       <!-- Even darker when clicked -->
  focus:ring-2             <!-- Ring on focus -->
  focus:ring-blue-500
  focus:ring-offset-2
  transition-colors        <!-- Smooth color transitions -->
  duration-200
">
  Interactive Button
</button>

Mehr zu Tailwind →

Custom CSS

Für komplexere Styles verwende <style> Blöcke:

html
<div class="hero">
  <h1 class="gradient-heading">{{title}}</h1>
</div>

<style>
  .gradient-heading {
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
  }

  /* Animations */
  @keyframes fadeIn {
    from { opacity: 0; transform: translateY(20px); }
    to { opacity: 1; transform: translateY(0); }
  }

  .hero {
    animation: fadeIn 0.6s ease-out;
  }
</style>

Wann Custom CSS verwenden?

  • ✅ Komplexe Gradients
  • ✅ Custom Animations
  • ✅ Pseudo-Elemente (::before, ::after)
  • ✅ Komplexe Selektoren
  • ❌ Einfache Farben, Padding, Margins (→ Tailwind)
  • ❌ Basic Layouts (→ Tailwind Grid/Flex)

Islands (Interaktivität)

Islands fügen JavaScript-Interaktivität hinzu:

Wann Islands verwenden?

Verwende Islands für:

  • ✅ Formulare mit Validierung
  • ✅ Image Carousels
  • ✅ Dropdowns, Modals
  • ✅ Dynamisches Laden von Daten
  • ✅ User-Interaktionen

Nicht verwenden für:

  • ❌ Statischer Text
  • ❌ Bilder
  • ❌ Links
  • ❌ Reine CSS-Animationen

Island hinzufügen

1. Island-File erstellen:

javascript
// islands/contact-form.js
export default class ContactFormIsland {
  constructor(element, props = {}) {
    this.element = element;
    this.init();
  }

  init() {
    // Island-Logic hier
  }

  destroy() {
    // Cleanup
  }
}

2. In component.html verwenden:

html
<div
  data-island="contact-form"
  data-island-props='{"title": "{{title}}"}'
>
  <!-- Island rendert hier -->
</div>

Mehr: Islands Architecture

Workflow

Component erstellen

bash
wm generate
# Oder kurz:
wm g

Interaktiver Wizard führt dich durch:

  • Component-Name
  • Props
  • Island-Framework (optional)

Testen

bash
wm dev

Preview-Server auf localhost:5173

Hochladen

bash
wm login        # Einmalig
wm push         # Upload zum CMS

Best Practices

1. Fokussierte Components

❌ Schlecht: Eine Component für alles

MegaComponent/ (50 props, 1000+ Zeilen)

✅ Gut: Kleine, fokussierte Components

Hero/, Features/, Testimonials/, CallToAction/

2. Semantisches HTML

html
<!-- ✅ Gut -->
<header>
  <nav>
    <ul>
      <li><a href="/">Home</a></li>
    </ul>
  </nav>
</header>

<!-- ❌ Divs für alles -->
<div class="header">
  <div class="nav">...</div>
</div>

3. Mobile-First

html
<div class="
  text-base        <!-- Mobile -->
  md:text-lg       <!-- Tablet -->
  lg:text-xl       <!-- Desktop -->
">
  Responsive
</div>

4. Accessibility

html
<!-- Alt-Text für Bilder -->
<img src="{{imageUrl}}" alt="{{imageAlt}}">

<!-- Beschreibende Link-Texte -->
<a href="/about">Mehr über uns erfahren</a>

<!-- Labels für Inputs -->
<label for="email">E-Mail</label>
<input id="email" type="email">

5. Performance

html
<!-- Lazy Loading -->
<img src="{{imageUrl}}" loading="lazy" alt="{{title}}">

<!-- Islands nur wo nötig -->
<!-- ✓ Für Interaktivität -->
<div data-island="contact-form"></div>

<!-- ✗ Nicht für statischen Content -->
<div data-island="static-text">Text</div>

Nächste Schritte

Webmate Studio Component Development Dokumentation