ADD: A LOT of pricing chart stuff
Added new pricing chart layout
This commit is contained in:
parent
a204be8ddc
commit
f263a5574a
14 changed files with 563 additions and 284 deletions
|
@ -4,26 +4,44 @@
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding: 1rem 1.5rem;
|
padding: 1rem 1.5rem;
|
||||||
outline: 3px solid transparent;
|
outline: 3px solid transparent;
|
||||||
box-shadow: 0 0 0 0 var(--color-orange);
|
|
||||||
border-radius: 99999px;
|
border-radius: 99999px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
&.default {
|
&.default {
|
||||||
background: var(--color-orange);
|
background: var(--color-orange);
|
||||||
color: var(--color-black);
|
color: var(--color-black);
|
||||||
|
box-shadow: 0 0 0 0 var(--color-orange);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
outline-color: var(--color-black);
|
||||||
|
box-shadow: 0 0 0 6px var(--color-orange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.dark {
|
||||||
|
background: var(--color-orange-black);
|
||||||
|
color: var(--color-white);
|
||||||
|
box-shadow: 0 0 0 0 var(--color-orange-black);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
outline-color: var(--color-orange);
|
||||||
|
box-shadow: 0 0 0 6px var(--color-black);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.white {
|
&.white {
|
||||||
background: var(--color-white);
|
background: var(--color-white);
|
||||||
color: var(--color-black);
|
color: var(--color-black);
|
||||||
}
|
box-shadow: 0 0 0 0 var(--color-white);
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
outline-color: var(--color-black);
|
outline-color: var(--color-black);
|
||||||
box-shadow: 0 0 0 6px var(--color-orange);
|
box-shadow: 0 0 0 6px var(--color-white);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.cta {
|
&.cta {
|
||||||
|
@ -73,3 +91,7 @@
|
||||||
margin-left: calc(var(--size) * -1 - 25px);
|
margin-left: calc(var(--size) * -1 - 25px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.button-wrapper {
|
||||||
|
border-radius: 99999px;
|
||||||
|
}
|
|
@ -46,6 +46,7 @@
|
||||||
--color-black-transparent: #2a2723aa;
|
--color-black-transparent: #2a2723aa;
|
||||||
|
|
||||||
--color-orange: #ff9100;
|
--color-orange: #ff9100;
|
||||||
|
--color-orange-dark: #945400;
|
||||||
--color-orange-light: #ffc36f;
|
--color-orange-light: #ffc36f;
|
||||||
--color-orange-black: #332b22;
|
--color-orange-black: #332b22;
|
||||||
}
|
}
|
||||||
|
@ -235,10 +236,18 @@ span.chip {
|
||||||
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23);
|
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.z-2-all {
|
||||||
|
box-shadow: 0 0 6px rgba(0, 0, 0, 0.16), 0 0 6px rgba(0, 0, 0, 0.23);
|
||||||
|
}
|
||||||
|
|
||||||
.z-3 {
|
.z-3 {
|
||||||
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23);
|
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.z-3-all {
|
||||||
|
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23), 0 -10px 20px rgba(0, 0, 0, 0.19), 0 -6px 6px rgba(0, 0, 0, 0.23);
|
||||||
|
}
|
||||||
|
|
||||||
.z-4 {
|
.z-4 {
|
||||||
box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25), 0 10px 10px rgba(0, 0, 0, 0.22);
|
box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25), 0 10px 10px rgba(0, 0, 0, 0.22);
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,6 @@
|
||||||
font-family: 'Roboto Condensed', sans-serif;
|
font-family: 'Roboto Condensed', sans-serif;
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
flex: 1.5;
|
flex: 1.5;
|
||||||
cursor: default;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
|
|
172
app/assets/css/pricing.css
Normal file
172
app/assets/css/pricing.css
Normal file
|
@ -0,0 +1,172 @@
|
||||||
|
.Pricing {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: max-content 1fr max-content;
|
||||||
|
border-radius: 20px;
|
||||||
|
|
||||||
|
& article:first-child {
|
||||||
|
border-radius: 20px 20px 0 0;
|
||||||
|
|
||||||
|
& header,
|
||||||
|
& main,
|
||||||
|
& footer {
|
||||||
|
padding-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
& header {
|
||||||
|
background-color: var(--color-orange-dark);
|
||||||
|
color: var(--color-white);
|
||||||
|
|
||||||
|
& .bg-icon {
|
||||||
|
top: 60%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& .cols {
|
||||||
|
& li .value {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& article:last-child {
|
||||||
|
border-radius: 0 0 20px 20px;
|
||||||
|
|
||||||
|
& header,
|
||||||
|
& main,
|
||||||
|
& footer {
|
||||||
|
padding-top: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
& header {
|
||||||
|
background-color: var(--color-orange-dark);
|
||||||
|
color: var(--color-white);
|
||||||
|
}
|
||||||
|
|
||||||
|
& .cols {
|
||||||
|
& li .value {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& article:nth-child(2) {
|
||||||
|
width: 105%;
|
||||||
|
margin-left: -2.5%;
|
||||||
|
margin-right: -2.5%;
|
||||||
|
border-radius: 20px;
|
||||||
|
z-index: 2;
|
||||||
|
|
||||||
|
& header {
|
||||||
|
--size-difference: 7rem;
|
||||||
|
width: calc(100% - 2rem);
|
||||||
|
padding-top: var(--size-difference);
|
||||||
|
padding-bottom: var(--size-difference);
|
||||||
|
background-color: var(--color-orange);
|
||||||
|
color: var(--color-black);
|
||||||
|
|
||||||
|
& .bg-icon {
|
||||||
|
right: 20%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& ul {
|
||||||
|
& li .value {
|
||||||
|
font-size: 3rem;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& article {
|
||||||
|
display: grid;
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
grid-template-columns: subgrid;
|
||||||
|
background-color: var(--color-orange-black);
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
& header,
|
||||||
|
& main,
|
||||||
|
& footer {
|
||||||
|
padding: var(--spacing-standard);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
& footer {
|
||||||
|
align-items: center;
|
||||||
|
color: var(--color-orange);
|
||||||
|
gap: .5rem;
|
||||||
|
|
||||||
|
& .price {
|
||||||
|
font-size: 2rem;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& main {
|
||||||
|
background-color: var(--color-orange-black);
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
& header {
|
||||||
|
position: relative;
|
||||||
|
padding-right: 8rem;
|
||||||
|
width: calc(100% + 8rem);
|
||||||
|
|
||||||
|
& strong {
|
||||||
|
font-size: 2rem;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
& .bg-icon {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
right: 40%;
|
||||||
|
translate: 50% -50%;
|
||||||
|
font-size: 6rem;
|
||||||
|
opacity: 0.15;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& li {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
& .list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: .5rem;
|
||||||
|
|
||||||
|
& .point-icon {
|
||||||
|
color: var(--color-orange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& .list-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
& .cols {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
gap: 1rem;
|
||||||
|
|
||||||
|
& li {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
& .color-icon {
|
||||||
|
color: var(--color-orange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,46 +3,7 @@
|
||||||
background-color: var(--color-orange-black);
|
background-color: var(--color-orange-black);
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
|
|
||||||
.service-list {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
|
||||||
grid-template-rows: repeat(4, auto);
|
|
||||||
|
|
||||||
& article {
|
|
||||||
grid-row: span 4;
|
|
||||||
display: grid;
|
|
||||||
grid-template-rows: subgrid;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
& .chip {
|
|
||||||
position: absolute;
|
|
||||||
right: -1rem;
|
|
||||||
top: calc(-1rem - 3px);
|
|
||||||
}
|
|
||||||
|
|
||||||
& header {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
& ul {
|
|
||||||
gap: 1rem;
|
|
||||||
|
|
||||||
& li {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 1rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
& .color-icon {
|
|
||||||
color: var(--color-orange);
|
|
||||||
}
|
|
||||||
|
|
||||||
& .extra {
|
|
||||||
margin-top: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.network-list {
|
.network-list {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
150
app/components/Booking.vue
Executable file
150
app/components/Booking.vue
Executable file
|
@ -0,0 +1,150 @@
|
||||||
|
<template>
|
||||||
|
<section id="services" class="Services content">
|
||||||
|
<h2>Services.</h2>
|
||||||
|
<h3>Du hast also beschlossen, dass du <span class="highlight">meine Hilfe</span> brauchst. Cool!</h3>
|
||||||
|
<p class="margin-top">Hinter meinen 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>
|
||||||
|
|
||||||
|
<article class="z-2 card flex-col margin-top font-big text-center">
|
||||||
|
<p>Derzeit habe ich <span class="highlight">keine freien Plätze</span>.</p>
|
||||||
|
<p class="margin-top-small">Das ändert sich ab <span class="highlight">01. Juli 2025</span>.</p>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<h3 class="margin-top-big">One off Projekte</h3>
|
||||||
|
<p class="margin-top-small">Manchmal brauchen wir alle einfach nur eine Kleinigkeit und will sich 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>
|
||||||
|
|
||||||
|
<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>{{service.smallClaim}}</p>
|
||||||
|
<div aria-hidden="true" class="bg-icon">
|
||||||
|
<Icon :name="`ph:${service.icon}-thin`" size="1.5em" mode="svg" />
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<main>
|
||||||
|
<div class="list-container">
|
||||||
|
<ul class="list" v-for="(list, index) in service.list" :key="index">
|
||||||
|
<li v-for="point in list">
|
||||||
|
<Icon class="point-icon" name="ph:caret-circle-double-right-duotone" size="1.5em" mode="svg" />
|
||||||
|
<span class="label">{{ point }}</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
<footer>
|
||||||
|
<span class="price">{{service.price.pre ? `${service.price.pre} ` : '' }}{{typeof service.price.value === 'number' ? intl.format(service.price.value) : service.price.value}}</span>
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<article class="z-2 card flex-col gap-default margin-top">
|
||||||
|
<h3>Keinen Bock auf Telen? Understandable.</h3>
|
||||||
|
<p>Dann schreib mir einfach gerne direkt eine E-Mail an
|
||||||
|
<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>
|
||||||
|
<h3>Keine Kohle? Kommt vor.</h3>
|
||||||
|
<p>Meld dich trotzdem. Eventuell ist dein Projekt ja cool genug, dass ich dir da auch entsprechend entgegenkommen kann. :)</p>
|
||||||
|
</article>
|
||||||
|
<h3 id="network" class="margin-top-big">Mein Netzwerk</h3>
|
||||||
|
<p class="margin-top">Doch auch wenn ich mal voll ausgelastet bin - keine Sorge!
|
||||||
|
Mein <span class="highlight">Netzwerk an Profis</span> kann dir sicher auch weiterhelfen.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
type Service = {
|
||||||
|
title: string
|
||||||
|
smallClaim: string
|
||||||
|
price: {
|
||||||
|
value: number | string
|
||||||
|
pre?: 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: 'magnifying-glass',
|
||||||
|
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: 'Projektbuchung',
|
||||||
|
price: {
|
||||||
|
pre: 'ab',
|
||||||
|
value: 999,
|
||||||
|
},
|
||||||
|
smallClaim: 'Deine Vision.',
|
||||||
|
icon: 'trend-up',
|
||||||
|
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: 'Enterprise',
|
||||||
|
price: {
|
||||||
|
value: 'Auf Anfrage',
|
||||||
|
},
|
||||||
|
smallClaim: 'Für die ganz großen Sachen.',
|
||||||
|
icon: 'building-office',
|
||||||
|
button: 'Frag nach!',
|
||||||
|
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'
|
||||||
|
],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
</script>
|
|
@ -9,7 +9,7 @@
|
||||||
type Props = {
|
type Props = {
|
||||||
type ?: 'a' | 'button'
|
type ?: 'a' | 'button'
|
||||||
href ?: string
|
href ?: string
|
||||||
design ?: string
|
design ?: 'default' | 'dark' | 'white'
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
|
128
app/components/Flatrate.vue
Executable file
128
app/components/Flatrate.vue
Executable file
|
@ -0,0 +1,128 @@
|
||||||
|
<template>
|
||||||
|
<section id="services" class="Services content">
|
||||||
|
<h2>Prepaid Flatrates.</h2>
|
||||||
|
<h3>Genieße fusselige Qualität ohne groß herumzurechnen.</h3>
|
||||||
|
|
||||||
|
<p class="margin-top-small">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>
|
||||||
|
|
||||||
|
<div class="Pricing margin-top">
|
||||||
|
<article v-for="(service, index) in flatrate" :class="{ 'z-3-all': index === 1, 'z-2-all': index !== 1}">
|
||||||
|
<header>
|
||||||
|
<strong>{{service.title}}</strong>
|
||||||
|
<p>{{service.smallClaim}}</p>
|
||||||
|
<div aria-hidden="true" class="bg-icon">
|
||||||
|
<Icon :name="`ph:${service.icon}-thin`" size="1.5em" mode="svg" />
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<main>
|
||||||
|
<ul class="cols">
|
||||||
|
<li v-for="point in service.list">
|
||||||
|
<span class="value">{{ typeof point.value === 'number' ? intl.format(point.value) : point.value }}</span>
|
||||||
|
<span class="label">{{ point.label }}</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</main>
|
||||||
|
<footer>
|
||||||
|
<!-- <span class="price">{{intl.format(service.price)}}</span>-->
|
||||||
|
<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>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
type Service = {
|
||||||
|
title: string
|
||||||
|
smallClaim: string
|
||||||
|
icon: string
|
||||||
|
button: string
|
||||||
|
link: string
|
||||||
|
list: {
|
||||||
|
label: string
|
||||||
|
value: string | number
|
||||||
|
}[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const intl = new Intl.NumberFormat(
|
||||||
|
'de-DE',
|
||||||
|
{
|
||||||
|
style: 'currency',
|
||||||
|
currency: 'EUR',
|
||||||
|
minimumFractionDigits: 0,
|
||||||
|
maximumFractionDigits: 0,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
const flatrate : Service[] =
|
||||||
|
[
|
||||||
|
{
|
||||||
|
title: 'Casual',
|
||||||
|
smallClaim: 'Für kleine Aufgaben nebenbei.',
|
||||||
|
icon: 'baby-carriage',
|
||||||
|
button: 'Jetzt klar machen',
|
||||||
|
link: 'https://tidycal.com/webfussel/flatrate-casual',
|
||||||
|
list: [
|
||||||
|
{
|
||||||
|
label: 'Ticket gleichzeitig',
|
||||||
|
value: '1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Erreichbarkeit',
|
||||||
|
value: 'Ganz okay',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Monatlich',
|
||||||
|
value: 2950,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Gold-Fussel',
|
||||||
|
smallClaim: 'Wenn\'s mal wieder zu viel wird.',
|
||||||
|
icon: 'coins',
|
||||||
|
button: 'Jetzt Gold schürfen',
|
||||||
|
link: 'https://tidycal.com/webfussel/flatrate-gold-fussel',
|
||||||
|
list: [
|
||||||
|
{
|
||||||
|
label: 'Tickets gleichzeitig',
|
||||||
|
value: '2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Erreichbarkeit',
|
||||||
|
value: 'Mo - Fr',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Monatlich',
|
||||||
|
value: 4950,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Big Chonker',
|
||||||
|
smallClaim: 'Für die richtig großen Sachen.',
|
||||||
|
icon: 'skull',
|
||||||
|
button: 'Jetzt Fett trimmen',
|
||||||
|
link: 'https://tidycal.com/webfussel/flatrate-big-chonker',
|
||||||
|
list: [
|
||||||
|
{
|
||||||
|
label: 'Tickets gleichzeitig',
|
||||||
|
value: '3',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Erreichbarkeit',
|
||||||
|
value: 'Ja.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Monatlich',
|
||||||
|
value: 8950,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
]
|
||||||
|
</script>
|
|
@ -2,10 +2,12 @@
|
||||||
<div ref="stickyWatch" />
|
<div ref="stickyWatch" />
|
||||||
<header ref="header" class="Header">
|
<header ref="header" class="Header">
|
||||||
<div ref="headerWrapper" class="wrapper z-0">
|
<div ref="headerWrapper" class="wrapper z-0">
|
||||||
<strong>
|
<NuxtLink to="/" aria-label="Link zur Startseite">
|
||||||
<Icon class="logo" name="wf:logo" mode="svg" size="40px" />
|
<strong>
|
||||||
webfussel
|
<Icon class="logo" name="wf:logo" mode="svg" size="40px" />
|
||||||
</strong>
|
webfussel
|
||||||
|
</strong>
|
||||||
|
</NuxtLink>
|
||||||
<input id="navToggle" v-model="isBurgerOpen" type="checkbox">
|
<input id="navToggle" v-model="isBurgerOpen" type="checkbox">
|
||||||
<label :aria-label="burgerLabel" for="navToggle">
|
<label :aria-label="burgerLabel" for="navToggle">
|
||||||
<span/><span/><span/><span/>
|
<span/><span/><span/><span/>
|
||||||
|
@ -37,21 +39,21 @@ const burgerLabel = computed(() => isBurgerOpen.value ? burgerCloseLabel : burge
|
||||||
|
|
||||||
const nav = [
|
const nav = [
|
||||||
{
|
{
|
||||||
to: `/`,
|
to: `/booking`,
|
||||||
label: 'Home',
|
label: 'Projektbuchung',
|
||||||
icon: 'house',
|
icon: 'calendar-check',
|
||||||
'aria-label': 'Link dieser Seite: Startseite'
|
aria: 'Link dieser Seite: Preise'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
to: `/services`,
|
to: `/flatrate`,
|
||||||
label: 'Leistungen',
|
label: 'Flatrate',
|
||||||
icon: 'coins',
|
icon: 'piggy-bank',
|
||||||
aria: 'Link dieser Seite: Preise'
|
aria: 'Link dieser Seite: Preise'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
to: `/references`,
|
to: `/references`,
|
||||||
label: 'Referenzen',
|
label: 'Referenzen',
|
||||||
icon: 'at',
|
icon: 'sparkle',
|
||||||
aria: 'Link dieser Seite: Referenzen'
|
aria: 'Link dieser Seite: Referenzen'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,224 +0,0 @@
|
||||||
<template>
|
|
||||||
<section id="services" class="Services content">
|
|
||||||
<h2>Services.</h2>
|
|
||||||
<h3>Du hast also beschlossen, dass du <span class="highlight">meine Hilfe</span> brauchst. Cool!</h3>
|
|
||||||
<p class="margin-top">Hinter meinen 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>
|
|
||||||
|
|
||||||
<article class="z-2 card flex-col margin-top font-big text-center">
|
|
||||||
<p>Derzeit habe ich <span class="highlight">keine freien Plätze</span>.</p>
|
|
||||||
<p class="margin-top-small">Das ändert sich ab <span class="highlight">01. Juli 2025</span>.</p>
|
|
||||||
</article>
|
|
||||||
|
|
||||||
<h3 class="margin-top-big">One off Projekte</h3>
|
|
||||||
<p class="margin-top-small">Manchmal brauchen wir alle einfach nur eine Kleinigkeit und will sich 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>
|
|
||||||
|
|
||||||
<div class="service-list margin-top gap-default">
|
|
||||||
<article v-for="service in oneOff" class="z-2 card flex-col gap-default">
|
|
||||||
<h3 class="flex-col gap-default">
|
|
||||||
<span>{{service.title}}</span>
|
|
||||||
<span class="highlight">{{'pre' in service.price ? `${service.price.pre} ` : ''}}{{service.price.value}} €* {{service.price.period}}</span>
|
|
||||||
</h3>
|
|
||||||
<p>{{service.smallClaim}}</p>
|
|
||||||
<Button :href="service.link" class="cta" aria-label="Zur externen Seite Terminbuchung">
|
|
||||||
{{ service.button }}
|
|
||||||
<Icon :name="`ph:${service.icon}-duotone`" size="1.5em" mode="svg" />
|
|
||||||
</Button>
|
|
||||||
<ul class="flex-col">
|
|
||||||
<li v-for="point in service.list">
|
|
||||||
<Icon class="color-icon" name="ph:caret-circle-double-right-duotone" aria-hidden="true" alt="checkmark icon" size="1.5em" mode="svg" />
|
|
||||||
<span>{{point}}</span>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</article>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h3 class="margin-top-big">Prepaid Flatrates</h3>
|
|
||||||
<p class="margin-top-small">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>
|
|
||||||
|
|
||||||
<div class="service-list margin-top gap-default">
|
|
||||||
<article v-for="service in flatrate" class="z-2 card flex-col gap-default">
|
|
||||||
<h3 class="flex-col gap-default">
|
|
||||||
<span>{{service.title}}</span>
|
|
||||||
<span class="highlight">{{'pre' in service.price ? `${service.price.pre} ` : ''}}{{service.price.value}} €* {{service.price.period}}</span>
|
|
||||||
</h3>
|
|
||||||
<p>{{service.smallClaim}}</p>
|
|
||||||
<Button :href="service.link" class="cta" aria-label="Zur externen Seite Terminbuchung">
|
|
||||||
{{ service.button }}
|
|
||||||
<Icon :name="`ph:${service.icon}-duotone`" size="1.5em" mode="svg" />
|
|
||||||
</Button>
|
|
||||||
<ul class="flex-col">
|
|
||||||
<li v-for="point in service.list">
|
|
||||||
<Icon class="color-icon" name="ph:caret-circle-double-right-duotone" aria-hidden="true" alt="checkmark icon" size="1.5em" mode="svg" />
|
|
||||||
<span>{{point}}</span>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</article>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<article class="z-2 card flex-col gap-default margin-top">
|
|
||||||
<h3>Keinen Bock auf Telen? Understandable.</h3>
|
|
||||||
<p>Dann schreib mir einfach gerne direkt eine E-Mail an
|
|
||||||
<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>
|
|
||||||
<h3>Keine Kohle? Kommt vor.</h3>
|
|
||||||
<p>Meld dich trotzdem. Eventuell ist dein Projekt ja cool genug, dass ich dir da auch entsprechend entgegenkommen kann. :)</p>
|
|
||||||
</article>
|
|
||||||
<h3 id="network" class="margin-top-big">Mein Netzwerk</h3>
|
|
||||||
<p class="margin-top">Doch auch wenn ich mal voll ausgelastet bin - keine Sorge!
|
|
||||||
Mein <span class="highlight">Netzwerk an Profis</span> kann dir sicher auch weiterhelfen.
|
|
||||||
</p>
|
|
||||||
<ClientOnly>
|
|
||||||
<div class="network-list margin-top">
|
|
||||||
<div class="scroll-container gap-default">
|
|
||||||
<Person ref="persons" v-for="person in network" v-bind="person" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ClientOnly>
|
|
||||||
</section>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
type Service = {
|
|
||||||
title: string
|
|
||||||
price: {
|
|
||||||
pre ?: string
|
|
||||||
value: number
|
|
||||||
period: string
|
|
||||||
}
|
|
||||||
smallClaim: string
|
|
||||||
icon: string
|
|
||||||
button: string
|
|
||||||
link: string
|
|
||||||
list: string[]
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const oneOff : Service[] = [
|
|
||||||
{
|
|
||||||
title: 'Quick Check',
|
|
||||||
price: {
|
|
||||||
value: 149,
|
|
||||||
period: '/ Einmalig',
|
|
||||||
},
|
|
||||||
smallClaim: 'Du hast eine Homepage und willst mal drüber schauen lassen?',
|
|
||||||
icon: 'magnifying-glass',
|
|
||||||
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: 'Projektbuchung',
|
|
||||||
price: {
|
|
||||||
pre: 'ab',
|
|
||||||
value: 999,
|
|
||||||
period: '/ Gestaffelt'
|
|
||||||
},
|
|
||||||
smallClaim: 'Umsetzung deiner Vision. Von einzelnen Tickets bis hin zu kompletten Anwendungen.',
|
|
||||||
icon: 'trend-up',
|
|
||||||
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'
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
const flatrate : Service[] =
|
|
||||||
[
|
|
||||||
{
|
|
||||||
title: 'Casual',
|
|
||||||
price: {
|
|
||||||
value: 4950,
|
|
||||||
period: '/ Monat',
|
|
||||||
},
|
|
||||||
smallClaim: 'Bei dir fällt immer mal was an und du willst nicht jedes mal neu buchen?',
|
|
||||||
icon: 'hand-grabbing',
|
|
||||||
button: 'Jetzt klar machen',
|
|
||||||
link: 'https://tidycal.com/webfussel/hourly-booking',
|
|
||||||
list: [
|
|
||||||
'1 Ticket gleichzeitig',
|
|
||||||
'Meetings alle 2 Wochen',
|
|
||||||
'Zur Mitte des Monats kündbar',
|
|
||||||
],
|
|
||||||
}, {
|
|
||||||
title: 'Gold-Fussel',
|
|
||||||
price: {
|
|
||||||
value: 8450,
|
|
||||||
period: '/ Monat',
|
|
||||||
},
|
|
||||||
smallClaim: 'Du hast häufig mehrere Aufgaben zu verteilen, aber eure Entwickler sind dauerhaft mit dem Tagesgeschäft ausgebucht?',
|
|
||||||
icon: 'crown',
|
|
||||||
button: 'Jetzt Gold schürfen',
|
|
||||||
link: 'https://tidycal.com/webfussel/hourly-booking',
|
|
||||||
list: [
|
|
||||||
'3 Tickets gleichzeitig',
|
|
||||||
'Wöchentliche Meetings',
|
|
||||||
'Zur Mitte des Monats kündbar',
|
|
||||||
],
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
const shuffle = <T>(unshuffled : T[]) => unshuffled
|
|
||||||
.map(value => ({ value, sort: Math.random() }))
|
|
||||||
.sort((a, b) => a.sort - b.sort)
|
|
||||||
.map(({ value }) => value)
|
|
||||||
|
|
||||||
const network = shuffle([
|
|
||||||
{
|
|
||||||
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: 'Maria Salcedo',
|
|
||||||
// img: 'maria',
|
|
||||||
// tags: ['Backend', 'DevOps', 'Architektur'],
|
|
||||||
// flavour: 'Effizient und kommunikativ. "You build it, you run it."',
|
|
||||||
// link: 'https://masagu.dev',
|
|
||||||
// },
|
|
||||||
{
|
|
||||||
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',
|
|
||||||
},
|
|
||||||
])
|
|
||||||
</script>
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<Services />
|
<Booking />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
|
@ -3,6 +3,60 @@
|
||||||
<Intro />
|
<Intro />
|
||||||
<Skills />
|
<Skills />
|
||||||
<Customers />
|
<Customers />
|
||||||
<Services />
|
<Booking />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
|
||||||
|
const shuffle = <T>(unshuffled : T[]) => unshuffled
|
||||||
|
.map(value => ({ value, sort: Math.random() }))
|
||||||
|
.sort((a, b) => a.sort - b.sort)
|
||||||
|
.map(({ value }) => value)
|
||||||
|
|
||||||
|
const network = shuffle([
|
||||||
|
{
|
||||||
|
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: 'Maria Salcedo',
|
||||||
|
// img: 'maria',
|
||||||
|
// tags: ['Backend', 'DevOps', 'Architektur'],
|
||||||
|
// flavour: 'Effizient und kommunikativ. "You build it, you run it."',
|
||||||
|
// link: 'https://masagu.dev',
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
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',
|
||||||
|
},
|
||||||
|
])
|
||||||
|
</script>
|
||||||
|
|
5
app/pages/flatrate.vue
Executable file
5
app/pages/flatrate.vue
Executable file
|
@ -0,0 +1,5 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<Flatrate />
|
||||||
|
</div>
|
||||||
|
</template>
|
|
@ -19,6 +19,7 @@ export default defineNuxtConfig({
|
||||||
'~/assets/css/header.css',
|
'~/assets/css/header.css',
|
||||||
'~/assets/css/intro.css',
|
'~/assets/css/intro.css',
|
||||||
'~/assets/css/skills.css',
|
'~/assets/css/skills.css',
|
||||||
|
'~/assets/css/pricing.css',
|
||||||
'~/assets/css/customers.css',
|
'~/assets/css/customers.css',
|
||||||
'~/assets/css/services.css',
|
'~/assets/css/services.css',
|
||||||
'~/assets/css/footer.css',
|
'~/assets/css/footer.css',
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue