401 lines
8.7 KiB
Markdown
401 lines
8.7 KiB
Markdown
# experimenta Vue Komponenten-Beispiele
|
|
|
|
Dieser Ordner enthält **Referenz-Implementierungen** der experimenta Design System Komponenten als Vue 3 Single File Components (SFC).
|
|
|
|
Diese Komponenten dienen als **Vorlagen und Beispiele** für die Entwicklung eigener Komponenten oder können direkt in das Projekt kopiert werden.
|
|
|
|
---
|
|
|
|
## Verfügbare Komponenten
|
|
|
|
### 1. ExperimentaButton.vue
|
|
|
|
Animierter Button mit Gradient-Hintergrund nach experimenta Design System.
|
|
|
|
**Features:**
|
|
|
|
- Primary & Secondary Variants
|
|
- Responsive Größen (Small, Medium, Large)
|
|
- Link-Verhalten (kann als `<a>` oder `<button>` gerendert werden)
|
|
- Hover-Animation mit Gradient-Shift
|
|
- Accessibility-Ready (Focus States, ARIA Labels)
|
|
|
|
**Verwendung:**
|
|
|
|
```vue
|
|
<script setup>
|
|
import ExperimentaButton from './ExperimentaButton.vue'
|
|
</script>
|
|
|
|
<template>
|
|
<!-- Primary Button (default) -->
|
|
<ExperimentaButton @click="handleClick"> Zur Startseite </ExperimentaButton>
|
|
|
|
<!-- Secondary Button -->
|
|
<ExperimentaButton variant="secondary"> Abbrechen </ExperimentaButton>
|
|
|
|
<!-- As Link -->
|
|
<ExperimentaButton href="https://www.experimenta.science" target="_blank">
|
|
Zur experimenta Website
|
|
</ExperimentaButton>
|
|
|
|
<!-- Small Size -->
|
|
<ExperimentaButton size="small"> Small Button </ExperimentaButton>
|
|
|
|
<!-- Disabled -->
|
|
<ExperimentaButton disabled> Disabled Button </ExperimentaButton>
|
|
</template>
|
|
```
|
|
|
|
---
|
|
|
|
### 2. ExperimentaCard.vue
|
|
|
|
Glass-morphism Card-Komponente mit verschiedenen Variants.
|
|
|
|
**Features:**
|
|
|
|
- Glass-morphism Styling (Backdrop Blur)
|
|
- Info, Contact, Progress Variants
|
|
- Optional: Akzent-Border (links)
|
|
- Slot-basiertes Design (flexibel)
|
|
|
|
**Verwendung:**
|
|
|
|
```vue
|
|
<script setup>
|
|
import ExperimentaCard from './ExperimentaCard.vue'
|
|
</script>
|
|
|
|
<template>
|
|
<!-- Glass Card (Main) -->
|
|
<ExperimentaCard>
|
|
<h1>Willkommen!</h1>
|
|
<p>Dies ist eine Glass-morphism Card.</p>
|
|
</ExperimentaCard>
|
|
|
|
<!-- Info Card mit Titel -->
|
|
<ExperimentaCard variant="info" title="Ihre Vorteile">
|
|
<p>Mit der Jahreskarte erhalten Sie...</p>
|
|
</ExperimentaCard>
|
|
|
|
<!-- Card mit Custom Title Slot -->
|
|
<ExperimentaCard variant="contact">
|
|
<template #title>
|
|
<span>Kontakt</span>
|
|
</template>
|
|
<p>E-Mail: info@experimenta.science</p>
|
|
</ExperimentaCard>
|
|
|
|
<!-- Progress Card -->
|
|
<ExperimentaCard variant="progress">
|
|
<!-- Progress Bar Content -->
|
|
</ExperimentaCard>
|
|
</template>
|
|
```
|
|
|
|
---
|
|
|
|
### 3. ExperimentaStatusMessage.vue
|
|
|
|
Status-Nachrichten mit animierten Icons (Success, Error, Warning, Info).
|
|
|
|
**Features:**
|
|
|
|
- 4 Status-Typen mit passenden Farben
|
|
- Animiertes Icon (Pulse Animation)
|
|
- Responsive Icon-Größe
|
|
- Slot-basierte Inhalte
|
|
|
|
**Verwendung:**
|
|
|
|
```vue
|
|
<script setup>
|
|
import ExperimentaStatusMessage from './ExperimentaStatusMessage.vue'
|
|
</script>
|
|
|
|
<template>
|
|
<!-- Success Message -->
|
|
<ExperimentaStatusMessage type="success" title="Verlängerung erfolgreich!">
|
|
<p>Ihre Jahreskarte wurde erfolgreich verlängert.</p>
|
|
</ExperimentaStatusMessage>
|
|
|
|
<!-- Error Message -->
|
|
<ExperimentaStatusMessage type="error" title="Ein Fehler ist aufgetreten">
|
|
<p>Bitte versuchen Sie es erneut.</p>
|
|
</ExperimentaStatusMessage>
|
|
|
|
<!-- Custom Icon -->
|
|
<ExperimentaStatusMessage type="warning" title="Achtung" icon="⚠">
|
|
<p>Dies ist eine Warnung.</p>
|
|
</ExperimentaStatusMessage>
|
|
|
|
<!-- Custom Title Slot -->
|
|
<ExperimentaStatusMessage type="info">
|
|
<template #title>
|
|
<span>Information</span>
|
|
</template>
|
|
<p>Hier ist eine Info.</p>
|
|
</ExperimentaStatusMessage>
|
|
</template>
|
|
```
|
|
|
|
---
|
|
|
|
### 4. ExperimentaLogo.vue
|
|
|
|
Offizielles experimenta X-Logo mit Farbverläufen.
|
|
|
|
**Features:**
|
|
|
|
- SVG-basiert (skalierbar, scharf)
|
|
- 3 Größen (Small, Medium, Large)
|
|
- Responsive (passt sich automatisch an)
|
|
- Optional als Link verwendbar
|
|
- Hover-Animation
|
|
|
|
**Verwendung:**
|
|
|
|
```vue
|
|
<script setup>
|
|
import ExperimentaLogo from './ExperimentaLogo.vue'
|
|
</script>
|
|
|
|
<template>
|
|
<!-- Logo (default: large) -->
|
|
<ExperimentaLogo />
|
|
|
|
<!-- Logo als Link -->
|
|
<ExperimentaLogo href="https://www.experimenta.science" target="_blank" />
|
|
|
|
<!-- Small Logo -->
|
|
<ExperimentaLogo size="small" />
|
|
|
|
<!-- Medium Logo -->
|
|
<ExperimentaLogo size="medium" />
|
|
</template>
|
|
```
|
|
|
|
---
|
|
|
|
## Integration in Nuxt 4
|
|
|
|
### Option 1: Komponenten in `components/` verschieben
|
|
|
|
Kopiere die gewünschten Komponenten nach `components/`:
|
|
|
|
```bash
|
|
cp docs/design-examples/components/ExperimentaButton.vue components/
|
|
```
|
|
|
|
Nuxt erkennt sie automatisch (Auto-Imports):
|
|
|
|
```vue
|
|
<template>
|
|
<div>
|
|
<ExperimentaButton>Click me</ExperimentaButton>
|
|
</div>
|
|
</template>
|
|
```
|
|
|
|
---
|
|
|
|
### Option 2: Als Composable verwenden
|
|
|
|
Erstelle eine Composable-Funktion in `composables/useExperimenta.ts`:
|
|
|
|
```typescript
|
|
export function useExperimenta() {
|
|
return {
|
|
// Export component references
|
|
ExperimentaButton: () => import('@/docs/design-examples/components/ExperimentaButton.vue'),
|
|
ExperimentaCard: () => import('@/docs/design-examples/components/ExperimentaCard.vue'),
|
|
// ...
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Anpassungen & Erweiterungen
|
|
|
|
### shadcn-nuxt Integration
|
|
|
|
Diese Komponenten können **shadcn-nuxt Komponenten ersetzen** oder ergänzen:
|
|
|
|
```vue
|
|
<!-- Statt shadcn Button: -->
|
|
<Button>Click me</Button>
|
|
|
|
<!-- Verwende experimenta Button: -->
|
|
<ExperimentaButton>Click me</ExperimentaButton>
|
|
```
|
|
|
|
### Tailwind Klassen verwenden
|
|
|
|
Alle Komponenten verwenden Tailwind CSS Utilities. Du kannst sie anpassen:
|
|
|
|
```vue
|
|
<ExperimentaButton class="mt-8">
|
|
Custom Margin
|
|
</ExperimentaButton>
|
|
```
|
|
|
|
### Custom Variants hinzufügen
|
|
|
|
Beispiel: Eine neue Button-Variant hinzufügen:
|
|
|
|
```vue
|
|
<!-- In ExperimentaButton.vue -->
|
|
<script setup lang="ts">
|
|
interface Props {
|
|
variant?: 'primary' | 'secondary' | 'tertiary' // Neu: tertiary
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
/* Neue Variant definieren */
|
|
.btn-tertiary {
|
|
@apply bg-info text-white;
|
|
}
|
|
|
|
.btn-tertiary:hover {
|
|
@apply bg-info-dark;
|
|
}
|
|
</style>
|
|
```
|
|
|
|
---
|
|
|
|
## TypeScript Support
|
|
|
|
Alle Komponenten sind **TypeScript-ready** mit vollständigen Prop-Definitionen.
|
|
|
|
Beispiel für Type-Safe Usage:
|
|
|
|
```vue
|
|
<script setup lang="ts">
|
|
import ExperimentaButton from './ExperimentaButton.vue'
|
|
|
|
function handleClick(event: MouseEvent) {
|
|
console.log('Button clicked', event)
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<ExperimentaButton variant="primary" size="large" :disabled="false" @click="handleClick">
|
|
Click me
|
|
</ExperimentaButton>
|
|
</template>
|
|
```
|
|
|
|
---
|
|
|
|
## Accessibility (A11y)
|
|
|
|
Alle Komponenten folgen **WCAG 2.1 AA Standards**:
|
|
|
|
- ✅ **Keyboard Navigation** (Tab, Enter, Space)
|
|
- ✅ **Focus Indicators** (sichtbarer Focus-Ring)
|
|
- ✅ **ARIA Labels** (Screen Reader Support)
|
|
- ✅ **Color Contrast** (mindestens 4.5:1 Ratio)
|
|
|
|
---
|
|
|
|
## Testing
|
|
|
|
Beispiel für Vitest Unit Tests:
|
|
|
|
```typescript
|
|
// ExperimentaButton.spec.ts
|
|
import { mount } from '@vue/test-utils'
|
|
import ExperimentaButton from './ExperimentaButton.vue'
|
|
|
|
describe('ExperimentaButton', () => {
|
|
it('renders primary button by default', () => {
|
|
const wrapper = mount(ExperimentaButton, {
|
|
slots: { default: 'Click me' },
|
|
})
|
|
|
|
expect(wrapper.find('.btn-primary').exists()).toBe(true)
|
|
expect(wrapper.text()).toBe('Click me')
|
|
})
|
|
|
|
it('emits click event', async () => {
|
|
const wrapper = mount(ExperimentaButton)
|
|
await wrapper.trigger('click')
|
|
|
|
expect(wrapper.emitted('click')).toBeTruthy()
|
|
})
|
|
|
|
it('renders as link when href is provided', () => {
|
|
const wrapper = mount(ExperimentaButton, {
|
|
props: { href: 'https://example.com' },
|
|
})
|
|
|
|
expect(wrapper.element.tagName).toBe('A')
|
|
expect(wrapper.attributes('href')).toBe('https://example.com')
|
|
})
|
|
})
|
|
```
|
|
|
|
---
|
|
|
|
## Storybook Integration (Optional)
|
|
|
|
Erstelle Stories für visuelle Dokumentation:
|
|
|
|
```typescript
|
|
// ExperimentaButton.stories.ts
|
|
import type { Meta, StoryObj } from '@storybook/vue3'
|
|
import ExperimentaButton from './ExperimentaButton.vue'
|
|
|
|
const meta: Meta<typeof ExperimentaButton> = {
|
|
title: 'Components/ExperimentaButton',
|
|
component: ExperimentaButton,
|
|
tags: ['autodocs'],
|
|
}
|
|
|
|
export default meta
|
|
type Story = StoryObj<typeof ExperimentaButton>
|
|
|
|
export const Primary: Story = {
|
|
args: {
|
|
variant: 'primary',
|
|
},
|
|
render: (args) => ({
|
|
components: { ExperimentaButton },
|
|
setup() {
|
|
return { args }
|
|
},
|
|
template: '<ExperimentaButton v-bind="args">Click me</ExperimentaButton>',
|
|
}),
|
|
}
|
|
|
|
export const Secondary: Story = {
|
|
args: {
|
|
variant: 'secondary',
|
|
},
|
|
render: (args) => ({
|
|
components: { ExperimentaButton },
|
|
setup() {
|
|
return { args }
|
|
},
|
|
template: '<ExperimentaButton v-bind="args">Cancel</ExperimentaButton>',
|
|
}),
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Weitere Ressourcen
|
|
|
|
- **Design System Dokumentation**: `docs/DESIGN_SYSTEM.md`
|
|
- **Tailwind Config**: `tailwind.config.ts`
|
|
- **CSS Custom Properties**: `assets/css/tailwind.css`
|
|
- **Design-Vorlagen**: `design-examples/*.html`
|
|
|
|
---
|
|
|
|
**Fragen oder Feedback?** → docs@experimenta.science
|