Update
This commit is contained in:
1 parent
0b775a938d
commit
8757431c79
35 files changed
+1507
-60
No files matched your search
@@ -0,0 +1,32 @@
|
||||
export type AboutUsMember = {
|
||||
name: string;
|
||||
imageSrc: string;
|
||||
imageAlt: string;
|
||||
};
|
||||
|
||||
export type AboutUsData = {
|
||||
title: string;
|
||||
paragraphs: string[];
|
||||
members: AboutUsMember[];
|
||||
};
|
||||
|
||||
export const aboutUsData: AboutUsData = {
|
||||
title: "Ihre Reinigungsfirma für professionelle Ansprüche",
|
||||
paragraphs: [
|
||||
"Basiclean ist ein junges Reinigungsunternehmen mit Fokus auf gewerbliche Kunden in Hannover und der umliegenden Region.",
|
||||
"Unser Ziel ist es, Unternehmen mit planbarer Sauberkeit, klaren Abläufen und zuverlässiger Zusammenarbeit zu entlasten. Statt starrer Standards bieten wir individuelle Reinigungslösungen, abgestimmt auf Ihr Objekt und Ihre Anforderungen.",
|
||||
"Persönlicher Kontakt, transparente Leistungen und geschulte Reinigungskräfte stehen dabei im Mittelpunkt.",
|
||||
],
|
||||
members: [
|
||||
{
|
||||
name: "Abdulkabi Dastan",
|
||||
imageSrc: "/images/portrait/abdulkabi-dastan.jpg",
|
||||
imageAlt: "Porträt von Abdulkabi Dastan",
|
||||
},
|
||||
{
|
||||
name: "Gökhan Özsayiner",
|
||||
imageSrc: "/images/portrait/goekhan-oezsayiner.jpg",
|
||||
imageAlt: "Porträt von Gökhan Özsayiner",
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,120 @@
|
||||
.section {
|
||||
padding-block: var(--space-16);
|
||||
}
|
||||
|
||||
.inner {
|
||||
width: 100%;
|
||||
max-width: var(--container-width);
|
||||
margin-inline: auto;
|
||||
padding-inline: var(--space-4);
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: var(--space-8);
|
||||
justify-items: center;
|
||||
}
|
||||
|
||||
.heading {
|
||||
max-width: 20ch;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.team {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: var(--space-4);
|
||||
max-width: 420px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-3);
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.imageWrap {
|
||||
position: relative;
|
||||
aspect-ratio: 4 / 5;
|
||||
overflow: hidden;
|
||||
border-radius: 20px;
|
||||
background: #e9e9e9;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.image {
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.name {
|
||||
font-size: var(----font-size-xs);
|
||||
color: var(--color-text);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.textBlock {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-5);
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.text {
|
||||
max-width: 56ch;
|
||||
color: var(--color-text-muted);
|
||||
font-size: var(----font-size-xs);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.section {
|
||||
padding-block: var(--space-24);
|
||||
}
|
||||
|
||||
.inner {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
column-gap: var(--space-16);
|
||||
row-gap: var(--space-8);
|
||||
align-items: start;
|
||||
justify-items: stretch;
|
||||
}
|
||||
|
||||
.heading {
|
||||
grid-column: 1 / -1;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.team {
|
||||
grid-column: 1;
|
||||
grid-row: 2;
|
||||
}
|
||||
|
||||
.card {
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.textBlock {
|
||||
grid-column: 2;
|
||||
grid-row: 2;
|
||||
align-self: center;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.text {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.section {
|
||||
padding-block: var(--space-32);
|
||||
}
|
||||
|
||||
.inner {
|
||||
padding-inline: var(--space-6);
|
||||
}
|
||||
|
||||
.team {
|
||||
max-width: 460px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
"use client";
|
||||
|
||||
import Image from "next/image";
|
||||
import styles from "./AboutUsSection.module.css";
|
||||
import { aboutUsData } from "./AboutUsData";
|
||||
|
||||
export default function AboutUsSection() {
|
||||
const { title, paragraphs, members } = aboutUsData;
|
||||
|
||||
return (
|
||||
<section className={styles.section} aria-labelledby="about-us-heading">
|
||||
<div className={styles.inner}>
|
||||
<h2 id="about-us-heading" className={styles.heading}>
|
||||
{title}
|
||||
</h2>
|
||||
|
||||
<div className={styles.team}>
|
||||
{members.map((member) => (
|
||||
<article key={member.name} className={styles.card}>
|
||||
<div className={styles.imageWrap}>
|
||||
<Image
|
||||
src={member.imageSrc}
|
||||
alt={member.imageAlt}
|
||||
fill
|
||||
className={styles.image}
|
||||
sizes="(max-width: 767px) 42vw, 180px"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<p className={styles.name}>{member.name}</p>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className={styles.textBlock}>
|
||||
{paragraphs.map((paragraph, index) => (
|
||||
<p key={index} className={styles.text}>
|
||||
{paragraph}
|
||||
</p>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -26,7 +26,7 @@
|
||||
}
|
||||
|
||||
.footer__inner {
|
||||
max-width: 1280px;
|
||||
max-width: var(--container-width);
|
||||
margin-inline: auto;
|
||||
padding: var(--footer-padding-y) clamp(1rem, 5vw, 2rem);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import styles from "./Header.module.css";
|
||||
import Navbar from "./Navbar/Navbar";
|
||||
import Navbar from "./navbar/Navbar";
|
||||
|
||||
export default function Header() {
|
||||
return (
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
.hero {
|
||||
position: relative;
|
||||
background: linear-gradient(to bottom, #f9f9f9 5%, #ffffff 50%, #f9f9f9 100%);
|
||||
background: var(--color-background-linear);
|
||||
padding-block: var(--space-16);
|
||||
width: 100vw;
|
||||
margin-left: calc(50% - 50vw);
|
||||
margin-right: calc(50% - 50vw);
|
||||
}
|
||||
|
||||
.inner {
|
||||
width: 100%;
|
||||
max-width: 1280px;
|
||||
max-width: var(--container-width);
|
||||
margin-inline: auto;
|
||||
padding-inline: var(--space-4);
|
||||
}
|
||||
@@ -35,25 +31,30 @@
|
||||
}
|
||||
|
||||
.heading {
|
||||
max-width: 20ch;
|
||||
max-width: 15ch;
|
||||
}
|
||||
|
||||
.lead {
|
||||
max-width: 56ch;
|
||||
max-width: 40ch;
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: var(--space-3);
|
||||
gap: var(--space-4);
|
||||
margin-top: var(--space-2);
|
||||
}
|
||||
|
||||
.actions :global(.btn) {
|
||||
width: fit-content;
|
||||
justify-content: center;
|
||||
min-height: 44px;
|
||||
font-weight: var(--font-weight-semibold);
|
||||
}
|
||||
|
||||
.actions :global(.btn--tertiary) {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
color: var(--font-body-color);
|
||||
}
|
||||
|
||||
/* Tablet */
|
||||
@@ -67,7 +68,7 @@
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
height: 90%;
|
||||
width:50%;
|
||||
width: 50%;
|
||||
}
|
||||
.actions {
|
||||
flex-direction: row;
|
||||
@@ -75,6 +76,13 @@
|
||||
align-items: center;
|
||||
gap: var(--space-6);
|
||||
}
|
||||
.heading {
|
||||
max-width: 24ch;
|
||||
}
|
||||
|
||||
.lead {
|
||||
max-width: 48ch;
|
||||
}
|
||||
}
|
||||
|
||||
/* Desktop */
|
||||
|
||||
@@ -14,11 +14,11 @@ export default function HeroSection() {
|
||||
<div className={styles.inner}>
|
||||
<div className={styles.content}>
|
||||
<h1 id="hero-heading" className={styles.heading}>
|
||||
Mehr Sauberkeit, weniger Aufwand für ein sauberes Arbeitsumfeld
|
||||
Gebäudereinigung für ein makelloses Arbeitsumfeld
|
||||
</h1>
|
||||
<p className={`lead ${styles.lead}`}>
|
||||
Professionelle Gebäudereinigung für Büros, Praxen und Gewerbeflächen
|
||||
nach festen Abläufen und höchsten Qualitätsstandards.
|
||||
Mit festen Abläufen und hohen Qualitätsstandards – für ein sauberes,
|
||||
professionelles Arbeitsumfeld.
|
||||
</p>
|
||||
<div className={styles.actions}>
|
||||
<Link href="/kontakt" className="btn btn--filled">
|
||||
@@ -27,11 +27,19 @@ export default function HeroSection() {
|
||||
</span>
|
||||
Jetzt Angebot anfragen
|
||||
</Link>
|
||||
<a href="tel:+495112345678" className="btn btn--outline">
|
||||
<Link href="/Leistungen" className="btn btn--outline">
|
||||
{/* <span className="btn-icon animate" aria-hidden="true">
|
||||
<ArrowRightIcon size={18} weight="bold" />
|
||||
</span> */}
|
||||
Leistungen ansehen
|
||||
</Link>
|
||||
</div>
|
||||
<div className={styles.actions}>
|
||||
<a href="tel:+491726210606" className="btn btn--tertiary">
|
||||
<span className="btn-icon" aria-hidden="true">
|
||||
<PhoneIcon size={18} weight="bold" />
|
||||
</span>
|
||||
0172 621 0606
|
||||
0172 6210606
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
.section {
|
||||
padding-block: var(--space-16);
|
||||
}
|
||||
|
||||
.inner {
|
||||
max-width: var(--container-width);
|
||||
margin-inline: auto;
|
||||
padding-inline: var(--space-4);
|
||||
}
|
||||
|
||||
.intro {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: var(--space-4);
|
||||
margin-bottom: var(--space-8);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.heading {
|
||||
max-width: 18ch;
|
||||
}
|
||||
|
||||
.lead {
|
||||
max-width: 58ch;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: var(--space-4);
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--space-4);
|
||||
background-color: #e2ebe5;
|
||||
color: var(--color-accent);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: var(--space-6) var(--space-6);
|
||||
text-decoration: none;
|
||||
min-height: 88px;
|
||||
transition:
|
||||
background-color 0.25s ease,
|
||||
color 0.25s ease,
|
||||
box-shadow 0.25s ease;
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.card:hover,
|
||||
.card:focus-visible {
|
||||
background-color: var(--color-accent);
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.cardText {
|
||||
font-size: var(--font-size-body);
|
||||
line-height: 1.2;
|
||||
font-weight: var(--font-weight-semibold);
|
||||
max-width: 18ch;
|
||||
}
|
||||
|
||||
.iconWrap {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50px;
|
||||
background-color: var(--color-accent);
|
||||
color: #ffffff;
|
||||
transition:
|
||||
background-color 0.25s ease,
|
||||
color 0.25s ease;
|
||||
}
|
||||
|
||||
.card:hover .iconWrap,
|
||||
.card:focus-visible .iconWrap {
|
||||
background-color: #ffffff;
|
||||
color: var(--color-accent);
|
||||
}
|
||||
|
||||
.icon {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.section {
|
||||
padding-block: var(--space-24);
|
||||
}
|
||||
|
||||
.intro {
|
||||
margin-bottom: var(--space-10);
|
||||
}
|
||||
|
||||
.heading {
|
||||
max-width: 18ch;
|
||||
}
|
||||
|
||||
.grid {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.card {
|
||||
min-height: 96px;
|
||||
}
|
||||
|
||||
.card:nth-child(4),
|
||||
.card:nth-child(5) {
|
||||
grid-column: span 1;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.section {
|
||||
padding-block: var(--space-24);
|
||||
}
|
||||
|
||||
.inner {
|
||||
padding-inline: var(--space-6);
|
||||
}
|
||||
|
||||
.grid {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
max-width: 860px;
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: var(--space-12) var(--space-6);
|
||||
}
|
||||
|
||||
.card:nth-child(4) {
|
||||
grid-column: 1 / 2;
|
||||
justify-self: end;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.card:nth-child(5) {
|
||||
grid-column: 2 / 3;
|
||||
justify-self: start;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (hover: none) {
|
||||
.card:hover,
|
||||
.card:focus-visible {
|
||||
background-color: #e2ebe5;
|
||||
color: var(--color-accent);
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.card:hover .iconWrap,
|
||||
.card:focus-visible .iconWrap {
|
||||
background-color: var(--color-accent);
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
import Link from "next/link";
|
||||
import styles from "./IndustrySolutionsSection.module.css";
|
||||
import {
|
||||
BuildingsIcon,
|
||||
StethoscopeIcon,
|
||||
WarehouseIcon,
|
||||
HouseLineIcon,
|
||||
ArrowRightIcon,
|
||||
} from "@phosphor-icons/react/dist/ssr";
|
||||
|
||||
const industries = [
|
||||
{
|
||||
title: "Büros",
|
||||
href: "/branchen/bueros",
|
||||
icon: BuildingsIcon,
|
||||
},
|
||||
{
|
||||
title: "Arztpraxen",
|
||||
href: "/branchen/arztpraxen",
|
||||
icon: StethoscopeIcon,
|
||||
},
|
||||
{
|
||||
title: "Hallen und Lagerflächen",
|
||||
href: "/branchen/hallen-lagerflaechen",
|
||||
icon: WarehouseIcon,
|
||||
},
|
||||
{
|
||||
title: "Wohn- und Geschäftshäuser",
|
||||
href: "/branchen/wohn-geschaeftshaeuser",
|
||||
icon: HouseLineIcon,
|
||||
},
|
||||
{
|
||||
title: "Zum Kontaktformular",
|
||||
href: "/kontakt",
|
||||
icon: ArrowRightIcon,
|
||||
},
|
||||
];
|
||||
|
||||
export default function IndustrySolutionsSection() {
|
||||
return (
|
||||
<section
|
||||
className={styles.section}
|
||||
aria-labelledby="industry-solutions-heading"
|
||||
>
|
||||
<div className={styles.inner}>
|
||||
<div className={styles.intro}>
|
||||
<h2 id="industry-solutions-heading" className={styles.heading}>
|
||||
Reinigungsleistungen für verschiedene Branchen
|
||||
</h2>
|
||||
<p className={styles.lead}>
|
||||
Als Reinigungsfirma betreuen wir Unternehmen aus unterschiedlichen
|
||||
Branchen. Unsere Leistungen passen wir individuell an Nutzung,
|
||||
Objektgröße und branchenspezifische Anforderungen an.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className={styles.grid}>
|
||||
{industries.map((industry) => {
|
||||
const Icon = industry.icon;
|
||||
|
||||
return (
|
||||
<Link
|
||||
key={industry.title}
|
||||
href={industry.href}
|
||||
className={styles.card}
|
||||
>
|
||||
<span className={styles.cardText}>{industry.title}</span>
|
||||
|
||||
<span className={styles.iconWrap} aria-hidden="true">
|
||||
<Icon size={18} weight="bold" className={styles.icon} />
|
||||
</span>
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -211,7 +211,10 @@
|
||||
padding: 24px 12px;
|
||||
}
|
||||
|
||||
.nav-links > li:first-child,
|
||||
.nav-links > li:first-child {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.nav-links > li:last-child {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
@@ -104,6 +104,20 @@
|
||||
box-shadow: 0 4px 6px -6px rgb(0 0 0 / 30%);
|
||||
opacity: 0;
|
||||
transform: translateY(-8px);
|
||||
transition:
|
||||
max-height 300ms ease,
|
||||
opacity 300ms ease,
|
||||
transform 300ms ease,
|
||||
visibility 0s linear 300ms;
|
||||
background-color: var(--nav-bg);
|
||||
}
|
||||
|
||||
.nav-submenu.open {
|
||||
transition:
|
||||
max-height 300ms ease,
|
||||
opacity 300ms ease,
|
||||
transform 300ms ease,
|
||||
visibility 0s linear 0s;
|
||||
}
|
||||
|
||||
.nav-submenu-list li a:focus-visible {
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
.services {
|
||||
padding-block: var(--space-16);
|
||||
}
|
||||
|
||||
.inner {
|
||||
max-width: var(--container-width);
|
||||
margin-inline: auto;
|
||||
padding-inline: var(--space-4);
|
||||
}
|
||||
|
||||
.intro {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-4);
|
||||
margin-bottom: var(--space-8);
|
||||
}
|
||||
|
||||
.heading,
|
||||
.lead {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
||||
justify-content: center;
|
||||
gap: var(--space-8);
|
||||
}
|
||||
|
||||
.card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100%;
|
||||
background: var(--color-surface);
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.imageWrap {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
aspect-ratio: 16 / 7;
|
||||
background: var(--color-surface-alt);
|
||||
overflow: hidden;
|
||||
border-bottom: 4px solid var(--color-accent);
|
||||
}
|
||||
|
||||
.image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.cardBody {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
gap: var(--space-5);
|
||||
padding: var(--space-5);
|
||||
}
|
||||
|
||||
.cardContent {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-3);
|
||||
}
|
||||
|
||||
.cardTitle {
|
||||
font-size: var(--font-size-s);
|
||||
background-color: var(--color-accent);
|
||||
width: fit-content;
|
||||
padding: 8px;
|
||||
color: #ffffff;
|
||||
letter-spacing: var(--letter-spacing-normal);
|
||||
border-radius: 4px;
|
||||
font-weight: var(--font-weight-regular);
|
||||
}
|
||||
|
||||
.cardText {
|
||||
max-width: 34ch;
|
||||
color: var(--color-text-muted);
|
||||
font-size: var(--font-size-xs);
|
||||
}
|
||||
|
||||
.cardBody :global(.btn) {
|
||||
width: fit-content;
|
||||
padding: 0;
|
||||
color: var(--font-body-color);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
}
|
||||
|
||||
/* Tablet */
|
||||
@media (min-width: 768px) {
|
||||
.services {
|
||||
padding-block: var(--space-24);
|
||||
}
|
||||
|
||||
.intro {
|
||||
margin-bottom: var(--space-10);
|
||||
}
|
||||
.heading {
|
||||
max-width: 22ch;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.lead {
|
||||
max-width: 62ch;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
/* .card {
|
||||
max-width: 100%;
|
||||
} */
|
||||
|
||||
.grid {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
/* Desktop */
|
||||
@media (min-width: 1024px) {
|
||||
.services {
|
||||
padding-block: var(--space-24);
|
||||
}
|
||||
|
||||
.inner {
|
||||
padding-inline: var(--space-6);
|
||||
}
|
||||
|
||||
.grid {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
align-items: stretch;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
import { ArrowRightIcon } from "@phosphor-icons/react/dist/ssr";
|
||||
import Link from "next/link";
|
||||
import styles from "./ServiceSection.module.css";
|
||||
import Image from "next/image";
|
||||
|
||||
const services = [
|
||||
{
|
||||
title: "Grundreinigung",
|
||||
description:
|
||||
"Regelmäßige Reinigung von Arbeitsplätzen, Besprechungsräumen und Gemeinschaftsbereichen für einen gepflegten Eindruck im Alltag.",
|
||||
image: "/images/services/grundreinigung.jpg",
|
||||
imageAlt: "Sauberer Büroarbeitsplatz nach der Reinigung",
|
||||
href: "/leistungen/bueroreinigung",
|
||||
},
|
||||
{
|
||||
title: "Glas- und Fensterreinigung",
|
||||
description:
|
||||
"Streifenfreie Reinigung von Fenstern, Glaselementen und Eingangsbereichen für einen professionellen ersten Eindruck.",
|
||||
image: "/images/services/Fensterreinigung.jpg",
|
||||
imageAlt: "Große gereinigte Fensterfront eines Geschäftsgebäudes",
|
||||
href: "/leistungen/glasreinigung",
|
||||
},
|
||||
{
|
||||
title: "Treppenhausreinigung",
|
||||
description:
|
||||
"Saubere Eingangs- und Treppenbereiche als Visitenkarte für Mitarbeitende, Kundschaft und Besucher.",
|
||||
image: "/images/services/treppenhausreinigung.jpg",
|
||||
imageAlt: "Sauberes Treppenhaus in einem Gewerbeobjekt",
|
||||
href: "/leistungen/treppenhausreinigung",
|
||||
},
|
||||
{
|
||||
title: "Unterhaltsreinigung",
|
||||
description:
|
||||
"Individuell abgestimmte Reinigungsintervalle für dauerhaft gepflegte Büro-, Praxis- und Gewerbeflächen.",
|
||||
image: "/images/services/unterhaltsreinigung.jpg",
|
||||
imageAlt: "Reinigungskraft bei der Unterhaltsreinigung in einem Büro",
|
||||
href: "/leistungen/unterhaltsreinigung",
|
||||
},
|
||||
{
|
||||
title: "Hausmeisterdienste",
|
||||
description:
|
||||
"Hausmeisterdienste für Kleinreparaturen bis zur Objektbetreuung – für reibungslose Abläufe.",
|
||||
image: "/images/services/Hausmeisterdienste.jpg",
|
||||
imageAlt: "Hausmeisterdienste für Gartenarbeit",
|
||||
href: "/leistungen/Hausmeisterdienste",
|
||||
},
|
||||
];
|
||||
|
||||
export default function ServiceSection() {
|
||||
return (
|
||||
<section className={styles.services} aria-labelledby="services-heading">
|
||||
<div className={styles.inner}>
|
||||
<div className={styles.intro}>
|
||||
<h2 id="services-heading" className={styles.heading}>
|
||||
Professionelle Reinigung für Büros, Praxen und Geschäftsräume
|
||||
</h2>
|
||||
<p className={`lead ${styles.lead}`}>
|
||||
Individuelle Reinigungskonzepte für Unternehmen – damit Ihre Räume sauber, zuverlässig und professions sorgenfrei bleiben.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className={styles.grid}>
|
||||
{services.map((service) => (
|
||||
<article key={service.title} className={styles.card}>
|
||||
<div className={styles.imageWrap}>
|
||||
<Image
|
||||
src={service.image}
|
||||
alt={service.imageAlt}
|
||||
fill
|
||||
sizes="(min-width: 1024px) 400px, (min-width: 768px) 50vw, 100vw"
|
||||
className={styles.image}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={styles.cardBody}>
|
||||
<div className={styles.cardContent}>
|
||||
<h3 className={styles.cardTitle}>{service.title}</h3>
|
||||
<p className={styles.cardText}>{service.description}</p>
|
||||
</div>
|
||||
|
||||
<Link href={service.href} className="btn btn--tertiary">
|
||||
Mehr
|
||||
<span className="btn-icon animate" aria-hidden="true">
|
||||
<ArrowRightIcon size={16} weight="bold" />
|
||||
</span>
|
||||
</Link>
|
||||
</div>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
.benefits {
|
||||
background: var(--color-background-linear);
|
||||
padding-block: var(--space-16);
|
||||
}
|
||||
|
||||
.inner {
|
||||
max-width: var(--container-width);
|
||||
margin-inline: auto;
|
||||
padding-inline: var(--space-4);
|
||||
}
|
||||
|
||||
/* Mobile: Intro oben, Grid unten */
|
||||
.intro {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-4);
|
||||
margin-bottom: var(--space-8);
|
||||
}
|
||||
|
||||
.heading,
|
||||
.lead {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
||||
gap: var(--space-5);
|
||||
}
|
||||
|
||||
.card {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
gap: var(--space-4);
|
||||
background: var(--color-surface);
|
||||
border-radius: 12px;
|
||||
padding: var(--space-5);
|
||||
box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.iconWrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
color: var(--color-accent);
|
||||
}
|
||||
|
||||
.cardContent {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-2);
|
||||
}
|
||||
|
||||
.cardTitle {
|
||||
font-size: var(--font-size-base);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
color: var(--font-heading-color);
|
||||
}
|
||||
|
||||
.cardText {
|
||||
color: var(--color-text-muted);
|
||||
font-size: var(--font-size-xs);
|
||||
max-width: 34ch;
|
||||
}
|
||||
|
||||
/* Tablet */
|
||||
@media (min-width: 768px) {
|
||||
.benefits {
|
||||
padding-block: var(--space-24);
|
||||
}
|
||||
|
||||
.intro {
|
||||
margin-bottom: var(--space-10);
|
||||
}
|
||||
|
||||
.heading {
|
||||
max-width: 16ch;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.lead {
|
||||
max-width: 48ch;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.grid {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
/* Desktop: Intro links, Grid rechts — nebeneinander */
|
||||
@media (min-width: 1024px) {
|
||||
.benefits {
|
||||
padding-block: var(--space-36);
|
||||
}
|
||||
|
||||
.inner {
|
||||
display: grid;
|
||||
grid-template-columns: 280px 1fr;
|
||||
gap: var(--space-16);
|
||||
align-items: start;
|
||||
padding-inline: var(--space-6);
|
||||
}
|
||||
|
||||
.intro {
|
||||
position: sticky;
|
||||
top: var(--space-8);
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.heading {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.lead {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
import {
|
||||
UsersThreeIcon,
|
||||
GraduationCapIcon,
|
||||
ClockIcon,
|
||||
SealCheckIcon,
|
||||
LeafIcon,
|
||||
CurrencyCircleDollarIcon,
|
||||
} from "@phosphor-icons/react/dist/ssr";
|
||||
import styles from "./benefitsSection.module.css";
|
||||
|
||||
const benefits = [
|
||||
{
|
||||
icon: UsersThreeIcon,
|
||||
title: "Feste Ansprechpartner",
|
||||
description:
|
||||
"Keine wechselnden Zuständigkeiten – wir sind immer persönlich für Sie da.",
|
||||
},
|
||||
{
|
||||
icon: GraduationCapIcon,
|
||||
title: "Geschultes Personal",
|
||||
description:
|
||||
"Unsere Teams werden kontinuierlich in modernen Reinigungstechniken geschult.",
|
||||
},
|
||||
{
|
||||
icon: ClockIcon,
|
||||
title: "Flexible Zeiten",
|
||||
description:
|
||||
"Wir reinigen dann, wenn es Ihren Betriebsablauf am wenigsten stört.",
|
||||
},
|
||||
{
|
||||
icon: SealCheckIcon,
|
||||
title: "Höchste Qualität",
|
||||
description:
|
||||
"Strenges Qualitätsmanagement durch regelmäßige Objektkontrollen.",
|
||||
},
|
||||
{
|
||||
icon: LeafIcon,
|
||||
title: "Umweltfreundlich",
|
||||
description:
|
||||
"Einsatz von biologisch abbaubaren Reinigungsmitteln für die Umwelt.",
|
||||
},
|
||||
{
|
||||
icon: CurrencyCircleDollarIcon,
|
||||
title: "Transparente Kosten",
|
||||
description:
|
||||
"Klare Preisgestaltung ohne versteckte Nebenkosten für Ihre Planung.",
|
||||
},
|
||||
];
|
||||
|
||||
export default function BenefitsSection() {
|
||||
return (
|
||||
<section className={styles.benefits} aria-labelledby="benefits-heading">
|
||||
<div className={styles.inner}>
|
||||
<div className={styles.intro}>
|
||||
<h2 id="benefits-heading" className={styles.heading}>
|
||||
Ihre Vorteile mit uns
|
||||
</h2>
|
||||
<p className={`lead ${styles.lead}`}>
|
||||
Wir verstehen die Anforderungen moderner Unternehmen an einen
|
||||
Facility-Partner: Verlässlichkeit, Professionalität und Transparenz.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className={styles.grid}>
|
||||
{benefits.map((benefit) => {
|
||||
const Icon = benefit.icon;
|
||||
return (
|
||||
<article key={benefit.title} className={styles.card}>
|
||||
<span className={styles.iconWrap} aria-hidden="true">
|
||||
<Icon size={24} weight="duotone" />
|
||||
</span>
|
||||
<div className={styles.cardContent}>
|
||||
<h3 className={styles.cardTitle}>{benefit.title}</h3>
|
||||
<p className={styles.cardText}>{benefit.description}</p>
|
||||
</div>
|
||||
</article>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import type { ElementType } from "react";
|
||||
import {
|
||||
PhoneIcon,
|
||||
EnvelopeSimpleIcon,
|
||||
ClockIcon,
|
||||
} from "@phosphor-icons/react/dist/ssr";
|
||||
|
||||
export type ContactInfo = {
|
||||
icon: ElementType;
|
||||
label: string;
|
||||
href?: string;
|
||||
obfuscated?: {
|
||||
user?: string;
|
||||
domain?: string;
|
||||
tel?: string;
|
||||
};
|
||||
};
|
||||
|
||||
export type ContactCtaData = {
|
||||
heading: string;
|
||||
lead: string;
|
||||
buttonLabel: string;
|
||||
buttonHref: string;
|
||||
contacts: ContactInfo[];
|
||||
};
|
||||
|
||||
export const contactCtaData: ContactCtaData = {
|
||||
heading: "Lassen Sie uns über Ihre Anforderungen sprechen",
|
||||
lead: "Wir erstellen ein individuelles Reinigungskonzept für Ihr Unternehmen in Hannover und Umgebung. Unverbindlich, transparent und exakt auf Ihre Anforderung abgestimmt.",
|
||||
buttonLabel: "Angebot anfragen",
|
||||
buttonHref: "/kontakt",
|
||||
contacts: [
|
||||
{
|
||||
icon: PhoneIcon,
|
||||
label: "0172 621 0606",
|
||||
obfuscated: { tel: "01726210606" },
|
||||
},
|
||||
{
|
||||
icon: EnvelopeSimpleIcon,
|
||||
label: "info@basiclean.de",
|
||||
obfuscated: { user: "info", domain: "basiclean.de" },
|
||||
},
|
||||
{
|
||||
icon: ClockIcon,
|
||||
label: "Mo. - Fr. 08:00 - 17:00 Uhr",
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,108 @@
|
||||
.section {
|
||||
background: #ffffff;
|
||||
padding-bottom: var(--space-16);
|
||||
}
|
||||
|
||||
.inner {
|
||||
max-width: var(--container-width);
|
||||
margin-inline: auto;
|
||||
padding-inline: var(--space-4);
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: var(--color-accent);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: var(--space-8) var(--space-6);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-6);
|
||||
}
|
||||
|
||||
.top {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-6);
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.top :global(.btn) {
|
||||
font-weight: var(--font-weight-semibold);
|
||||
}
|
||||
|
||||
.text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-3);
|
||||
}
|
||||
|
||||
.heading {
|
||||
color: #ffffff;
|
||||
max-width: 24ch;
|
||||
}
|
||||
|
||||
.lead {
|
||||
color: rgba(255, 255, 255);
|
||||
font-size: var(--font-size-xs);
|
||||
max-width: 72ch;
|
||||
}
|
||||
|
||||
.divider {
|
||||
border: none;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.2);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.contactRow {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-3);
|
||||
}
|
||||
|
||||
.contactItem {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
color: #ffffff;
|
||||
font-size: var(--font-size-xs);
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.section {
|
||||
padding-bottom: var(--space-24);
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: var(--space-10) var(--space-10);
|
||||
}
|
||||
|
||||
.top {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--space-8);
|
||||
}
|
||||
|
||||
.contactRow {
|
||||
flex-direction: row;
|
||||
gap: var(--space-8);
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.inner {
|
||||
padding-inline: var(--space-6);
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: var(--space-12) var(--space-12);
|
||||
}
|
||||
}
|
||||
|
||||
@media (hover: none) {
|
||||
.button:hover,
|
||||
.button:focus-visible {
|
||||
background: #ffffff;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { useEffect, useRef } from "react";
|
||||
import { ArrowRightIcon } from "@phosphor-icons/react/dist/ssr";
|
||||
import styles from "./ContactCtaSection.module.css";
|
||||
import { contactCtaData, type ContactInfo } from "./ContactCtaData";
|
||||
|
||||
function ObfuscatedLink({ item }: { item: ContactInfo }) {
|
||||
const ref = useRef<HTMLAnchorElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!ref.current) return;
|
||||
|
||||
if (item.obfuscated?.tel) {
|
||||
ref.current.href = `tel:${item.obfuscated.tel}`;
|
||||
} else if (item.obfuscated?.user && item.obfuscated?.domain) {
|
||||
ref.current.href = `mailto:${item.obfuscated.user}@${item.obfuscated.domain}`;
|
||||
}
|
||||
}, [item.obfuscated]);
|
||||
|
||||
const Icon = item.icon;
|
||||
|
||||
return (
|
||||
<a ref={ref} href="#" className={styles.contactItem}>
|
||||
<Icon size={20} weight="regular" aria-hidden="true" />
|
||||
{item.label}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
export default function ContactCtaSection() {
|
||||
const { heading, lead, buttonLabel, buttonHref, contacts } = contactCtaData;
|
||||
|
||||
return (
|
||||
<section className={styles.section} aria-labelledby="contact-cta-heading">
|
||||
<div className={styles.inner}>
|
||||
<div className={styles.card}>
|
||||
<div className={styles.top}>
|
||||
<div className={styles.text}>
|
||||
<h2 id="contact-cta-heading" className={styles.heading}>
|
||||
{heading}
|
||||
</h2>
|
||||
<p className={styles.lead}>{lead}</p>
|
||||
</div>
|
||||
|
||||
<Link href={buttonHref} className="btn btn--filled-inverse">
|
||||
<span className="btn-icon animate" aria-hidden="true">
|
||||
<ArrowRightIcon size={16} weight="bold" />
|
||||
</span>
|
||||
{buttonLabel}
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<hr className={styles.divider} aria-hidden="true" />
|
||||
|
||||
<div className={styles.contactRow}>
|
||||
{contacts.map((item: ContactInfo) =>
|
||||
item.obfuscated ? (
|
||||
<ObfuscatedLink key={item.label} item={item} />
|
||||
) : (
|
||||
<span key={item.label} className={styles.contactItem}>
|
||||
<item.icon size={20} weight="regular" aria-hidden="true" />
|
||||
{item.label}
|
||||
</span>
|
||||
),
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
.section {
|
||||
background: var(--color-background-linear);
|
||||
padding-block: var(--space-16);
|
||||
}
|
||||
|
||||
.inner {
|
||||
max-width: var(--container-width);
|
||||
margin-inline: auto;
|
||||
padding-inline: var(--space-4);
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: var(--space-8);
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.heading {
|
||||
max-width: 11ch;
|
||||
text-align: left;
|
||||
color: var(--color-accent);
|
||||
}
|
||||
|
||||
.accordion {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-3);
|
||||
}
|
||||
|
||||
.item {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.itemHeading {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.trigger {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--space-4);
|
||||
width: 100%;
|
||||
min-height: 52px;
|
||||
padding: var(--space-3) var(--space-5);
|
||||
background: var(--color-accent);
|
||||
color: #ffffff;
|
||||
border: 0;
|
||||
border-radius: 9999px;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.trigger:hover,
|
||||
.trigger:focus-visible {
|
||||
background: #1f6152;
|
||||
}
|
||||
|
||||
.triggerText {
|
||||
font-size: var(--font-size-s);
|
||||
font-weight: var(--font-weight-medium);
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.iconWrap {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
transition: transform 0.25s ease;
|
||||
}
|
||||
|
||||
.trigger[aria-expanded="true"] .iconWrap {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.panel {
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
transition: max-height 0.4s ease;
|
||||
}
|
||||
|
||||
.panelInner {
|
||||
padding: var(--space-4) var(--space-5) var(--space-2);
|
||||
}
|
||||
|
||||
.answer {
|
||||
max-width: 52ch;
|
||||
color: var(--color-text-muted);
|
||||
font-size: var(----font-size-xs);
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.section {
|
||||
padding-block: var(--space-24);
|
||||
}
|
||||
|
||||
.inner {
|
||||
grid-template-columns: 280px 1fr;
|
||||
gap: var(--space-16);
|
||||
}
|
||||
|
||||
.trigger {
|
||||
min-height: 56px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.section {
|
||||
padding-block: var(--space-36);
|
||||
}
|
||||
|
||||
.inner {
|
||||
padding-inline: var(--space-6);
|
||||
}
|
||||
|
||||
.accordion {
|
||||
max-width: 560px;
|
||||
justify-self: end;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (hover: none) {
|
||||
.trigger:hover,
|
||||
.trigger:focus-visible {
|
||||
background: var(--color-accent);
|
||||
box-shadow: 0 4px 14px rgba(38, 115, 97, 0.14);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
"use client";
|
||||
|
||||
import { useId, useRef, useState } from "react";
|
||||
import { CaretDownIcon } from "@phosphor-icons/react/dist/ssr";
|
||||
import styles from "./FaqSection.module.css";
|
||||
|
||||
export type FaqItem = {
|
||||
question: string;
|
||||
answer: string;
|
||||
};
|
||||
|
||||
type FaqSectionProps = {
|
||||
title: string;
|
||||
items: FaqItem[];
|
||||
};
|
||||
|
||||
type AccordionItemProps = {
|
||||
item: FaqItem;
|
||||
isOpen: boolean;
|
||||
buttonId: string;
|
||||
panelId: string;
|
||||
onToggle: () => void;
|
||||
};
|
||||
|
||||
function AccordionItem({
|
||||
item,
|
||||
isOpen,
|
||||
buttonId,
|
||||
panelId,
|
||||
onToggle,
|
||||
}: AccordionItemProps) {
|
||||
const panelRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
const handleToggle = () => {
|
||||
const panel = panelRef.current;
|
||||
if (!panel) return;
|
||||
|
||||
if (panel.style.maxHeight) {
|
||||
panel.style.maxHeight = "";
|
||||
} else {
|
||||
panel.style.maxHeight = panel.scrollHeight + "px";
|
||||
}
|
||||
|
||||
onToggle();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.item}>
|
||||
<h3 className={styles.itemHeading}>
|
||||
<button
|
||||
id={buttonId}
|
||||
type="button"
|
||||
className={styles.trigger}
|
||||
aria-expanded={isOpen}
|
||||
aria-controls={panelId}
|
||||
onClick={handleToggle}
|
||||
>
|
||||
<span className={styles.triggerText}>{item.question}</span>
|
||||
|
||||
<span className={styles.iconWrap} aria-hidden="true">
|
||||
<CaretDownIcon size={14} weight="bold" />
|
||||
</span>
|
||||
</button>
|
||||
</h3>
|
||||
|
||||
<div
|
||||
id={panelId}
|
||||
ref={panelRef}
|
||||
role="region"
|
||||
aria-labelledby={buttonId}
|
||||
className={styles.panel}
|
||||
>
|
||||
<div className={styles.panelInner}>
|
||||
<p className={styles.answer}>{item.answer}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function FaqSection({ title, items }: FaqSectionProps) {
|
||||
const baseId = useId();
|
||||
const [openIndex, setOpenIndex] = useState<number | null>(null);
|
||||
|
||||
return (
|
||||
<section className={styles.section} aria-labelledby="faq-heading">
|
||||
<div className={styles.inner}>
|
||||
<div className={styles.content}>
|
||||
<h2 id="faq-heading" className={styles.heading}>
|
||||
{title}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className={styles.accordion}>
|
||||
{items.map((item, index) => {
|
||||
const isOpen = openIndex === index;
|
||||
|
||||
return (
|
||||
<AccordionItem
|
||||
key={item.question}
|
||||
item={item}
|
||||
isOpen={isOpen}
|
||||
buttonId={`${baseId}-btn-${index}`}
|
||||
panelId={`${baseId}-panel-${index}`}
|
||||
onToggle={() => setOpenIndex(isOpen ? null : index)}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
export const homepageFaq = [
|
||||
{
|
||||
question: "Kostenlose Beratung und Angebotserstellung",
|
||||
answer:
|
||||
"Wir besprechen Ihr Objekt, den Reinigungsbedarf und die gewünschten Intervalle. Anschließend erhalten Sie ein transparentes Angebot.",
|
||||
},
|
||||
{
|
||||
question: "Individuelle Reinigungslösungen",
|
||||
answer:
|
||||
"Unsere Leistungen passen wir an Ihre Branche, Objektgröße und Anforderungen an. So entsteht ein Reinigungsplan, der wirklich zu Ihrem Alltag passt.",
|
||||
},
|
||||
{
|
||||
question: "Zeitverfügbare Umsetzung",
|
||||
answer:
|
||||
"Wir planen flexibel und reinigen dann, wenn es Ihren Betrieb am wenigsten stört. Auf Wunsch auch früh morgens, abends oder am Wochenende.",
|
||||
},
|
||||
];
|
||||
Reference in new issue
Block a user