new Routing
This commit is contained in:
parent
e97365b0b6
commit
5b16d988f5
10
app.vue
10
app.vue
@ -7,8 +7,16 @@
|
||||
<script setup>
|
||||
import { onMounted, onBeforeUnmount } from 'vue'
|
||||
import { useMainStore } from '@/stores/main'
|
||||
|
||||
import { useRouter } from 'vue-router'
|
||||
const mainStore = useMainStore()
|
||||
const router = useRouter()
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
mainStore.setDarkHeroBack(false)
|
||||
next()
|
||||
})
|
||||
|
||||
|
||||
|
||||
// Scroll- und Resize-Listener in den Lifecycle-Hooks registrieren
|
||||
onMounted(() => {
|
||||
|
||||
5
assets/images/DML_Logo.svg
Normal file
5
assets/images/DML_Logo.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 21 KiB |
@ -4,6 +4,7 @@
|
||||
v-for="(item, i) in items"
|
||||
:key="i"
|
||||
class="accordion-item"
|
||||
:class="{ open: openIndex === i }"
|
||||
>
|
||||
<!-- Header --------------------------------------------------->
|
||||
<button
|
||||
@ -44,6 +45,18 @@ const toggle = (i: number) =>
|
||||
.accordion-item
|
||||
border-bottom: 1px solid $lightgrey
|
||||
width: 100%
|
||||
transition: .8s
|
||||
|
||||
&.open
|
||||
background-image: linear-gradient(to right, $lightgrey, white)
|
||||
padding: 1rem
|
||||
|
||||
.accordion-header
|
||||
background: transparent
|
||||
font-size: 120%
|
||||
|
||||
&:hover
|
||||
background: transparent
|
||||
|
||||
.accordion-header
|
||||
all: unset
|
||||
@ -93,6 +106,16 @@ const toggle = (i: number) =>
|
||||
.accordion-content
|
||||
padding: 0 1rem 1rem
|
||||
|
||||
h3
|
||||
font-size: 1rem
|
||||
margin: 1.2rem 0 .6rem 0
|
||||
p
|
||||
font-size: 1rem
|
||||
ul
|
||||
li
|
||||
line-height: 140%
|
||||
margin-bottom: 1rem
|
||||
|
||||
// simple height-fade transition
|
||||
.accordion-enter-from,
|
||||
.accordion-leave-to
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<transition name="fade">
|
||||
<button class="back-to-top" v-show="isVisible" @click="scrollToTop" aria-label="Zurück nach oben">
|
||||
<span class="arrow-up"></span>
|
||||
<button v-show="isVisible" class="back-to-top" aria-label="Zurück nach oben" @click="scrollToTop">
|
||||
<span class="arrow-up"/>
|
||||
</button>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<div class="ctaBox">
|
||||
<h3>{{ headline }}</h3>
|
||||
<p>{{ text }}</p>
|
||||
<button class="pinkBtn mt-1" @click.prevent="toggleContactBubble" role="button">
|
||||
<button class="pinkBtn mt-1" role="button" @click.prevent="toggleContactBubble">
|
||||
{{ buttonText }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@ -47,8 +47,8 @@
|
||||
<div v-if="screenWidth > 768" class="pt-3">
|
||||
<h3>{{ $t('contactForm.ourOffice') }}</h3>
|
||||
<p class="address">
|
||||
{{ companyinfo.company }}<br />
|
||||
{{ companyinfo.street }} <br />
|
||||
{{ companyinfo.company }}<br >
|
||||
{{ companyinfo.street }} <br >
|
||||
{{ companyinfo.postalcode }} {{ companyinfo.city }}
|
||||
</p>
|
||||
<p class="aspProf">{{ $t('contactForm.yourcontactperson') }} <b>Sabrina Hennrich</b></p>
|
||||
@ -68,21 +68,21 @@
|
||||
<!-- Rechte Seite -->
|
||||
<div class="col-md-6">
|
||||
<div v-if="!formSent">
|
||||
<form @submit.prevent="submitForm" novalidate>
|
||||
<form novalidate @submit.prevent="submitForm">
|
||||
<div class="form-group">
|
||||
<label for="name">{{ $t('contactForm.name') }}</label>
|
||||
<input
|
||||
id="name"
|
||||
class="form-control"
|
||||
v-model="form.name"
|
||||
class="form-control"
|
||||
type="text"
|
||||
name="name"
|
||||
required
|
||||
autocomplete="name"
|
||||
@blur="validateName"
|
||||
:aria-invalid="!!errors.name"
|
||||
:aria-describedby="errors.name ? 'error-name' : null"
|
||||
/>
|
||||
@blur="validateName"
|
||||
>
|
||||
<span
|
||||
v-if="errors.name"
|
||||
id="error-name"
|
||||
@ -97,16 +97,16 @@
|
||||
<label for="email">{{ $t('contactForm.email') }}</label>
|
||||
<input
|
||||
id="email"
|
||||
class="form-control"
|
||||
v-model="form.email"
|
||||
class="form-control"
|
||||
type="email"
|
||||
name="email"
|
||||
required
|
||||
autocomplete="email"
|
||||
@blur="validateEmail"
|
||||
:aria-invalid="!!errors.email"
|
||||
:aria-describedby="errors.email ? 'error-email' : null"
|
||||
/>
|
||||
@blur="validateEmail"
|
||||
>
|
||||
<span
|
||||
v-if="errors.email"
|
||||
id="error-email"
|
||||
@ -121,15 +121,15 @@
|
||||
<label for="phone">{{ $t('contactForm.phone') }}</label>
|
||||
<input
|
||||
id="phone"
|
||||
class="form-control"
|
||||
v-model="form.phone"
|
||||
class="form-control"
|
||||
type="tel"
|
||||
name="phone"
|
||||
autocomplete="tel"
|
||||
@blur="validatePhone"
|
||||
:aria-invalid="!!errors.phone"
|
||||
:aria-describedby="errors.phone ? 'error-phone' : null"
|
||||
/>
|
||||
@blur="validatePhone"
|
||||
>
|
||||
<span
|
||||
v-if="errors.phone"
|
||||
id="error-phone"
|
||||
@ -144,19 +144,19 @@
|
||||
<label for="message">{{ $t('contactForm.message') }}</label>
|
||||
<textarea
|
||||
id="message"
|
||||
class="form-control mt-4"
|
||||
v-model="form.message"
|
||||
class="form-control mt-4"
|
||||
name="message"
|
||||
rows="4"
|
||||
required
|
||||
></textarea>
|
||||
/>
|
||||
</div>
|
||||
|
||||
<p class="smallText">
|
||||
<span class="check">✔</span>
|
||||
{{ $t('contactForm.privacyInfotextBeforeLink') }}
|
||||
<NuxtLinkLocale
|
||||
:to="getRoute('privacy')"
|
||||
:to="'privacy'"
|
||||
:aria-label="$t('privacy')"
|
||||
>
|
||||
{{ $t('contactForm.privacyInfotextLinkText') }}
|
||||
@ -191,7 +191,7 @@ import { useMainStore } from '@/stores/main';
|
||||
import { ref, reactive, computed } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
const { getRoute } = useI18nPages()
|
||||
|
||||
|
||||
// i18n Setup
|
||||
const { t } = useI18n();
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// components/HeroBox.vue
|
||||
<template>
|
||||
<section class="heroBox" :aria-label="ariaLabel">
|
||||
<section ref="heroBoxRef" class="heroBox" :aria-label="ariaLabel">
|
||||
<NuxtImg
|
||||
provider="strapi"
|
||||
:src="image"
|
||||
@ -13,6 +13,20 @@
|
||||
preload
|
||||
fetchpriority="high"
|
||||
/>
|
||||
<!-- Optionales Overlay-Bild -->
|
||||
<NuxtImg
|
||||
v-if="overlayImage"
|
||||
provider="strapi"
|
||||
:src="overlayImage"
|
||||
class="overlay-img"
|
||||
:alt="overlayAltText"
|
||||
:style="{
|
||||
...overlayPosition,
|
||||
width: typeof overlayWidth === 'number' ? overlayWidth + 'px' : overlayWidth,
|
||||
transform: `translate3d(0, ${parallaxY}px, 0)`
|
||||
}"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<div class="container-10 content">
|
||||
<h1>{{ $t(content.headline1) }}</h1>
|
||||
<h2>{{ $t(content.headline2) }}</h2>
|
||||
@ -25,14 +39,78 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
defineProps({
|
||||
// Parallax Overlay-Image
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
import { useMainStore } from '@/stores/main'
|
||||
const mainStore = useMainStore()
|
||||
const props = defineProps({
|
||||
image: String,
|
||||
ariaLabel: String,
|
||||
content: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
overlayImage: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
overlayAltText: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
overlayWidth: {
|
||||
type: [String, Number],
|
||||
default: '200px'
|
||||
},
|
||||
darkBackground: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
|
||||
overlayPosition: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
bottom: '0px',
|
||||
right: '0px'
|
||||
})
|
||||
}
|
||||
})
|
||||
const parallaxY = ref(0)
|
||||
const heroBoxRef = ref(null)
|
||||
let targetY = 0
|
||||
let animationFrame
|
||||
const updateParallax = () => {
|
||||
// Ease: Linear interpolation (lerp) zwischen aktuellem Wert und Zielwert
|
||||
parallaxY.value += (targetY - parallaxY.value) * 0.1
|
||||
animationFrame = requestAnimationFrame(updateParallax)
|
||||
}
|
||||
|
||||
const onScroll = () => {
|
||||
targetY = window.scrollY * 0.3 // oder was dir gefällt
|
||||
}
|
||||
|
||||
const updateHeight = () => {
|
||||
if (heroBoxRef.value) {
|
||||
mainStore.setHeroBoxHeight(heroBoxRef.value.offsetHeight)
|
||||
mainStore.setDarkHeroBack(props.darkBackground)
|
||||
}
|
||||
}
|
||||
onMounted(() => {
|
||||
|
||||
window.addEventListener('scroll', onScroll, { passive: true })
|
||||
updateParallax()
|
||||
// HeroBoxHeight for Logo
|
||||
|
||||
updateHeight()
|
||||
window.addEventListener('resize', updateHeight)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('scroll', onScroll)
|
||||
cancelAnimationFrame(animationFrame)
|
||||
window.removeEventListener('resize', updateHeight)
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="sass" scoped>
|
||||
@ -71,13 +149,22 @@ defineProps({
|
||||
object-position: center top
|
||||
z-index: -1
|
||||
|
||||
.overlay-img
|
||||
position: absolute
|
||||
bottom: 0
|
||||
right: 0
|
||||
z-index: 1
|
||||
height: auto
|
||||
max-height: 100%
|
||||
object-fit: contain
|
||||
|
||||
.content, h1, h2, h3
|
||||
position: relative
|
||||
z-index: 1
|
||||
z-index: 2
|
||||
|
||||
h1, h2, h3
|
||||
color: white
|
||||
z-index: 2
|
||||
z-index: 3
|
||||
line-height: 1.5
|
||||
max-width: 70%
|
||||
@media (max-width: 768px)
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="languageBox" v-if="locales.length >=2" @click="toggleOpen">
|
||||
<div v-if="locales.length >=2" class="languageBox" @click="toggleOpen">
|
||||
<div v-if="!open" class="current">{{ currentLanguage }}</div>
|
||||
|
||||
<transition name="slide">
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<section class="topSpace" v-if="currentPage">
|
||||
<section v-if="currentPage" class="topSpace">
|
||||
<div v-if="currentPage.pageSections[0].sectionText">
|
||||
<div class="container content" v-html="htmlContent(currentPage.pageSections[0].sectionText)"></div>
|
||||
<div class="container content" v-html="htmlContent(currentPage.pageSections[0].sectionText)"/>
|
||||
</div>
|
||||
</section>
|
||||
<section class="topSpace" v-else>
|
||||
<section v-else class="topSpace">
|
||||
<h1>Seite nicht gefunden</h1>
|
||||
<p>Die angeforderte Seite existiert nicht.</p>
|
||||
</section>
|
||||
|
||||
@ -2,11 +2,11 @@
|
||||
<section class="recommendations">
|
||||
<!-- Vor dem Container: Welle oben -->
|
||||
<svg class="sectionWave wave-top" :style="`height: ${waveHeight};top:-${waveHeight-2}`" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 20">
|
||||
<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="#EEEBE5"></path>
|
||||
<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="#EEEBE5"/>
|
||||
</svg>
|
||||
<div class="container">
|
||||
<h2>Das sagen andere Designer und Kreative über digimedialoop</h2>
|
||||
<div class="personBox" v-for="person, index in persons" :key="index" :class="person.active ? 'active' : ''" @click="setActive(index)">
|
||||
<div v-for="person, index in persons" :key="index" class="personBox" :class="person.active ? 'active' : ''" @click="setActive(index)">
|
||||
<img :src="person.image" alt="">
|
||||
<div class="infoBox">
|
||||
<h3>{{ person.name }}</h3>
|
||||
@ -17,7 +17,7 @@
|
||||
|
||||
<!-- Nach dem Container: Spiegelwelle unten -->
|
||||
<svg class="sectionWave wave-bottom" :style="`height: ${waveHeight};bottom:-${waveHeight-2}`" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 20">
|
||||
<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="#EEEBE5"></path>
|
||||
<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="#EEEBE5"/>
|
||||
</svg>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
180
components/SchemaCoder.vue
Normal file
180
components/SchemaCoder.vue
Normal file
@ -0,0 +1,180 @@
|
||||
<template>
|
||||
<div class="schema-typer">
|
||||
<span class="token-tag"><<span class="token-tag-name">script</span> <span class="token-attr">type</span>=<span class="token-string">"application/ld+json"</span>></span><br >
|
||||
<span
|
||||
v-for="(line, lineIndex) in displayedLines"
|
||||
:key="lineIndex"
|
||||
class="code-line"
|
||||
>
|
||||
<template v-for="(segment, index) in line" :key="index">
|
||||
<span :class="segment.class">{{ segment.text }}</span>
|
||||
</template>
|
||||
<template v-if="(lineIndex === currentLine || (isTypingDone && lineIndex === displayedLines.length - 1)) && showCursor">
|
||||
<span class="cursor">|</span>
|
||||
</template>
|
||||
<br >
|
||||
</span>
|
||||
<span class="token-tag"></<span class="token-tag-name">script</span>></span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
|
||||
const schema = `{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "HowTo",
|
||||
"name": "So machen wir deine Website KI-kompatibel",
|
||||
"description": "Wir optimieren deine Website gezielt für künstliche Intelligenz. Strukturiert, effizient und zukunftssicher.",
|
||||
"step1": "Wir integrieren strukturierte Daten nach schema.org, damit KI-Systeme Inhalte korrekt interpretieren können.",
|
||||
"step2": "Wir überarbeiten den Quellcode mit semantischem HTML und klaren Überschriften zur besseren maschinellen Lesbarkeit.",
|
||||
"step3": "Wir stellen relevante Inhalte zusätzlich in maschinenfreundlichen Formaten wie JSON-LD oder über APIs bereit.",
|
||||
"step4": "Wir sorgen für barrierefreie Gestaltung und klare Sprache, so verstehen auch KI-Sprachmodelle deine Inhalte besser.",
|
||||
"step5": "Wir ergänzen deine Seiten mit Metadaten wie OpenGraph, Twitter Cards und Robots-Tags für optimalen Kontext.",
|
||||
"estimatedTime": "ca. 45 Minuten Analyse + Umsetzung je nach Umfang",
|
||||
"image": "/images/ki-optimierung.jpg"
|
||||
}`
|
||||
|
||||
function parseLine(line) {
|
||||
const segments = []
|
||||
const regex = /("[^"]+":)|("[^"]+")|(\d+)|(true|false|null)|(\s+|[{}[\],:])/g
|
||||
let match
|
||||
let lastIndex = 0
|
||||
|
||||
while ((match = regex.exec(line)) !== null) {
|
||||
const start = match.index
|
||||
if (start > lastIndex) {
|
||||
segments.push({ text: line.slice(lastIndex, start), class: 'token-text' })
|
||||
}
|
||||
|
||||
const [matched, key, str, num, bool, symbol] = match
|
||||
let cls = 'token-text'
|
||||
if (key) cls = 'token-key'
|
||||
else if (str) cls = 'token-string'
|
||||
else if (num) cls = 'token-number'
|
||||
else if (bool) cls = 'token-boolean'
|
||||
else if (symbol) cls = 'token-text'
|
||||
|
||||
segments.push({ text: matched, class: cls })
|
||||
lastIndex = start + matched.length
|
||||
}
|
||||
|
||||
if (lastIndex < line.length) {
|
||||
segments.push({ text: line.slice(lastIndex), class: 'token-text' })
|
||||
}
|
||||
|
||||
return segments
|
||||
}
|
||||
|
||||
const lines = schema.split('\n').map(parseLine)
|
||||
const displayedLines = ref([])
|
||||
const showCursor = ref(true)
|
||||
const currentLine = ref(0)
|
||||
const isTypingDone = ref(false)
|
||||
|
||||
const startTyping = () => {
|
||||
let lineIndex = 0
|
||||
let charIndex = 0
|
||||
displayedLines.value.push([])
|
||||
|
||||
const typeChar = () => {
|
||||
if (lineIndex >= lines.length) {
|
||||
isTypingDone.value = true
|
||||
return
|
||||
}
|
||||
|
||||
const segment = lines[lineIndex][charIndex]
|
||||
if (segment) {
|
||||
displayedLines.value[lineIndex].push(segment)
|
||||
charIndex++
|
||||
} else {
|
||||
lineIndex++
|
||||
charIndex = 0
|
||||
currentLine.value = lineIndex
|
||||
if (lineIndex < lines.length) displayedLines.value.push([])
|
||||
}
|
||||
|
||||
setTimeout(typeChar, Math.random() * 80 + 40)
|
||||
}
|
||||
|
||||
typeChar()
|
||||
setInterval(() => (showCursor.value = !showCursor.value), 500)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
const observer = new IntersectionObserver(([entry]) => {
|
||||
if (entry.isIntersecting) {
|
||||
observer.disconnect()
|
||||
startTyping()
|
||||
}
|
||||
}, { threshold: 0.3 })
|
||||
|
||||
const el = document.querySelector('.schema-typer')
|
||||
if (el) observer.observe(el)
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
<style lang="sass" scoped>
|
||||
.schema-typer
|
||||
background-color: #1e1e1e
|
||||
padding: 1.5rem
|
||||
margin: 2rem
|
||||
border-radius: 0.5rem
|
||||
font-family: monospace
|
||||
font-size: 0.7rem
|
||||
color: #ffffff
|
||||
overflow-x: auto
|
||||
overflow-y: auto
|
||||
line-height: 1.4
|
||||
height: auto
|
||||
min-height: 450px
|
||||
width: 400px
|
||||
transform: rotate(6deg)
|
||||
box-shadow: -3px 3px 6px 4px grey
|
||||
|
||||
span
|
||||
margin: 0
|
||||
padding: 0
|
||||
|
||||
.code-line
|
||||
display: block
|
||||
line-height: 1.2
|
||||
|
||||
.token-tag
|
||||
color: #569cd6
|
||||
|
||||
.token-tag-name
|
||||
color: #4ec9b0
|
||||
|
||||
.token-attr
|
||||
color: #c586c0
|
||||
|
||||
.token-key
|
||||
color: #9cdcfe
|
||||
margin-left: 1rem
|
||||
|
||||
.token-string
|
||||
color: #ce9178
|
||||
|
||||
.token-number
|
||||
color: #b5cea8
|
||||
|
||||
.token-boolean
|
||||
color: #dcdcaa
|
||||
|
||||
.token-text
|
||||
color: #d4d4d4
|
||||
margin-left: .2rem
|
||||
|
||||
.cursor
|
||||
display: inline-block
|
||||
width: 1ch
|
||||
background-color: white
|
||||
animation: blink 1s steps(2, start) infinite
|
||||
|
||||
@keyframes blink
|
||||
to
|
||||
visibility: hidden
|
||||
</style>
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
<template>
|
||||
<div class="sideBarNaviSlider"
|
||||
<div
|
||||
class="sideBarNaviSlider"
|
||||
:class="{ 'slide-in': showSideBar }"
|
||||
@click="navigate">
|
||||
<slot></slot>
|
||||
<slot/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -1,4 +1,4 @@
|
||||
import { useI18n } from 'vue-i18n'
|
||||
/* import { useI18n } from 'vue-i18n'
|
||||
import { i18nPages } from '@/i18n/i18n-pages'
|
||||
|
||||
export function useI18nPages () {
|
||||
@ -31,3 +31,4 @@ export function useI18nPages () {
|
||||
getArticleLink
|
||||
}
|
||||
}
|
||||
*/
|
||||
@ -7,7 +7,7 @@ export const i18nPages = {
|
||||
es: '/inicio',
|
||||
tr: '/anasayfa'
|
||||
},
|
||||
webagency: {
|
||||
webagency: {
|
||||
de: '/webagentur',
|
||||
en: '/webagency',
|
||||
fr: '/agence-web',
|
||||
@ -15,7 +15,7 @@ export const i18nPages = {
|
||||
es: '/agencia-web',
|
||||
tr: '/web-ajansi'
|
||||
},
|
||||
services: {
|
||||
services: {
|
||||
de: '/leistungen',
|
||||
en: '/services',
|
||||
fr: '/services',
|
||||
@ -23,23 +23,39 @@ export const i18nPages = {
|
||||
es: '/servicios',
|
||||
tr: '/hizmetler'
|
||||
},
|
||||
'services/accessibility': {
|
||||
de: '/leistungen/barrierefreiheit',
|
||||
en: '/services/accessibility',
|
||||
fr: '/services/accessibilite',
|
||||
it: '/servizi/accessibilita',
|
||||
es: '/servicios/accesibilidad',
|
||||
tr: '/hizmetler/erisilebilirlik'
|
||||
'services-accessibility': {
|
||||
de: '/leistungen/barrierefreie-webseiten',
|
||||
en: '/services/accessible-websites',
|
||||
fr: '/services/sites-accessibles',
|
||||
it: '/servizi/siti-accessibili',
|
||||
es: '/servicios/sitios-accesibles',
|
||||
tr: '/hizmetler/erisilebilir-websiteler'
|
||||
},
|
||||
'services/seo': {
|
||||
de: '/leistungen/barrierefreiheit',
|
||||
en: '/services/accessibility',
|
||||
fr: '/services/accessibilite',
|
||||
it: '/servizi/accessibilita',
|
||||
es: '/servicios/accesibilidad',
|
||||
tr: '/hizmetler/erisilebilirlik'
|
||||
'services-seo': {
|
||||
de: '/leistungen/suchmaschinenoptimierung',
|
||||
en: '/services/search-engine-optimization',
|
||||
fr: '/services/optimisation-pour-moteurs-de-recherche',
|
||||
it: '/servizi/ottimizzazione-per-motori-di-ricerca',
|
||||
es: '/servicios/optimizacion-en-buscadores',
|
||||
tr: '/hizmetler/arama-motoru-optimizasyonu'
|
||||
},
|
||||
references: {
|
||||
'services-cms': {
|
||||
de: '/leistungen/headless-cms',
|
||||
en: '/services/headless-cms',
|
||||
fr: '/services/headless-cms',
|
||||
it: '/servizi/headless-cms',
|
||||
es: '/servicios/headless-cms',
|
||||
tr: '/hizmetler/headless-cms'
|
||||
},
|
||||
'services-ai': {
|
||||
de: '/leistungen/ki-kompatible-webseiten',
|
||||
en: '/services/ai-compatible-websites',
|
||||
fr: '/services/sites-compatibles-ia',
|
||||
it: '/servizi/siti-compatibili-ai',
|
||||
es: '/servicios/sitios-compatibles-ai',
|
||||
tr: '/hizmetler/ai-uyumlu-websiteler'
|
||||
},
|
||||
references: {
|
||||
de: '/referenzen',
|
||||
en: '/references',
|
||||
fr: '/références',
|
||||
@ -47,7 +63,7 @@ export const i18nPages = {
|
||||
es: '/referencias',
|
||||
tr: '/referanslar'
|
||||
},
|
||||
imprint: {
|
||||
imprint: {
|
||||
de: '/impressum',
|
||||
en: '/imprint',
|
||||
fr: '/mentions-legales',
|
||||
@ -55,17 +71,15 @@ export const i18nPages = {
|
||||
es: '/aviso-legal',
|
||||
tr: '/künye'
|
||||
},
|
||||
'projekt___link': {
|
||||
paths: {
|
||||
de: '/projekt/:link',
|
||||
en: '/projekt/:link',
|
||||
fr: '/projekt/:link',
|
||||
it: '/projekt/:link',
|
||||
es: '/projekt/:link',
|
||||
tr: '/projekt/:link'
|
||||
}
|
||||
'project-link': {
|
||||
de: '/projekt/[link]',
|
||||
en: '/project/[link]',
|
||||
fr: '/projet/[link]',
|
||||
it: '/progetto/[link]',
|
||||
es: '/proyecto/[link]',
|
||||
tr: '/proje/[link]'
|
||||
},
|
||||
designer: {
|
||||
designer: {
|
||||
de: '/designer',
|
||||
en: '/designers',
|
||||
fr: '/createurs',
|
||||
@ -73,7 +87,7 @@ export const i18nPages = {
|
||||
es: '/disenadores',
|
||||
tr: '/tasarimcilar'
|
||||
},
|
||||
privacy: {
|
||||
privacy: {
|
||||
de: '/datenschutz',
|
||||
en: '/privacy',
|
||||
fr: '/confidentialite',
|
||||
@ -81,7 +95,7 @@ export const i18nPages = {
|
||||
es: '/privacidad',
|
||||
tr: '/gizlilik'
|
||||
},
|
||||
terms: {
|
||||
terms: {
|
||||
de: '/agb',
|
||||
en: '/terms',
|
||||
fr: '/conditions',
|
||||
@ -89,7 +103,7 @@ export const i18nPages = {
|
||||
es: '/condiciones',
|
||||
tr: '/kosullar'
|
||||
},
|
||||
magazin: {
|
||||
magazin: {
|
||||
de: '/wissenswertes',
|
||||
en: '/magazine',
|
||||
fr: '/magazine',
|
||||
@ -97,14 +111,12 @@ export const i18nPages = {
|
||||
es: '/revista',
|
||||
tr: '/dergi'
|
||||
},
|
||||
'artikel___link': {
|
||||
paths: {
|
||||
de: '/artikel/:link',
|
||||
en: '/artikel/:link',
|
||||
fr: '/artikel/:link',
|
||||
it: '/artikel/:link',
|
||||
es: '/artikel/:link',
|
||||
tr: '/artikel/:link'
|
||||
}
|
||||
},
|
||||
'article-link': {
|
||||
de: '/artikel/[link]',
|
||||
en: '/article/[link]',
|
||||
fr: '/article/[link]',
|
||||
it: '/articolo/[link]',
|
||||
es: '/articulo/[link]',
|
||||
tr: '/makale/[link]'
|
||||
},
|
||||
}
|
||||
@ -4,6 +4,10 @@
|
||||
"upperBavaria": "Oberbayern",
|
||||
"webagency": "Webagentur",
|
||||
"services": "Leistungen",
|
||||
"menuAi": "KI-kompatible Webseiten",
|
||||
"menuCms": "Headless Content-Management-System (CMS)",
|
||||
"menuAccessibility": "Barrierefeies Webdesign",
|
||||
"menuSEO": "Suchmaschinen-Optimierung (SEO)",
|
||||
"contact": "Kontakt",
|
||||
"references": "Referenzen",
|
||||
"referenceoverview": "Referenzübersicht",
|
||||
@ -51,7 +55,7 @@
|
||||
}
|
||||
},
|
||||
"faqBox": {
|
||||
"questions": "Fragen?",
|
||||
"questions": "Weitere Fragen?",
|
||||
"faqsDefault": "Häufig gestellte Fragen (FAQs)",
|
||||
"btnDefault": "Sprechen Sie uns gerne an!"
|
||||
},
|
||||
@ -166,14 +170,71 @@
|
||||
"hero": {
|
||||
"headline1": "Barrierefeies Webdesign",
|
||||
"headline2": "Zugänglichkeit für Mensch und Maschine nach den Standards WCAG und BITV",
|
||||
"headline3": "Verständlich, nutzbar und robust, für eine inklusive und zukunftssichere Webseite"
|
||||
"headline3": "Verständlich, nutzbar und robust, für eine inklusive und zukunftssichere Webseite",
|
||||
"ariaLabel": "Barrierefreies Webdesign"
|
||||
}
|
||||
},
|
||||
"seo": {
|
||||
"hero": {
|
||||
"headline1": "Suchmaschinen-Optimierung",
|
||||
"headline2": "mit strukturierten Daten, Meta-Tags und semantischem HTML",
|
||||
"headline3": "Mehr Sichbarkeit durch technische Exzellenz!"
|
||||
"headline3": "Mehr Sichbarkeit durch technische Exzellenz!",
|
||||
"ariaLabel": "Suchmaschinen-Optimierung"
|
||||
}
|
||||
},
|
||||
"cms": {
|
||||
"hero": {
|
||||
"headline1": "Flexibles Headless Content-Management-System",
|
||||
"headline2": "mit strukturierten Daten, Meta-Tags und semantischem HTML",
|
||||
"headline3": "Mehr Sichbarkeit durch technische Exzellenz",
|
||||
"ariaLabel": "Content-Management-System"
|
||||
}
|
||||
},
|
||||
"ai": {
|
||||
"hero": {
|
||||
"ariaLabel": "Hero-Bereich: KI-kompatible Webseiten",
|
||||
"headline1": "KI-kompatible Webseiten",
|
||||
"headline2": "Strukturierte, maschinenlesbare Inhalte für eine neue Generation digitaler Assistenten.",
|
||||
"headline3": "Bereit für Chatbots, Sprachsuche und generative KI-Systeme wie ChatGPT & Co.?"
|
||||
},
|
||||
"whyAI": {
|
||||
"headline": "Warum KI-Kompatibilität im Web immer wichtiger wird",
|
||||
"text1": "KI-Assistenten greifen heutzutage nicht nur auf strukturierte Inhalte im Web zu, um präzise Antworten zu liefern, sondern auch, um direkt mit Webseiten zu interagieren. Sie nehmen Buchungen vor, lösen Bestellungen aus oder vereinbaren Termine.",
|
||||
"text2": "Wer sicherstellen möchte, dass die eigene Webseite dabei berücksichtigt wird, muss eine technische Grundlage schaffen, um Sichtbarkeit zu sein und Interaktion zu ermöglichen."
|
||||
},
|
||||
"targetGroup": {
|
||||
"headline": "Für wen lohnt sich eine KI-kompatible Webseite?",
|
||||
"point1_headline": "Content-Ersteller & Medienunternehmen",
|
||||
"point1_description": "Maximieren Sie Ihre Reichweite, indem KI Ihre Inhalte intelligent versteht und verteilt.",
|
||||
"point2_headline": "Onlineshop-Betreiber",
|
||||
"point2_description": "Ermöglichen Sie Kunden, Ihre Produkte per Sprachbefehl zu finden und direkt zu kaufen.",
|
||||
"point3_headline": "Dienstleister & Freiberufler",
|
||||
"point3_description": "Werden Sie per Sprachsuche schnell gefunden und bieten Sie unkomplizierte Terminbuchungen an.",
|
||||
"point4_headline": "Unternehmen mit digitaler Wachstumsstrategie",
|
||||
"point4_description": "Bleiben Sie auf Suchmaschinen und bei KI-Assistenten an der Spitze der Sichtbarkeit."
|
||||
},
|
||||
"howItWorks": {
|
||||
"headline": "Was macht eine Webseite KI-kompatibel?",
|
||||
"pretext": "Damit künstliche Intelligenzen Webseiten richtig verstehen und Inhalte optimal nutzen können, braucht es mehr als nur gute Texte und schönes Design. Es braucht eine klar strukturierte, technisch saubere Basis.",
|
||||
"point1": "Strukturierte Daten via Schema.org & JSON-LD",
|
||||
"point2": "Semantisches HTML für kontextuelles Verständnis",
|
||||
"point3": "Logische Inhalts-Hierarchien",
|
||||
"point4": "Optimierung für Sprachsuche & Featured Snippets",
|
||||
"point5": "Content-Strategien für Large Language Models (LLMs)",
|
||||
"point6": "Maschinenlesbare Metadaten & Open Graph Tags",
|
||||
"posttext": "All diese maschinenlesbaren Strukturen integrieren wir direkt im Code. Ohne Plugins, ohne Umwege. So bleiben Performance und Kontrolle erhalten und die Basis der Website ist von Grund auf KI-optimiert."
|
||||
},
|
||||
"howWeDo": {
|
||||
"headline": "Wie wir KI-Kompatibilität mit Nuxt + Strapi umsetzen",
|
||||
"text": "Unser Headless-Ansatz mit Nuxt 3 und Strapi ermöglicht volle Kontrolle über Metadaten, Struktur und Performance.",
|
||||
"point1": "Integration strukturierter Daten direkt im CMS",
|
||||
"point2": "SEO-optimiertes Rendering durch SSR & SSG",
|
||||
"point3": "Zentrale Steuerung aller Meta- und JSON-LD-Daten",
|
||||
"point4": "Flexible Inhaltsmodelle für semantischen Aufbau",
|
||||
"point5": "Anpassung für Chatbots, Sprachassistenten und KI-Crawler"
|
||||
},
|
||||
"faq": {
|
||||
"headline": "Weitere Fragen zum Thema künstliche Intelligenz im Webdesign"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -17,11 +17,16 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
import { useMainStore } from '@/stores/main'
|
||||
const mainStore = useMainStore()
|
||||
import { usePageMeta } from '~/composables/usePageMeta'
|
||||
|
||||
usePageMeta()
|
||||
|
||||
onBeforeMount(() => {
|
||||
mainStore.setDarkHeroBack(false) // Standardmäßig false
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="sass">
|
||||
@ -168,19 +173,22 @@ main
|
||||
|
||||
.check
|
||||
list-style: none
|
||||
padding: .2rem 1rem
|
||||
margin: 0
|
||||
padding: 0 0 1rem 1rem
|
||||
|
||||
li
|
||||
position: relative
|
||||
padding-left: 1.5em
|
||||
margin: .8em 0
|
||||
position: relative
|
||||
padding-left: 2rem
|
||||
font-size: 1rem
|
||||
margin-bottom: 1rem
|
||||
|
||||
&::before
|
||||
content: "\2713"
|
||||
position: absolute
|
||||
left: 0
|
||||
color: $primaryColor
|
||||
&::before
|
||||
content: "✔"
|
||||
color: $primaryColor
|
||||
font-weight: bold
|
||||
position: absolute
|
||||
font-size: 1.4rem
|
||||
left: 0
|
||||
top: 0
|
||||
|
||||
section
|
||||
margin-bottom: 5vh
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="article" v-if="article">
|
||||
<div v-if="article" class="article">
|
||||
<SideBarNaviSlider link="/wissenswertes">
|
||||
{{ $t('pages.article.artikelUebersicht') }}
|
||||
</SideBarNaviSlider>
|
||||
@ -30,12 +30,12 @@
|
||||
</section>
|
||||
|
||||
<section class="articleBox container">
|
||||
<div v-html="htmlContent(article.content)" class="content"></div>
|
||||
<div class="content" v-html="htmlContent(article.content)"/>
|
||||
<button
|
||||
@click.prevent="toggleContactBubble"
|
||||
class="pinkBtn"
|
||||
role="button"
|
||||
aria-label="Kontakt aufnehmen"
|
||||
@click.prevent="toggleContactBubble"
|
||||
>
|
||||
{{ $t('pages.article.kontaktieren') }}
|
||||
</button>
|
||||
@ -49,6 +49,9 @@
|
||||
|
||||
|
||||
<script setup>
|
||||
definePageMeta({
|
||||
name: 'article-link'
|
||||
})
|
||||
import { computed } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { useMainStore } from '@/stores/main'
|
||||
@ -37,18 +37,18 @@
|
||||
</ul>
|
||||
<p><b>Du bleibst der kreative Kopf!</b><br>
|
||||
Ich sorge dafür, dass dein Design genauso im Web erscheint, wie du es geplant hast – mit Top-Performance & perfekter technischer Umsetzung.</p>
|
||||
<button class="pinkBtn" @click.prevent="toggleContactBubble" role="button">Lass uns kennenlernen</button>
|
||||
<button class="pinkBtn" role="button" @click.prevent="toggleContactBubble">Lass uns kennenlernen</button>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
<section class="deviceCheck" v-if="false">
|
||||
<section v-if="false" class="deviceCheck">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<p class="supheadlinePink">Responsive Check</p>
|
||||
<h2>Wie sieht deine Webseite auf den verschiedenen Bildschirmbreiten aus?</h2>
|
||||
<h3>Probiers einfach aus!</h3>
|
||||
<input type="text" placeholder="https://www.deinewebseite.de" v-model="weblink" @keyup.enter="updateIframe">
|
||||
<input v-model="weblink" type="text" placeholder="https://www.deinewebseite.de" @keyup.enter="updateIframe">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<svg v-if="desk" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 450">
|
||||
@ -81,7 +81,7 @@
|
||||
<rect id="deskScreenBlank" x="46" y="47.5" width="409.5" height="221.5" transform="matrix(1,0,0,1,0,0)" fill="rgb(0,0,0)"/>
|
||||
<g>
|
||||
<foreignObject v-if="isValidUrl(weblink)" id="deskScreen" x="46" y="47.5" width="409.5" height="221.5">
|
||||
<iframe ref="iframeRef" :src="weblink" frameborder="0"></iframe>
|
||||
<iframe ref="iframeRef" :src="weblink" frameborder="0"/>
|
||||
</foreignObject>
|
||||
</g>
|
||||
</g>
|
||||
@ -119,19 +119,20 @@
|
||||
</div>
|
||||
</section>
|
||||
<Recommendations />
|
||||
<section class="contrastCalcLink" v-if="false">
|
||||
<section v-if="false" class="contrastCalcLink">
|
||||
<div class="container">
|
||||
<img src="https://strapi.digimedialoop.de/uploads/wcag_kontrastrechner_77abf9d9be.png" alt="kontrast check" class="imgRight">
|
||||
<p class="supheadlinePink">Barrierefreies Webdesign</p>
|
||||
<h2>Hat Dein Design den richtigen Kontrast?</h2>
|
||||
<p>Mit dem praktischen Kontrastchecker kannst Du herausfinden, ob die Schrift und der Hintergrund in Deinem Design den WCAG_Richtlinien entsprechen. So stellst Du sicher, dass Dein Design für alle gut lesbar ist.</p>
|
||||
<button class="mintBtn" @click.prevent="navigateTo('/toolbox/kontrastchecker')"
|
||||
role="button"
|
||||
aria-label="Zum Kontrastchecker">Jetzt kostenlos Konstrast prüfen</button>
|
||||
<button
|
||||
class="mintBtn" role="button"
|
||||
aria-label="Zum Kontrastchecker"
|
||||
@click.prevent="navigateTo('/toolbox/kontrastchecker')">Jetzt kostenlos Konstrast prüfen</button>
|
||||
</div>
|
||||
</section>
|
||||
<FAQArea
|
||||
pageLink="/webentwicklung-fuer-designer-und-mediengestalter"
|
||||
page-link="/webentwicklung-fuer-designer-und-mediengestalter"
|
||||
headline="Wichtige Antworten zur Zusammenarbeit im Überblick für Dich"
|
||||
button="Dann lass uns quatschen!"
|
||||
/>
|
||||
@ -139,11 +140,11 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, onUnmounted, nextTick } from "vue";
|
||||
import { useMainStore } from '@/stores/main';
|
||||
definePageMeta({
|
||||
name: 'designer'
|
||||
})
|
||||
import { ref, onMounted, onUnmounted, nextTick } from "vue";
|
||||
import { useMainStore } from '@/stores/main';
|
||||
// Lade diese Komponente synchron wegen SEO
|
||||
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
headline2: 'pages.services.hero.headline2',
|
||||
headline3: 'pages.services.hero.headline3'
|
||||
}"
|
||||
:dark-background="true"
|
||||
/>
|
||||
|
||||
<section class="explainBox" :aria-label="$t('pages.services.explain.ariaLabel')">
|
||||
@ -32,7 +33,9 @@
|
||||
<li>{{ $t('pages.services.explain.bullet4') }}</li>
|
||||
<li>{{ $t('pages.services.explain.bullet5') }}</li>
|
||||
</ul>
|
||||
<button @click="navigateTo(localePath('accessibility'))">Informationen zu barrierefreien Webseiten</button>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
@ -88,6 +91,10 @@ class="pinkBtn mt-3"
|
||||
import { useMainStore } from '@/stores/main';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useRuntimeConfig, navigateTo } from '#app'
|
||||
import { useLocalePath } from '#i18n'
|
||||
|
||||
const localePath = useLocalePath()
|
||||
|
||||
const mainStore = useMainStore();
|
||||
const { customers } = storeToRefs(mainStore);
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
class="article"
|
||||
>
|
||||
|
||||
<NuxtLink :to="getArticleLink(article.slug)" class="article-link">
|
||||
<NuxtLinkLocale :to="localePath({ name: 'article-link', params: { link: article.slug } })" class="article-link">
|
||||
<div class="image-wrapper">
|
||||
<NuxtImg
|
||||
v-if="article.image?.url"
|
||||
@ -31,7 +31,7 @@
|
||||
<button class="mintBtn">{{ $t('pages.magazin.readmore') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</NuxtLink>
|
||||
</NuxtLinkLocale>
|
||||
|
||||
|
||||
</div>
|
||||
@ -42,14 +42,14 @@
|
||||
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, watch } from 'vue'
|
||||
import { watch } from 'vue'
|
||||
import { useMainStore } from '@/stores/main'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useLocalePath } from '#i18n'
|
||||
const localePath = useLocalePath()
|
||||
|
||||
const mainStore = useMainStore()
|
||||
const { articles, cmsUrl } = storeToRefs(mainStore)
|
||||
|
||||
const { getArticleLink } = useI18nPages()
|
||||
const { articles } = storeToRefs(mainStore)
|
||||
|
||||
const truncateText = (text: string, length = 200) =>
|
||||
text?.length > length ? text.substring(0, length) + '…' : text
|
||||
@ -58,6 +58,7 @@ const currentDomain = typeof window !== 'undefined'
|
||||
? window.location.origin
|
||||
: 'https://www.digimedialoop.de'
|
||||
|
||||
|
||||
// SEO: JSON-LD für Artikelübersicht
|
||||
watch(articles, (newVal) => {
|
||||
if (newVal?.length) {
|
||||
|
||||
@ -46,8 +46,8 @@
|
||||
:src="img.url"
|
||||
:alt="img.alternativeText"
|
||||
provider="strapi"
|
||||
@click="setCurrentImage(img)"
|
||||
:class="{ active: currentImage?.url === img.url }"
|
||||
@click="setCurrentImage(img)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -55,7 +55,7 @@
|
||||
|
||||
|
||||
<div class="col-lg-8 pt-4">
|
||||
<span v-html="htmlContent(project.projectDescription)"></span>
|
||||
<span v-html="htmlContent(project.projectDescription)"/>
|
||||
|
||||
|
||||
<h4>{{ t('project.detail.technologies', 'Verwendete Technologien') }}</h4>
|
||||
@ -68,7 +68,7 @@
|
||||
{{ tech.titel }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="row" v-if="project.webpage">
|
||||
<div v-if="project.webpage" class="row">
|
||||
<div class="col-12 text-end">
|
||||
<a
|
||||
class="webPageBtn"
|
||||
@ -93,15 +93,20 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
name: 'project-link'
|
||||
})
|
||||
import { ref, computed, watch } from "vue"
|
||||
import { useI18n } from 'vue-i18n'
|
||||
const { t } = useI18n()
|
||||
const localePath = useLocalePath()
|
||||
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
const route = useRoute()
|
||||
|
||||
import { useMainStore } from '@/stores/main'
|
||||
|
||||
import { useHtmlConverter } from '@/composables/useHTMLConverter'
|
||||
const { t } = useI18n()
|
||||
const localePath = useLocalePath()
|
||||
const route = useRoute()
|
||||
const mainStore = useMainStore()
|
||||
const project = computed(() => mainStore.getProjectByLink(route.params.link))
|
||||
const customer = computed(() => {
|
||||
@ -121,8 +126,6 @@ watch(project, (newProject) => {
|
||||
currentImage.value = null
|
||||
}
|
||||
})
|
||||
|
||||
import { useHtmlConverter } from '@/composables/useHTMLConverter'
|
||||
const { convertToHTML } = useHtmlConverter()
|
||||
const htmlContent = (data: string) => {
|
||||
return convertToHTML(data)
|
||||
@ -3,18 +3,18 @@
|
||||
<section class="topSpace">
|
||||
<div class="container">
|
||||
<h1>{{ $t('pages.references.hero.h1') }}</h1>
|
||||
<p v-html="$t('pages.references.hero.p')"></p>
|
||||
<p v-html="$t('pages.references.hero.p')"/>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div class="referenceBox" v-if="projects && projects.length">
|
||||
<div v-if="projects && projects.length" class="referenceBox">
|
||||
<slot>
|
||||
<NuxtLink
|
||||
class="reference"
|
||||
<NuxtLinkLocale
|
||||
v-for="project in projects"
|
||||
:key="project.id"
|
||||
:to="getProjectLink(project.link)"
|
||||
class="reference"
|
||||
:to="localePath({ name: 'project-link', params: { link: project.link } })"
|
||||
>
|
||||
<NuxtImg
|
||||
provider="strapi"
|
||||
@ -38,7 +38,7 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</NuxtLink>
|
||||
</NuxtLinkLocale>
|
||||
</slot>
|
||||
</div>
|
||||
</section>
|
||||
@ -46,7 +46,7 @@
|
||||
<CallToActionBox
|
||||
:headline="$t('pages.references.ctaBox.headline')"
|
||||
:text="$t('pages.references.ctaBox.text')"
|
||||
:buttonText="$t('pages.references.ctaBox.button')"
|
||||
:button-text="$t('pages.references.ctaBox.button')"
|
||||
/>
|
||||
|
||||
</div>
|
||||
@ -55,6 +55,8 @@
|
||||
<script setup>
|
||||
import { useMainStore } from '@/stores/main'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useLocalePath } from '#i18n'
|
||||
const localePath = useLocalePath()
|
||||
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const cmsUrl = runtimeConfig.public.cmsBaseUrl
|
||||
@ -62,8 +64,6 @@ const cmsUrl = runtimeConfig.public.cmsBaseUrl
|
||||
const mainStore = useMainStore()
|
||||
const { projects } = storeToRefs(mainStore)
|
||||
|
||||
import { useI18nPages } from '@/composables/useI18nPages'
|
||||
const { getProjectLink } = useI18nPages()
|
||||
|
||||
function getCustomerLogo(customerId) {
|
||||
if (!customerId) return ''
|
||||
@ -95,7 +95,7 @@ const jsonLdProjects = computed(() => {
|
||||
"position": index + 1,
|
||||
"item": {
|
||||
"@type": "CreativeWork", // alternativ "WebPage", wenn es sich um Projektseiten handelt
|
||||
"@id": origin + (project.link ? getProjectLink(project.link) : ''),
|
||||
"@id": origin + (project.link ? project.link : ''),
|
||||
"name": project.projectTitle || 'Projekt',
|
||||
"image": cmsUrl + (project.projectImages?.[0]?.url || ''),
|
||||
"description": project.projectDescription?.[0]?.children?.[0]?.text || ''
|
||||
|
||||
@ -8,11 +8,89 @@
|
||||
headline2: 'pages.services.accessibility.hero.headline2',
|
||||
headline3: 'pages.services.accessibility.hero.headline3'
|
||||
}"
|
||||
overlay-image="/uploads/DML_Access_Key_3ccf07fbb4.webp"
|
||||
overlay-alt-text="barrierefreiheit symbol"
|
||||
:overlay-width="'30%'"
|
||||
:overlay-position="{ bottom: '5%', right: '1vw' }"
|
||||
:dark-background="true"
|
||||
/>
|
||||
<section class="targetGroup">
|
||||
|
||||
<div class="container-10">
|
||||
<NuxtImg
|
||||
provider="strapi"
|
||||
src="/uploads/rollstuhl_a67c73b6a9.webp"
|
||||
alt="accessibility"
|
||||
/>
|
||||
<h2>Für wen ist Barrierefreiheit wichtig?</h2>
|
||||
<p>Barrierefreiheit macht Ihre Webseite nicht nur für alle Menschen, insbesondere für Menschen mit Einschränkungen zugänglich, sondern verbessert auch die Auffindbarkeit bei Suchmaschinen und die Nutzbarkeit durch moderne KI-Systeme.</p>
|
||||
<h3>Auf was kommt es bei Barrierefreiheit im Web an?</h3>
|
||||
<ul class="check">
|
||||
<li><b>Tastaturbedienbarkeit:</b> Alle Funktionen sind ohne Maus erreichbar.</li>
|
||||
<li><b>Kontraststarke Gestaltung:</b> Gute Lesbarkeit für alle Sehfähigkeiten.</li>
|
||||
<li><b>Screenreader-Kompatibilität:</b> Inhalte werden klar und verständlich vorgelesen.</li>
|
||||
<li><b>Logische Struktur:</b> Überschriften und Inhalte folgen einer nachvollziehbaren Reihenfolge.</li>
|
||||
<li><b>Barrierefreie Medien:</b> Texte als Alternativen, keine automatisch startenden Videos ohne Kontrolle.</li>
|
||||
</ul>
|
||||
<h3>Möchten Sie wissen, wie barrierefrei Ihre aktuelle Webseite ist?</h3>
|
||||
<button
|
||||
class="pinkBtn" role="button"
|
||||
aria-label="Barrierefreiheitscheck"
|
||||
@click.prevent="toggleContactBubble">Kostenlosen Barrierefreiheits-Check anfordern!</button>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
<section class="legalBasis">
|
||||
<svg class="sectionWave wave-top" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 20">
|
||||
<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">
|
||||
<h2>Rechtliche Grundlagen für Barrierefreiheit in Deutschland und der EU</h2>
|
||||
<p>Barrierefreiheit im Web ist nicht nur eine gute Praxis, sondern in Deutschland und der Europäischen Union auch gesetzlich vorgeschrieben. Diese Vorgaben sollen sicherstellen, dass digitale Angebote für alle Menschen uneingeschränkt nutzbar sind.</p>
|
||||
|
||||
<h3>Wichtige Gesetze und Verordnungen:</h3>
|
||||
<ul class="check">
|
||||
<li><b>Behindertengleichstellungsgesetz (BGG):</b> Regelt die Barrierefreiheit öffentlicher Einrichtungen und deren digitale Angebote in Deutschland.</li>
|
||||
<li><b>Barrierefreie-Informationstechnik-Verordnung (BITV 2.0):</b> Konkretisiert die Anforderungen des BGG für Webseiten und mobile Anwendungen öffentlicher Stellen.</li>
|
||||
<li><b>EU-Richtlinie 2016/2102:</b> Verpflichtet öffentliche Stellen der Mitgliedstaaten, ihre Websites und mobilen Apps barrierefrei zu gestalten.</li>
|
||||
<li><b>European Accessibility Act (EAA):</b> Richtlinie für barrierefreie Produkte und Dienstleistungen, inklusive digitaler Angebote.</li>
|
||||
<li><b>WCAG 2.1 (Web Content Accessibility Guidelines):</b> International anerkannte technische Standards, die als Basis für gesetzliche Vorgaben dienen.</li>
|
||||
</ul>
|
||||
<p>Die Einhaltung dieser Regelungen schützt Sie vor Abmahnungen und verbessert Ihre Reichweite durch bessere Zugänglichkeit für alle Nutzer.</p>
|
||||
</div>
|
||||
<svg class="sectionWave wave-bottom" style="transform: scale(1,-1)" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 20">
|
||||
<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="implementation">
|
||||
<div class="container-10">
|
||||
<h2>Wie wir Barrierefreiheit für Sie umsetzen</h2>
|
||||
<p>
|
||||
Mit Nuxt im Front-End und Strapi als flexiblem Headless CMS im Back-End, realisieren wir barrierefreie Webseiten, die sowohl technisch sauber als auch leicht wartbar sind.
|
||||
Durch die Trennung von Front-End und Back-End können Inhalte effizient gepflegt und barrierefreie Standards systematisch umgesetzt werden.
|
||||
</p>
|
||||
<h3>Vorteile gegenüber klassischen CMS wie WordPress oder Contao</h3>
|
||||
<ul class="check">
|
||||
<li><b>Modulare Architektur:</b> Flexible Komponenten und wiederverwendbare Module erleichtern die Umsetzung von Barrierefreiheitsstandards.</li>
|
||||
<li><b>Performance:</b> Nuxt generiert schnelle, serverseitig gerenderte Seiten, die für Nutzer und Suchmaschinen gleichermaßen optimal sind.</li>
|
||||
<li><b>SEO & KI-Freundlichkeit:</b> Durch gezielte Meta-Tags und strukturierte Daten unterstützen wir bessere Auffindbarkeit und Kompatibilität mit KI-Systemen.</li>
|
||||
<li><b>Content-Management:</b> Strapi ermöglicht barrierefreie Inhalte mit benutzerfreundlichen Editoren und unterstützt mehrsprachige Seiten einfach.</li>
|
||||
<li><b>Zukunftssicherheit:</b> Moderne Technologien garantieren nachhaltige Wartbarkeit und Erweiterbarkeit der Webseite.</li>
|
||||
</ul>
|
||||
<button v-if="false" class="mintBtn">Mehr zum Thema Headless CMS</button>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
<FAQArea page-link="/leistungen/barrierefreie-webseiten" headline="Weitere Fragen zum Thema Barrierefreiheit im Web" />
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useMainStore } from '@/stores/main';
|
||||
definePageMeta({
|
||||
alias: [
|
||||
'/leistungen/barrierefreie-webseiten', // Deutsch
|
||||
@ -21,11 +99,27 @@ definePageMeta({
|
||||
'/servizi/accessibilita', // Italienisch
|
||||
'/servicios/accesibilidad', // Spanisch
|
||||
'/hizmetler/erisilebilirlik' // Türkisch
|
||||
]
|
||||
],
|
||||
name: 'services-accessibility'
|
||||
})
|
||||
|
||||
const mainStore = useMainStore();
|
||||
const toggleContactBubble = () => mainStore.toggleContactBubble();
|
||||
</script>
|
||||
|
||||
<style lang="sass">
|
||||
|
||||
.accessiblityPage
|
||||
.legalBasis
|
||||
background: linear-gradient(90deg, #39324A 0%, #403871 100%);
|
||||
color: white
|
||||
padding: 12vh 0
|
||||
margin: 8vh 0
|
||||
.targetGroup
|
||||
img
|
||||
float: left
|
||||
width: 30%
|
||||
max-width: 400px
|
||||
margin: 1rem 2rem 2rem 0
|
||||
.implementation
|
||||
margin: 8vh 0
|
||||
padding: 1vh 0 3vh 0
|
||||
</style>
|
||||
173
pages/services/ai/index.vue
Normal file
173
pages/services/ai/index.vue
Normal file
@ -0,0 +1,173 @@
|
||||
<template>
|
||||
<div class="aiPage">
|
||||
<HeroBox
|
||||
image="/uploads/DML_Service_Header_AI_639dd0d7b1.webp"
|
||||
:aria-label="$t('pages.services.ai.hero.ariaLabel')"
|
||||
:content="{
|
||||
headline1: 'pages.services.ai.hero.headline1',
|
||||
headline2: 'pages.services.ai.hero.headline2',
|
||||
headline3: 'pages.services.ai.hero.headline3'
|
||||
}"
|
||||
overlay-image="/uploads/kirby_fly_3de66b2839.webp"
|
||||
overlay-alt-text="ki roboter fliegt"
|
||||
:overlay-width="'32%'"
|
||||
:overlay-position="{ top: '28%', right: '3vw' }"
|
||||
:dark-background="true"
|
||||
/>
|
||||
<section class="whyAI">
|
||||
<div class="container-10">
|
||||
<NuxtImg
|
||||
provider="strapi"
|
||||
src="/uploads/AI_robot_67b96e6a17.webp"
|
||||
alt="roboter sitzt auf mouse"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<h2>{{ $t('pages.services.ai.whyAI.headline') }}</h2>
|
||||
<p>{{ $t('pages.services.ai.whyAI.text1') }}</p>
|
||||
<p>{{ $t('pages.services.ai.whyAI.text2') }}</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="targetGroup">
|
||||
<svg class="sectionWave wave-top" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 20">
|
||||
<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">
|
||||
<h2>{{ $t('pages.services.ai.targetGroup.headline') }}</h2>
|
||||
<ul class="check">
|
||||
<li>
|
||||
<strong>{{ $t('pages.services.ai.targetGroup.point1_headline') }}</strong><br>
|
||||
{{ $t('pages.services.ai.targetGroup.point1_description') }}
|
||||
</li>
|
||||
<li>
|
||||
<strong>{{ $t('pages.services.ai.targetGroup.point2_headline') }}</strong><br>
|
||||
{{ $t('pages.services.ai.targetGroup.point2_description') }}
|
||||
</li>
|
||||
<li>
|
||||
<strong>{{ $t('pages.services.ai.targetGroup.point3_headline') }}</strong><br>
|
||||
{{ $t('pages.services.ai.targetGroup.point3_description') }}
|
||||
</li>
|
||||
<li>
|
||||
<strong>{{ $t('pages.services.ai.targetGroup.point4_headline') }}</strong><br>
|
||||
{{ $t('pages.services.ai.targetGroup.point4_description') }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<svg class="sectionWave wave-bottom" style="transform: scale(1,-1)" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 20">
|
||||
<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="howItWorks">
|
||||
<div class="container-10">
|
||||
<h2>{{ $t('pages.services.ai.howItWorks.headline') }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="coderWrap">
|
||||
<div class="text">
|
||||
|
||||
<p>{{ $t('pages.services.ai.howItWorks.pretext') }}</p>
|
||||
<ul class="check">
|
||||
<li>{{ $t('pages.services.ai.howItWorks.point1') }}</li>
|
||||
<li>{{ $t('pages.services.ai.howItWorks.point2') }}</li>
|
||||
<li>{{ $t('pages.services.ai.howItWorks.point3') }}</li>
|
||||
<li>{{ $t('pages.services.ai.howItWorks.point4') }}</li>
|
||||
<li>{{ $t('pages.services.ai.howItWorks.point5') }}</li>
|
||||
<li>{{ $t('pages.services.ai.howItWorks.point6') }}</li>
|
||||
</ul>
|
||||
<p>{{ $t('pages.services.ai.howItWorks.posttext') }}</p>
|
||||
</div>
|
||||
<div class="coderArea">
|
||||
<SchemaCoder />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="howWeDo">
|
||||
<svg class="sectionWave wave-top" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 20">
|
||||
<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">
|
||||
<h2>{{ $t('pages.services.ai.howWeDo.headline') }}</h2>
|
||||
<p>{{ $t('pages.services.ai.howWeDo.text') }}</p>
|
||||
<ul class="check">
|
||||
<li>{{ $t('pages.services.ai.howWeDo.point1') }}</li>
|
||||
<li>{{ $t('pages.services.ai.howWeDo.point2') }}</li>
|
||||
<li>{{ $t('pages.services.ai.howWeDo.point3') }}</li>
|
||||
<li>{{ $t('pages.services.ai.howWeDo.point4') }}</li>
|
||||
<li>{{ $t('pages.services.ai.howWeDo.point5') }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<svg class="sectionWave wave-bottom" style="transform: scale(-1,-1)" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 20">
|
||||
<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>
|
||||
|
||||
<FAQArea
|
||||
page-link="/leistungen/ki-kompatible-webseiten"
|
||||
:headline="$t('pages.services.ai.faq.headline')"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useMainStore } from '@/stores/main'
|
||||
const mainStore = useMainStore()
|
||||
definePageMeta({
|
||||
alias: [
|
||||
'/leistungen/ki-kompatible-webseiten', // Deutsch
|
||||
'/services/ai-compatible-websites', // Englisch
|
||||
'/services/sites-compatibles-ia', // Französisch
|
||||
'/servizi/siti-compatibili-ai', // Italienisch
|
||||
'/servicios/sitios-compatibles-ai', // Spanisch
|
||||
'/hizmetler/ai-uyumlu-websiteler' // Türkisch
|
||||
],
|
||||
name: 'services-ai'
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="sass">
|
||||
.aiPage
|
||||
.targetGroup
|
||||
background: linear-gradient(90deg, #3A283E 0%, #6A3385 100%);
|
||||
color: white
|
||||
padding: 12vh 0
|
||||
margin: 8vh 0
|
||||
li
|
||||
line-height: 1.5rem
|
||||
margin-bottom: 1.5rem
|
||||
strong
|
||||
font-size: 110%
|
||||
|
||||
.howWeDo
|
||||
background: linear-gradient(90deg, #6A3385 0%, #3A283E 100%);
|
||||
color: white
|
||||
padding: 12vh 0
|
||||
margin: 8vh 0
|
||||
.whyAI
|
||||
padding: 3% 0
|
||||
img
|
||||
width: 30%
|
||||
max-width: 280px
|
||||
float: left
|
||||
margin: 1% 6vw 5% 0
|
||||
.howItWorks
|
||||
overflow: hidden
|
||||
.coderWrap
|
||||
display: flex
|
||||
align-items: center
|
||||
justify-content: flex-start
|
||||
padding-left: 10%
|
||||
position: relative
|
||||
.text
|
||||
flex: 0 0 auto
|
||||
width: 50%
|
||||
margin-left: 2%
|
||||
|
||||
.coderArea
|
||||
flex: 0 0 auto
|
||||
width: 60%
|
||||
transform: translateX(10%)
|
||||
overflow: hidden
|
||||
</style>
|
||||
32
pages/services/cms/index.vue
Normal file
32
pages/services/cms/index.vue
Normal file
@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<div class="cmsPage">
|
||||
<HeroBox
|
||||
image="/uploads/DML_Service_Header_CMS_a438599970.webp"
|
||||
:aria-label="$t('pages.services.cms.hero.ariaLabel')"
|
||||
:content="{
|
||||
headline1: 'pages.services.cms.hero.headline1',
|
||||
headline2: 'pages.services.cms.hero.headline2',
|
||||
headline3: 'pages.services.cms.hero.headline3'
|
||||
}"
|
||||
:dark-background="true"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
definePageMeta({
|
||||
alias: [
|
||||
'/leistungen/headless-cms', // Deutsch
|
||||
'/services/headless-cms', // Englisch
|
||||
'/services/headless-cms', // Französisch
|
||||
'/servizi/headless-cms', // Italienisch
|
||||
'/servicios/headless-cms', // Spanisch
|
||||
'/hizmetler/headless-cms' // Türkisch
|
||||
],
|
||||
name: 'services-cms'
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="sass">
|
||||
|
||||
</style>
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="servicesPage">
|
||||
<section class="heroBox_service" aria-labelledby="hero-heading">
|
||||
<NuxtImg
|
||||
provider="strapi"
|
||||
@ -102,7 +102,7 @@ class="pinkBtn" role="button"
|
||||
|
||||
<MarqueeBanner
|
||||
:items="projectItems"
|
||||
:logoHeight="200"
|
||||
:logo-height="200"
|
||||
:title="$t('pages.services.marquee.title')"
|
||||
link="projekt"
|
||||
:aria-label="$t('pages.services.marquee.title')"
|
||||
@ -112,7 +112,7 @@ class="pinkBtn" role="button"
|
||||
<CallToActionBox
|
||||
:headline="$t('pages.services.ctaBox.headline')"
|
||||
:text="$t('pages.services.ctaBox.text')"
|
||||
:buttonText="$t('pages.services.ctaBox.button')"
|
||||
:button-text="$t('pages.services.ctaBox.button')"
|
||||
/>
|
||||
|
||||
</div>
|
||||
@ -253,179 +253,180 @@ watchEffect(() => {
|
||||
</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
|
||||
.servicesPage
|
||||
.heroBox_service
|
||||
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
|
||||
min-height: 35rem
|
||||
height: 70vh
|
||||
display: flex
|
||||
align-items: center
|
||||
justify-content: center
|
||||
align-items: stretch
|
||||
grid-template-columns: 1fr // Default: 1 Spalte
|
||||
overflow: hidden
|
||||
|
||||
@media (min-width: $breakPointMD)
|
||||
grid-template-columns: repeat(2, 1fr)
|
||||
|
||||
@media (min-width: $breakPointXL)
|
||||
grid-template-columns: repeat(4, 1fr)
|
||||
|
||||
.canDoBox
|
||||
.hero-bg
|
||||
position: absolute
|
||||
inset: 0
|
||||
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%
|
||||
object-fit: cover
|
||||
object-position: center bottom
|
||||
z-index: 0
|
||||
|
||||
.canDoItem
|
||||
width: 100%
|
||||
.container-10, h1, h2, h3
|
||||
position: relative
|
||||
z-index: 1
|
||||
|
||||
.imageBox
|
||||
margin: 0rem auto 1rem auto
|
||||
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%
|
||||
max-width: 180px
|
||||
aspect-ratio: 5 / 4
|
||||
object-fit: cover
|
||||
border-radius: 1rem
|
||||
display: block
|
||||
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%
|
||||
|
||||
&:nth-child(1)
|
||||
border-radius: $loopShape1
|
||||
&:nth-child(2)
|
||||
border-radius: $loopShape2
|
||||
&:nth-child(3)
|
||||
border-radius: $loopShape3
|
||||
&:nth-child(4)
|
||||
border-radius: $loopShape4
|
||||
.canDoItem
|
||||
width: 100%
|
||||
|
||||
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
|
||||
.imageBox
|
||||
margin: 0rem auto 1rem auto
|
||||
width: 100%
|
||||
max-width: 180px
|
||||
aspect-ratio: 5 / 4
|
||||
object-fit: cover
|
||||
border-radius: 1rem
|
||||
display: block
|
||||
|
||||
p
|
||||
font-size: .9rem
|
||||
text-align: left !important
|
||||
&: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%
|
||||
.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>
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="accessiblityPage">
|
||||
<div class="seoPage">
|
||||
<HeroBox
|
||||
image="/uploads/DML_Service_Header_SEO_b11ae8940a.webp"
|
||||
:aria-label="$t('pages.services.seo.hero.ariaLabel')"
|
||||
@ -8,6 +8,7 @@
|
||||
headline2: 'pages.services.seo.hero.headline2',
|
||||
headline3: 'pages.services.seo.hero.headline3'
|
||||
}"
|
||||
:dark-background="true"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@ -21,7 +22,8 @@ definePageMeta({
|
||||
'/servizi/ottimizzazione-motori-di-ricerca',// Italienisch
|
||||
'/servicios/optimizacion-motores-busqueda', // Spanisch
|
||||
'/hizmetler/arama-motoru-optimizasyonu' // Türkisch
|
||||
]
|
||||
],
|
||||
name: 'services-seo'
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
@ -10,9 +10,9 @@
|
||||
<p v-html="$t('pages.webagency.hero.text3')" />
|
||||
<button
|
||||
class="my-4 pinkBtn"
|
||||
@click.prevent="toggleContactBubble"
|
||||
role="button"
|
||||
aria-label="Kontaktformular öffnen"
|
||||
@click.prevent="toggleContactBubble"
|
||||
>
|
||||
{{ $t('pages.webagency.hero.button') }}
|
||||
</button>
|
||||
@ -75,9 +75,9 @@
|
||||
|
||||
<button
|
||||
class="pinkBtn mt-4"
|
||||
@click.prevent="toggleContactBubble"
|
||||
role="button"
|
||||
aria-label="Kontaktformular öffnen"
|
||||
@click.prevent="toggleContactBubble"
|
||||
>
|
||||
{{ $t('pages.webagency.team.button') }}
|
||||
</button>
|
||||
@ -91,9 +91,9 @@
|
||||
<h2>{{ $t('pages.webagency.grafiker.title') }}</h2>
|
||||
<button
|
||||
class="mintBtn"
|
||||
@click.prevent="navigateTo(designerLink)"
|
||||
role="button"
|
||||
aria-label="Zum Angebot für Kreative"
|
||||
@click.prevent="navigateTo(designerLink)"
|
||||
>
|
||||
{{ $t('pages.webagency.grafiker.button') }}
|
||||
</button>
|
||||
|
||||
@ -111,6 +111,8 @@ export const useMainStore = defineStore('main', {
|
||||
contactBoxOpen: false,
|
||||
scrollPosition: 0,
|
||||
screenWidth: 1440,
|
||||
heroBoxHeight: 0,
|
||||
darkHeroBack: false,
|
||||
companyinfo: null as CompanyInfo | null,
|
||||
pages: [] as Page[],
|
||||
customers: [] as Customer[],
|
||||
@ -162,6 +164,12 @@ export const useMainStore = defineStore('main', {
|
||||
setScreenWidth(width: number) {
|
||||
this.screenWidth = width
|
||||
},
|
||||
setHeroBoxHeight(height: number) {
|
||||
this.heroBoxHeight = height
|
||||
},
|
||||
setDarkHeroBack(value: boolean) {
|
||||
this.darkHeroBack = value
|
||||
},
|
||||
|
||||
// SEND CONTACT REQUEST TO STRAPI
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user