accessability check

This commit is contained in:
Sabrina Hennrich 2025-05-20 15:05:05 +02:00
parent fa1f912089
commit 5771bb7cdd
3 changed files with 397 additions and 396 deletions

View File

@ -1,149 +1,190 @@
<template> <template>
<div <div
class="contactBubble" class="contactBubble"
:class="{ active: isContactBubbleOpen }" :class="{ active: isContactBubbleOpen }"
aria-hidden="false" :aria-hidden="!isContactBubbleOpen"
:aria-expanded="isContactBubbleOpen" :aria-expanded="isContactBubbleOpen"
aria-labelledby="controlIcon" aria-labelledby="controlIcon"
role="dialog" role="dialog"
> >
<svg <svg
id="controlIcon" id="controlIcon"
tabindex="0" tabindex="0"
role="button" role="button"
aria-label="Toggle contact form" aria-label="Toggle contact form"
@click="toggleContactBubble" @click="toggleContactBubble"
> >
<use :xlink:href="`/assets/icons/collection.svg#${isContactBubbleOpen ? 'times' : 'talk'}`"/> <use :xlink:href="`/assets/icons/collection.svg#${isContactBubbleOpen ? 'times' : 'talk'}`" />
</svg> </svg>
<div
v-show="isContactBubbleOpen"
class="contactContainer"
role="form"
aria-labelledby="contactTitle"
>
<div class="row left m-2">
<div id="hintBox" class="col-md-6">
<NuxtImg
v-if="screenWidth <= 768"
class="mobileAspBox"
:src="companyinfo?.profileImage?.data?.attributes?.url"
alt="Sabrina Hennrich"
:width="400"
format="webp"
provider="strapi"
loading="lazy"
/>
<h2 id="contactTitle">{{ $t('contactForm.yourcontact2us') }}</h2>
<p class="my-4">
<svg aria-hidden="true">
<use xlink:href="/assets/icons/collection.svg#phone"/>
</svg>
<span>{{ companyinfo.phone }}</span>
</p>
<div v-if="screenWidth > 768" class="pt-3">
<h3>{{ $t('contactForm.ourOffice') }}</h3>
<p class="address">
{{ companyinfo.company }}<br>{{ companyinfo.street }} <br>{{ companyinfo.postalcode }} {{ companyinfo.city }}
</p>
<p class="aspProf">{{ $t('contactForm.yourcontactperson') }} <b>Sabrina Hennrich</b></p>
<div class="aspBox">
<NuxtImg
:src="companyinfo?.profileImage?.data?.attributes?.url"
alt="Ansprechpartner Sabrina Hennrich"
:width="150"
format="webp"
provider="strapi"
loading="lazy"
/>
</div>
</div>
</div>
<div class="col-md-6">
<div v-if="!formSent"> <div
<div class="form-group"> v-show="isContactBubbleOpen"
<label for="name">{{ $t('contactForm.name') }}</label> class="contactContainer"
<input role="form"
id="name" aria-labelledby="contactTitle"
v-model="form.name" >
type="text" <div class="row left m-2">
name="name" <!-- Linke Seite -->
aria-required="true" <div id="hintBox" class="col-md-6">
autocomplete="name" <NuxtImg
@blur="validateName" v-if="screenWidth <= 768"
> class="mobileAspBox"
<span v-if="errors.name" class="error">{{ errors.name }}</span> :src="companyinfo?.profileImage?.data?.attributes?.url"
</div> alt="Sabrina Hennrich"
:width="400"
format="webp"
provider="strapi"
loading="lazy"
/>
<h2 id="contactTitle">{{ $t('contactForm.yourcontact2us') }}</h2>
<p class="my-4">
<svg aria-hidden="true">
<use xlink:href="/assets/icons/collection.svg#phone" />
</svg>
<span>{{ companyinfo.phone }}</span>
</p>
<div class="form-group"> <div v-if="screenWidth > 768" class="pt-3">
<label for="email">{{ $t('contactForm.email') }}</label> <h3>{{ $t('contactForm.ourOffice') }}</h3>
<input <p class="address">
id="email" {{ companyinfo.company }}<br />
v-model="form.email" {{ companyinfo.street }} <br />
type="email" {{ companyinfo.postalcode }} {{ companyinfo.city }}
name="email" </p>
autocomplete="email" <p class="aspProf">{{ $t('contactForm.yourcontactperson') }} <b>Sabrina Hennrich</b></p>
@blur="validateEmail" <div class="aspBox">
> <NuxtImg
<span v-if="errors.email" class="error">{{ errors.email }}</span> :src="companyinfo?.profileImage?.data?.attributes?.url"
</div> alt="Ansprechpartner Sabrina Hennrich"
:width="150"
<div class="form-group"> format="webp"
<label for="phone">{{ $t('contactForm.phone') }}</label> provider="strapi"
<input loading="lazy"
id="phone" />
v-model="form.phone" </div>
type="tel"
name="phone"
autocomplete="tel"
@blur="validatePhone"
>
<span v-if="errors.phone" class="error">{{ errors.phone }}</span>
</div>
<div class="form-group">
<label for="message">{{ $t('contactForm.message') }}</label>
<textarea
id="message"
v-model="form.message"
name="message"
class="mt-4"
/>
</div>
<p class="smallText">
<span class="check"></span>
{{ $t('contactForm.privacyInfotextBeforeLink') }}
<NuxtLinkLocale
:to="getRoute('privacy')"
:aria-label="$t('privacy')"
>
{{ $t('contactForm.privacyInfotextLinkText') }}
</NuxtLinkLocale>
</p>
<button
type="submit"
:aria-label="$t('contactForm.sendMessage')"
class="pinkBtn"
@click="submitForm"
>
{{ $t('contactForm.sendMessage') }}
</button>
</div>
<div v-else class="mt-5 thx">
<h3 class="pt-5">{{ $t('contactForm.confirmation.thx') }}</h3>
<p>{{ $t('contactForm.confirmation.info') }}</p>
<p>{{ $t('contactForm.confirmation.salutation') }}</p>
</div>
</div>
</div> </div>
</div>
<!-- Rechte Seite -->
<div class="col-md-6">
<div v-if="!formSent">
<form @submit.prevent="submitForm" novalidate>
<div class="form-group">
<label for="name">{{ $t('contactForm.name') }}</label>
<input
id="name"
class="form-control"
v-model="form.name"
type="text"
name="name"
required
autocomplete="name"
@blur="validateName"
:aria-invalid="!!errors.name"
:aria-describedby="errors.name ? 'error-name' : null"
/>
<span
v-if="errors.name"
id="error-name"
class="error"
role="alert"
>
{{ errors.name }}
</span>
</div>
<div class="form-group">
<label for="email">{{ $t('contactForm.email') }}</label>
<input
id="email"
class="form-control"
v-model="form.email"
type="email"
name="email"
required
autocomplete="email"
@blur="validateEmail"
:aria-invalid="!!errors.email"
:aria-describedby="errors.email ? 'error-email' : null"
/>
<span
v-if="errors.email"
id="error-email"
class="error"
role="alert"
>
{{ errors.email }}
</span>
</div>
<div class="form-group">
<label for="phone">{{ $t('contactForm.phone') }}</label>
<input
id="phone"
class="form-control"
v-model="form.phone"
type="tel"
name="phone"
autocomplete="tel"
@blur="validatePhone"
:aria-invalid="!!errors.phone"
:aria-describedby="errors.phone ? 'error-phone' : null"
/>
<span
v-if="errors.phone"
id="error-phone"
class="error"
role="alert"
>
{{ errors.phone }}
</span>
</div>
<div class="form-group">
<label for="message">{{ $t('contactForm.message') }}</label>
<textarea
id="message"
class="form-control mt-4"
v-model="form.message"
name="message"
rows="4"
required
></textarea>
</div>
<p class="smallText">
<span class="check"></span>
{{ $t('contactForm.privacyInfotextBeforeLink') }}
<NuxtLinkLocale
:to="getRoute('privacy')"
:aria-label="$t('privacy')"
>
{{ $t('contactForm.privacyInfotextLinkText') }}
</NuxtLinkLocale>
</p>
<button
type="submit"
class="pinkBtn"
:aria-label="$t('contactForm.sendMessage')"
>
{{ $t('contactForm.sendMessage') }}
</button>
</form>
</div>
<!-- Dankeschön-Text -->
<div v-else class="mt-5 thx">
<h3 class="pt-5">{{ $t('contactForm.confirmation.thx') }}</h3>
<p>{{ $t('contactForm.confirmation.info') }}</p>
<p>{{ $t('contactForm.confirmation.salutation') }}</p>
</div>
</div>
</div> </div>
</div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { useMainStore } from '@/stores/main'; import { useMainStore } from '@/stores/main';

View File

@ -1,272 +1,232 @@
<template> <template>
<div> <div class="banner-wrapper">
<div class="banner-wrapper"> <!-- Obere Welle -->
<!-- Obere Welle --> <svg
<svg xmlns="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 20"
viewBox="0 0 500 20" class="svgwavetop"
class="svgwavetop" style="transform: scaleY(-1)"
style="transform: scaleY(-1)" >
> <g clip-path="url(#_clipPath_5kVoellZ93LI5Lc2i2b27JZsraaBm0XM)">
<g clip-path="url(#_clipPath_5kVoellZ93LI5Lc2i2b27JZsraaBm0XM)"> <path
<path id="wave"
id="wave" 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"
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"
fill="#EEEBE5" />
/> </g>
</g> </svg>
</svg>
<div class="box pb-5"> <div class="box pb-5">
<div class="container"> <div class="container">
<h2 class="pt-4 pb-3">{{ title }}</h2> <h2 class="pt-4 pb-3">{{ title }}</h2>
<div class="marquee marquee--hover-pause mt-5"> <div class="marquee">
<!-- Haupt-Liste --> <div class="marquee-track">
<ul class="marquee__content"> <ul class="marquee-list">
<li v-for="(item, index) in items" :key="index"> <li
<NuxtLink v-if="item.link" :to="item.link" class="custLogoLink"> v-for="(item, index) in items"
<NuxtImg :key="index"
provider="strapi" class="marquee-item"
:src="item.logo.url" >
:alt="item.logo.alternativeText || 'Logo'" <NuxtLink v-if="item.link" :to="item.link" class="custLogoLink">
width="250"
format="webp"
loading="lazy"
class="custLogo"
/>
</NuxtLink>
<NuxtImg <NuxtImg
v-else
provider="strapi" provider="strapi"
:src="item.logo.url" :src="item.logo.url"
:alt="item.logo.alternativeText || 'Logo'" :alt="item.logo.alternativeText || 'Logo'"
width="250" width="250"
height="50"
format="webp" format="webp"
loading="lazy" loading="lazy"
class="custLogo" class="custLogo"
/> />
</li> </NuxtLink>
</ul> <NuxtImg
v-else
<!-- Duplizierte Liste für Endlos-Scroll --> provider="strapi"
<ul class="marquee__content duplicate" aria-hidden="true"> :src="item.logo.url"
<li v-for="(item, index) in items" :key="'dup-' + index"> :alt="item.logo.alternativeText || 'Logo'"
<NuxtLink v-if="item.link" :to="item.link" class="custLogoLink"> width="250"
<NuxtImg height="50"
provider="strapi" format="webp"
:src="item.logo.url" loading="lazy"
:alt="item.logo.alternativeText || 'Logo'" class="custLogo"
width="250"
format="webp"
loading="lazy"
class="custLogo"
/>
</NuxtLink>
<NuxtImg
v-else
provider="strapi"
:src="item.logo.url"
:alt="item.logo.alternativeText || 'Logo'"
width="250"
format="webp"
loading="lazy"
class="custLogo"
/>
</li>
</ul>
</div>
</div>
</div>
<!-- Untere Wellen -->
<div class="waveBox">
<div id="waver">
<div class="waveWrapper waveAnimation">
<div class="waveWrapperInner bgTop">
<div
class="wave waveTop"
:style="{ backgroundImage: `url('${cmsUrl}/uploads/wave_top_8fe067e598.svg')` }"
/> />
</div> </li>
<div class="waveWrapperInner bgMiddle"> </ul>
<div
class="wave waveMiddle"
:style="{ backgroundImage: `url('${cmsUrl}/uploads/wave_middle_24d8a84a35.svg')` }"
/>
</div>
<div class="waveWrapperInner bgBottom">
<div
class="wave waveBottom"
:style="{ backgroundImage: `url('${cmsUrl}/uploads/wave_bottom_6fc8184efb.svg')` }"
/>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</template>
<!-- Wellen unten -->
<div class="waveBox">
<div id="waver">
<div class="waveWrapper waveAnimation">
<div class="waveWrapperInner bgTop">
<div
class="wave waveTop"
:style="{ backgroundImage: `url('${cmsUrl}/uploads/wave_top_8fe067e598.svg')` }"
/>
</div>
<div class="waveWrapperInner bgMiddle">
<div
class="wave waveMiddle"
:style="{ backgroundImage: `url('${cmsUrl}/uploads/wave_middle_24d8a84a35.svg')` }"
/>
</div>
<div class="waveWrapperInner bgBottom">
<div
class="wave waveBottom"
:style="{ backgroundImage: `url('${cmsUrl}/uploads/wave_bottom_6fc8184efb.svg')` }"
/>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
const runtimeConfig = useRuntimeConfig()
const cmsUrl = computed(() => runtimeConfig.public.cmsBaseUrl)
<script setup> const props = defineProps({
import { computed } from 'vue' items: {
type: Array,
required: true,
},
title: {
type: String,
default: '',
},
})
</script>
// Runtime config & base CMS URL <style lang="sass" scoped>
const runtimeConfig = useRuntimeConfig() .banner-wrapper
const cmsUrl = computed(() => runtimeConfig.public.cmsBaseUrl) position: relative
svg
// Props margin: 0 0 -3px 0
const props = defineProps({ @media(max-width: $breakPointSM)
items: {
type: Array,
required: true,
},
logoHeight: {
type: Number,
default: 50,
},
title: {
type: String,
default: '',
},
link: {
type: String,
default: 'projekt',
},
})
</script>
<style lang="sass">
.banner-wrapper
position: relative
svg svg
margin: 0 0 -3px 0
@media(max-width: $breakPointSM)
svg
margin: 0
.box
background-color: $beige
width: 100%
min-height: 50px
margin-top: -20px
h2
color: #333
font-size: 1.2rem
font-family: 'Mainfont-Bold'
.marquee
--gap: 1rem
position: relative
display: flex
overflow: hidden
user-select: none
gap: var(--gap)
ul
list-style-type: none
&:hover .marquee__content
animation-play-state: paused
.marquee__content
flex-shrink: 0
display: flex
justify-content: space-around
gap: var(--gap)
min-width: 100%
animation: scroll 30s linear infinite
li
&::before
display: none
@keyframes scroll
from
transform: translateX(0)
to
transform: translateX(calc(-100% - var(--gap)))
.custLogo
width: auto
max-width: 250px
height: 50px
margin: 0 3rem
filter: grayscale(100%)
transition: filter 0.3s ease
&:hover
filter: grayscale(0)
.waveBox
position: relative
height: 120px
#waver
display: block
position: absolute
left: 0
height: 120px
width: 100%
padding: 0
margin: 0 margin: 0
@keyframes move_wave .box
0% background-color: $beige
transform: translateX(0) translateZ(0) scaleY(1) width: 100%
50% min-height: 50px
transform: translateX(-25%) translateZ(0) scaleY(0.55) margin-top: -20px
100%
transform: translateX(-50%) translateZ(0) scaleY(1)
.waveWrapper h2
overflow: hidden color: #333
position: absolute font-size: 1.2rem
left: 0 font-family: 'Mainfont-Bold'
right: 0
bottom: 0
top: 0
margin: auto
.waveWrapperInner .marquee
position: absolute overflow: hidden
width: 100% width: 100%
overflow: hidden
height: 120px
top: 0
background-image: linear-gradient(to top, $beige 20%, $beige 80%)
@media (max-width: 1024px) .marquee-track
.waveWrapperInner display: flex
height: 50px animation: scroll 20s linear infinite
width: max-content
.bgTop .marquee-list
z-index: 15 display: flex
opacity: 0.5 gap: 3rem
list-style: none
padding: 1rem 0
margin: 0
.bgMiddle .marquee-item
z-index: 10 flex-shrink: 0
opacity: 0.75
.bgBottom .custLogo
z-index: 5 width: auto
max-width: 250px
height: 50px
filter: grayscale(100%)
transition: filter 0.3s ease
&:hover
filter: grayscale(0)
.wave @keyframes scroll
position: absolute from
left: 0 transform: translateX(0)
width: 200% to
height: 100% transform: translateX(-50%)
background-repeat: repeat no-repeat
background-position: 0 bottom
transform-origin: center bottom
.waveTop .waveBox
background-size: auto 100% position: relative
animation: move_wave 18s linear infinite height: 120px
.waveMiddle #waver
background-size: auto 100% display: block
animation: move_wave 11s linear infinite position: absolute
left: 0
height: 120px
width: 100%
padding: 0
margin: 0
.waveBottom @keyframes move_wave
background-size: auto 100% 0%
animation: move_wave 15s linear infinite transform: translateX(0) translateZ(0) scaleY(1)
</style> 50%
transform: translateX(-25%) translateZ(0) scaleY(0.55)
100%
transform: translateX(-50%) translateZ(0) scaleY(1)
.waveWrapper
overflow: hidden
position: absolute
left: 0
right: 0
bottom: 0
top: 0
margin: auto
.waveWrapperInner
position: absolute
width: 100%
overflow: hidden
height: 120px
top: 0
background-image: linear-gradient(to top, $beige 20%, $beige 80%)
@media (max-width: 1024px)
.waveWrapperInner
height: 50px
.bgTop
z-index: 15
opacity: 0.5
.bgMiddle
z-index: 10
opacity: 0.75
.bgBottom
z-index: 5
.wave
position: absolute
left: 0
width: 200%
height: 100%
background-repeat: repeat no-repeat
background-position: 0 bottom
transform-origin: center bottom
.waveTop
background-size: auto 100%
animation: move_wave 18s linear infinite
.waveMiddle
background-size: auto 100%
animation: move_wave 11s linear infinite
.waveBottom
background-size: auto 100%
animation: move_wave 15s linear infinite
</style>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 198 KiB