mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-20 14:49:24 +02:00
style: auto-format codebase with Prettier
Applied formatting to 1487+ files using pnpm format:write - TypeScript/JavaScript files - Svelte components - Astro pages - JSON configs - Markdown docs 13 files still need manual review (Astro JSX comments)
This commit is contained in:
parent
0241f5554c
commit
d36b321d9d
3952 changed files with 661498 additions and 739751 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -7,7 +7,7 @@ Basierend auf der Analyse empfehle ich die **PocketBase-Integration (Option 1)**
|
|||
## Projektziele
|
||||
|
||||
- ✅ Content Marketing Platform für SEO & Thought Leadership
|
||||
- ✅ Nahtlose Integration in bestehendes uload-System
|
||||
- ✅ Nahtlose Integration in bestehendes uload-System
|
||||
- ✅ Skalierbar für 100+ Artikel
|
||||
- ✅ DSGVO-konform
|
||||
- ✅ Mobile-optimiert
|
||||
|
|
@ -16,75 +16,90 @@ Basierend auf der Analyse empfehle ich die **PocketBase-Integration (Option 1)**
|
|||
## Timeline & Milestones
|
||||
|
||||
### Tag 1: Database & Backend Setup
|
||||
|
||||
**Ziel**: Komplette Datenbank-Struktur und API-Endpoints
|
||||
|
||||
#### Vormittag (4h)
|
||||
|
||||
- [ ] PocketBase Collections erstellen
|
||||
- [ ] Relationen definieren
|
||||
- [ ] Validation Rules setzen
|
||||
- [ ] Test-Daten einfügen
|
||||
|
||||
#### Nachmittag (4h)
|
||||
|
||||
- [ ] Server-Routes implementieren
|
||||
- [ ] API-Endpoints testen
|
||||
- [ ] Error Handling
|
||||
- [ ] Pagination Logic
|
||||
|
||||
### Tag 2: Frontend Basis-Komponenten
|
||||
|
||||
**Ziel**: Blog-Übersicht und Artikel-Ansicht funktionsfähig
|
||||
|
||||
#### Vormittag (4h)
|
||||
|
||||
- [ ] Blog-Übersichtsseite
|
||||
- [ ] BlogCard Komponente
|
||||
- [ ] Kategorie-Filter
|
||||
- [ ] Tag-Cloud
|
||||
|
||||
#### Nachmittag (4h)
|
||||
|
||||
- [ ] Artikel-Detailseite
|
||||
- [ ] Reading Progress Bar
|
||||
- [ ] Table of Contents
|
||||
- [ ] Share Buttons
|
||||
|
||||
### Tag 3: Admin-Interface
|
||||
|
||||
**Ziel**: Vollständiges CMS für Blog-Verwaltung
|
||||
|
||||
#### Vormittag (4h)
|
||||
|
||||
- [ ] Admin-Dashboard
|
||||
- [ ] Artikel-Liste mit Status
|
||||
- [ ] Bulk-Actions
|
||||
- [ ] Suchfunktion
|
||||
|
||||
#### Nachmittag (4h)
|
||||
|
||||
- [ ] Rich-Text Editor (Tiptap)
|
||||
- [ ] Media Upload
|
||||
- [ ] Preview-Funktion
|
||||
- [ ] Auto-Save
|
||||
|
||||
### Tag 4: SEO & Performance
|
||||
|
||||
**Ziel**: Optimale Sichtbarkeit und Geschwindigkeit
|
||||
|
||||
#### Vormittag (4h)
|
||||
|
||||
- [ ] Meta-Tags Management
|
||||
- [ ] Schema.org Markup
|
||||
- [ ] XML Sitemap
|
||||
- [ ] RSS/Atom Feed
|
||||
|
||||
#### Nachmittag (4h)
|
||||
|
||||
- [ ] Image Optimization
|
||||
- [ ] Lazy Loading
|
||||
- [ ] Cache-Strategy
|
||||
- [ ] CDN-Integration
|
||||
|
||||
### Tag 5: Features & Polish
|
||||
|
||||
**Ziel**: Premium-Features und finale Optimierungen
|
||||
|
||||
#### Vormittag (4h)
|
||||
|
||||
- [ ] Verwandte Artikel
|
||||
- [ ] Lesezeit-Berechnung
|
||||
- [ ] Newsletter-Integration
|
||||
- [ ] Social Media Auto-Post
|
||||
|
||||
#### Nachmittag (4h)
|
||||
|
||||
- [ ] Analytics Dashboard
|
||||
- [ ] A/B Testing Setup
|
||||
- [ ] Mobile Optimierung
|
||||
|
|
@ -146,21 +161,21 @@ CREATE TABLE blog_post_tags (
|
|||
|
||||
```typescript
|
||||
// Blog API Routes
|
||||
GET /api/blog/posts // Liste mit Pagination
|
||||
GET /api/blog/posts/[slug] // Einzelner Artikel
|
||||
POST /api/blog/posts/[slug]/view // View Counter
|
||||
GET /api/blog/categories // Alle Kategorien
|
||||
GET /api/blog/tags // Alle Tags
|
||||
GET /api/blog/search // Volltextsuche
|
||||
GET /api/blog/feed.xml // RSS Feed
|
||||
GET /api/blog/sitemap.xml // Sitemap
|
||||
GET / api / blog / posts; // Liste mit Pagination
|
||||
GET / api / blog / posts / [slug]; // Einzelner Artikel
|
||||
POST / api / blog / posts / [slug] / view; // View Counter
|
||||
GET / api / blog / categories; // Alle Kategorien
|
||||
GET / api / blog / tags; // Alle Tags
|
||||
GET / api / blog / search; // Volltextsuche
|
||||
GET / api / blog / feed.xml; // RSS Feed
|
||||
GET / api / blog / sitemap.xml; // Sitemap
|
||||
|
||||
// Admin API Routes (auth required)
|
||||
POST /api/admin/blog/posts // Neuer Artikel
|
||||
PUT /api/admin/blog/posts/[id] // Update
|
||||
DELETE /api/admin/blog/posts/[id] // Löschen
|
||||
POST /api/admin/blog/posts/[id]/publish // Veröffentlichen
|
||||
POST /api/admin/blog/upload // Bild-Upload
|
||||
POST / api / admin / blog / posts; // Neuer Artikel
|
||||
PUT / api / admin / blog / posts / [id]; // Update
|
||||
DELETE / api / admin / blog / posts / [id]; // Löschen
|
||||
POST / api / admin / blog / posts / [id] / publish; // Veröffentlichen
|
||||
POST / api / admin / blog / upload; // Bild-Upload
|
||||
```
|
||||
|
||||
### Komponenten-Architektur
|
||||
|
|
@ -212,18 +227,21 @@ src/lib/components/blog/
|
|||
### Content-Kalender (erste 3 Monate)
|
||||
|
||||
**Monat 1**: Foundations (4 Artikel/Monat)
|
||||
|
||||
- Woche 1: Psychology & UX
|
||||
- Woche 2: Technical Tutorial
|
||||
- Woche 3: Case Study
|
||||
- Woche 4: Industry Trends
|
||||
|
||||
**Monat 2**: Deep Dives (6 Artikel/Monat)
|
||||
|
||||
- Analytics Deep Dives
|
||||
- Integration Guides
|
||||
- Performance Optimization
|
||||
- Security Best Practices
|
||||
|
||||
**Monat 3**: Growth (8 Artikel/Monat)
|
||||
|
||||
- Guest Posts
|
||||
- User Success Stories
|
||||
- Feature Announcements
|
||||
|
|
@ -234,11 +252,13 @@ src/lib/components/blog/
|
|||
### Target Keywords
|
||||
|
||||
**Primary Keywords** (Schwierigkeit: Mittel)
|
||||
|
||||
- "url kürzen" (9.900 Suchen/Monat)
|
||||
- "link verkürzen" (6.600 Suchen/Monat)
|
||||
- "kurze urls" (2.400 Suchen/Monat)
|
||||
|
||||
**Long-Tail Keywords** (Schwierigkeit: Leicht)
|
||||
|
||||
- "kostenlos url kürzen ohne anmeldung"
|
||||
- "eigene domain für kurze links"
|
||||
- "qr code mit logo erstellen"
|
||||
|
|
@ -260,12 +280,14 @@ src/lib/components/blog/
|
|||
## Performance KPIs
|
||||
|
||||
### Technical Metrics
|
||||
|
||||
- **Page Load**: < 2s (Mobile 3G)
|
||||
- **Time to Interactive**: < 3.5s
|
||||
- **Core Web Vitals**: Alle grün
|
||||
- **Lighthouse Score**: > 90
|
||||
|
||||
### Business Metrics
|
||||
|
||||
- **Organic Traffic**: +50% in 3 Monaten
|
||||
- **Conversion Rate**: Blog → Sign-up > 3%
|
||||
- **Engagement Rate**: > 2 Min Average Time
|
||||
|
|
@ -275,26 +297,29 @@ src/lib/components/blog/
|
|||
|
||||
### Potenzielle Risiken & Mitigationen
|
||||
|
||||
| Risiko | Wahrscheinlichkeit | Impact | Mitigation |
|
||||
|--------|-------------------|---------|------------|
|
||||
| Editor-Komplexität | Mittel | Hoch | Start mit SimpleMDE, später Upgrade |
|
||||
| Performance-Probleme | Niedrig | Mittel | Caching-Strategy von Anfang an |
|
||||
| Content-Erstellung | Hoch | Mittel | Freelancer-Pool aufbauen |
|
||||
| SEO-Konkurrenz | Mittel | Niedrig | Nischen-Keywords fokussieren |
|
||||
| Risiko | Wahrscheinlichkeit | Impact | Mitigation |
|
||||
| -------------------- | ------------------ | ------- | ----------------------------------- |
|
||||
| Editor-Komplexität | Mittel | Hoch | Start mit SimpleMDE, später Upgrade |
|
||||
| Performance-Probleme | Niedrig | Mittel | Caching-Strategy von Anfang an |
|
||||
| Content-Erstellung | Hoch | Mittel | Freelancer-Pool aufbauen |
|
||||
| SEO-Konkurrenz | Mittel | Niedrig | Nischen-Keywords fokussieren |
|
||||
|
||||
## Budget & Ressourcen
|
||||
|
||||
### Entwicklung (5 Tage)
|
||||
|
||||
- **Developer**: 5 Tage × 8h = 40h
|
||||
- **Design Assets**: Vorhandene UI-Komponenten nutzen
|
||||
- **Testing**: 1 Tag zusätzlich
|
||||
|
||||
### Content (Ongoing)
|
||||
|
||||
- **Launch Content**: 5 Artikel (intern)
|
||||
- **Monthly Content**: 4-8 Artikel
|
||||
- **Freelancer Budget**: €500-1000/Monat
|
||||
|
||||
### Tools & Services
|
||||
|
||||
- **Grammarly**: Rechtschreibprüfung
|
||||
- **Canva Pro**: Grafiken
|
||||
- **Unsplash+**: Stock-Fotos
|
||||
|
|
@ -305,6 +330,7 @@ src/lib/components/blog/
|
|||
### Pre-Launch Checklist
|
||||
|
||||
#### Funktionalität
|
||||
|
||||
- [ ] Alle CRUD-Operationen funktionieren
|
||||
- [ ] Pagination arbeitet korrekt
|
||||
- [ ] Suche liefert relevante Ergebnisse
|
||||
|
|
@ -312,24 +338,28 @@ src/lib/components/blog/
|
|||
- [ ] RSS Feed validiert
|
||||
|
||||
#### Performance
|
||||
|
||||
- [ ] Lighthouse Audit > 90
|
||||
- [ ] Mobile-Responsive
|
||||
- [ ] Bilder optimiert
|
||||
- [ ] Caching aktiviert
|
||||
|
||||
#### SEO
|
||||
|
||||
- [ ] Meta-Tags vollständig
|
||||
- [ ] Sitemap generiert
|
||||
- [ ] Schema.org implementiert
|
||||
- [ ] Social Cards funktionieren
|
||||
|
||||
#### Security
|
||||
|
||||
- [ ] Input-Validation
|
||||
- [ ] XSS-Protection
|
||||
- [ ] CSRF-Token
|
||||
- [ ] Rate Limiting
|
||||
|
||||
#### Accessibility
|
||||
|
||||
- [ ] WCAG 2.1 AA konform
|
||||
- [ ] Keyboard-Navigation
|
||||
- [ ] Screen-Reader kompatibel
|
||||
|
|
@ -338,18 +368,21 @@ src/lib/components/blog/
|
|||
## Post-Launch Plan
|
||||
|
||||
### Woche 1
|
||||
|
||||
- Monitoring Setup
|
||||
- Bug Fixes
|
||||
- Performance Tuning
|
||||
- Erste Analytics
|
||||
|
||||
### Monat 1
|
||||
|
||||
- Content-Pipeline etablieren
|
||||
- SEO-Optimierungen
|
||||
- User-Feedback sammeln
|
||||
- A/B Tests starten
|
||||
|
||||
### Quartal 1
|
||||
|
||||
- Feature-Erweiterungen
|
||||
- Newsletter-Integration
|
||||
- Kommentar-System
|
||||
|
|
@ -358,16 +391,19 @@ src/lib/components/blog/
|
|||
## Success Metrics
|
||||
|
||||
### Launch (Tag 1)
|
||||
|
||||
- ✅ Blog live und funktional
|
||||
- ✅ 5 Launch-Artikel online
|
||||
- ✅ Keine kritischen Bugs
|
||||
|
||||
### Monat 1
|
||||
|
||||
- ✅ 20+ Blog-Artikel
|
||||
- ✅ 1000+ Unique Visitors
|
||||
- ✅ 5+ Backlinks
|
||||
|
||||
### Quartal 1
|
||||
|
||||
- ✅ 50+ Blog-Artikel
|
||||
- ✅ 10.000+ Monthly Visitors
|
||||
- ✅ Top 10 Rankings für Target Keywords
|
||||
|
|
@ -394,4 +430,4 @@ src/lib/components/blog/
|
|||
|
||||
**Empfehlung**: Sofortiger Start mit der PocketBase-Integration. Der vorgeschlagene 5-Tage-Plan ist realistisch und liefert ein production-ready Blog-System, das perfekt in die bestehende uload-Architektur passt.
|
||||
|
||||
**Kontakt für Rückfragen**: Bei Fragen zur Implementierung oder für Detail-Diskussionen stehe ich zur Verfügung.
|
||||
**Kontakt für Rückfragen**: Bei Fragen zur Implementierung oder für Detail-Diskussionen stehe ich zur Verfügung.
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@
|
|||
## Übersicht: 3 Ansätze für statische Markdown-Blogs
|
||||
|
||||
### 1. **mdsvex** - Markdown als Svelte-Komponenten (Empfohlen)
|
||||
### 2. **Vite Glob Import** - Dynamisches Laden zur Build-Zeit
|
||||
|
||||
### 2. **Vite Glob Import** - Dynamisches Laden zur Build-Zeit
|
||||
|
||||
### 3. **Content Collections** - Strukturierte Markdown-Verwaltung
|
||||
|
||||
---
|
||||
|
|
@ -26,7 +28,7 @@ import { sveltekit } from '@sveltejs/kit/vite';
|
|||
import { defineConfig } from 'vite';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [sveltekit()]
|
||||
plugins: [sveltekit()],
|
||||
});
|
||||
```
|
||||
|
||||
|
|
@ -41,23 +43,20 @@ import rehypeAutolinkHeadings from 'rehype-autolink-headings';
|
|||
|
||||
/** @type {import('mdsvex').MdsvexOptions} */
|
||||
const mdsvexOptions = {
|
||||
extensions: ['.md', '.mdx'],
|
||||
layout: {
|
||||
blog: './src/lib/layouts/BlogLayout.svelte'
|
||||
},
|
||||
rehypePlugins: [
|
||||
rehypeSlug,
|
||||
[rehypeAutolinkHeadings, { behavior: 'wrap' }]
|
||||
]
|
||||
extensions: ['.md', '.mdx'],
|
||||
layout: {
|
||||
blog: './src/lib/layouts/BlogLayout.svelte',
|
||||
},
|
||||
rehypePlugins: [rehypeSlug, [rehypeAutolinkHeadings, { behavior: 'wrap' }]],
|
||||
};
|
||||
|
||||
/** @type {import('@sveltejs/kit').Config} */
|
||||
const config = {
|
||||
extensions: ['.svelte', '.md', '.mdx'],
|
||||
preprocess: [vitePreprocess(), mdsvex(mdsvexOptions)],
|
||||
kit: {
|
||||
adapter: adapter()
|
||||
}
|
||||
extensions: ['.svelte', '.md', '.mdx'],
|
||||
preprocess: [vitePreprocess(), mdsvex(mdsvexOptions)],
|
||||
kit: {
|
||||
adapter: adapter(),
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
|
|
@ -68,54 +67,54 @@ export default config;
|
|||
```svelte
|
||||
<!-- src/lib/layouts/BlogLayout.svelte -->
|
||||
<script>
|
||||
export let title = '';
|
||||
export let date = '';
|
||||
export let author = '';
|
||||
export let excerpt = '';
|
||||
export let tags = [];
|
||||
export let image = '';
|
||||
|
||||
let formattedDate = $derived(
|
||||
new Date(date).toLocaleDateString('de-DE', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
})
|
||||
);
|
||||
export let title = '';
|
||||
export let date = '';
|
||||
export let author = '';
|
||||
export let excerpt = '';
|
||||
export let tags = [];
|
||||
export let image = '';
|
||||
|
||||
let formattedDate = $derived(
|
||||
new Date(date).toLocaleDateString('de-DE', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
})
|
||||
);
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>{title} | uload Blog</title>
|
||||
<meta name="description" content={excerpt} />
|
||||
<meta property="og:title" content={title} />
|
||||
<meta property="og:description" content={excerpt} />
|
||||
{#if image}
|
||||
<meta property="og:image" content={image} />
|
||||
{/if}
|
||||
<title>{title} | uload Blog</title>
|
||||
<meta name="description" content={excerpt} />
|
||||
<meta property="og:title" content={title} />
|
||||
<meta property="og:description" content={excerpt} />
|
||||
{#if image}
|
||||
<meta property="og:image" content={image} />
|
||||
{/if}
|
||||
</svelte:head>
|
||||
|
||||
<article class="prose prose-lg mx-auto px-4 py-8 max-w-4xl">
|
||||
<header class="mb-8">
|
||||
<h1 class="text-4xl font-bold mb-2">{title}</h1>
|
||||
<div class="text-gray-600 flex gap-4 items-center">
|
||||
<time datetime={date}>{formattedDate}</time>
|
||||
<span>•</span>
|
||||
<span>{author}</span>
|
||||
</div>
|
||||
{#if tags.length > 0}
|
||||
<div class="flex gap-2 mt-4">
|
||||
{#each tags as tag}
|
||||
<span class="bg-gray-100 px-3 py-1 rounded-full text-sm">
|
||||
{tag}
|
||||
</span>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</header>
|
||||
|
||||
<div class="content">
|
||||
<slot />
|
||||
</div>
|
||||
<header class="mb-8">
|
||||
<h1 class="text-4xl font-bold mb-2">{title}</h1>
|
||||
<div class="text-gray-600 flex gap-4 items-center">
|
||||
<time datetime={date}>{formattedDate}</time>
|
||||
<span>•</span>
|
||||
<span>{author}</span>
|
||||
</div>
|
||||
{#if tags.length > 0}
|
||||
<div class="flex gap-2 mt-4">
|
||||
{#each tags as tag}
|
||||
<span class="bg-gray-100 px-3 py-1 rounded-full text-sm">
|
||||
{tag}
|
||||
</span>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</header>
|
||||
|
||||
<div class="content">
|
||||
<slot />
|
||||
</div>
|
||||
</article>
|
||||
```
|
||||
|
||||
|
|
@ -162,52 +161,52 @@ src/routes/
|
|||
```javascript
|
||||
// src/routes/blog/+page.js
|
||||
export async function load() {
|
||||
const posts = import.meta.glob('/src/content/blog/*.md');
|
||||
const postPromises = Object.entries(posts).map(async ([path, resolver]) => {
|
||||
const { metadata } = await resolver();
|
||||
const slug = path.split('/').pop().replace('.md', '');
|
||||
|
||||
return {
|
||||
slug,
|
||||
...metadata
|
||||
};
|
||||
});
|
||||
|
||||
const allPosts = await Promise.all(postPromises);
|
||||
|
||||
// Nach Datum sortieren
|
||||
allPosts.sort((a, b) => new Date(b.date) - new Date(a.date));
|
||||
|
||||
return {
|
||||
posts: allPosts
|
||||
};
|
||||
const posts = import.meta.glob('/src/content/blog/*.md');
|
||||
const postPromises = Object.entries(posts).map(async ([path, resolver]) => {
|
||||
const { metadata } = await resolver();
|
||||
const slug = path.split('/').pop().replace('.md', '');
|
||||
|
||||
return {
|
||||
slug,
|
||||
...metadata,
|
||||
};
|
||||
});
|
||||
|
||||
const allPosts = await Promise.all(postPromises);
|
||||
|
||||
// Nach Datum sortieren
|
||||
allPosts.sort((a, b) => new Date(b.date) - new Date(a.date));
|
||||
|
||||
return {
|
||||
posts: allPosts,
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
```svelte
|
||||
<!-- src/routes/blog/+page.svelte -->
|
||||
<script>
|
||||
let { data } = $props();
|
||||
let { data } = $props();
|
||||
</script>
|
||||
|
||||
<div class="container mx-auto px-4 py-8">
|
||||
<h1 class="text-3xl font-bold mb-8">Blog</h1>
|
||||
|
||||
<div class="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
|
||||
{#each data.posts as post}
|
||||
<article class="border rounded-lg p-6 hover:shadow-lg transition">
|
||||
<h2 class="text-xl font-semibold mb-2">
|
||||
<a href="/blog/{post.slug}" class="hover:text-blue-600">
|
||||
{post.title}
|
||||
</a>
|
||||
</h2>
|
||||
<p class="text-gray-600 mb-4">{post.excerpt}</p>
|
||||
<div class="text-sm text-gray-500">
|
||||
{new Date(post.date).toLocaleDateString('de-DE')}
|
||||
</div>
|
||||
</article>
|
||||
{/each}
|
||||
</div>
|
||||
<h1 class="text-3xl font-bold mb-8">Blog</h1>
|
||||
|
||||
<div class="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
|
||||
{#each data.posts as post}
|
||||
<article class="border rounded-lg p-6 hover:shadow-lg transition">
|
||||
<h2 class="text-xl font-semibold mb-2">
|
||||
<a href="/blog/{post.slug}" class="hover:text-blue-600">
|
||||
{post.title}
|
||||
</a>
|
||||
</h2>
|
||||
<p class="text-gray-600 mb-4">{post.excerpt}</p>
|
||||
<div class="text-sm text-gray-500">
|
||||
{new Date(post.date).toLocaleDateString('de-DE')}
|
||||
</div>
|
||||
</article>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
|
|
@ -231,13 +230,13 @@ import { marked } from 'marked';
|
|||
import matter from 'gray-matter';
|
||||
|
||||
export function parseMarkdown(content) {
|
||||
const { data, content: markdown } = matter(content);
|
||||
const html = marked(markdown);
|
||||
|
||||
return {
|
||||
metadata: data,
|
||||
html
|
||||
};
|
||||
const { data, content: markdown } = matter(content);
|
||||
const html = marked(markdown);
|
||||
|
||||
return {
|
||||
metadata: data,
|
||||
html,
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -248,28 +247,28 @@ export function parseMarkdown(content) {
|
|||
import { parseMarkdown } from '$lib/utils/markdown';
|
||||
|
||||
export async function load() {
|
||||
const postFiles = import.meta.glob('/src/content/blog/*.md', {
|
||||
query: '?raw',
|
||||
import: 'default'
|
||||
});
|
||||
|
||||
const posts = await Promise.all(
|
||||
Object.entries(postFiles).map(async ([path, resolver]) => {
|
||||
const content = await resolver();
|
||||
const { metadata, html } = parseMarkdown(content);
|
||||
const slug = path.split('/').pop().replace('.md', '');
|
||||
|
||||
return {
|
||||
slug,
|
||||
...metadata,
|
||||
content: html
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
return {
|
||||
posts: posts.sort((a, b) => new Date(b.date) - new Date(a.date))
|
||||
};
|
||||
const postFiles = import.meta.glob('/src/content/blog/*.md', {
|
||||
query: '?raw',
|
||||
import: 'default',
|
||||
});
|
||||
|
||||
const posts = await Promise.all(
|
||||
Object.entries(postFiles).map(async ([path, resolver]) => {
|
||||
const content = await resolver();
|
||||
const { metadata, html } = parseMarkdown(content);
|
||||
const slug = path.split('/').pop().replace('.md', '');
|
||||
|
||||
return {
|
||||
slug,
|
||||
...metadata,
|
||||
content: html,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
return {
|
||||
posts: posts.sort((a, b) => new Date(b.date) - new Date(a.date)),
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -281,37 +280,37 @@ import { parseMarkdown } from '$lib/utils/markdown';
|
|||
import { error } from '@sveltejs/kit';
|
||||
|
||||
export async function load({ params }) {
|
||||
try {
|
||||
const post = await import(`../../../content/blog/${params.slug}.md?raw`);
|
||||
const { metadata, html } = parseMarkdown(post.default);
|
||||
|
||||
return {
|
||||
...metadata,
|
||||
content: html,
|
||||
slug: params.slug
|
||||
};
|
||||
} catch (e) {
|
||||
throw error(404, 'Post nicht gefunden');
|
||||
}
|
||||
try {
|
||||
const post = await import(`../../../content/blog/${params.slug}.md?raw`);
|
||||
const { metadata, html } = parseMarkdown(post.default);
|
||||
|
||||
return {
|
||||
...metadata,
|
||||
content: html,
|
||||
slug: params.slug,
|
||||
};
|
||||
} catch (e) {
|
||||
throw error(404, 'Post nicht gefunden');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```svelte
|
||||
<!-- src/routes/blog/[slug]/+page.svelte -->
|
||||
<script>
|
||||
let { data } = $props();
|
||||
let { data } = $props();
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>{data.title}</title>
|
||||
<meta name="description" content={data.excerpt} />
|
||||
<title>{data.title}</title>
|
||||
<meta name="description" content={data.excerpt} />
|
||||
</svelte:head>
|
||||
|
||||
<article class="prose mx-auto px-4 py-8">
|
||||
<h1>{data.title}</h1>
|
||||
<time>{new Date(data.date).toLocaleDateString('de-DE')}</time>
|
||||
|
||||
{@html data.content}
|
||||
<h1>{data.title}</h1>
|
||||
<time>{new Date(data.date).toLocaleDateString('de-DE')}</time>
|
||||
|
||||
{@html data.content}
|
||||
</article>
|
||||
```
|
||||
|
||||
|
|
@ -340,20 +339,20 @@ src/content/
|
|||
import { z } from 'zod';
|
||||
|
||||
export const blogSchema = z.object({
|
||||
title: z.string(),
|
||||
date: z.date(),
|
||||
author: z.string(),
|
||||
excerpt: z.string(),
|
||||
tags: z.array(z.string()),
|
||||
image: z.string().optional(),
|
||||
draft: z.boolean().default(false)
|
||||
title: z.string(),
|
||||
date: z.date(),
|
||||
author: z.string(),
|
||||
excerpt: z.string(),
|
||||
tags: z.array(z.string()),
|
||||
image: z.string().optional(),
|
||||
draft: z.boolean().default(false),
|
||||
});
|
||||
|
||||
export const collections = {
|
||||
blog: {
|
||||
schema: blogSchema,
|
||||
directory: 'src/content/blog'
|
||||
}
|
||||
blog: {
|
||||
schema: blogSchema,
|
||||
directory: 'src/content/blog',
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
|
|
@ -366,45 +365,45 @@ import matter from 'gray-matter';
|
|||
import { marked } from 'marked';
|
||||
|
||||
export async function getCollection(collection) {
|
||||
const posts = import.meta.glob('/src/content/blog/*.md', {
|
||||
query: '?raw',
|
||||
import: 'default'
|
||||
});
|
||||
|
||||
const entries = await Promise.all(
|
||||
Object.entries(posts).map(async ([path, resolver]) => {
|
||||
// Drafts überspringen
|
||||
if (path.includes('_drafts')) return null;
|
||||
|
||||
const content = await resolver();
|
||||
const { data, content: markdown } = matter(content);
|
||||
|
||||
// Schema validieren
|
||||
const metadata = blogSchema.parse({
|
||||
...data,
|
||||
date: new Date(data.date)
|
||||
});
|
||||
|
||||
// Draft-Posts in Production ausblenden
|
||||
if (metadata.draft && import.meta.env.PROD) return null;
|
||||
|
||||
const slug = path.split('/').pop().replace('.md', '');
|
||||
const html = marked(markdown);
|
||||
|
||||
return {
|
||||
slug,
|
||||
...metadata,
|
||||
content: html
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
return entries.filter(Boolean);
|
||||
const posts = import.meta.glob('/src/content/blog/*.md', {
|
||||
query: '?raw',
|
||||
import: 'default',
|
||||
});
|
||||
|
||||
const entries = await Promise.all(
|
||||
Object.entries(posts).map(async ([path, resolver]) => {
|
||||
// Drafts überspringen
|
||||
if (path.includes('_drafts')) return null;
|
||||
|
||||
const content = await resolver();
|
||||
const { data, content: markdown } = matter(content);
|
||||
|
||||
// Schema validieren
|
||||
const metadata = blogSchema.parse({
|
||||
...data,
|
||||
date: new Date(data.date),
|
||||
});
|
||||
|
||||
// Draft-Posts in Production ausblenden
|
||||
if (metadata.draft && import.meta.env.PROD) return null;
|
||||
|
||||
const slug = path.split('/').pop().replace('.md', '');
|
||||
const html = marked(markdown);
|
||||
|
||||
return {
|
||||
slug,
|
||||
...metadata,
|
||||
content: html,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
return entries.filter(Boolean);
|
||||
}
|
||||
|
||||
export async function getEntry(collection, slug) {
|
||||
const posts = await getCollection(collection);
|
||||
return posts.find(post => post.slug === slug);
|
||||
const posts = await getCollection(collection);
|
||||
return posts.find((post) => post.slug === slug);
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -419,31 +418,35 @@ export async function getEntry(collection, slug) {
|
|||
import { getCollection } from '$lib/content';
|
||||
|
||||
export async function GET() {
|
||||
const posts = await getCollection('blog');
|
||||
const site = 'https://ulo.ad';
|
||||
|
||||
const xml = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
const posts = await getCollection('blog');
|
||||
const site = 'https://ulo.ad';
|
||||
|
||||
const xml = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<rss version="2.0">
|
||||
<channel>
|
||||
<title>uload Blog</title>
|
||||
<link>${site}/blog</link>
|
||||
<description>Insights über URLs, Marketing und Psychologie</description>
|
||||
${posts.map(post => `
|
||||
${posts
|
||||
.map(
|
||||
(post) => `
|
||||
<item>
|
||||
<title>${post.title}</title>
|
||||
<link>${site}/blog/${post.slug}</link>
|
||||
<description>${post.excerpt}</description>
|
||||
<pubDate>${new Date(post.date).toUTCString()}</pubDate>
|
||||
</item>
|
||||
`).join('')}
|
||||
`
|
||||
)
|
||||
.join('')}
|
||||
</channel>
|
||||
</rss>`;
|
||||
|
||||
return new Response(xml, {
|
||||
headers: {
|
||||
'Content-Type': 'application/xml'
|
||||
}
|
||||
});
|
||||
|
||||
return new Response(xml, {
|
||||
headers: {
|
||||
'Content-Type': 'application/xml',
|
||||
},
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -452,26 +455,30 @@ export async function GET() {
|
|||
```javascript
|
||||
// src/routes/sitemap.xml/+server.js
|
||||
export async function GET() {
|
||||
const posts = await getCollection('blog');
|
||||
const site = 'https://ulo.ad';
|
||||
|
||||
const xml = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
const posts = await getCollection('blog');
|
||||
const site = 'https://ulo.ad';
|
||||
|
||||
const xml = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
${posts.map(post => `
|
||||
${posts
|
||||
.map(
|
||||
(post) => `
|
||||
<url>
|
||||
<loc>${site}/blog/${post.slug}</loc>
|
||||
<lastmod>${new Date(post.date).toISOString()}</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
`).join('')}
|
||||
`
|
||||
)
|
||||
.join('')}
|
||||
</urlset>`;
|
||||
|
||||
return new Response(xml, {
|
||||
headers: {
|
||||
'Content-Type': 'application/xml'
|
||||
}
|
||||
});
|
||||
|
||||
return new Response(xml, {
|
||||
headers: {
|
||||
'Content-Type': 'application/xml',
|
||||
},
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -486,10 +493,10 @@ npm install -D shiki
|
|||
import { codeToHtml } from 'shiki';
|
||||
|
||||
export async function highlightCode(code, lang = 'javascript') {
|
||||
return await codeToHtml(code, {
|
||||
lang,
|
||||
theme: 'github-dark'
|
||||
});
|
||||
return await codeToHtml(code, {
|
||||
lang,
|
||||
theme: 'github-dark',
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -498,15 +505,15 @@ export async function highlightCode(code, lang = 'javascript') {
|
|||
```javascript
|
||||
// src/lib/utils/reading-time.js
|
||||
export function calculateReadingTime(content) {
|
||||
const wordsPerMinute = 200;
|
||||
const words = content.split(/\s+/).length;
|
||||
const minutes = Math.ceil(words / wordsPerMinute);
|
||||
|
||||
return {
|
||||
minutes,
|
||||
words,
|
||||
text: `${minutes} Min. Lesezeit`
|
||||
};
|
||||
const wordsPerMinute = 200;
|
||||
const words = content.split(/\s+/).length;
|
||||
const minutes = Math.ceil(words / wordsPerMinute);
|
||||
|
||||
return {
|
||||
minutes,
|
||||
words,
|
||||
text: `${minutes} Min. Lesezeit`,
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -515,19 +522,19 @@ export function calculateReadingTime(content) {
|
|||
```javascript
|
||||
// src/lib/utils/toc.js
|
||||
export function extractHeadings(html) {
|
||||
const regex = /<h([2-3])[^>]*id="([^"]*)"[^>]*>(.*?)<\/h\1>/g;
|
||||
const headings = [];
|
||||
let match;
|
||||
|
||||
while ((match = regex.exec(html)) !== null) {
|
||||
headings.push({
|
||||
level: parseInt(match[1]),
|
||||
id: match[2],
|
||||
text: match[3].replace(/<[^>]*>/g, '')
|
||||
});
|
||||
}
|
||||
|
||||
return headings;
|
||||
const regex = /<h([2-3])[^>]*id="([^"]*)"[^>]*>(.*?)<\/h\1>/g;
|
||||
const headings = [];
|
||||
let match;
|
||||
|
||||
while ((match = regex.exec(html)) !== null) {
|
||||
headings.push({
|
||||
level: parseInt(match[1]),
|
||||
id: match[2],
|
||||
text: match[3].replace(/<[^>]*>/g, ''),
|
||||
});
|
||||
}
|
||||
|
||||
return headings;
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -546,17 +553,17 @@ export const prerender = true; // Statisch zur Build-Zeit generieren
|
|||
|
||||
```svelte
|
||||
<script>
|
||||
import { inview } from 'svelte-inview';
|
||||
|
||||
let isInView = $state(false);
|
||||
import { inview } from 'svelte-inview';
|
||||
|
||||
let isInView = $state(false);
|
||||
</script>
|
||||
|
||||
<div use:inview on:inview_enter={() => isInView = true}>
|
||||
{#if isInView}
|
||||
<img src={image} alt={alt} loading="lazy" />
|
||||
{:else}
|
||||
<div class="skeleton h-64 bg-gray-200" />
|
||||
{/if}
|
||||
<div use:inview on:inview_enter={() => (isInView = true)}>
|
||||
{#if isInView}
|
||||
<img src={image} {alt} loading="lazy" />
|
||||
{:else}
|
||||
<div class="skeleton h-64 bg-gray-200" />
|
||||
{/if}
|
||||
</div>
|
||||
```
|
||||
|
||||
|
|
@ -568,16 +575,16 @@ const cache = new Map();
|
|||
const CACHE_DURATION = 1000 * 60 * 5; // 5 Minuten
|
||||
|
||||
export function getCached(key, fetcher) {
|
||||
const cached = cache.get(key);
|
||||
|
||||
if (cached && Date.now() - cached.timestamp < CACHE_DURATION) {
|
||||
return cached.data;
|
||||
}
|
||||
|
||||
const data = fetcher();
|
||||
cache.set(key, { data, timestamp: Date.now() });
|
||||
|
||||
return data;
|
||||
const cached = cache.get(key);
|
||||
|
||||
if (cached && Date.now() - cached.timestamp < CACHE_DURATION) {
|
||||
return cached.data;
|
||||
}
|
||||
|
||||
const data = fetcher();
|
||||
cache.set(key, { data, timestamp: Date.now() });
|
||||
|
||||
return data;
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -590,16 +597,16 @@ export function getCached(key, fetcher) {
|
|||
```javascript
|
||||
// vite.config.js
|
||||
export default defineConfig({
|
||||
build: {
|
||||
rollupOptions: {
|
||||
output: {
|
||||
manualChunks: {
|
||||
'markdown': ['marked', 'gray-matter'],
|
||||
'highlight': ['shiki']
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
build: {
|
||||
rollupOptions: {
|
||||
output: {
|
||||
manualChunks: {
|
||||
markdown: ['marked', 'gray-matter'],
|
||||
highlight: ['shiki'],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
|
|
@ -614,17 +621,17 @@ npm install -D @sveltejs/adapter-static
|
|||
import adapter from '@sveltejs/adapter-static';
|
||||
|
||||
export default {
|
||||
kit: {
|
||||
adapter: adapter({
|
||||
pages: 'build',
|
||||
assets: 'build',
|
||||
fallback: null,
|
||||
precompress: true
|
||||
}),
|
||||
prerender: {
|
||||
entries: ['*'] // Alle Seiten prerendern
|
||||
}
|
||||
}
|
||||
kit: {
|
||||
adapter: adapter({
|
||||
pages: 'build',
|
||||
assets: 'build',
|
||||
fallback: null,
|
||||
precompress: true,
|
||||
}),
|
||||
prerender: {
|
||||
entries: ['*'], // Alle Seiten prerendern
|
||||
},
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
|
|
@ -692,4 +699,4 @@ Besuche `http://localhost:5173/blog`
|
|||
|
||||
---
|
||||
|
||||
**Empfehlung**: Starte mit **mdsvex** (Ansatz 1) - es bietet die beste Balance zwischen Einfachheit und Features, plus du kannst Svelte-Components direkt in Markdown verwenden!
|
||||
**Empfehlung**: Starte mit **mdsvex** (Ansatz 1) - es bietet die beste Balance zwischen Einfachheit und Features, plus du kannst Svelte-Components direkt in Markdown verwenden!
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -30,7 +30,7 @@ In einer Welt, in der über 60% des Web-Traffics von mobilen Geräten kommt, sin
|
|||
|
||||
Die Cognitive Load Theory erklärt, warum kurze URLs so effektiv sind. Unser Gehirn ist darauf programmiert, Energie zu sparen – es ist evolutionär faul, aber auf eine intelligente Weise. Bei der Verarbeitung von Informationen sucht es immer nach dem Weg des geringsten Widerstands.
|
||||
|
||||
Wenn wir einen kurzen, klaren Link sehen, kann unser Gehirn ihn schnell verarbeiten und kategorisieren. Diese mühelose Verarbeitung erzeugt ein positives Gefühl – wir verbinden "einfach" automatisch mit "sicher" und "vertrauenswürdig".
|
||||
Wenn wir einen kurzen, klaren Link sehen, kann unser Gehirn ihn schnell verarbeiten und kategorisieren. Diese mühelose Verarbeitung erzeugt ein positives Gefühl – wir verbinden "einfach" automatisch mit "sicher" und "vertrauenswürdig".
|
||||
|
||||
### Der Halo-Effekt kurzer URLs
|
||||
|
||||
|
|
@ -43,6 +43,7 @@ Unsere Analyse von über 10.000 Link-Klicks hat vier Hauptfaktoren identifiziert
|
|||
### 1. Erkennbare Domain (60% Wichtigkeit)
|
||||
|
||||
Menschen wollen wissen, wo sie landen werden. Eine klare, erkennbare Domain ist der wichtigste Vertrauensfaktor. Das bedeutet:
|
||||
|
||||
- Verwenden Sie Ihre Marken-Domain wenn möglich
|
||||
- Bei Kurz-URLs: Wählen Sie einen Service mit gutem Ruf
|
||||
- Vermeiden Sie obskure URL-Shortener
|
||||
|
|
@ -50,6 +51,7 @@ Menschen wollen wissen, wo sie landen werden. Eine klare, erkennbare Domain ist
|
|||
### 2. Keine kryptischen Zeichen (25% Wichtigkeit)
|
||||
|
||||
Zufällige Zahlen-Buchstaben-Kombinationen wie "x7h9k2p" schrecken Nutzer ab. Stattdessen:
|
||||
|
||||
- Nutzen Sie sprechende Begriffe
|
||||
- Verwenden Sie relevante Keywords
|
||||
- Halten Sie es lesbar und merkbar
|
||||
|
|
@ -57,6 +59,7 @@ Zufällige Zahlen-Buchstaben-Kombinationen wie "x7h9k2p" schrecken Nutzer ab. St
|
|||
### 3. Optimale Länge (10% Wichtigkeit)
|
||||
|
||||
Die magische Grenze liegt bei etwa 50 Zeichen. Alles darüber hinaus wird als zu lang wahrgenommen. Studien zeigen:
|
||||
|
||||
- 15-30 Zeichen: Optimal für Social Media
|
||||
- 30-50 Zeichen: Ideal für E-Mail-Marketing
|
||||
- Über 50 Zeichen: Deutlicher Rückgang der Klickrate
|
||||
|
|
@ -81,6 +84,7 @@ Sonderzeichen wie %, &, = oder ? in der sichtbaren URL verwirren Nutzer und ersc
|
|||
### 3. Die 50-Zeichen-Regel
|
||||
|
||||
Halten Sie Ihre URLs unter 50 Zeichen. Das ist:
|
||||
|
||||
- Kurz genug für Twitter/X
|
||||
- Lesbar auf Mobilgeräten
|
||||
- Merkbar für Nutzer
|
||||
|
|
@ -89,12 +93,14 @@ Halten Sie Ihre URLs unter 50 Zeichen. Das ist:
|
|||
### 4. A/B-Testing ist Ihr Freund
|
||||
|
||||
Testen Sie verschiedene URL-Varianten:
|
||||
|
||||
- Kurz vs. deskriptiv
|
||||
- Mit Markenname vs. ohne
|
||||
- Verschiedene Keywords
|
||||
- Unterschiedliche Strukturen
|
||||
|
||||
Messen Sie dabei:
|
||||
|
||||
- Klickrate (CTR)
|
||||
- Conversion Rate
|
||||
- Bounce Rate
|
||||
|
|
@ -103,6 +109,7 @@ Messen Sie dabei:
|
|||
### 5. Performance-Tracking implementieren
|
||||
|
||||
Ohne Daten keine Optimierung. Moderne Link-Management-Tools bieten:
|
||||
|
||||
- Detaillierte Klick-Statistiken
|
||||
- Geografische Verteilung
|
||||
- Geräteerkennung
|
||||
|
|
@ -112,18 +119,22 @@ Ohne Daten keine Optimierung. Moderne Link-Management-Tools bieten:
|
|||
## Psychologische Trigger in URLs nutzen
|
||||
|
||||
### Urgency (Dringlichkeit)
|
||||
|
||||
- `ulo.ad/flash-sale-24h`
|
||||
- `ulo.ad/limited-offer`
|
||||
|
||||
### Curiosity (Neugier)
|
||||
|
||||
- `ulo.ad/geheimtipp-2024`
|
||||
- `ulo.ad/insider-trick`
|
||||
|
||||
### Value (Wert)
|
||||
|
||||
- `ulo.ad/gratis-guide`
|
||||
- `ulo.ad/50-prozent-rabatt`
|
||||
|
||||
### Social Proof
|
||||
|
||||
- `ulo.ad/bestseller-2024`
|
||||
- `ulo.ad/meistgelesen`
|
||||
|
||||
|
|
@ -139,7 +150,8 @@ Zu viele technische Parameter lassen einen Link "unmenschlich" wirken. Nutzer sp
|
|||
|
||||
### Mobile Usability Disaster
|
||||
|
||||
Auf Smartphones werden lange URLs oft abgeschnitten oder umbrechen. Das Resultat:
|
||||
Auf Smartphones werden lange URLs oft abgeschnitten oder umbrechen. Das Resultat:
|
||||
|
||||
- Unleserlichkeit
|
||||
- Professioneller Eindruck geht verloren
|
||||
- Nutzer können das Ziel nicht einschätzen
|
||||
|
|
@ -150,6 +162,7 @@ Auf Smartphones werden lange URLs oft abgeschnitten oder umbrechen. Das Resultat
|
|||
### E-Commerce: 67% mehr Conversions
|
||||
|
||||
Ein großer Online-Händler verkürzte seine Produkt-URLs von durchschnittlich 120 auf 45 Zeichen. Das Ergebnis:
|
||||
|
||||
- 67% höhere Conversion Rate
|
||||
- 42% mehr Social Shares
|
||||
- 31% niedrigere Bounce Rate
|
||||
|
|
@ -157,6 +170,7 @@ Ein großer Online-Händler verkürzte seine Produkt-URLs von durchschnittlich 1
|
|||
### Newsletter-Marketing: Verdoppelte Klickrate
|
||||
|
||||
Ein B2B-Unternehmen wechselte von langen Tracking-URLs zu personalisierten Kurz-URLs:
|
||||
|
||||
- Vorher: `company.com/newsletter/2024/march/article-5?utm_source=email&utm_medium=newsletter&subscriber=12345`
|
||||
- Nachher: `co.link/cloud-guide`
|
||||
- Resultat: 2,1x höhere Klickrate
|
||||
|
|
@ -164,6 +178,7 @@ Ein B2B-Unternehmen wechselte von langen Tracking-URLs zu personalisierten Kurz-
|
|||
### Social Media: 3x mehr Engagement
|
||||
|
||||
Ein Influencer nutzte branded Short-URLs statt generischer Affiliate-Links:
|
||||
|
||||
- Engagement stieg um 300%
|
||||
- Trust-Score verbesserte sich um 85%
|
||||
- Follower-Wachstum +45%
|
||||
|
|
@ -173,6 +188,7 @@ Ein Influencer nutzte branded Short-URLs statt generischer Affiliate-Links:
|
|||
### KI-optimierte Personalisierung
|
||||
|
||||
Moderne Systeme nutzen KI, um für jeden Nutzer die optimale URL-Variante zu generieren – basierend auf:
|
||||
|
||||
- Demografischen Daten
|
||||
- Bisherigem Klickverhalten
|
||||
- Kontext der Interaktion
|
||||
|
|
@ -181,6 +197,7 @@ Moderne Systeme nutzen KI, um für jeden Nutzer die optimale URL-Variante zu gen
|
|||
### Voice-First Optimization
|
||||
|
||||
Mit dem Aufstieg von Sprachassistenten werden "sprechbare" URLs wichtiger:
|
||||
|
||||
- Einfache Wörter statt Buchstaben-Zahlen-Kombinationen
|
||||
- Vermeidung ähnlich klingender Begriffe
|
||||
- Klare, eindeutige Aussprache
|
||||
|
|
@ -233,6 +250,7 @@ Die Psychologie kurzer URLs ist keine Raketenwissenschaft, aber ihre Auswirkunge
|
|||
### Ein Werkzeug, das hilft
|
||||
|
||||
Tools wie [uload](https://ulo.ad) wurden speziell entwickelt, um die Erkenntnisse der URL-Psychologie in die Praxis umzusetzen. Mit Features wie:
|
||||
|
||||
- Personalisierte Kurz-URLs
|
||||
- Detaillierte Analytics
|
||||
- A/B-Testing
|
||||
|
|
@ -246,10 +264,11 @@ können Sie sofort damit beginnen, Ihre Link-Performance zu optimieren.
|
|||
**Über diesen Artikel**: Basierend auf aktuellen Studien zur Nutzerpsychologie und realen Performance-Daten von über 10 Millionen Link-Klicks. Die präsentierten Strategien wurden in der Praxis getestet und validiert.
|
||||
|
||||
**Weiterführende Ressourcen**:
|
||||
|
||||
- [Cognitive Load Theory in UX Design](https://www.nngroup.com/articles/minimize-cognitive-load/)
|
||||
- [The Psychology of Web Performance](https://www.smashingmagazine.com/2024/01/psychology-web-performance/)
|
||||
- [Mobile-First URL Strategy Guide](https://moz.com/blog/mobile-first-indexing)
|
||||
|
||||
---
|
||||
|
||||
*Haben Sie Fragen oder möchten Sie Ihre eigenen Erfahrungen teilen? Kontaktieren Sie uns oder hinterlassen Sie einen Kommentar. Wir freuen uns auf den Austausch!*
|
||||
_Haben Sie Fragen oder möchten Sie Ihre eigenen Erfahrungen teilen? Kontaktieren Sie uns oder hinterlassen Sie einen Kommentar. Wir freuen uns auf den Austausch!_
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue