FIX: Directories
Better directory structure
This commit is contained in:
parent
726c54d11f
commit
8943512328
16 changed files with 16 additions and 21 deletions
163
app/components/Section/Booking.vue
Executable file
163
app/components/Section/Booking.vue
Executable file
|
@ -0,0 +1,163 @@
|
|||
<template>
|
||||
<section id="services" class="Services content">
|
||||
<h1>Projekt buchen</h1>
|
||||
<h2>Du hast also beschlossen, dass du <span class="highlight">meine Hilfe</span> brauchst. Cool!</h2>
|
||||
<p class="margin-top">Manchmal brauchen wir alle einfach nur eine Kleinigkeit und wollen uns nicht lange binden. Das ist natürlich
|
||||
völlig in Ordnung und genau deshalb biete ich dir die Möglichkeit mich gezielt für <span class="highlight">kleinere Projekte</span> zu buchen.</p>
|
||||
<p class="margin-top-small">Hinter diesen Angeboten gibt es <span class="highlight">keinerlei Abos oder versteckte Kosten</span>.
|
||||
Aus Transparenzgründen sei aber gesagt, dass sich <span class="highlight">alle Preise zzgl. 19 % Umsatzsteuer</span> verstehen.</p>
|
||||
|
||||
<FreeInfo />
|
||||
|
||||
<div class="Pricing margin-top">
|
||||
<article v-for="(service, index) in oneOff" :class="{ 'z-3-all': index === 1, 'z-2-all': index !== 1}">
|
||||
<header>
|
||||
<strong>{{service.title}}</strong>
|
||||
<p class="claim">{{service.smallClaim}}</p>
|
||||
<div class="price">
|
||||
<span v-if="service.price.pre">{{service.price.pre}}</span>
|
||||
<span>{{typeof service.price.value === 'number' ? intl.format(service.price.value) : service.price.value}}</span>
|
||||
<span v-if="service.price.post" class="post">{{service.price.post}}</span>
|
||||
</div>
|
||||
<div aria-hidden="true" class="bg-icon">
|
||||
<Icon :name="service.icon" size="1.5em" mode="svg" />
|
||||
</div>
|
||||
</header>
|
||||
<main>
|
||||
<div class="list-container">
|
||||
<ul>
|
||||
<li v-for="point in service.list">
|
||||
<Icon class="icon yes" name="ph:caret-circle-double-right-duotone" size="1.5em" mode="svg" />
|
||||
<span class="label">{{ point }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</main>
|
||||
<footer>
|
||||
<div class="button-wrapper z-2">
|
||||
<Button :href="service.link" aria-label="Zur externen Seite Terminbuchung" :design="index === 1 ? 'default' : 'white'">
|
||||
{{ service.button }}
|
||||
</Button>
|
||||
</div>
|
||||
</footer>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<div class="flex-col gap-sm margin-top">
|
||||
<Spoiler v-for="entry in faq" v-bind="entry" class="z-2" />
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
type Service = {
|
||||
title: string
|
||||
smallClaim: string
|
||||
price: {
|
||||
value: number | string
|
||||
pre?: string
|
||||
post?: string
|
||||
}
|
||||
icon: string
|
||||
button: string
|
||||
link: string
|
||||
list: string[]
|
||||
}
|
||||
|
||||
const intl = new Intl.NumberFormat(
|
||||
'de-DE',
|
||||
{
|
||||
style: 'currency',
|
||||
currency: 'EUR',
|
||||
minimumFractionDigits: 0,
|
||||
maximumFractionDigits: 0,
|
||||
})
|
||||
|
||||
const oneOff : Service[] = [
|
||||
{
|
||||
title: 'Quick Check',
|
||||
price: {
|
||||
value: 149,
|
||||
},
|
||||
smallClaim: 'A11y, bugs, schlechter Code.',
|
||||
icon: 'ph:magnifying-glass-thin',
|
||||
button: 'Jetzt untersuchen',
|
||||
link: 'https://tidycal.com/webfussel/quick-check',
|
||||
list: [
|
||||
'Untersuchung des Quellcodes',
|
||||
'Untersuchung der Performance',
|
||||
'Tipps zu CSS und Best Practices',
|
||||
'Behebung unkompliziert nachbuchen',
|
||||
'Für selbst gebaute Seiten',
|
||||
],
|
||||
}, {
|
||||
title: 'Projekt',
|
||||
price: {
|
||||
pre: 'ab',
|
||||
value: 999,
|
||||
},
|
||||
smallClaim: 'Deine Vision.',
|
||||
icon: 'ph:trend-up-thin',
|
||||
button: 'Jetzt durchstarten',
|
||||
link: 'https://tidycal.com/webfussel/project-booking',
|
||||
list: [
|
||||
'Anforderungsanalyse',
|
||||
'Kontinuierliche Projekt-Updates',
|
||||
'Fixe Kosten und Feature-Sets',
|
||||
'Nur 50 % Projektpreis als Anzahlung',
|
||||
'Gestaffelte Abrechnung nach Milestones',
|
||||
],
|
||||
}, {
|
||||
title: 'Schulung',
|
||||
price: {
|
||||
pre: 'ab',
|
||||
value: 1499,
|
||||
post: '/ Tag / Person',
|
||||
},
|
||||
smallClaim: 'Wenn man\'s selber können muss.',
|
||||
icon: 'ph:graduation-cap-thin',
|
||||
button: 'Frag nach!',
|
||||
link: 'https://tidycal.com/webfussel/teaching',
|
||||
list: [
|
||||
'Schulungsinhalte klären',
|
||||
'Team-Workshops',
|
||||
'Azubi-Betreuung',
|
||||
'1:1 Mentoring',
|
||||
'Abrechnung pro Person und Tag',
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
const faq = [
|
||||
{
|
||||
header: 'Warum machst du keine Stundensätze?',
|
||||
content: [
|
||||
'Ich finde Stundensätze haben für beide Seiten nur Nachteile:',
|
||||
'Wenn ich schnell und gut arbeite, dann bekomme ich weniger Geld. Hab ich mal einen Knoten im Gehirn und brauche sehr lange, muss der Kunde mehr zahlen.',
|
||||
'Klar kann man sagen, dass sich das irgendwann ausgleichen könnte - aber so weit will ich es garnicht erst kommen lassen.'
|
||||
]
|
||||
},
|
||||
{
|
||||
header: 'Welche Themen bietest du für deine Schulungen an?',
|
||||
content: [
|
||||
'Sprachen: JavaScript, TypeScript, HTML, CSS',
|
||||
'Frameworks: Vue, Nuxt',
|
||||
]
|
||||
},
|
||||
{
|
||||
header: 'Wo finden die Schulungen statt?',
|
||||
content: [
|
||||
'Die Schulungen finden online statt. Normalerweise nutze ich dafür Google Meet, aber wenn du oder deine Firma eine andere Plattform wünschen und bereitstellen bin ich natürlich flexibel.',
|
||||
'Wenn sich deine Firma in der Nähe meines Wohnortes befindet - und damit meine ich "In einer Stunde mit der Straßenbahn zu erreichen", dann kann alles natürlich auch vor Ort stattfinden.',
|
||||
]
|
||||
},
|
||||
{
|
||||
header: 'Ich hab ein cooles Projekt! Aber kein Geld...',
|
||||
content: [
|
||||
'Tja.',
|
||||
'Ne, awas. Meld dich einfach trotzdem über meine E-Mail-Adresse und vielleicht finden wir eine Lösung.'
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
</script>
|
93
app/components/Section/Contact.vue
Normal file
93
app/components/Section/Contact.vue
Normal file
|
@ -0,0 +1,93 @@
|
|||
<template>
|
||||
<section id="contact" class="Contact content full gap-default">
|
||||
<h1>Kontakt <span class="highlight">&</span> Social Media</h1>
|
||||
<h2>Kannst ruhig in meine <span class="highlight">DMs sliden</span>. Oder in's Postfach.</h2>
|
||||
|
||||
<article class="z-2 card flex-col gap-sm margin-top">
|
||||
<h3>Du willst einfach nur 'ne Mail schreiben?</h3>
|
||||
<p>Ja gut, passt. Meld dich unter
|
||||
<ClientOnly><a class="mail" href="mailto:anfragen@webfussel.de">anfragen@webfussel.de<Icon name="ph:envelope-duotone" aria-hidden="true" alt="mail icon" size="1.2em" mode="svg" /></a></ClientOnly>
|
||||
</p>
|
||||
</article>
|
||||
|
||||
<article class="z-2 card flex-col gap-sm margin-top">
|
||||
<h3>Ich auf Social Media</h3>
|
||||
<p>Falls du irgendwo einen anderen Social Media Account von mir findest, der nicht hier aufgelistet ist, aber aktiv postet, dann ist dieser höchstwahrscheinlich <span class="highlight">Fake</span>.
|
||||
<br />Meld' dich gerne bei mir, wenn du so einen findest.
|
||||
</p>
|
||||
<ul class="social-media">
|
||||
<li v-for="({icon, name, ...rest}) in socials" :key="rest.href">
|
||||
<a v-bind="rest" target="_blank">
|
||||
<Icon :name="icon" :alt="rest['aria-label']" size="1.5em" mode="svg" />
|
||||
<span>{{ name }}</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</article>
|
||||
|
||||
<article class="z-2 card flex-col gap-sm margin-top">
|
||||
<h3>Wenn ich mal ausgebucht bin</h3>
|
||||
<p>Dann buch doch einfach einen der tollen Menschen aus meinem Netzwerk!</p>
|
||||
|
||||
<div class="network margin-top">
|
||||
<Person ref="persons" v-for="person in network" v-bind="person" />
|
||||
</div>
|
||||
</article>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { socials } from '~/utils/socials'
|
||||
|
||||
const network = [
|
||||
{
|
||||
name: 'Robert Janus',
|
||||
img: 'robert',
|
||||
tags: ['Digitalberatung', 'Webentwicklung', 'eCommerce'],
|
||||
flavour: 'Website, SEO und Conversions. Auf einen Klick.',
|
||||
link: 'https://robertjanus.de/webertoire',
|
||||
},
|
||||
{
|
||||
name: 'Matthias Lehmann',
|
||||
img: 'matthias',
|
||||
tags: ['Onlineportale für Patienten', 'Kunden', 'Mitarbeiter'],
|
||||
flavour: 'Software die macht, was DU willst!',
|
||||
link: 'https://mind-deploy.de',
|
||||
},
|
||||
{
|
||||
name: 'Maximilian Schluer',
|
||||
img: 'max',
|
||||
tags: ['iOS Development', 'Software-QA'],
|
||||
flavour: 'Kann dein iOS-Team unterstützen oder dein Software-Qualitätsproblem lösen – egal welches.',
|
||||
link: 'https://max-schluer.de',
|
||||
},
|
||||
{
|
||||
name: 'Judith Böhlert',
|
||||
img: 'judith',
|
||||
tags: ['Full-stack', 'Frontend'],
|
||||
flavour: 'MVPs und Prototypen - schnell, schick und ohne Drama.',
|
||||
link: 'https://judithboehlert.com',
|
||||
},
|
||||
{
|
||||
name: 'Kevin Damiani',
|
||||
img: 'kevin',
|
||||
tags: ['Webentwicklung', 'Frontend'],
|
||||
flavour: 'Erfahrener Frontend-Entwickler mit Fokus auf Performance, Barrierefreiheit und moderne Technologien.',
|
||||
link: 'https://kevin-damiani.de',
|
||||
},
|
||||
{
|
||||
name: 'Roman Nenstiel',
|
||||
img: 'roman',
|
||||
tags: ['E-Commerce', 'Shopify', 'CRO', 'UX'],
|
||||
flavour: 'Ich betreue deinen Shop, als wäre es mein eigener.',
|
||||
link: 'https://shrimp-commerce.com',
|
||||
},
|
||||
{
|
||||
name: 'Orell Garten',
|
||||
img: 'orell',
|
||||
tags: ['Backend', 'Data'],
|
||||
flavour: 'Automatische Auswertung von Betriebsdaten - von Requirements bis Production.',
|
||||
link: 'https://orellgarten.com',
|
||||
},
|
||||
].sort((a, b) => a.name.localeCompare(b.name))
|
||||
</script>
|
215
app/components/Section/Customers.vue
Executable file
215
app/components/Section/Customers.vue
Executable file
|
@ -0,0 +1,215 @@
|
|||
<template>
|
||||
<section id="customers" class="Customers content">
|
||||
<h1>Kunden <span class="highlight">&</span> Projekte.</h1>
|
||||
<h2>Meine bisherigen Geschäftpartner</h2>
|
||||
<div class="customer-list margin-top gap-default">
|
||||
<a v-for="customer in customers" :href="customer.link" target="_blank" rel="noopener noreferrer">
|
||||
<img
|
||||
loading="lazy"
|
||||
height="50"
|
||||
:width="customer.logo.width"
|
||||
:alt="customer.name"
|
||||
:src="`/img/customers/${customer.logo.src}.svg`"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
<h2 class="margin-top-big">Projektauswahl</h2>
|
||||
<div class="projects-list margin-top">
|
||||
<article v-for="pr in projects">
|
||||
<div class="bg">
|
||||
<img height="350" width="400" loading="lazy" :alt="pr.title" :src="pr.image" aria-hidden="true"/>
|
||||
</div>
|
||||
<div>
|
||||
<main>
|
||||
<small class="customer">{{ pr.customer }}</small>
|
||||
<h3 class="title">{{ pr.title }}</h3>
|
||||
<ul>
|
||||
<li v-for="skill in pr.technologies">
|
||||
<Technology v-bind="skill" link="" />
|
||||
</li>
|
||||
</ul>
|
||||
<p v-for="d in pr.desc">{{ d }}</p>
|
||||
<a v-if="pr.link" :href="pr.link" target="_blank">Zur {{pr.type ?? 'Seite'}}</a>
|
||||
</main>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { css, html, js, njs, nuxt, pcss, scss, ts, tw, vue } from '~/utils/skills'
|
||||
|
||||
const customers = [
|
||||
{
|
||||
name: 'Bounce Commerce',
|
||||
link: 'https://bounce-commerce.de',
|
||||
logo: {
|
||||
src: 'bounce',
|
||||
width: 150,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'GMX',
|
||||
link: 'https://gmx.net',
|
||||
logo: {
|
||||
src: 'gmx',
|
||||
width: 148,
|
||||
white: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'WEB.DE',
|
||||
link: 'https://web.de',
|
||||
logo: {
|
||||
src: 'webde',
|
||||
width: 50,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: '1&1',
|
||||
link: 'https://1und1.de',
|
||||
logo: {
|
||||
src: '1u1',
|
||||
width: 50,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Körrie',
|
||||
link: 'https://körrie.de',
|
||||
logo: {
|
||||
src: 'koerrie',
|
||||
width: 50,
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Pembe',
|
||||
link: 'https://pembe.io',
|
||||
logo: {
|
||||
src: 'pembe',
|
||||
width: 48,
|
||||
white: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'SAE Institute Germany',
|
||||
link: 'https://www.sae.edu/deu/en/sae-home/',
|
||||
logo: {
|
||||
src: 'sae',
|
||||
width: 77,
|
||||
white: true,
|
||||
},
|
||||
}
|
||||
]
|
||||
|
||||
const projects = [
|
||||
// {
|
||||
// title: 'Kauft Körrie! App',
|
||||
// customer: 'KVK Berlin',
|
||||
// image: '/img/projects/koerrie_app.webp',
|
||||
// desc: [
|
||||
// 'Entwicklung einer Android Info-App für die Gewürzmischungen "Körrie" und passendem Zubehör.',
|
||||
// 'Zusätzlich die Übertragung des Körrie-O-Mat von der Landingpage in die App mit Ergebnisverlauf.',
|
||||
// ],
|
||||
// technologies: [flutter, dart, android],
|
||||
// link: 'https://play.google.com/store/apps/details?id=com.koerrieomat&hl=de',
|
||||
// type: 'App',
|
||||
// },
|
||||
{
|
||||
title: 'ProPapier',
|
||||
customer: 'webfussel',
|
||||
image: '/img/projects/propapier.webp',
|
||||
desc: [
|
||||
'Klopapier-Preise einfach vergleichen.',
|
||||
'Zahlst du zu viel für\'s Papier?',
|
||||
],
|
||||
technologies: [nuxt, ts, pcss],
|
||||
link: 'https://pro-papier.de',
|
||||
},
|
||||
{
|
||||
title: 'Unterricht',
|
||||
customer: 'SAE Institute Germany',
|
||||
image: '/img/projects/education.webp',
|
||||
desc: [
|
||||
'Vorbereitung und Durchführung von Unterricht in JavaScript und TypeScript.',
|
||||
],
|
||||
technologies: [js, ts]
|
||||
},
|
||||
{
|
||||
title: 'Headless CMS & Cache',
|
||||
customer: 'DEKRA',
|
||||
image: '/img/projects/dekra.webp',
|
||||
desc: [
|
||||
'Anbindung an ein Headless CMS und Entwicklung der dazugehörigen Komponentenbibliothek unter Einsatz von Tailwind, sowie serverseitiges Caching.',
|
||||
],
|
||||
technologies: [ts, nuxt, tw, njs]
|
||||
},
|
||||
{
|
||||
title: 'Bounce Script',
|
||||
customer: 'Bounce Commerce',
|
||||
link: 'https://bounce-commerce.de',
|
||||
image: '/img/projects/bounce.webp',
|
||||
desc: [
|
||||
'Script zum Einbinden in Web Shops für Bounce Management.',
|
||||
'Pures JavaScript, so klein gehalten wie möglich zur einfach Integration.',
|
||||
],
|
||||
technologies: [js]
|
||||
},
|
||||
{
|
||||
title: 'WEB.DE / GMX',
|
||||
customer: '1&1 Mail & Media',
|
||||
link: 'https://web.de',
|
||||
image: '/img/projects/webde.webp',
|
||||
desc: [
|
||||
'Neubau der Seiten web.de und GMX mit einem komponentenbasierten Ansatz unter Verwendung von VueJS.',
|
||||
'Optimiert für moderne Browser, während Internet Explorer in einer Extraversion angefertigt wurde.',
|
||||
],
|
||||
technologies: [js, vue, scss]
|
||||
},
|
||||
{
|
||||
title: 'Körrie! Landingpage',
|
||||
customer: 'KVK Berlin',
|
||||
link: 'https://körrie.de',
|
||||
image: '/img/projects/krrie.webp',
|
||||
desc: [
|
||||
'Neubau der Landingpage für "Kauft Körrie!". Die Prämisse war: Kein Schnickschnack.',
|
||||
'Deshalb aufgebaut mit simplem Js, HTML und CSS',
|
||||
],
|
||||
technologies: [html, css, js],
|
||||
},
|
||||
{
|
||||
title: 'UI Tools',
|
||||
customer: 'webfussel',
|
||||
link: 'https://uitools.webfussel.de',
|
||||
image: '/img/projects/uitools.webp',
|
||||
desc: [
|
||||
'Eine kleine Sammlung an Tools für die Erstellung von UIs.',
|
||||
'Farbpalette, Kontraste und CSS Variablen.',
|
||||
'Ist in aktiver Entwicklung.',
|
||||
],
|
||||
technologies: [nuxt, ts, pcss]
|
||||
},
|
||||
// {
|
||||
// customer: 'webfussel',
|
||||
// title: 'Shnaik - Teh Gaem',
|
||||
// link: 'https://shnaik.webfussel.de',
|
||||
// image: '/img/projects/shnaik.webp',
|
||||
// desc: [
|
||||
// 'Nachbau des bekannten Spiels "Snake" für die damaligen Nokia Handys.',
|
||||
// 'Meine erste Erfahrung mit Gaming Libraries und wurde eher als Experiment und Zeitvertreib angefertigt.',
|
||||
// ],
|
||||
// technologies: [ts, css]
|
||||
// },
|
||||
// {
|
||||
// title: 'PixelPalette',
|
||||
// customer: 'webfussel',
|
||||
// link: 'https://pixelpalette.webfussel.de',
|
||||
// image: '/img/projects/pp.webp',
|
||||
// desc: [
|
||||
// 'Ich hatte einige Tage eine Idee, wie man Grafiken mit 4 Farben - angelehnt den Gameboy - komprimieren und im Speicher unterbringen kann.',
|
||||
// 'Prototypisch zum Spaß erstellt.',
|
||||
// ],
|
||||
// technologies: [js, html, css]
|
||||
// },
|
||||
]
|
||||
</script>
|
185
app/components/Section/Flatrate.vue
Executable file
185
app/components/Section/Flatrate.vue
Executable file
|
@ -0,0 +1,185 @@
|
|||
<template>
|
||||
<section id="services" class="Services content">
|
||||
<h1>Prepaid Flatrates</h1>
|
||||
<h2>Genieße fusselige <span class="highlight">Qualität</span> ohne groß herumzurechnen.</h2>
|
||||
|
||||
<p class="margin-top">Bei dir fällt ständig was an oder du hast ein langlaufendes Projekt, bei dem du immer wieder mal Unterstützung brauchst? Kein Ding.
|
||||
Hier gibt's die <span class="highlight">Entwickler-Flat</span> für planbare Kosten und On-Demand-Entwicklung.</p>
|
||||
<p class="margin-top-small">Aus Transparenzgründen sei gesagt, dass sich <span class="highlight">alle Preise zzgl. 19 % Umsatzsteuer</span> verstehen.</p>
|
||||
|
||||
<FreeInfo />
|
||||
|
||||
<div class="Pricing margin-top">
|
||||
<article v-for="(service, index) in flatrate" :class=" { 'z-2' : index === 1, 'z-1' : index !== 1 }">
|
||||
<header>
|
||||
<span v-if="service.best" class="chip dark z-2"><Icon name="ph:fire-duotone" mode="svg"/> Beschd</span>
|
||||
<strong class="margin-top-small">{{service.title}}</strong>
|
||||
<p class="claim">{{service.smallClaim}}</p>
|
||||
<p class="price">{{intl.format(service.price)}}<span class="post">/ Monat</span></p>
|
||||
<div aria-hidden="true" class="bg-icon">
|
||||
<Icon :name="service.icon" size="1.5em" mode="svg" />
|
||||
</div>
|
||||
</header>
|
||||
<main>
|
||||
<ul>
|
||||
<li>
|
||||
<Icon class="icon yes" name="ph:check-circle-duotone" mode="svg" />
|
||||
<span class="value">{{ service.hours }} Stunden pro Woche zugesichert</span>
|
||||
</li>
|
||||
<li v-for="(check, index) in service.checks">
|
||||
<Icon class="icon" :class="{ 'yes' : check, 'no' : !check }" :name="check ? 'ph:check-circle-duotone' : 'ph:x-circle-duotone'" mode="svg" />
|
||||
<span class="value">{{ points[index] }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</main>
|
||||
<footer>
|
||||
<div class="button-wrapper z-2">
|
||||
<Button :href="service.link" aria-label="Zur externen Seite Terminbuchung" :design="index === 1 ? 'default' : 'white'">
|
||||
{{ service.button }}
|
||||
</Button>
|
||||
</div>
|
||||
<p class="conmin margin-top-small">Die Mindestvertragslaufzeit beträgt {{ service.contractMin }} Monate.</p>
|
||||
</footer>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<div class="flex-col gap-sm margin-top">
|
||||
<Spoiler v-for="entry in faq" v-bind="entry" class="z-2" />
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
type Service = {
|
||||
title: string
|
||||
smallClaim: string
|
||||
icon: string
|
||||
button: string
|
||||
link: string
|
||||
best: boolean
|
||||
price: number
|
||||
hours: number
|
||||
contractMin: number
|
||||
checks: boolean[]
|
||||
}
|
||||
|
||||
const intl = new Intl.NumberFormat(
|
||||
'de-DE',
|
||||
{
|
||||
style: 'currency',
|
||||
currency: 'EUR',
|
||||
minimumFractionDigits: 0,
|
||||
maximumFractionDigits: 0,
|
||||
})
|
||||
|
||||
const points : string[] = [
|
||||
'Kontakt per E-Mail',
|
||||
'Kontakt per Instant Messaging',
|
||||
'Kontakt per Video Call',
|
||||
'Weiterführende Performance Optimierung',
|
||||
'Framework Migration',
|
||||
]
|
||||
|
||||
const flatrate : Service[] =
|
||||
[
|
||||
{
|
||||
title: 'Casual',
|
||||
smallClaim: 'Für kleine Aufgaben nebenbei.',
|
||||
icon: 'ph:baby-carriage-thin',
|
||||
button: 'Jetzt klar machen',
|
||||
link: 'https://tidycal.com/webfussel/flatrate-casual',
|
||||
best: false,
|
||||
price: 2950,
|
||||
hours: 5,
|
||||
contractMin: 3,
|
||||
checks: [
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Gold-Fussel',
|
||||
smallClaim: 'Wenn\'s mal wieder zu viel wird.',
|
||||
icon: 'ph:coins-thin',
|
||||
button: 'Jetzt Gold schürfen',
|
||||
link: 'https://tidycal.com/webfussel/flatrate-gold-fussel',
|
||||
best: true,
|
||||
price: 5555,
|
||||
hours: 10,
|
||||
contractMin: 3,
|
||||
checks: [
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Big Chonker',
|
||||
smallClaim: 'Für die richtig großen Sachen.',
|
||||
icon: 'ph:skull-thin',
|
||||
button: 'Jetzt Fett trimmen',
|
||||
link: 'https://tidycal.com/webfussel/flatrate-big-chonker',
|
||||
best: false,
|
||||
price: 8550,
|
||||
hours: 15,
|
||||
contractMin: 6,
|
||||
checks: [
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
],
|
||||
}
|
||||
]
|
||||
|
||||
const faq = [
|
||||
{
|
||||
header: 'Was ist eine Entwickler-Flatrate?',
|
||||
content: [
|
||||
'Die Entwickler-Flatrate ist ein Angebot, bei dem du für eine bestimmte Zeit eine bestimmte Menge an Leistungen erhältst.',
|
||||
'Sie wird auch oft als sogenannter "Retainer" bezeichnet und ist eine günstige Art immer wieder anfallende Aufgaben auszulagern.'
|
||||
]
|
||||
},
|
||||
{
|
||||
header: 'Wie läuft die Zusammenarbeit ab?',
|
||||
content: [
|
||||
'Nach einer ersten Analyse deines Projekts legen wir gemeinsam den Umfang und die monatlichen Aufgaben fest. Du kannst mich je nach Paket auf den vereinbarten Wegen jederzeit kontaktieren.',
|
||||
'Die Bearbeitung erfolgt innerhalb der vereinbarten Fristen.',
|
||||
]
|
||||
},
|
||||
{
|
||||
header: 'Wie wird abgerechnet?',
|
||||
content: [
|
||||
'Du zahlst monatlich einen festen Betrag im Prepaid-Format unabhängig davon wie viele Aufgaben tatsächlich anfallen.',
|
||||
'Dadurch kannst du dein Budget besser einplanen und Aufgaben verteilen.'
|
||||
]
|
||||
},
|
||||
{
|
||||
header: 'Kann ich den Retainer jederzeit kündigen?',
|
||||
content: [
|
||||
'Ja klar. Du musst dabei nur die Mindestvertragslaufzeit beachten - die bezahlte Leistung steht dir weiterhin zu.',
|
||||
'Falls wir merken, dass wir für eine Zusammenarbeit mehr als ungeeignet sind, bekommst du dein Geld anteilig der verbleibenden Zeit zurück und verlierst den Anspruch auf Leistungen ab diesem Zeitpunkt.'
|
||||
]
|
||||
},
|
||||
{
|
||||
header: 'Was passiert, wenn ich dich mal weniger brauche?',
|
||||
content: [
|
||||
'Der monatliche feste Betrag bleibt bestehen. Ähnlich wie bei einer Versicherung bezahlst du hier für den Fall, dass du mich brauchst und erhältst dann entsprechend die vereinbarten Leistungen.',
|
||||
'Wenn du merkst, dass du mich nicht mehr brauchst, dann kannst du den Vertrag jederzeit kündigen und die Leistungen auslaufen lassen.'
|
||||
]
|
||||
},
|
||||
{
|
||||
header: 'Für wen lohnt sich sowas überhaupt?',
|
||||
content: [
|
||||
'Vor allem Unternehmen, bei denen immer wieder Aufgaben anfallen für die sich aber kein komplettes Projekt oder gar eine feste Stelle in der Firma lohnt, profitieren von dieser Art von Leistung.',
|
||||
'Sie ist ideal für eine langfristige und zuverlässige Zusammenarbeit.'
|
||||
]
|
||||
}
|
||||
]
|
||||
</script>
|
59
app/components/Section/Footer.vue
Executable file
59
app/components/Section/Footer.vue
Executable file
|
@ -0,0 +1,59 @@
|
|||
<template>
|
||||
<footer class="Footer flex-col gap-default">
|
||||
<ul class="sitemap gap-default">
|
||||
<li v-for="{ label, ...rest} in nav" :key="label">
|
||||
<NuxtLink v-bind="rest">{{label}}</NuxtLink>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="sitemap gap-default">
|
||||
<li v-for="({icon, ...rest}) in socials" :key="rest.href">
|
||||
<a v-bind="rest" target="_blank">
|
||||
<Icon :name="icon" :alt="rest['aria-label']" size="1.5em" mode="svg" />
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="sitemap gap-default">
|
||||
<li class="tip-container">
|
||||
<Icon name="wf:cookie-slash" size="1.5rem" mode="svg" />
|
||||
<span class="tip">Ohne Cookies</span>
|
||||
</li>
|
||||
<li class="tip-container">
|
||||
<Icon name="wf:fingerprint-slash" size="1.5rem" mode="svg" />
|
||||
<span class="tip">Ohne Tracker</span>
|
||||
</li>
|
||||
</ul>
|
||||
<p class="inline-flex-row"><Icon name="ph:copyright-duotone" mode="svg"/> 2024 by <a href="https://webfussel.de">webfussel</a></p>
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { socials } from '~/utils/socials'
|
||||
|
||||
const nav = [
|
||||
{
|
||||
to: `/`,
|
||||
label: 'Home',
|
||||
'aria-label': 'Link dieser Seite: Home'
|
||||
}, {
|
||||
to: `/booking`,
|
||||
label: 'Projektbuchung',
|
||||
'aria-label': 'Link dieser Seite: Projektbuchung'
|
||||
}, {
|
||||
to: `/flatrate`,
|
||||
label: 'Flatrate',
|
||||
'aria-label': 'Link dieser Seite: Flatrate'
|
||||
}, {
|
||||
to: `/references`,
|
||||
label: 'Referenzen',
|
||||
'aria-label': 'Link dieser Seite: Referenzen'
|
||||
}, {
|
||||
to: `/contact`,
|
||||
label: 'Kontakt',
|
||||
'aria-label': 'Link dieser Seite: Kontakt'
|
||||
}, {
|
||||
to: '/imp',
|
||||
label: 'Impressum',
|
||||
'aria-label': 'Link dieser Seite: Impressum'
|
||||
}
|
||||
]
|
||||
</script>
|
57
app/components/Section/Header.vue
Executable file
57
app/components/Section/Header.vue
Executable file
|
@ -0,0 +1,57 @@
|
|||
<template>
|
||||
<div ref="stickyWatch" />
|
||||
<header ref="header" class="Header">
|
||||
<div ref="headerWrapper" class="wrapper z-0">
|
||||
<NuxtLink to="/" aria-label="Link zur Startseite">
|
||||
<strong>
|
||||
<Icon class="logo" name="wf:logo" mode="svg" size="40px" />
|
||||
webfussel
|
||||
</strong>
|
||||
</NuxtLink>
|
||||
<button class="burger" :aria-label="burgerLabel" @click="() => nav.toggleNavigation()">
|
||||
<Icon name="ph:waves" mode="svg" size="2em" />
|
||||
</button>
|
||||
<nav>
|
||||
<ul class="main-nav">
|
||||
<li v-for="({label, to, aria, icon}) in navigation" :key="label">
|
||||
<NuxtLink :to="to" :aria-label="aria" active-class="active" class="inline-flex-row big-gap">
|
||||
<Icon :name="icon" mode="svg" />
|
||||
{{ label }}
|
||||
</NuxtLink>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { navigation } from '~/utils/navigation'
|
||||
|
||||
let observer: IntersectionObserver
|
||||
const header = useTemplateRef('header')
|
||||
const headerWrapper = useTemplateRef('headerWrapper')
|
||||
const stickyWatch = useTemplateRef('stickyWatch')
|
||||
|
||||
const nav = useNavigation()
|
||||
const burgerOpenLabel = 'Burgermenü öffnen'
|
||||
const burgerCloseLabel = 'Burgermenü schließen'
|
||||
const burgerLabel = computed(() => nav.isNavigationVisible.value ? burgerCloseLabel : burgerOpenLabel)
|
||||
|
||||
onMounted(() => {
|
||||
observer = new IntersectionObserver(([entry]) => {
|
||||
if (!entry) return
|
||||
const { isIntersecting } = entry
|
||||
header.value?.classList.toggle('sticks', !isIntersecting)
|
||||
headerWrapper.value?.classList.toggle('z-4', !isIntersecting)
|
||||
headerWrapper.value?.classList.toggle('z-0', isIntersecting)
|
||||
}, {
|
||||
rootMargin: '3% 0px 0px 0px'
|
||||
})
|
||||
observer.observe(stickyWatch.value!)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
observer.disconnect()
|
||||
})
|
||||
</script>
|
37
app/components/Section/Intro.vue
Executable file
37
app/components/Section/Intro.vue
Executable file
|
@ -0,0 +1,37 @@
|
|||
<template>
|
||||
<section id="intro" class="Intro content full gap-default">
|
||||
<div class="intro-text flex-col gap-default">
|
||||
<h1 class="flex-col">
|
||||
<span class="greeting inline-flex-row">Moin. <Icon name="ph:hand-peace-duotone" mode="svg"/></span>
|
||||
<span class="my-name-wrapper">Ich bin <span class="highlight">Fiona</span>.</span>
|
||||
</h1>
|
||||
<h2>
|
||||
Component <span class="highlight">&</span> API Entwicklerin
|
||||
</h2>
|
||||
<p class="fulltext">
|
||||
Ich unterstütze Unternehmen dabei, ihre Daten von verschiedenen Endpunkten sauber aufzubereiten
|
||||
und anschließend in einer Webapplication schön zu verpacken.
|
||||
</p>
|
||||
<p class="fulltext">
|
||||
Mit über 20 Jahren Erfahrung in der Webentwicklung habe ich
|
||||
inzwischen so ziemlich jeden Stuff miterlebt.
|
||||
</p>
|
||||
<p class="fulltext">
|
||||
Egal, ob Komponenten, Schnittstellen oder Anbindung an Headless CMS.
|
||||
Ich biete dir genau das, was du brauchst, um eine individuelle WebApp in Fahrt zu bringen, deren Inhalte einfach zu verändern sind.
|
||||
</p>
|
||||
<Button class="cta" href="#skills">
|
||||
<Icon name="ph:lightbulb-duotone" size="1.5em" mode="svg" />
|
||||
Fussel erklärt's dir
|
||||
</Button>
|
||||
</div>
|
||||
<div class="intro-img">
|
||||
<picture>
|
||||
<source width="750" height="866" media="(min-width: 431px)" srcset="/img/profile_big.webp" />
|
||||
<img width="430" height="866" src="/img/profile_small.webp" alt="Bild von Fiona Urban" />
|
||||
</picture>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
</script>
|
62
app/components/Section/Skills.vue
Executable file
62
app/components/Section/Skills.vue
Executable file
|
@ -0,0 +1,62 @@
|
|||
<template>
|
||||
<section id="skills" class="Skills content">
|
||||
<h2>Meine Expertise.</h2>
|
||||
<h3>Dies sind meine <span class="highlight">Spezialgebiete</span> - aber ich bin flexibel!</h3>
|
||||
<div class="skill-list margin-top gap-default">
|
||||
<article class="z-2 card flex-col gap-sm" v-for="skill in skills">
|
||||
<h3>{{skill.title}}</h3>
|
||||
<p v-for="(t, i) in skill.text" :class="[i === skill.text.length - 1 && 'bold']">{{t}}</p>
|
||||
</article>
|
||||
</div>
|
||||
<article class="tech-list z-2 card flex-col gap-default margin-top">
|
||||
<h3>Technologien</h3>
|
||||
<p>Neben den klassischen Webentwicklungsstandards JavaScript, HTML und CSS biete ich außerdem folgende Technologien.</p>
|
||||
<ul class="gap-default">
|
||||
<li v-for="tech in technologies">
|
||||
<Technology v-bind="tech" size="l"/>
|
||||
</li>
|
||||
</ul>
|
||||
</article>
|
||||
<div class="bottom flex-col margin-top gap-default">
|
||||
<h3>Manche von euch haben hier sicher kein Wort verstanden.</h3>
|
||||
<Button href="#skills_easy" class="cta">
|
||||
<span class="animate-up-down">
|
||||
<Icon name="ph:caret-double-down-duotone" size="1.5em" mode="svg" />
|
||||
</span>
|
||||
Für normale Menschen
|
||||
</Button>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
import { gl, njs, nuxt, pcss, react, rust, ts, vitest, vue, webstorm } from '~/utils/skills'
|
||||
|
||||
const technologies = [nuxt, ts, pcss, vue, react, njs, vitest, gl, webstorm, rust]
|
||||
|
||||
const skills = [
|
||||
{
|
||||
title: 'Komponenten',
|
||||
text: [
|
||||
'Komponenten sind die Teile in deiner Applikation, die alles ansehnlich machen.',
|
||||
'Mit sauber implementierten, responsiven Bausteinen kannst du deine Seite gut Strukturieren, Daten sauber darstellen und den User einspannen.',
|
||||
'Vom kleinen Button bis hin zur Umfangreichen Tabelle bau ich dir (fast) alles.'
|
||||
],
|
||||
}, {
|
||||
title: 'APIs',
|
||||
text: [
|
||||
'Du hast Daten in einer Datenbank liegen, aber keine Ahnung, wie du da gescheit rankommen sollst?',
|
||||
'Liegen deine Daten eventuell sogar verstreut an mehreren Orten, über Datenbanken, Dateien und anderen Storagemöglichkeiten verteilt?',
|
||||
'Kein Ding. Ich bau dir eine Schnittstelle, die alles easy zusammenträgt.'
|
||||
],
|
||||
}, {
|
||||
title: 'Headless CMS',
|
||||
text: [
|
||||
'Wenn man ein Headless CMS anbinden will, dann verknüpft das Komponenten und APIs.',
|
||||
'Für eine saubere und dynamische Einbindung reicht die Library, die euch vom Hersteller zur Verfügung gestellt wird, oft nicht aus.',
|
||||
'Übersichtliche Projektstruktur und saubere Auflösung der Daten - mit Fusselgarantie.'
|
||||
],
|
||||
}
|
||||
]
|
||||
</script>
|
277
app/components/Section/SkillsEasy.vue
Executable file
277
app/components/Section/SkillsEasy.vue
Executable file
|
@ -0,0 +1,277 @@
|
|||
<template>
|
||||
<section id="skills_easy" class="Skills content">
|
||||
<h2>Jetzt mal ganz konkret.</h2>
|
||||
<h3>In diesem Abschnitt ganz <span class="highlight">ohne Technik-Blabla</span>.</h3>
|
||||
<div class="skill-container flex-col margin-top gap-default">
|
||||
<article class="skill-card z-2 card gap-default" v-for="(skill, skillIndex) in skills" :class="[skillIndex % 2 && 'reverse']">
|
||||
<div class="image-container">
|
||||
<img
|
||||
loading="lazy"
|
||||
width="550"
|
||||
height="350"
|
||||
:srcset="[skill.img('1x', true), skill.img('2x', true), skill.img('3x', true)].join(', ')"
|
||||
:src="skill.img('1x', false)"
|
||||
aria-hidden="true"
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
<div class="text-container flex-col gap-default">
|
||||
<h3>{{skill.title}}</h3>
|
||||
<main class="flex-col gap-sm">
|
||||
<RichText :elements="skill.text" />
|
||||
</main>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
<div class="bottom flex-col margin-top gap-default">
|
||||
<h3>Verwirkliche jetzt dein Webprojekt.</h3>
|
||||
<Button href="/booking" class="cta">
|
||||
<Icon name="ph:chat-circle-text-duotone" size="1.5em" mode="svg" />
|
||||
Lass mal reden
|
||||
</Button>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { RichText } from '@/components/RichText/Types'
|
||||
|
||||
type Skill = {
|
||||
img: Function
|
||||
title: string
|
||||
text: RichText[]
|
||||
}
|
||||
|
||||
const getExplanationImage = (img : string) => getImage('/img/explanations/', img)
|
||||
|
||||
const skills : Skill[] = [
|
||||
{
|
||||
img: getExplanationImage('components'),
|
||||
title: 'Das, was du sehen kannst',
|
||||
text: [
|
||||
{
|
||||
type: 'p',
|
||||
children: [
|
||||
{
|
||||
type: 'plain',
|
||||
content: 'Die meisten Anwendungen haben eine grafische Benutzeroberfläche - die so genannte GUI. Wenn etwas graphisch ist, ' +
|
||||
'dann bedeutet das auch natürlich, dass es sinnvoll dargestellt werden muss. Dafür verwenden wir kleine Bausteine, die wir Komponenten nennen.',
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'p',
|
||||
children: [
|
||||
{
|
||||
type: 'plain',
|
||||
content: 'Grundsätzlich lassen sich komplette Anwendungen und auch einfache Webseiten ziemlich cool über Komponenten aufbauen, sodass wir wiederkehrende Elemente ' +
|
||||
'wie Buttons, Textabschnitte, Links oder Teaser immer wieder verwenden können - selbst wenn sich diese in ihren Details wie Farben oder Icons unterscheiden.',
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'p',
|
||||
children: [
|
||||
{
|
||||
type: 'plain',
|
||||
content: 'Diese Komponenten so zu entwickeln, dass sie wirklich flexibel sind und auch perfekt mit dem Design übereinstimmen ist gar nicht so einfach, denn oft sollten sie ' +
|
||||
'auch mehr können als im Design kurzfristig ersichtlich ist.',
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'p',
|
||||
css: 'place-bottom bold',
|
||||
children: [
|
||||
{
|
||||
type: 'plain',
|
||||
content: 'Diese Voraussicht, für den Fall der Fälle vorzusorgen:',
|
||||
},
|
||||
{
|
||||
type: 'br',
|
||||
},
|
||||
{
|
||||
type: 'plain',
|
||||
content: 'Die gibt\'s bei mir dazu.',
|
||||
},
|
||||
{
|
||||
type: 'br',
|
||||
},
|
||||
{
|
||||
type: 'span',
|
||||
css: 'highlight',
|
||||
content: 'Fussel-Ehrenwort.'
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
}, {
|
||||
img: getExplanationImage('cms'),
|
||||
title: 'Da, wo du eintragen kannst',
|
||||
text: [
|
||||
{
|
||||
type: 'p',
|
||||
children: [
|
||||
{
|
||||
type: 'plain',
|
||||
content: 'In vielen Fällen ist es natürlich praktisch, wenn du einfach Inhalte einer Seite auf das ändern kannst, was gerade aktuell ist, ohne auf andere angewiesen zu sein.'
|
||||
+ 'Damit das reibungslos möglich ist, entwickle ich dir gerne eine Anwendung oder Homepage, deren Inhalte du komplett selbst auf dem neuesten Stand halten kannst - und zwar '
|
||||
+ 'mit einem sogenannten CMS - ein ',
|
||||
},
|
||||
{
|
||||
type: 'span',
|
||||
css: 'highlight',
|
||||
content: 'C'
|
||||
},
|
||||
{
|
||||
type: 'plain',
|
||||
content: 'ontent '
|
||||
},
|
||||
{
|
||||
type: 'span',
|
||||
css: 'highlight',
|
||||
content: 'M'
|
||||
},
|
||||
{
|
||||
type: 'plain',
|
||||
content: 'anagement '
|
||||
},
|
||||
{
|
||||
type: 'span',
|
||||
css: 'highlight',
|
||||
content: 'S'
|
||||
},
|
||||
{
|
||||
type: 'plain',
|
||||
content: 'ystem.'
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'p',
|
||||
children: [
|
||||
{
|
||||
type: 'plain',
|
||||
content: 'Für CMS setze ich in erster Linie auf die cloudbasierte Lösung ',
|
||||
},
|
||||
{
|
||||
type: 'a',
|
||||
target: '_blank',
|
||||
css: 'text',
|
||||
href: 'https://www.storyblok.com',
|
||||
content: 'Storyblok'
|
||||
},
|
||||
{
|
||||
type: 'plain',
|
||||
content: '. Dies stellt für die Meisten eine kostenlose bis kostengünstige Lösung dar ohne viel technisches Wissen mitbringen zu müssen.',
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'p',
|
||||
children: [
|
||||
{
|
||||
type: 'plain',
|
||||
content: 'Falls du aber nicht möchtest, dass deine Daten auf irgendeinem Fremden server liegen - was ich durchaus verstehen kann! - dann gibt es auch die Möglichkeit mit ',
|
||||
},
|
||||
{
|
||||
type: 'a',
|
||||
target: '_blank',
|
||||
css: 'text',
|
||||
href: 'https://www.strapi.io',
|
||||
content: 'Strapi'
|
||||
},
|
||||
{
|
||||
type: 'plain',
|
||||
content: ' selbst das eigene CMS zu hosten. Das musst du dann aber allerdings selbst erledigen oder ich erledige das für dich für einen Aufpreis.',
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'p',
|
||||
css: 'place-bottom bold',
|
||||
children: [
|
||||
{
|
||||
type: 'plain',
|
||||
content: 'Nie wieder jemand anderen Fragen zu müssen, um deine Website auf dem neuesten Stand zu halten.',
|
||||
},
|
||||
{
|
||||
type: 'br',
|
||||
},
|
||||
{
|
||||
type: 'span',
|
||||
css: 'highlight',
|
||||
content: 'Mit Fussel-Garantie.'
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
}, {
|
||||
img: getExplanationImage('result'),
|
||||
title: 'Was dabei am Ende rauskommt',
|
||||
text: [
|
||||
{
|
||||
type: 'p',
|
||||
children: [
|
||||
{
|
||||
type: 'plain',
|
||||
content: 'Grundsätzlich lässt sich das ganz einfach zusammenfassen: ',
|
||||
},
|
||||
{
|
||||
type: 'span',
|
||||
css: 'highlight',
|
||||
content: 'Dein persönlicher Webauftritt.',
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'p',
|
||||
children: [
|
||||
{
|
||||
type: 'plain',
|
||||
content: 'Ob du nun etwas kleineres brauchst, um ein paar Hobbies zu zeigen. Oder vielleicht etwas größeres, weil du dir ein eigenes Business aufbauen willst. Eventuell ein eigener Blog, eine komplette Applikation oder '
|
||||
+ 'die Möglichkeit für andere Leute deine Daten bereit zu stellen. All das, das kann ich dir mit meinen Fähigkeiten und meiner Erfahrung bieten.'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'p',
|
||||
children: [
|
||||
{
|
||||
type: 'plain',
|
||||
content: 'Erkunde einfach meine ',
|
||||
},
|
||||
{
|
||||
type: 'a',
|
||||
href: '/references',
|
||||
content: 'Referenzen'
|
||||
},
|
||||
{
|
||||
type: 'plain',
|
||||
content: ' und dir wird auffallen, dass diese äußerst durchmischt und bunt sind. So individuell wie die Projekte meiner bisherigen Kunden wird auch dein Projekt behandelt. Und auch, wenn '
|
||||
+ 'du glaubst, dass die Referenzen nicht zu deinem Projekt passen, werden wir deine ganz individuelle Lösung gemeinsam erarbeiten.'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'p',
|
||||
css: 'place-bottom bold',
|
||||
children: [
|
||||
{
|
||||
type: 'plain',
|
||||
content: 'Denn jedes Projekt ist etwas eigenes und besonderes.',
|
||||
},
|
||||
{
|
||||
type: 'br',
|
||||
},
|
||||
{
|
||||
type: 'span',
|
||||
css: 'highlight',
|
||||
content: 'Auch deins.'
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
</script>
|
Loading…
Add table
Add a link
Reference in a new issue