add components refinements, accessibility quick wins
This commit is contained in:
parent
28803b266a
commit
92cdd44af3
@ -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" />
|
||||
|
||||
@ -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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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" },
|
||||
|
||||
@ -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">
|
||||
|
||||
@ -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" },
|
||||
],
|
||||
};
|
||||
@ -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: [
|
||||
{
|
||||
|
||||
10
relume-test/src/components/ScrollToTop.tsx
Normal file
10
relume-test/src/components/ScrollToTop.tsx
Normal 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;
|
||||
};
|
||||
@ -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" /> }],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@ -10,11 +10,13 @@ export const Home = () => {
|
||||
return (
|
||||
<div>
|
||||
<Navbar3 />
|
||||
<Header1 />
|
||||
<Content11 />
|
||||
<Event17 />
|
||||
<Event20 />
|
||||
<Cta30 />
|
||||
<main>
|
||||
<Header1 />
|
||||
<Content11 />
|
||||
<Event17 />
|
||||
<Event20 />
|
||||
<Cta30 />
|
||||
</main>
|
||||
<Footer3 />
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -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>
|
||||
);
|
||||
|
||||
@ -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>
|
||||
);
|
||||
|
||||
@ -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>
|
||||
);
|
||||
|
||||
@ -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>
|
||||
);
|
||||
|
||||
@ -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'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'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>
|
||||
);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user