431 lines
12 KiB
Vue

<template>
<div>
<section class="heroBox_service" aria-labelledby="hero-heading">
<NuxtImg
provider="strapi"
src="/uploads/large_DML_Home_Hero_4f27bc7f8e.webp"
class="hero-bg"
sizes="sm:100vw md:100vw lg:100vw"
alt="website example"
aria-hidden="true"
priority
loading="eager"
preload
fetchpriority="high"
/>
<div class="container-10">
<h1 id="hero-heading">{{ $t('pages.home.heroBox.h1') }}</h1>
<h2>{{ $t('pages.home.heroBox.h2') }}</h2>
<h3>{{ $t('pages.home.heroBox.h3') }}</h3>
</div>
<!-- Nach dem Container: Spiegelwelle unten -->
<svg class="sectionWave wave-bottom" style="" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 20" aria-hidden="true">
<path d="M 0 0 L 500 0 L 500 14 Q 354.4 -2.8 250 11 Q 145.6 24.8 0 14 L 0 0 Z" fill="#FFF"/>
</svg>
</section>
<section aria-labelledby="solution-title">
<div class="container-10 webStrategy">
<h2 id="solution-title">{{ $t('pages.home.solution.title') }}</h2>
<h3>{{ $t('pages.home.solution.teaser') }}</h3>
<p>{{ $t('pages.home.solution.text') }}</p>
<button
class="mintBtn"
role="button"
aria-describedby="solution-title"
aria-label="headless CMS Info" @click="navigateToArticle">
{{ $t('pages.home.solution.buttonText') }}
</button>
</div>
</section>
<section class="targetGroup" aria-labelledby="invitation-title">
<svg class="sectionWave wave-top" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 20" aria-hidden="true">
<path d="M 0 0 L 500 0 L 500 14 Q 354.4 -2.8 250 11 Q 145.6 24.8 0 14 L 0 0 Z" fill="#FFF"/>
</svg>
<div class="container-10">
<div class="row">
<div class="col-md-4"/>
<div class="col-md-8 pt-5 pb-5">
<h2 id="invitation-title">{{ $t('pages.home.invitation.title') }}</h2>
<h3>{{ $t('pages.home.invitation.teaser') }}</h3>
<button
class="pinkBtn" role="button"
aria-describedby="invitation-title"
aria-label="Kontaktformular öffnen"
@click.prevent="toggleContactBubble">{{ $t('pages.home.invitation.button') }}</button>
</div>
</div>
</div>
<svg class="sectionWave wave-bottom" style="transform: scale(-1,-1)" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 20" aria-hidden="true">
<path d="M 0 0 L 500 0 L 500 14 Q 354.4 -2.8 250 11 Q 145.6 24.8 0 14 L 0 0 Z" fill="#FFF"/>
</svg>
</section>
<section class="canDo" aria-labelledby="canDo-section-title">
<div class="container">
<h2 id="canDo-section-title" class="text-center">
{{ $t('pages.home.canDo.title') }}
</h2>
<div class="canDoGrid">
<div
v-for="(item, index) in canDoItems"
:key="index"
class="canDoBox"
>
<div
class="canDoItem"
:role="index === 2 ? 'group' : null"
:aria-labelledby="index === 2 ? 'cando-title' : null"
>
<NuxtImg
provider="strapi"
:src="item.img"
format="webp"
:modifiers="{ quality: 80 }"
sizes="xs:280px sm:300px md:350px"
class="imageBox"
loading="lazy"
:alt="item.alt"
/>
<h3 :id="index === 2 ? 'cando-title' : null">
{{ $t(item.title) }}
</h3>
<p>{{ $t(item.text) }}</p>
</div>
</div>
</div>
</div>
</section>
<MarqueeBanner
:items="projectItems"
:logoHeight="200"
:title="$t('pages.services.marquee.title')"
link="projekt"
:aria-label="$t('pages.services.marquee.title')"
speed="15"
/>
<CallToActionBox
:headline="$t('pages.services.ctaBox.headline')"
:text="$t('pages.services.ctaBox.text')"
:buttonText="$t('pages.services.ctaBox.button')"
/>
</div>
</template>
<script setup>
import { storeToRefs } from 'pinia';
import { useMainStore } from '@/stores/main';
import { useI18n } from 'vue-i18n';
import { computed } from 'vue';
const runtimeConfig = useRuntimeConfig()
const origin = runtimeConfig.public.appUrl
const { t } = useI18n();
const mainStore = useMainStore();
const { projects, companyinfo } = storeToRefs(mainStore)
const navigateToArticle = () => {
router.push('/wissenswertes/artikel/design-und-inhalt-sauber-getrennt-warum-headless-webdesign-die-beste-wahl-fuer-moderne-unternehmen-ist');
};
const projectItems = computed(() => {
return projects.value
.filter(project => project.customer && project.projectImages.length > 0)
.map(project => ({
text: project.customer?.company || '',
image: {
url: project.projectImages[0]?.url || '',
alternativeText: project.projectImages[0]?.alternativeText || project.customer?.company || ''
},
link: '/projekt/' + project.link || ''
}))
})
const logoUrl = computed(() => {
if (!companyinfo) return origin + '/logo.svg';
return companyinfo.logo?.data?.attributes?.url
? origin + companyinfo.logo.data.attributes.url
: origin + '/logo.svg';
})
const canDoItems = [
{
img: '/uploads/website_Erfolg_Marketing_3c36a43ba5.png',
alt: 'Website Erfolg Marketing',
title: 'pages.home.canDo.item1.title',
text: 'pages.home.canDo.item1.text',
},
{
img: '/uploads/Kundenbindung_45d45ef3fc.png',
alt: 'Kundenbindung Strategie',
title: 'pages.home.canDo.item2.title',
text: 'pages.home.canDo.item2.text',
},
{
img: '/uploads/Screen_Shot_Tool_20250228133408_beb2a11980.png',
alt: 'Screen Tool Beispiel 1',
title: 'pages.home.canDo.item3.title',
text: 'pages.home.canDo.item3.text',
},
{
img: '/uploads/Screen_Shot_Tool_20250228133812_0a20d4320e.png',
alt: 'Screen Tool Beispiel 2',
title: 'pages.home.canDo.item4.title',
text: 'pages.home.canDo.item4.text',
},
];
// JSON_LD für Services
const jsonLdServices = computed(() => {
if (!companyinfo?.value || !companyinfo.value.company) return null;
return {
"@context": "https://schema.org",
"@type": "ProfessionalService",
"name": companyinfo.value.company,
"url": origin,
"logo": logoUrl.value || (origin + '/logo.svg'),
"description": "Spezialisiert auf JAMstack-Webentwicklung, Headless CMS-Integration und moderne Frontend-Lösungen.",
"address": {
"@type": "PostalAddress",
"streetAddress": companyinfo.value.street || '',
"addressLocality": companyinfo.value.city || '',
"addressRegion": "Bayern",
"postalCode": companyinfo.value.postalcode || '',
"addressCountry": "DE"
},
"hasOfferCatalog": {
"@type": "OfferCatalog",
"name": "Leistungen",
"itemListElement": [
{
"@type": "Offer",
"itemOffered": {
"@type": "Service",
"name": "JAMstack-Webentwicklung",
"description": "Moderne Webentwicklung mit statischen Seiten und Headless CMS."
}
},
{
"@type": "Offer",
"itemOffered": {
"@type": "Service",
"name": "Headless CMS Integration",
"description": "Flexible CMS-Lösungen für maximale Freiheit im Frontend."
}
},
{
"@type": "Offer",
"itemOffered": {
"@type": "Service",
"name": "Frontend-Entwicklung",
"description": "Moderne Frontend-Frameworks und UI/UX Design."
}
}
]
}
}
})
watchEffect(() => {
if (companyinfo.value && jsonLdServices.value) {
useHead({
script: [
{
type: 'application/ld+json',
children: JSON.stringify(jsonLdServices.value)
}
]
})
}
})
</script>
<style lang="sass">
.heroBox_service
position: relative
min-height: 35rem
height: 70vh
display: flex
align-items: center
justify-content: center
overflow: hidden
.hero-bg
position: absolute
inset: 0
width: 100%
height: 100%
object-fit: cover
object-position: center bottom
z-index: 0
.container-10, h1, h2, h3
position: relative
z-index: 1
h1
margin-top: 3rem
z-index: 2
color: mix(black, $pink, 2%)
font-size: clamp(1rem, .8rem + 1vw, 1.2rem)
line-height: 1.5
margin-bottom: 0
max-width: 70%
@media (max-width: $breakPointMD)
max-width: 100%
h2
z-index: 2
font-size: clamp(1.4rem, .8rem + 2vw, 2.4rem)
line-height: 1.5
margin-top: 1rem
max-width: 55%
font-family: 'Comfortaa'
@media (max-width: $breakPointMD)
max-width: 90%
h3
max-width: 55%
line-height: 1.5
font-size: clamp(1rem, .8rem + 1vw, 1.6rem)
@media (max-width: $breakPointMD)
max-width: 90%
&::after
content: ''
position: absolute
top: 0
left: 0
width: 50%
height: 100%
background-image: linear-gradient(to right, rgba(white, .3), transparent)
z-index: 1
.container
z-index: 2
.webStrategy
padding: 4rem 0 3.5rem 0
h2
font-size: clamp(1.6rem, 1rem + 2vw, 1.8rem)
line-height: 150%
font-family: 'Comfortaa'
margin-left: 30%
h3
font-size: clamp(1.1rem, .75rem + 1vw, 1.25rem)
line-height: 150%
margin-left: 30%
p
margin-left: 30%
button
margin-left: 30%
&::after
content: ''
position: absolute
top: 5%
left: -36vw
width: 60vw
height: 90%
min-height: 550px
max-height: 800px
background-image: url('https://strapi.digimedialoop.de/uploads/Net_f1020a2216.png')
background-repeat: no-repeat
background-position: center right
background-size: cover
border-radius: 42% 49% 52% 48% / 53% 38% 62% 47%
animation: bubble-wobble 25s infinite ease alternate, gradient-animation 70s infinite alternate ease-in-out
box-shadow: $innerShadow
@media(max-width: $breakPointMD)
right: -50vw
.canDo
margin: 12vh 0
h2
margin-bottom: 3.5rem
font-size: clamp(1.6rem, 1rem + 2vw, 1.8rem)
.canDoGrid
display: grid
gap: 2rem
justify-content: center
align-items: stretch
grid-template-columns: 1fr // Default: 1 Spalte
@media (min-width: $breakPointMD)
grid-template-columns: repeat(2, 1fr)
@media (min-width: $breakPointXL)
grid-template-columns: repeat(4, 1fr)
.canDoBox
width: 100%
display: flex
flex-direction: column
align-items: center
justify-content: flex-start
background-image: linear-gradient(to bottom right, transparent , white)
box-shadow: 3px 3px 8px 1px $lightgrey
border-bottom-right-radius: 1rem
padding: 1rem 1.5rem
border-right: 1px solid lighten($beige, 0%)
border-bottom: 1px solid lighten($beige, 0%)
height: 100%
.canDoItem
width: 100%
.imageBox
margin: 0rem auto 1rem auto
width: 100%
max-width: 180px
aspect-ratio: 5 / 4
object-fit: cover
border-radius: 1rem
display: block
&:nth-child(1)
border-radius: $loopShape1
&:nth-child(2)
border-radius: $loopShape2
&:nth-child(3)
border-radius: $loopShape3
&:nth-child(4)
border-radius: $loopShape4
h3
font-size: 1.2rem
line-height: 1.5rem
text-align: center
font-family: 'Mainfont-Bold'
color: darken($pink, 10%)
text-transform: uppercase
margin-bottom: .5rem
p
font-size: .9rem
text-align: left !important
.targetGroup
background-image: url('https://strapi.digimedialoop.de/uploads/smartphone_Contacts_40ae56a178.jpg')
background-repeat: no-repeat
background-size: cover
background-position: center top
min-height: 70vh
display: flex
align-items: center
justify-content: center
position: relative
padding: 3rem 0
h2
font-size: clamp(1.6rem, 1rem + 2vw, 1.8rem)
h3
font-size: clamp(1.1rem, .8rem + 1vw, 1.2rem)
line-height: 150%
</style>