add components refinements, accessibility quick wins

This commit is contained in:
Alisa Cantillo-Olsson 2026-05-06 18:44:50 +02:00
parent 28803b266a
commit 92cdd44af3
15 changed files with 342 additions and 310 deletions

View File

@ -1,5 +1,5 @@
<!doctype html>
<html lang="en">
<html lang="de">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />

View File

@ -5,9 +5,12 @@ import ProgrammDetail from "./pages/ProgrammDetail";
import Speaker from "./pages/Speaker";
import SpeakerDetail from "./pages/SpeakerDetail";
import Tickets from "./pages/Tickets";
import { ScrollToTop } from "./components/ScrollToTop";
function App() {
return (
<>
<ScrollToTop />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/programm" element={<Programm />} />
@ -16,6 +19,7 @@ function App() {
<Route path="/speaker/:slug" element={<SpeakerDetail />} />
<Route path="/tickets" element={<Tickets />} />
</Routes>
</>
);
}

View File

@ -39,8 +39,9 @@ export const Cta30 = (props: Cta30Props) => {
className="rb-4 mb-4 grid max-w-sm grid-cols-1 gap-y-3 sm:grid-cols-[1fr_max-content] sm:gap-4"
onSubmit={handleSubmit}
>
<label htmlFor="cta-email" className="sr-only">E-Mail-Adresse</label>
<Input
id="email"
id="cta-email"
type="email"
placeholder={inputPlaceholder}
value={emailInput}

View File

@ -4,6 +4,7 @@ import speaker3 from "../assets/speaker3.png";
import * as React from "react";
import type { ButtonProps } from "@relume_io/relume-ui";
import { Button } from "@relume_io/relume-ui";
import { Link } from "react-router-dom";
//import { RxChevronRight } from "react-icons/rx";
//import { BiMap, BiCalendarAlt } from "react-icons/bi";
@ -34,7 +35,7 @@ type Props = {
tagline: string;
heading: string;
description: string;
button: ButtonProps;
button: ButtonProps & { url?: string };
featuredEvents: FeaturedEvent[];
};
@ -49,8 +50,8 @@ export const Event17 = (props: Event17Props) => {
<section id="relume" className="px-[5%] py-16 md:py-24 lg:py-28 bg-tech-navy font-barlow">
<div className="container">
<div className="mx-auto mb-12 max-w-lg text-center md:mb-18 lg:mb-20">
<h4 className="font-semibold">{tagline}</h4>
<h1 className="mt-3 text-5xl font-bold md:mt-4 md:text-7xl lg:text-8xl text-acid-lime">{heading}</h1>
{tagline && <p className="font-semibold">{tagline}</p>}
<h2 className="mt-3 text-5xl font-bold md:mt-4 md:text-7xl lg:text-8xl text-acid-lime">{heading}</h2>
<p className="mt-5 text-base md:mt-6 md:text-md text-cloud-white whitespace-pre-wrap">{description}</p>
</div>
<div className="grid auto-cols-fr grid-cols-1 gap-x-8 gap-y-12 md:grid-cols-2 md:gap-y-16 lg:grid-cols-3">
@ -59,7 +60,13 @@ export const Event17 = (props: Event17Props) => {
))}
</div>
<div className="mt-12 flex justify-center md:mt-18 lg:mt-20">
<Button {...button} className="bg-acid-lime text-tech-navy font-semibold px-6 py-3 rounded-2xl border-acid-lime">{button.title}</Button>
{button.url ? (
<Link to={button.url}>
<Button {...button} className="bg-acid-lime text-tech-navy font-semibold px-6 py-3 rounded-2xl border-acid-lime">{button.title}</Button>
</Link>
) : (
<Button {...button} className="bg-acid-lime text-tech-navy font-semibold px-6 py-3 rounded-2xl border-acid-lime">{button.title}</Button>
)}
</div>
</div>
</section>
@ -83,7 +90,7 @@ export const Event17Defaults: Props = {
tagline: "",
heading: "Speaker:innen",
description: "Überwältigt von unserem Programm?\nLerne unsere diesjährigen Speakerinnen und Speaker kennen.",
button: { variant: "primary", size: "primary", title: "Zu den Speakern" },
button: { variant: "primary", size: "primary", title: "Zu den Speakern", url: "/speaker" },
featuredEvents: [
{
url: "/speaker", image: { src: speaker1, alt: "Dr. Elena Rossi" },

View File

@ -51,7 +51,7 @@ export const Event20 = (props: Event20Props) => {
<div className="mb-12 grid auto-cols-fr grid-cols-1 items-end gap-12 md:mb-18 md:grid-cols-[1fr_max-content] lg:mb-20 lg:gap-20">
<div className="max-w-lg">
<p className="mb-3 font-semibold md:mb-4">{tagline}</p>
<h1 className="mb-3 text-5xl font-bold md:mb-4 md:text-7xl lg:text-8xl text-cloud-white">{heading}</h1>
<h2 className="mb-3 text-5xl font-bold md:mb-4 md:text-7xl lg:text-8xl text-cloud-white">{heading}</h2>
<p className="md:text-md text-cloud-white">{description}</p>
</div>
<Button {...button} className="hidden md:flex bg-acid-lime text-tech-navy font-semibold px-6 py-3 rounded-2xl border-acid-lime">

View File

@ -21,6 +21,7 @@ type Links = {
type SocialMediaLinks = {
url: string;
icon: React.ReactNode;
label: string;
};
type ColumnLinks = {
@ -80,7 +81,7 @@ export const Footer3 = (props: Footer3Props) => {
</div>
<div className="grid grid-flow-col grid-cols-[max-content] items-start justify-start gap-x-3">
{socialMediaLinks.map((link, index) => (
<a key={index} href={link.url}>
<a key={index} href={link.url} aria-label={link.label}>
{link.icon}
</a>
))}
@ -134,24 +135,24 @@ export const Footer3Defaults: Props = {
links: [
{ title: "Programm", url: "/programm" },
{ title: "Speaker", url: "/speaker" },
{ title: "Location", url: "#" },
{ title: "Tickets", url: "#tickets" },
{ title: "FAQ", url: "#faq" },
{ title: "Location", url: "/location" },
{ title: "Tickets", url: "/tickets" },
{ title: "FAQ", url: "/programm#faq" },
],
},
{ links: [] },
],
socialMediaLinks: [
{ url: "#", icon: <BiLogoFacebookCircle className="size-6 text-neutral-dark" /> },
{ url: "#", icon: <BiLogoInstagram className="size-6 text-neutral-dark" /> },
{ url: "#", icon: <FaXTwitter className="size-6 p-0.5 text-neutral-dark" /> },
{ url: "#", icon: <BiLogoLinkedinSquare className="size-6 text-neutral-dark" /> },
{ url: "#", icon: <BiLogoYoutube className="size-6 text-neutral-dark" /> },
{ url: "#", label: "UXplore auf Facebook", icon: <BiLogoFacebookCircle className="size-6 text-neutral-dark" /> },
{ url: "#", label: "UXplore auf Instagram", icon: <BiLogoInstagram className="size-6 text-neutral-dark" /> },
{ url: "#", label: "UXplore auf X (Twitter)", icon: <FaXTwitter className="size-6 p-0.5 text-neutral-dark" /> },
{ url: "#", label: "UXplore auf LinkedIn", icon: <BiLogoLinkedinSquare className="size-6 text-neutral-dark" /> },
{ url: "#", label: "UXplore auf YouTube", icon: <BiLogoYoutube className="size-6 text-neutral-dark" /> },
],
footerText: "© 2026 UXplore",
footerLinks: [
{ title: "AGB", url: "#" },
{ title: "Datenschutz", url: "#" },
{ title: "Cookie Einstellungen", url: "#" },
{ title: "AGB", url: "/agb" },
{ title: "Datenschutz", url: "/datenschutz" },
{ title: "Cookie Einstellungen", url: "/cookie-einstellungen" },
],
};

View File

@ -37,12 +37,15 @@ export const Navbar3 = (props: Navbar3Props) => {
const isMobile = useMediaQuery("(max-width: 991px)");
return (
<section
<nav
id="relume"
className="z-[999] grid w-full grid-cols-[1fr_max-content_1fr] items-center justify-between border-b border-neutral-dark bg-tech-navy px-[5%] md:min-h-18 font-barlow"
aria-label="Hauptnavigation"
className="sticky top-0 z-[999] grid w-full grid-cols-[1fr_max-content_1fr] items-center justify-between border-b border-neutral-dark bg-tech-navy px-[5%] md:min-h-18 font-barlow"
>
<button
className="flex size-12 flex-col justify-center lg:hidden"
aria-label={isMobileMenuOpen ? "Menü schließen" : "Menü öffnen"}
aria-expanded={isMobileMenuOpen}
onClick={() => setIsMobileMenuOpen((prev) => !prev)}
>
{Array(3)
@ -134,7 +137,7 @@ export const Navbar3 = (props: Navbar3Props) => {
)}
</div>
</div>
</section>
</nav>
);
};
@ -209,7 +212,7 @@ export const Navbar3Defaults: Props = {
navLinks: [
{ title: "Programm", url: "/programm" },
{ title: "Speaker", url: "/speaker" },
{ title: "Location", url: "#" },
{ title: "Location", url: "/location" },
],
buttons: [
{

View File

@ -0,0 +1,10 @@
import { useEffect } from "react";
import { useLocation } from "react-router-dom";
export const ScrollToTop = () => {
const { pathname } = useLocation();
useEffect(() => {
window.scrollTo(0, 0);
}, [pathname]);
return null;
};

View File

@ -16,6 +16,7 @@ type ImageProps = {
type SocialLink = {
href: string;
icon: React.ReactNode;
label: string;
};
type Talk = {
@ -67,7 +68,7 @@ const MemberCard = ({ member }: { member: TeamMember }) => (
{member.socialLinks.length > 0 && (
<div className="flex gap-2 shrink-0">
{member.socialLinks.map((link, i) => (
<a key={i} href={link.href}>{link.icon}</a>
<a key={i} href={link.href} aria-label={link.label}>{link.icon}</a>
))}
</div>
)}
@ -95,8 +96,8 @@ export const Team4 = (props: Team4Props) => {
<p className="py-16 text-center text-gray-400">Keine Speaker gefunden.</p>
) : (
<div className="grid grid-cols-1 gap-x-8 gap-y-12 sm:grid-cols-2 lg:grid-cols-4">
{teamMembers.map((member, i) => (
<MemberCard key={i} member={member} />
{teamMembers.map((member) => (
<MemberCard key={member.slug} member={member} />
))}
</div>
)}
@ -124,7 +125,7 @@ export const Team4Defaults: Props = {
{ title: "Talk: Scaling Research", type: "Talk" },
{ title: "Workshop: Skalierbare Research-Infrastruktur", type: "Workshop" },
],
socialLinks: [{ href: "#", icon: <BiLogoLinkedinSquare className="size-6" /> }],
socialLinks: [{ href: "#", label: "LinkedIn-Profil", icon: <BiLogoLinkedinSquare className="size-6" /> }],
},
{
image: { src: speaker2, alt: "Marcus J. Low" },
@ -139,7 +140,7 @@ export const Team4Defaults: Props = {
"Marcus J. Low ist Principal Designer bei Airbnb und war massgeblich an der Entwicklung des ersten Airbnb Design Systems beteiligt. Er spricht über die Balance zwischen Markenästhetik und funktionaler Logik und teilt seine Erfahrungen aus über 10 Jahren in der Produktgestaltung für globale Plattformen.",
category: "Design / UX",
talks: [{ title: "Workshop: The Future of Tokens", type: "Workshop" }],
socialLinks: [{ href: "#", icon: <BiLogoLinkedinSquare className="size-6" /> }],
socialLinks: [{ href: "#", label: "LinkedIn-Profil", icon: <BiLogoLinkedinSquare className="size-6" /> }],
},
{
image: { src: speaker3, alt: "Reto Gwerder" },
@ -154,7 +155,7 @@ export const Team4Defaults: Props = {
"Reto Gwerder ist Head of Product bei Ginetta und steht für konsequente Schweizer Design-Qualität. Er analysiert, warum Simplizität oft die größte technische Herausforderung ist und wie man in einem schnellen Produktumfeld trotzdem Qualität bewahrt.",
category: "Strategie",
talks: [{ title: "Talk: Simplify or Die", type: "Talk" }],
socialLinks: [{ href: "#", icon: <BiLogoLinkedinSquare className="size-6" /> }],
socialLinks: [{ href: "#", label: "LinkedIn-Profil", icon: <BiLogoLinkedinSquare className="size-6" /> }],
},
{
image: { src: speaker4, alt: "Dr. Elena Rossi" },
@ -169,7 +170,7 @@ export const Team4Defaults: Props = {
"Dr. Elena Rossi ist Kognitionspsychologin an der Universität Mailand und verbindet Wissenschaft mit Design. Sie erklärt, wie unser Gehirn auf Micro-Interactions reagiert und wann wir uns manipuliert fühlen. Elena forscht seit über einem Jahrzehnt zur Schnittstelle von Psychologie und digitaler Produktgestaltung.",
category: "Psychologie",
talks: [{ title: "Deep Dive: Psychological UX", type: "Talk" }],
socialLinks: [{ href: "#", icon: <BiLogoLinkedinSquare className="size-6" /> }],
socialLinks: [{ href: "#", label: "LinkedIn-Profil", icon: <BiLogoLinkedinSquare className="size-6" /> }],
},
{
image: { src: speaker5, alt: "Liam O'Connor" },
@ -184,7 +185,7 @@ export const Team4Defaults: Props = {
"Liam O'Connor ist Lead Product Designer bei Spotify und gibt Einblicke, wie das Unternehmen Personalisierung nutzt, ohne die Privatsphäre der Nutzer zu verletzen. Er arbeitet an der Schnittstelle von Daten, Design und Ethik.",
category: "AI & Future",
talks: [{ title: "Talk: Trust in AI UX", type: "Talk" }],
socialLinks: [{ href: "#", icon: <BiLogoLinkedinSquare className="size-6" /> }],
socialLinks: [{ href: "#", label: "LinkedIn-Profil", icon: <BiLogoLinkedinSquare className="size-6" /> }],
},
{
image: { src: speaker6, alt: "Fabienne Keller" },
@ -199,7 +200,7 @@ export const Team4Defaults: Props = {
"Fabienne Keller ist Gründerin der User-First Agency und Expertin für User Research in komplexen B2B-Umfeldern. Sie zeigt, wie man Stakeholder von Test-Ergebnissen überzeugt und eine nachhaltige Research-Kultur in Unternehmen aufbaut.",
category: "Research",
talks: [{ title: "Workshop: Research Repositories", type: "Workshop" }],
socialLinks: [{ href: "#", icon: <BiLogoLinkedinSquare className="size-6" /> }],
socialLinks: [{ href: "#", label: "LinkedIn-Profil", icon: <BiLogoLinkedinSquare className="size-6" /> }],
},
],
};

View File

@ -10,11 +10,13 @@ export const Home = () => {
return (
<div>
<Navbar3 />
<Header1 />
<Content11 />
<Event17 />
<Event20 />
<Cta30 />
<main>
<Header1 />
<Content11 />
<Event17 />
<Event20 />
<Cta30 />
</main>
<Footer3 />
</div>
);

View File

@ -66,19 +66,21 @@ const Programm = () => {
return (
<div>
<Navbar3 />
<Header23 />
<Filters5
activeTabs={activeTabs}
onTabsChange={setActiveTabs}
searchQuery={searchQuery}
onSearchChange={setSearchQuery}
activeTags={activeTags}
onReset={handleReset}
sortOrder={sortOrder}
onSortChange={setSortOrder}
/>
<Event33 events={filteredEvents} />
<Banner15 />
<main>
<Header23 />
<Filters5
activeTabs={activeTabs}
onTabsChange={setActiveTabs}
searchQuery={searchQuery}
onSearchChange={setSearchQuery}
activeTags={activeTags}
onReset={handleReset}
sortOrder={sortOrder}
onSortChange={setSortOrder}
/>
<Event33 events={filteredEvents} />
<Banner15 />
</main>
<Footer3 />
</div>
);

View File

@ -50,110 +50,107 @@ const ProgrammDetail = () => {
return (
<div>
<Navbar3 />
{/* Hero section */}
<section className="bg-tech-navy px-[5%] py-16 md:py-24 font-barlow">
<div className="container">
<div className="grid grid-cols-1 items-center gap-12 lg:grid-cols-2 lg:gap-20">
{/* Left: event info */}
<div>
<span className="mb-4 inline-block rounded-full border border-electric-violet px-3 py-1 text-xs font-semibold text-electric-violet">
{event.type}
</span>
<h1 className="mb-4 text-5xl font-bold text-cloud-white md:text-6xl lg:text-7xl">
{event.title}
</h1>
{event.subtitle && (
<p className="mb-8 text-lg text-cloud-white/70">{event.subtitle}</p>
)}
<div className="mb-8 flex flex-col gap-3">
<div className="flex items-center gap-3 text-cloud-white/80">
<BiCalendarAlt className="size-5 shrink-0 text-acid-lime" />
<span className="text-sm">{dateLabel}, {event.time}</span>
</div>
<div className="flex items-center gap-3 text-cloud-white/80">
<BiUser className="size-5 shrink-0 text-acid-lime" />
<span className="text-sm">{event.speaker}</span>
</div>
</div>
<Link
to="/tickets"
className="inline-block rounded-2xl bg-acid-lime px-8 py-3 text-base font-bold text-tech-navy hover:opacity-90 transition-opacity"
>
Ticket kaufen
</Link>
</div>
{/* Right: speaker image */}
<div className="aspect-square w-full max-w-md overflow-hidden lg:ml-auto">
<img
src={event.image.src}
alt={event.image.alt}
className="size-full object-cover object-top"
/>
</div>
</div>
</div>
</section>
{/* Speaker bio + event description */}
<section className="bg-tech-navy px-[5%] pb-16 md:pb-24 font-barlow">
<div className="container border-t border-white/10 pt-16">
<div className="grid grid-cols-1 gap-12 lg:grid-cols-2 lg:gap-20">
{/* Left: über den speaker */}
<div>
<RxCube className="mb-4 size-8 text-electric-violet" />
<h2 className="mb-6 text-2xl font-bold text-cloud-white md:text-3xl">
Über den Speaker
</h2>
{speaker ? (
<>
<p className="mb-1 text-lg font-bold text-cloud-white">{speaker.name}</p>
<p className="mb-1 text-sm text-cloud-white/60">{speaker.role}</p>
<p className="mb-4 text-sm text-cloud-white/60">{speaker.company}</p>
<div className="flex gap-3 mb-6">
<a href="#" className="text-cloud-white/60 hover:text-acid-lime transition-colors">
<BiLogoLinkedinSquare className="size-6" />
</a>
<a href="#" className="text-cloud-white/60 hover:text-acid-lime transition-colors">
<SiXing className="size-5" />
</a>
<a href="#" className="text-cloud-white/60 hover:text-acid-lime transition-colors">
<BiLogoDribbble className="size-6" />
</a>
<main>
{/* Hero section */}
<section className="bg-tech-navy px-[5%] py-16 md:py-24 font-barlow">
<div className="container">
<div className="grid grid-cols-1 items-center gap-12 lg:grid-cols-2 lg:gap-20">
{/* Left: event info */}
<div>
<span className="mb-4 inline-block rounded-full border border-electric-violet px-3 py-1 text-xs font-semibold text-electric-violet">
{event.type}
</span>
<h1 className="mb-4 text-5xl font-bold text-cloud-white md:text-6xl lg:text-7xl">
{event.title}
</h1>
{event.subtitle && (
<p className="mb-8 text-lg text-cloud-white/70">{event.subtitle}</p>
)}
<div className="mb-8 flex flex-col gap-3">
<div className="flex items-center gap-3 text-cloud-white/80">
<BiCalendarAlt className="size-5 shrink-0 text-acid-lime" />
<span className="text-sm">{dateLabel}, {event.time}</span>
</div>
<Link
to={`/speaker/${speaker.slug}`}
className="inline-block rounded-2xl border border-acid-lime px-6 py-2.5 text-sm font-semibold text-acid-lime hover:bg-acid-lime hover:text-tech-navy transition-colors"
>
Speaker Details
</Link>
</>
) : (
<p className="text-cloud-white/60">{event.speaker}</p>
)}
</div>
<div className="flex items-center gap-3 text-cloud-white/80">
<BiUser className="size-5 shrink-0 text-acid-lime" />
<span className="text-sm">{event.speaker}</span>
</div>
</div>
<Link
to="/tickets"
className="inline-block rounded-2xl bg-acid-lime px-8 py-3 text-base font-bold text-tech-navy hover:opacity-90 transition-opacity"
>
Ticket kaufen
</Link>
</div>
{/* Right: das erwartete dich */}
<div>
<RxCube className="mb-4 size-8 text-electric-violet" />
<h2 className="mb-6 text-2xl font-bold text-cloud-white md:text-3xl">
Das erwartet dich
</h2>
<p className="text-base leading-relaxed text-cloud-white/80">
{event.description ?? "Weitere Informationen folgen in Kürze."}
</p>
{/* Right: speaker image */}
<div className="aspect-square w-full max-w-md overflow-hidden lg:ml-auto">
<img
src={event.image.src}
alt={event.image.alt}
className="size-full object-cover object-top"
/>
</div>
</div>
</div>
</div>
</section>
</section>
{/* Stay tuned */}
<Cta30 />
{/* Speaker bio + event description */}
<section className="bg-tech-navy px-[5%] pb-16 md:pb-24 font-barlow">
<div className="container border-t border-white/10 pt-16">
<div className="grid grid-cols-1 gap-12 lg:grid-cols-2 lg:gap-20">
{/* Left: über den speaker */}
<div>
<RxCube className="mb-4 size-8 text-electric-violet" />
<h2 className="mb-6 text-2xl font-bold text-cloud-white md:text-3xl">
Über den Speaker
</h2>
{speaker ? (
<>
<p className="mb-1 text-lg font-bold text-cloud-white">{speaker.name}</p>
<p className="mb-1 text-sm text-cloud-white/60">{speaker.role}</p>
<p className="mb-4 text-sm text-cloud-white/60">{speaker.company}</p>
<div className="flex gap-3 mb-6">
<a href="#" aria-label={`${speaker.name} auf LinkedIn`} className="text-cloud-white/60 hover:text-acid-lime transition-colors">
<BiLogoLinkedinSquare className="size-6" />
</a>
<a href="#" aria-label={`${speaker.name} auf Xing`} className="text-cloud-white/60 hover:text-acid-lime transition-colors">
<SiXing className="size-5" />
</a>
<a href="#" aria-label={`${speaker.name} auf Dribbble`} className="text-cloud-white/60 hover:text-acid-lime transition-colors">
<BiLogoDribbble className="size-6" />
</a>
</div>
<Link
to={`/speaker/${speaker.slug}`}
className="inline-block rounded-2xl border border-acid-lime px-6 py-2.5 text-sm font-semibold text-acid-lime hover:bg-acid-lime hover:text-tech-navy transition-colors"
>
Speaker Details
</Link>
</>
) : (
<p className="text-cloud-white/60">{event.speaker}</p>
)}
</div>
{/* Sponsors */}
<SponsorsSection />
{/* Right: das erwartet dich */}
<div>
<RxCube className="mb-4 size-8 text-electric-violet" />
<h2 className="mb-6 text-2xl font-bold text-cloud-white md:text-3xl">
Das erwartet dich
</h2>
<p className="text-base leading-relaxed text-cloud-white/80">
{event.description ?? "Weitere Informationen folgen in Kürze."}
</p>
</div>
</div>
</div>
</section>
<Cta30 />
<SponsorsSection />
</main>
<Footer3 />
</div>
);

View File

@ -47,28 +47,30 @@ const Speaker = () => {
return (
<div>
<Navbar3 />
<Header23
heading="Unsere Speaker"
description="Erfahre von den klügsten Köpfen der Branche, wie sie komplexe Design-Herausforderungen meistern. Von Schweizer Präzision im Interface bis hin zu globalen Strategien führender Tech-Giganten unsere Speaker bringen handfeste Insights statt nur Buzzwords."
buttons={[
{ title: "Zum Programm", href: "/programm", className: "bg-transparent border-acid-lime text-acid-lime hover:bg-acid-lime/10" },
]}
/>
<Filters5
tabs={SPEAKER_TABS}
activeTabs={activeTabs}
onTabsChange={setActiveTabs}
searchQuery={searchQuery}
onSearchChange={setSearchQuery}
searchLabel="Stichwortsuche"
resetLabel="Löschen"
activeTags={activeTags}
onReset={() => { setSearchQuery(""); setActiveTabs([]); }}
useFilterToggle={false}
showDayFilter={false}
showSortButton={false}
/>
<Team4 teamMembers={filteredSpeakers} />
<main>
<Header23
heading="Unsere Speaker"
description="Erfahre von den klügsten Köpfen der Branche, wie sie komplexe Design-Herausforderungen meistern. Von Schweizer Präzision im Interface bis hin zu globalen Strategien führender Tech-Giganten unsere Speaker bringen handfeste Insights statt nur Buzzwords."
buttons={[
{ title: "Zum Programm", href: "/programm", className: "bg-transparent border-acid-lime text-acid-lime hover:bg-acid-lime/10" },
]}
/>
<Filters5
tabs={SPEAKER_TABS}
activeTabs={activeTabs}
onTabsChange={setActiveTabs}
searchQuery={searchQuery}
onSearchChange={setSearchQuery}
searchLabel="Stichwortsuche"
resetLabel="Löschen"
activeTags={activeTags}
onReset={() => { setSearchQuery(""); setActiveTabs([]); }}
useFilterToggle={false}
showDayFilter={false}
showSortButton={false}
/>
<Team4 teamMembers={filteredSpeakers} />
</main>
<Footer3 />
</div>
);

View File

@ -46,103 +46,102 @@ const SpeakerDetail = () => {
<div>
<Navbar3 />
{/* Hero image */}
<div className="w-full aspect-[16/7] overflow-hidden">
<img
src={(speaker.detailImage ?? speaker.image).src}
alt={(speaker.detailImage ?? speaker.image).alt}
className="size-full object-cover object-top"
/>
</div>
{/* Speaker info */}
<section className="bg-tech-navy px-[5%] py-16 md:py-24 font-barlow">
<div className="container">
<div className="grid grid-cols-1 gap-12 lg:grid-cols-2 lg:gap-20">
{/* Left: badge, name, location */}
<div>
<span className="mb-6 inline-block rounded-full bg-acid-lime px-4 py-1.5 text-sm font-bold text-tech-navy">
{speaker.category}
</span>
<h1 className="mb-4 text-5xl font-bold text-cloud-white md:text-7xl">
{speaker.name}
</h1>
<p className="text-base font-semibold text-cloud-white/80">{speaker.role}</p>
<p className="mb-2 text-base text-cloud-white/60">{speaker.company}</p>
{speaker.location && (
<p className="text-base text-cloud-white/60">{speaker.location}</p>
)}
</div>
{/* Right: bio + social */}
<div className="flex flex-col justify-center gap-6">
<p className="text-base leading-relaxed text-cloud-white/80 md:text-lg">
{speaker.fullBio ?? speaker.description}
</p>
{speaker.socialLinks.length > 0 && (
<div className="flex gap-4">
{speaker.socialLinks.map((link, i) => (
<a
key={i}
href={link.href}
className="text-cloud-white/60 hover:text-acid-lime transition-colors"
>
{link.icon}
</a>
))}
</div>
)}
</div>
</div>
</div>
</section>
{/* Sessions */}
{speakerEvents.length > 0 && (
<section className="bg-cloud-white px-[5%] py-16 font-barlow">
<main>
{/* Speaker info */}
<section className="bg-tech-navy px-[5%] py-16 md:py-24 font-barlow">
<div className="container">
<h2 className="mb-8 text-3xl font-bold text-tech-navy md:text-4xl">Sessions</h2>
<div className="flex flex-col gap-4">
{speakerEvents.map((event) => (
<a
key={event.id}
href={`/programm/${event.id}`}
className="group flex flex-col gap-3 rounded-2xl border border-gray-200 p-6 transition-colors hover:border-electric-violet sm:flex-row sm:items-center sm:justify-between"
>
<div className="flex flex-col gap-2">
<div className="flex flex-wrap items-center gap-2">
<span className="rounded-md bg-electric-violet/10 px-3 py-1 text-xs font-semibold text-electric-violet">
{event.type}
</span>
<span className="text-sm text-gray-400">
{event.day} · {event.time}
</span>
</div>
<p className="text-base font-semibold text-tech-navy group-hover:text-electric-violet transition-colors">
{event.title}
</p>
<div className="grid grid-cols-1 items-center gap-12 lg:grid-cols-2 lg:gap-20">
{/* Left: badge, name, bio, social */}
<div>
<span className="mb-6 inline-block rounded-full bg-acid-lime px-4 py-1.5 text-sm font-bold text-tech-navy">
{speaker.category}
</span>
<h1 className="mb-4 text-5xl font-bold text-cloud-white md:text-7xl">
{speaker.name}
</h1>
<p className="text-base font-semibold text-cloud-white/80">{speaker.role}</p>
<p className="mb-2 text-base text-cloud-white/60">{speaker.company}</p>
{speaker.location && (
<p className="mb-6 text-base text-cloud-white/60">{speaker.location}</p>
)}
<p className="text-base leading-relaxed text-cloud-white/80 md:text-lg">
{speaker.fullBio ?? speaker.description}
</p>
{speaker.socialLinks.length > 0 && (
<div className="mt-6 flex gap-4">
{speaker.socialLinks.map((link, i) => (
<a
key={i}
href={link.href}
aria-label={link.label}
className="text-cloud-white/60 hover:text-acid-lime transition-colors"
>
{link.icon}
</a>
))}
</div>
<span className="shrink-0 text-sm font-semibold text-electric-violet">
Zur Session
</span>
</a>
))}
)}
</div>
{/* Right: speaker image */}
<div className="aspect-square w-full max-w-md overflow-hidden lg:ml-auto">
<img
src={(speaker.detailImage ?? speaker.image).src}
alt={(speaker.detailImage ?? speaker.image).alt}
className="size-full object-cover object-top"
/>
</div>
</div>
</div>
</section>
)}
{/* Other speakers */}
<div className="bg-cloud-white">
<div className="px-[5%] pt-16 font-barlow">
<div className="container mb-10">
<h2 className="text-3xl font-bold text-tech-navy md:text-4xl">Weitere Speaker</h2>
{/* Sessions */}
{speakerEvents.length > 0 && (
<section className="bg-cloud-white px-[5%] py-16 font-barlow">
<div className="container">
<h2 className="mb-8 text-3xl font-bold text-tech-navy md:text-4xl">Sessions</h2>
<div className="flex flex-col gap-4">
{speakerEvents.map((event) => (
<a
key={event.id}
href={`/programm/${event.id}`}
className="group flex flex-col gap-3 rounded-2xl border border-gray-200 p-6 transition-colors hover:border-electric-violet sm:flex-row sm:items-center sm:justify-between"
>
<div className="flex flex-col gap-2">
<div className="flex flex-wrap items-center gap-2">
<span className="rounded-md bg-electric-violet/10 px-3 py-1 text-xs font-semibold text-electric-violet">
{event.type}
</span>
<span className="text-sm text-gray-400">
{event.day} · {event.time}
</span>
</div>
<p className="text-base font-semibold text-tech-navy group-hover:text-electric-violet transition-colors">
{event.title}
</p>
</div>
<span className="shrink-0 text-sm font-semibold text-electric-violet">
Zur Session
</span>
</a>
))}
</div>
</div>
</section>
)}
{/* Other speakers */}
<div className="bg-cloud-white">
<div className="px-[5%] pt-16 font-barlow">
<div className="container mb-10">
<h2 className="text-3xl font-bold text-tech-navy md:text-4xl">Weitere Speaker</h2>
</div>
</div>
<Team4 teamMembers={otherSpeakers} />
</div>
<Team4 teamMembers={otherSpeakers} />
</div>
<SponsorsSection />
<SponsorsSection />
</main>
<Footer3 />
</div>
);

View File

@ -77,60 +77,63 @@ const Tickets = () => {
return (
<div>
<Navbar3 />
<Header23
heading="Ticketshop"
description="Erfahre von den klügsten Köpfen der Branche, wie sie komplexe Design-Herausforderungen meistern. Von Schweizer Präzision im Interface bis hin zu globalen Strategien führender Tech-Giganten unsere Speaker bringen handfeste Insights statt nur Buzzwords."
buttons={[]}
showTicketButton={false}
>
<div className="mt-5 font-semibold text-cloud-white md:mt-6 md:text-md">
DO FR, 25. 26. Juni 2026
</div>
<p className="mt-4 inline-block bg-electric-violet px-2 py-1 text-sm font-semibold text-cloud-white">
10 Spots left!
</p>
<div className="mt-8 flex justify-center">
<div className="w-fit">
<Countdown targetDate={COUNTDOWN_TARGET} />
<main>
<Header23
heading="Ticketshop"
description="Erfahre von den klügsten Köpfen der Branche, wie sie komplexe Design-Herausforderungen meistern. Von Schweizer Präzision im Interface bis hin zu globalen Strategien führender Tech-Giganten unsere Speaker bringen handfeste Insights statt nur Buzzwords."
buttons={[]}
showTicketButton={false}
>
<div className="mt-5 font-semibold text-cloud-white md:mt-6 md:text-md">
DO FR, 25. 26. Juni 2026
</div>
</div>
<div className="mt-6 w-full max-w-sm md:mt-8 mx-auto">
<form
className="mb-4 grid max-w-sm grid-cols-1 gap-y-3 sm:grid-cols-[1fr_max-content] sm:gap-4"
onSubmit={handleSubmit}
>
<Input
id="email"
type="email"
placeholder="Enter your email"
value={emailInput}
onChange={(e) => setEmailInput(e.target.value)}
className="rounded-lg border-electric-violet text-electric-violet placeholder:text-electric-violet/50"
/>
<button
type="submit"
className="rounded-2xl bg-acid-lime px-5 py-2 text-sm font-normal uppercase text-tech-navy hover:opacity-90 transition-opacity"
>
Get Updates
</button>
</form>
<p className="text-xs text-cloud-white">
By clicking Get Updates you&apos;re confirming that you agree with our{" "}
<a href="#" className="underline">Terms and Conditions</a>.
<p className="mt-4 inline-block bg-electric-violet px-2 py-1 text-sm font-semibold text-cloud-white">
10 Spots left!
</p>
</div>
</Header23>
<Pricing39Ticket
heading="1-Tagespässe"
plans={[
{ day: "Donnerstag", date: "25. Juni 2026", earlyBirdPrice: 99, regularPrice: 131, earlyBirdDeadline: "31. Mai 2026" },
{ day: "Freitag", date: "26. Juni 2026", earlyBirdPrice: 99, regularPrice: 131, earlyBirdDeadline: "31. Mai 2026" },
]}
/>
<Pricing39Ticket
heading="2-Tagespass"
plans={[{ day: "Donnerstag Freitag", date: "25. 26. Juni 2026", earlyBirdPrice: 179, regularPrice: 236, earlyBirdDeadline: "31. Mai 2026" }]}
/>
<div className="mt-8 flex justify-center">
<div className="w-fit">
<Countdown targetDate={COUNTDOWN_TARGET} />
</div>
</div>
<div className="mt-6 w-full max-w-sm md:mt-8 mx-auto">
<form
className="mb-4 grid max-w-sm grid-cols-1 gap-y-3 sm:grid-cols-[1fr_max-content] sm:gap-4"
onSubmit={handleSubmit}
>
<label htmlFor="tickets-email" className="sr-only">E-Mail-Adresse</label>
<Input
id="tickets-email"
type="email"
placeholder="Enter your email"
value={emailInput}
onChange={(e) => setEmailInput(e.target.value)}
className="rounded-lg border-electric-violet text-electric-violet placeholder:text-electric-violet/50"
/>
<button
type="submit"
className="rounded-2xl bg-acid-lime px-5 py-2 text-sm font-normal uppercase text-tech-navy hover:opacity-90 transition-opacity"
>
Get Updates
</button>
</form>
<p className="text-xs text-cloud-white">
By clicking Get Updates you&apos;re confirming that you agree with our{" "}
<a href="/agb" className="underline">Terms and Conditions</a>.
</p>
</div>
</Header23>
<Pricing39Ticket
heading="1-Tagespässe"
plans={[
{ day: "Donnerstag", date: "25. Juni 2026", earlyBirdPrice: 99, regularPrice: 131, earlyBirdDeadline: "31. Mai 2026" },
{ day: "Freitag", date: "26. Juni 2026", earlyBirdPrice: 99, regularPrice: 131, earlyBirdDeadline: "31. Mai 2026" },
]}
/>
<Pricing39Ticket
heading="2-Tagespass"
plans={[{ day: "Donnerstag Freitag", date: "25. 26. Juni 2026", earlyBirdPrice: 179, regularPrice: 236, earlyBirdDeadline: "31. Mai 2026" }]}
/>
</main>
<Footer3 />
</div>
);