adjustments based on the coaching feedback

This commit is contained in:
Alisa Cantillo-Olsson 2026-05-02 16:43:37 +02:00
parent ed0d261056
commit 28803b266a
16 changed files with 421 additions and 278 deletions

View File

@ -0,0 +1 @@
/* /index.html 200

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="m13.966 22.624l-1.69-4.281H8.122l3.892-9.144l5.662 13.425zM8.884 1.376H0v21.248zm15.116 0h-8.884L24 22.624Z"/></svg>

After

Width:  |  Height:  |  Size: 230 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M15.852 8.981h-4.588V0h4.588c2.476 0 4.49 2.014 4.49 4.49s-2.014 4.491-4.49 4.491M12.735 7.51h3.117c1.665 0 3.019-1.355 3.019-3.019s-1.355-3.019-3.019-3.019h-3.117zm0 1.471H8.148c-2.476 0-4.49-2.014-4.49-4.49S5.672 0 8.148 0h4.588v8.981zm-4.587-7.51c-1.665 0-3.019 1.355-3.019 3.019s1.354 3.02 3.019 3.02h3.117V1.471zm4.587 15.019H8.148c-2.476 0-4.49-2.014-4.49-4.49s2.014-4.49 4.49-4.49h4.588v8.98zM8.148 8.981c-1.665 0-3.019 1.355-3.019 3.019s1.355 3.019 3.019 3.019h3.117V8.981zM8.172 24c-2.489 0-4.515-2.014-4.515-4.49s2.014-4.49 4.49-4.49h4.588v4.441c0 2.503-2.047 4.539-4.563 4.539m-.024-7.51a3.023 3.023 0 0 0-3.019 3.019c0 1.665 1.365 3.019 3.044 3.019c1.705 0 3.093-1.376 3.093-3.068v-2.97zm7.704 0h-.098c-2.476 0-4.49-2.014-4.49-4.49s2.014-4.49 4.49-4.49h.098c2.476 0 4.49 2.014 4.49 4.49s-2.014 4.49-4.49 4.49m-.097-7.509c-1.665 0-3.019 1.355-3.019 3.019s1.355 3.019 3.019 3.019h.098c1.665 0 3.019-1.355 3.019-3.019s-1.355-3.019-3.019-3.019z"/></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M12.48 10.92v3.28h7.84c-.24 1.84-.853 3.187-1.787 4.133c-1.147 1.147-2.933 2.4-6.053 2.4c-4.827 0-8.6-3.893-8.6-8.72s3.773-8.72 8.6-8.72c2.6 0 4.507 1.027 5.907 2.347l2.307-2.307C18.747 1.44 16.133 0 12.48 0C5.867 0 .307 5.387.307 12s5.56 12 12.173 12c3.573 0 6.267-1.173 8.373-3.36c2.16-2.16 2.84-5.213 2.84-7.667c0-.76-.053-1.467-.173-2.053z"/></svg>

After

Width:  |  Height:  |  Size: 466 B

View File

@ -1,13 +1,11 @@
import React from "react"; import React from "react";
import logoFigma from "../assets/logo-figma.svg";
type ImageProps = { import logoAdobe from "../assets/logo-adobe.svg";
src: string; import logoGoogle from "../assets/logo-google.svg";
alt?: string;
};
type Section = { type Section = {
title: string; title: string;
image: ImageProps; image?: { src: string; alt: string };
}; };
type Props = { type Props = {
@ -17,12 +15,9 @@ type Props = {
export type Banner15Props = React.ComponentPropsWithoutRef<"section"> & Partial<Props>; export type Banner15Props = React.ComponentPropsWithoutRef<"section"> & Partial<Props>;
export const Banner15 = (props: Banner15Props) => { export const Banner15 = (props: Banner15Props) => {
const { sections } = { const { sections } = { ...Banner15Defaults, ...props };
...Banner15Defaults,
...props,
};
return ( return (
<section id="relume" className="flex w-screen max-w-full justify-end overflow-hidden bg-cloud-white"> <section className="flex w-screen max-w-full justify-end overflow-hidden bg-cloud-white">
<div className="flex justify-end"> <div className="flex justify-end">
{Array(2) {Array(2)
.fill(0) .fill(0)
@ -31,18 +26,20 @@ export const Banner15 = (props: Banner15Props) => {
key={index} key={index}
className="grid animate-marquee-right auto-cols-max grid-flow-col grid-cols-[max-content] items-center justify-around py-4" className="grid animate-marquee-right auto-cols-max grid-flow-col grid-cols-[max-content] items-center justify-around py-4"
> >
{sections.map((section, index) => ( {sections.map((section, i) => (
<React.Fragment key={index}> <React.Fragment key={i}>
<div className="flex items-center justify-center whitespace-nowrap px-8 text-center lg:text-left"> <div className="flex items-center justify-center whitespace-nowrap px-8 text-center lg:text-left">
<h1 className="text-6xl font-bold md:text-9xl lg:text-10xl">{section.title}</h1> <h1 className="text-6xl font-bold md:text-9xl lg:text-10xl">{section.title}</h1>
</div> </div>
<div className="relative aspect-[3/2] w-full overflow-hidden object-cover"> {section.image && (
<img <div className="relative flex items-center justify-center px-4">
src={section.image.src} <img
alt={section.image.alt} src={section.image.src}
className="aspect-[3/2] size-full h-16 max-h-24 object-cover md:h-auto" alt={section.image.alt}
/> className="h-16 max-h-24 w-auto object-contain md:h-20"
</div> />
</div>
)}
</React.Fragment> </React.Fragment>
))} ))}
</div> </div>
@ -55,32 +52,21 @@ export const Banner15 = (props: Banner15Props) => {
export const Banner15Defaults: Props = { export const Banner15Defaults: Props = {
sections: [ sections: [
{ {
title: "UX Schweiz", title: "Figma",
image: { image: { src: logoFigma, alt: "Figma" },
src: "https://relume-assets.s3.us-east-1.amazonaws.com/placeholder-image.svg",
alt: "UX Schweiz",
},
}, },
{ {
title: "Adobe", title: "Adobe",
image: { image: { src: logoAdobe, alt: "Adobe" },
src: "https://relume-assets.s3.us-east-1.amazonaws.com/placeholder-image.svg",
alt: "Adobe",
},
}, },
{ {
title: "Google", title: "Google",
image: { image: { src: logoGoogle, alt: "Google" },
src: "https://relume-assets.s3.us-east-1.amazonaws.com/placeholder-image.svg",
alt: "Google",
},
}, },
{ {
title: "Figma", title: "UX Schweiz",
image: {
src: "https://relume-assets.s3.us-east-1.amazonaws.com/placeholder-image.svg",
alt: "Figma",
},
}, },
], ],
}; };
export default Banner15;

View File

@ -29,8 +29,11 @@ const levelStyles: Record<EventLevel, string> = {
}; };
const EventRow = ({ event }: { event: ProgramEvent }) => ( const EventRow = ({ event }: { event: ProgramEvent }) => (
<div className="grid grid-cols-1 items-center gap-4 border-t border-gray-200 py-6 last-of-type:border-b md:grid-cols-[5.5rem_5.5rem_1fr_max-content_max-content] md:gap-6 md:py-7"> <div className="grid grid-cols-1 items-center gap-4 border-t border-gray-200 py-6 last-of-type:border-b md:grid-cols-[8rem_5.5rem_1fr_max-content] md:gap-6 md:py-7">
<span className="text-sm font-semibold text-tech-navy">{event.time}</span> <div className="flex flex-col gap-0.5">
<span className="text-xs font-bold uppercase tracking-wide text-electric-violet">{event.day}</span>
<span className="text-sm font-semibold text-tech-navy">{event.time}</span>
</div>
<div className="w-14 h-14 shrink-0 md:w-[5.5rem] md:h-[5.5rem]"> <div className="w-14 h-14 shrink-0 md:w-[5.5rem] md:h-[5.5rem]">
<img src={event.image.src} alt={event.image.alt} className="size-full object-cover" /> <img src={event.image.src} alt={event.image.alt} className="size-full object-cover" />
@ -52,13 +55,6 @@ const EventRow = ({ event }: { event: ProgramEvent }) => (
> >
Über {event.speaker.split(" ")[0]} {event.speaker.split(" ").slice(-1)[0]} Über {event.speaker.split(" ")[0]} {event.speaker.split(" ").slice(-1)[0]}
</a> </a>
<a
href="/tickets"
className="shrink-0 rounded-full bg-electric-violet px-4 py-1.5 text-sm font-semibold text-cloud-white hover:opacity-90 transition-opacity"
>
Tickets kaufen
</a>
</div> </div>
); );
@ -75,6 +71,14 @@ export const Event33 = ({ events }: Props) => {
))} ))}
</div> </div>
)} )}
<div className="mt-10 flex justify-center">
<a
href="/tickets"
className="rounded-2xl bg-electric-violet px-8 py-3 text-base font-normal text-cloud-white hover:opacity-90 transition-opacity"
>
Jetzt Ticket sichern
</a>
</div>
</div> </div>
</section> </section>
); );

View File

@ -1,31 +1,39 @@
import { useState } from "react";
import { BiFilter, BiSearch } from "react-icons/bi"; import { BiFilter, BiSearch } from "react-icons/bi";
import { IoCloseOutline } from "react-icons/io5"; import { IoCloseOutline } from "react-icons/io5";
import { RxChevronDown } from "react-icons/rx"; import { RxChevronDown } from "react-icons/rx";
import { useState } from "react";
export type FilterTab = { value: string; label: string }; export type FilterTab = { value: string; label: string };
type ActiveTag = { label: string; onRemove: () => void }; type ActiveTag = { label: string; onRemove: () => void };
type Props = { type Props = {
// Speaker page (single-select)
tabs?: FilterTab[]; tabs?: FilterTab[];
activeTab?: string; activeTab?: string;
onTabChange?: (tab: string) => void; onTabChange?: (tab: string) => void;
// Programm page (multi-select)
activeTabs?: string[];
onTabsChange?: (tabs: string[]) => void;
// Shared
searchQuery?: string; searchQuery?: string;
onSearchChange?: (q: string) => void; onSearchChange?: (q: string) => void;
searchLabel?: string; searchLabel?: string;
resetLabel?: string; resetLabel?: string;
activeTags?: ActiveTag[]; activeTags?: ActiveTag[];
onReset?: () => void; onReset?: () => void;
// Programm-only
useFilterToggle?: boolean; useFilterToggle?: boolean;
showDayFilter?: boolean; showDayFilter?: boolean;
showSortButton?: boolean; showSortButton?: boolean;
sortOrder?: "asc" | "desc";
onSortChange?: (order: "asc" | "desc") => void;
// Legacy day props (kept for backwards compat)
activeDay?: string; activeDay?: string;
onDayChange?: (day: string) => void; onDayChange?: (day: string) => void;
}; };
const PROGRAMM_TABS: FilterTab[] = [ const PROGRAMM_TABS: FilterTab[] = [
{ value: "all", label: "Alle Anzeigen" }, { value: "Donnerstag", label: "Donnerstag" },
{ value: "Freitag", label: "Freitag" },
{ value: "Talk", label: "Talk" }, { value: "Talk", label: "Talk" },
{ value: "Workshop", label: "Workshop" }, { value: "Workshop", label: "Workshop" },
{ value: "Interaction", label: "Interaction" }, { value: "Interaction", label: "Interaction" },
@ -35,16 +43,12 @@ const PROGRAMM_TABS: FilterTab[] = [
{ value: "Junior", label: "Junior-Level" }, { value: "Junior", label: "Junior-Level" },
]; ];
const DAYS = [
{ value: "all", label: "Alle Tage" },
{ value: "Donnerstag", label: "Donnerstag" },
{ value: "Freitag", label: "Freitag" },
];
export const Filters5 = ({ export const Filters5 = ({
tabs = PROGRAMM_TABS, tabs,
activeTab = "all", activeTab = "all",
onTabChange = () => {}, onTabChange = () => {},
activeTabs = [],
onTabsChange = () => {},
searchQuery = "", searchQuery = "",
onSearchChange = () => {}, onSearchChange = () => {},
searchLabel = "Stichwortsuche", searchLabel = "Stichwortsuche",
@ -52,40 +56,51 @@ export const Filters5 = ({
activeTags = [], activeTags = [],
onReset = () => {}, onReset = () => {},
useFilterToggle = true, useFilterToggle = true,
showDayFilter = true,
showSortButton = true, showSortButton = true,
activeDay = "all", sortOrder = "asc",
onDayChange = () => {}, onSortChange = () => {},
}: Props) => { }: Props) => {
const [sortOpen, setSortOpen] = useState(false); const [sortOpen, setSortOpen] = useState(false);
const [dayOpen, setDayOpen] = useState(false);
const selectedDayLabel = DAYS.find((d) => d.value === activeDay)?.label ?? "Alle Tage"; const toggleTab = (value: string) => {
if (activeTabs.includes(value)) {
onTabsChange(activeTabs.filter((t) => t !== value));
} else {
onTabsChange([...activeTabs, value]);
}
};
const TabPills = () => ( // Speaker page: multi-select tabs + search
<div className="flex items-center gap-1 overflow-x-auto no-scrollbar">
{tabs.map((tab) => (
<button
key={tab.value}
onClick={() => onTabChange(tab.value)}
className={`shrink-0 rounded-2xl px-4 py-2 text-base font-semibold transition-colors ${
activeTab === tab.value
? "bg-electric-violet text-cloud-white"
: "text-tech-navy hover:text-electric-violet"
}`}
>
{tab.label}
</button>
))}
</div>
);
// Speaker page: just tabs + search, no day filter or sort
if (!useFilterToggle) { if (!useFilterToggle) {
const speakerTabs = tabs ?? [];
return ( return (
<section className="bg-cloud-white font-barlow border-b border-gray-200 px-[5%] py-8"> <section className="bg-cloud-white font-barlow border-b border-gray-200 px-[5%] py-8">
<div className="container flex flex-col gap-6"> <div className="container flex flex-col gap-6">
<TabPills /> <div className="flex items-center gap-1 overflow-x-auto no-scrollbar">
<button
onClick={() => onTabsChange([])}
className={`shrink-0 rounded-2xl px-4 py-2 text-base font-semibold transition-colors ${
activeTabs.length === 0
? "bg-electric-violet text-cloud-white"
: "text-tech-navy hover:text-electric-violet"
}`}
>
Alle ansehen
</button>
{speakerTabs.map((tab) => (
<button
key={tab.value}
onClick={() => toggleTab(tab.value)}
className={`shrink-0 rounded-2xl px-4 py-2 text-base font-semibold transition-colors ${
activeTabs.includes(tab.value)
? "bg-electric-violet text-cloud-white"
: "text-tech-navy hover:text-electric-violet"
}`}
>
{tab.label}
</button>
))}
</div>
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<span className="text-base font-medium text-tech-navy">{searchLabel}</span> <span className="text-base font-medium text-tech-navy">{searchLabel}</span>
@ -93,17 +108,17 @@ export const Filters5 = ({
{resetLabel} {resetLabel}
</button> </button>
</div> </div>
<div className="flex h-12 items-center gap-3 rounded-2xl border border-tech-navy px-3"> <div className="flex h-12 items-center gap-3 rounded-2xl border border-electric-violet px-3">
<BiSearch className="size-6 shrink-0 text-tech-navy" /> <BiSearch className="size-6 shrink-0 text-electric-violet" />
<input <input
className="flex-1 bg-transparent text-base text-tech-navy outline-none placeholder:text-tech-navy/40" className="flex-1 bg-transparent text-base text-electric-violet outline-none placeholder:text-electric-violet/50"
placeholder="Suchen..." placeholder="Suchen..."
value={searchQuery} value={searchQuery}
onChange={(e) => onSearchChange(e.target.value)} onChange={(e) => onSearchChange(e.target.value)}
/> />
{searchQuery && ( {searchQuery && (
<button onClick={() => onSearchChange("")}> <button onClick={() => onSearchChange("")}>
<IoCloseOutline className="size-5 text-tech-navy" /> <IoCloseOutline className="size-5 text-electric-violet" />
</button> </button>
)} )}
</div> </div>
@ -128,20 +143,43 @@ export const Filters5 = ({
); );
} }
// Programm page: full filter bar, always visible // Programm page: multi-select filter bar
return ( return (
<section className="bg-cloud-white font-barlow px-[5%] py-8"> <section className="bg-cloud-white font-barlow px-[5%] py-8">
<div className="container flex flex-col gap-8"> <div className="container flex flex-col gap-8">
{/* Row 1: Filter label + category pills + sort */} {/* Row 1: Filter label + multi-select pills + sort */}
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
<div className="flex shrink-0 items-center gap-3 rounded-2xl bg-electric-violet px-6 py-3 text-base font-semibold text-cloud-white"> <div className="flex shrink-0 items-center gap-2 text-base font-semibold text-tech-navy">
<BiFilter className="size-6" /> <BiFilter className="size-6" />
Filter Filter
</div> </div>
<div className="flex flex-1 items-center overflow-x-auto no-scrollbar"> <div className="flex flex-1 items-center gap-1 overflow-x-auto no-scrollbar">
<TabPills /> {/* "Alle" pill — active only when nothing selected */}
<button
onClick={() => onTabsChange([])}
className={`shrink-0 rounded-2xl px-4 py-2 text-base font-semibold transition-colors ${
activeTabs.length === 0
? "bg-electric-violet text-cloud-white"
: "text-tech-navy hover:text-electric-violet"
}`}
>
Alle
</button>
{PROGRAMM_TABS.map((tab) => (
<button
key={tab.value}
onClick={() => toggleTab(tab.value)}
className={`shrink-0 rounded-2xl px-4 py-2 text-base font-semibold transition-colors ${
activeTabs.includes(tab.value)
? "bg-electric-violet text-cloud-white"
: "text-tech-navy hover:text-electric-violet"
}`}
>
{tab.label}
</button>
))}
</div> </div>
{showSortButton && ( {showSortButton && (
@ -157,85 +195,48 @@ export const Filters5 = ({
</button> </button>
{sortOpen && ( {sortOpen && (
<div className="absolute right-0 top-9 z-50 w-48 overflow-hidden rounded-xl border border-gray-200 bg-white shadow-md"> <div className="absolute right-0 top-9 z-50 w-48 overflow-hidden rounded-xl border border-gray-200 bg-white shadow-md">
{["Zeit aufsteigend", "Zeit absteigend"].map((opt) => ( {(["asc", "desc"] as const).map((order) => {
<button const label = order === "asc" ? "Zeit aufsteigend" : "Zeit absteigend";
key={opt} return (
className="block w-full px-4 py-2.5 text-left text-sm text-tech-navy hover:bg-electric-violet/10" <button
onClick={() => setSortOpen(false)} key={order}
> className={`block w-full px-4 py-2.5 text-left text-sm transition-colors hover:bg-electric-violet/10 ${
{opt} sortOrder === order ? "font-semibold text-electric-violet" : "text-tech-navy"
</button> }`}
))} onClick={() => { onSortChange(order); setSortOpen(false); }}
>
{label}
</button>
);
})}
</div> </div>
)} )}
</div> </div>
)} )}
</div> </div>
{/* Row 2: Search + Day filter — always visible */} {/* Row 2: Full-width search */}
<div className="grid grid-cols-1 gap-8 md:grid-cols-2"> <div className="flex flex-col gap-2">
<div className="flex items-center justify-between">
{/* Search */} <span className="text-base font-medium text-tech-navy">{searchLabel}</span>
<div className="flex flex-col gap-2"> <button onClick={onReset} className="text-base font-medium text-tech-navy">
<div className="flex items-center justify-between"> {resetLabel}
<span className="text-base font-medium text-tech-navy">{searchLabel}</span> </button>
<button onClick={onReset} className="text-base font-medium text-tech-navy"> </div>
{resetLabel} <div className="flex h-12 items-center gap-3 rounded-2xl border border-electric-violet px-3">
</button> <BiSearch className="size-6 shrink-0 text-electric-violet" />
</div> <input
<div className="flex h-12 items-center gap-3 rounded-2xl border border-electric-violet px-3"> className="flex-1 bg-transparent text-base text-electric-violet outline-none placeholder:text-electric-violet/50"
<BiSearch className="size-6 shrink-0 text-electric-violet" /> placeholder="Titel oder Speaker..."
<input value={searchQuery}
className="flex-1 bg-transparent text-base text-electric-violet outline-none placeholder:text-electric-violet/50" onChange={(e) => onSearchChange(e.target.value)}
placeholder="Titel oder Speaker..." />
value={searchQuery} {searchQuery && (
onChange={(e) => onSearchChange(e.target.value)} <button onClick={() => onSearchChange("")}>
/> <IoCloseOutline className="size-5 text-electric-violet" />
{searchQuery && ( </button>
<button onClick={() => onSearchChange("")}> )}
<IoCloseOutline className="size-5 text-electric-violet" />
</button>
)}
</div>
</div> </div>
{/* Day dropdown */}
{showDayFilter && (
<div className="flex flex-col gap-2">
<span className="text-base font-medium text-tech-navy">Filtern nach Eventtage</span>
<div className="relative">
<button
onClick={() => setDayOpen((v) => !v)}
className="flex h-12 w-full items-center justify-between gap-4 rounded-2xl border border-electric-violet px-4 text-base text-electric-violet"
>
<span>{selectedDayLabel}</span>
<RxChevronDown
className={`size-6 shrink-0 transition-transform ${dayOpen ? "rotate-180" : ""}`}
/>
</button>
{dayOpen && (
<div className="absolute left-0 top-[calc(100%+4px)] z-50 w-full overflow-hidden rounded-2xl border border-electric-violet bg-white shadow-md">
{DAYS.map((d) => (
<button
key={d.value}
className={`block w-full px-4 py-2.5 text-left text-base transition-colors hover:bg-electric-violet/10 ${
activeDay === d.value
? "font-semibold text-electric-violet"
: "text-tech-navy"
}`}
onClick={() => {
onDayChange(d.value);
setDayOpen(false);
}}
>
{d.label}
</button>
))}
</div>
)}
</div>
</div>
)}
</div> </div>
{/* Active tags */} {/* Active tags */}

View File

@ -1,43 +1,114 @@
import headerImage from "../assets/Header.png"; import headerImage from "../assets/Header.png";
import { Button } from "@relume_io/relume-ui"; import logoAdobe from "../assets/logo-adobe.svg";
import type { ButtonProps } from "@relume_io/relume-ui"; import logoFigma from "../assets/logo-figma.svg";
import logoGoogle from "../assets/logo-google.svg";
type ImageProps = { export type Header1Props = React.ComponentPropsWithoutRef<"section">;
src: string;
alt?: string;
};
type Props = {
heading: string;
description: string;
buttons: ButtonProps[];
image: ImageProps;
};
export type Header1Props = React.ComponentPropsWithoutRef<"section"> & Partial<Props>;
export const Header1 = (props: Header1Props) => { export const Header1 = (props: Header1Props) => {
const { heading, description, buttons, image } = {
...Header1Defaults,
...props,
};
return ( return (
<section id="relume" className="px-[5%] py-16 md:py-24 lg:py-28 bg-tech-navy font-barlow"> <section
<div className="container"> className="relative flex min-h-[92vh] flex-col overflow-hidden bg-tech-navy font-barlow"
<div className="grid grid-cols-1 gap-x-20 gap-y-12 md:gap-y-16 lg:grid-cols-2 lg:items-center"> {...props}
<div> >
<h1 className="mb-5 text-6xl font-bold md:mb-6 md:text-9xl lg:text-10xl text-acid-lime whitespace-pre-wrap">{heading}</h1> {/* Mobile-only faint background image */}
<p className="text-[48px] font-bold leading-[1.2] text-cloud-white whitespace-pre-wrap">{description}</p> <div className="absolute inset-0 lg:hidden pointer-events-none">
<div className="mt-6 flex flex-wrap gap-4 md:mt-8"> <img src={headerImage} alt="" className="h-full w-full object-cover opacity-10" />
{buttons.map((button, index) => ( <div className="absolute inset-0 bg-gradient-to-b from-tech-navy/60 to-tech-navy" />
<Button key={index} {...button}> </div>
{button.title}
</Button> {/* Desktop right-side image panel */}
<div className="absolute right-0 top-0 hidden h-full w-[52%] lg:block pointer-events-none">
<img
src={headerImage}
alt=""
className="h-full w-full object-cover opacity-90"
/>
{/* Blend edge left */}
<div className="absolute inset-y-0 left-0 w-56 bg-gradient-to-r from-tech-navy to-transparent" />
{/* Top vignette */}
<div className="absolute inset-x-0 top-0 h-40 bg-gradient-to-b from-tech-navy to-transparent" />
{/* Bottom vignette */}
<div className="absolute inset-x-0 bottom-0 h-28 bg-gradient-to-t from-tech-navy to-transparent" />
</div>
{/* Glow blobs */}
<div className="absolute -left-24 top-1/4 h-[480px] w-[480px] rounded-full bg-electric-violet/20 blur-3xl pointer-events-none" />
<div className="absolute left-1/3 top-0 h-[280px] w-[280px] rounded-full bg-electric-violet/10 blur-3xl pointer-events-none" />
<div className="absolute bottom-0 left-0 h-[220px] w-[460px] rounded-full bg-acid-lime/5 blur-3xl pointer-events-none" />
{/* Main content */}
<div className="relative z-10 flex flex-1 items-center px-[5%] py-24 md:py-32">
<div className="container">
<div className="mx-auto max-w-xl text-center lg:mx-0 lg:text-left">
{/* Date badge */}
<div className="mb-8 flex items-center justify-center gap-3 lg:justify-start">
<div className="h-px w-10 bg-acid-lime/60" />
<span className="text-xs font-semibold uppercase tracking-[0.2em] text-acid-lime">
25. 26. Juni 2026 &nbsp;·&nbsp; Zürich
</span>
<div className="h-px w-10 bg-acid-lime/60" />
</div>
{/* Heading */}
<h1 className="mb-3 text-[80px] font-bold leading-none tracking-tight text-acid-lime md:text-[110px] lg:text-[130px]">
UXplore
</h1>
<p className="mb-6 text-3xl font-bold text-cloud-white md:text-4xl">
Konferenz 2026
</p>
{/* Tagline */}
<p className="mb-10 max-w-md text-base leading-relaxed text-cloud-white/60 md:text-lg lg:mx-0 mx-auto">
Zwei Tage, zwölf Sessions, ein Ziel: UX, das wirklich wirkt. Die führende Schweizer Konferenz für Designer, Produktteams und alle, die nutzerzentriertes Denken in die Praxis bringen.
</p>
{/* CTAs */}
<div className="flex flex-wrap items-center justify-center gap-4 lg:justify-start">
<a
href="/tickets"
className="rounded-2xl bg-acid-lime px-8 py-3 text-base font-semibold text-tech-navy transition-opacity hover:opacity-90"
>
Jetzt Ticket sichern
</a>
<a
href="/programm"
className="rounded-2xl border border-cloud-white/25 px-8 py-3 text-base font-semibold text-cloud-white transition-colors hover:border-cloud-white/50 hover:bg-white/5"
>
Programm ansehen
</a>
</div>
{/* Stats */}
<div className="mt-14 flex flex-wrap items-center justify-center gap-8 lg:justify-start md:gap-12">
{[
{ value: "2", label: "Tage" },
{ value: "12+", label: "Sessions" },
{ value: "6", label: "Workshops" },
{ value: "500+", label: "Teilnehmer" },
].map((stat, i) => (
<div key={i} className="text-center lg:text-left">
<div className="text-2xl font-bold text-acid-lime md:text-3xl">{stat.value}</div>
<div className="mt-0.5 text-[11px] uppercase tracking-widest text-cloud-white/40">{stat.label}</div>
</div>
))} ))}
</div> </div>
</div> </div>
<div> </div>
<img src={image.src} className="w-full object-cover" alt={image.alt} /> </div>
{/* Speaker companies bar */}
<div className="relative z-10 border-t border-white/10 px-[5%] py-5">
<div className="container">
<div className="flex flex-wrap items-center justify-center gap-6 md:gap-10 lg:justify-start">
<span className="text-[10px] font-semibold uppercase tracking-[0.18em] text-cloud-white/30">
Speaker von
</span>
<img src={logoAdobe} alt="Adobe" className="h-4 brightness-0 invert opacity-35" />
<img src={logoFigma} alt="Figma" className="h-4 brightness-0 invert opacity-35" />
<img src={logoGoogle} alt="Google" className="h-4 brightness-0 invert opacity-35" />
</div> </div>
</div> </div>
</div> </div>
@ -45,13 +116,6 @@ export const Header1 = (props: Header1Props) => {
); );
}; };
export const Header1Defaults: Props = { export const Header1Defaults = {};
heading: "UXplore Konferenz 2026",
description: export default Header1;
"25-26 Juni Zürich",
buttons: [],
image: {
src: headerImage,
alt: "UXplore Konferenz 2026",
},
};

View File

@ -1,17 +1,22 @@
import headerBg from "../assets/program-header.png"; import headerBg from "../assets/program-header.png";
import { Button } from "@relume_io/relume-ui"; type LinkButton = {
import type { ButtonProps } from "@relume_io/relume-ui"; title: string;
href?: string;
variant?: string;
className?: string;
};
type Props = { type Props = {
heading: string; heading: string;
description: string; description: string;
buttons: ButtonProps[]; buttons: LinkButton[];
showTicketButton?: boolean;
}; };
export type Header23Props = React.ComponentPropsWithoutRef<"section"> & Partial<Props>; export type Header23Props = React.ComponentPropsWithoutRef<"section"> & Partial<Props>;
export const Header23 = (props: Header23Props) => { export const Header23 = (props: Header23Props) => {
const { heading, description, buttons, children } = { const { heading, description, buttons, showTicketButton = true, children } = {
...Header23Defaults, ...Header23Defaults,
...props, ...props,
}; };
@ -29,18 +34,38 @@ export const Header23 = (props: Header23Props) => {
{/* Content */} {/* Content */}
<div className="container relative z-10"> <div className="container relative z-10">
<div className="mx-auto w-full max-w-lg text-center"> <div className="mx-auto w-full max-w-lg text-center">
<div className="mb-5 flex items-center justify-center gap-3 md:mb-6">
<span className="rounded-full bg-white/15 px-4 py-1.5 text-sm font-semibold text-cloud-white">
2-Tage-Konferenz
</span>
<span className="rounded-full border border-acid-lime bg-acid-lime/10 px-4 py-1.5 text-sm font-semibold text-acid-lime">
Workshops inklusive
</span>
</div>
<h1 className="mb-5 text-6xl font-bold text-cloud-white md:mb-6 md:text-9xl lg:text-10xl"> <h1 className="mb-5 text-6xl font-bold text-cloud-white md:mb-6 md:text-9xl lg:text-10xl">
{heading} {heading}
</h1> </h1>
<p className="md:text-md text-cloud-white text-[18px] leading-[1.5]"> <p className="md:text-md text-cloud-white text-[18px] leading-[1.5]">
{description} {description}
</p> </p>
{showTicketButton && (
<a
href="/tickets"
className="mt-8 inline-block rounded-2xl bg-acid-lime px-8 py-3 text-base font-normal text-tech-navy hover:opacity-90 transition-opacity"
>
Jetzt Ticket sichern
</a>
)}
{buttons.length > 0 && ( {buttons.length > 0 && (
<div className="mt-6 flex items-center justify-center gap-x-4 md:mt-8"> <div className="mt-4 flex items-center justify-center gap-x-4">
{buttons.map((button, index) => ( {buttons.map((button, index) => (
<Button key={index} {...button}> <a
key={index}
href={button.href ?? "#"}
className={`inline-flex items-center justify-center rounded-2xl border px-6 py-3 text-sm font-semibold transition-colors ${button.className ?? ""}`}
>
{button.title} {button.title}
</Button> </a>
))} ))}
</div> </div>
)} )}
@ -53,7 +78,7 @@ export const Header23 = (props: Header23Props) => {
export const Header23Defaults: Props = { export const Header23Defaults: Props = {
heading: "UXplore 2026 Programm", heading: "UXplore 2026 Programm",
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.", description: "Erlebe zwei Tage voller Insights von den klügsten Köpfen der Branche. Jedes Ticket beinhaltet den Zugang zu allen Sessions und Workshops nach dem Kauf kannst du dich für deine Wunsch-Workshops anmelden.",
buttons: [], buttons: [],
}; };

View File

@ -95,10 +95,10 @@ export const Navbar3 = (props: Navbar3Props) => {
{buttons.map(({ url, ...button }, index) => {buttons.map(({ url, ...button }, index) =>
url ? ( url ? (
<Link key={index} to={url}> <Link key={index} to={url}>
<Button {...button} className="w-full">{button.title}</Button> <Button {...button} className="w-full rounded-2xl bg-electric-violet border-electric-violet text-cloud-white font-barlow font-semibold">{button.title}</Button>
</Link> </Link>
) : ( ) : (
<Button key={index} {...button} className="w-full">{button.title}</Button> <Button key={index} {...button} className="w-full rounded-2xl bg-electric-violet border-electric-violet text-cloud-white font-barlow font-semibold">{button.title}</Button>
) )
)} )}
</div> </div>

View File

@ -2,8 +2,10 @@ import React, { useState } from "react";
type TicketPlan = { type TicketPlan = {
day: string; day: string;
price: number;
date: string; date: string;
earlyBirdPrice: number;
regularPrice: number;
earlyBirdDeadline: string;
}; };
type Props = { type Props = {
@ -13,15 +15,28 @@ type Props = {
export type Pricing39TicketProps = React.ComponentPropsWithoutRef<"section"> & Partial<Props>; export type Pricing39TicketProps = React.ComponentPropsWithoutRef<"section"> & Partial<Props>;
const TicketCardLime = ({ plan }: { plan: TicketPlan }) => { const TicketCard = ({ plan }: { plan: TicketPlan }) => {
const [qty, setQty] = useState(0); const [qty, setQty] = useState(0);
const total = qty * plan.price; const total = qty * plan.earlyBirdPrice;
return ( return (
<div className="bg-acid-lime text-tech-navy p-6 md:p-8 flex flex-col gap-5 font-barlow border border-electric-violet"> <div className="bg-electric-violet/10 text-tech-navy p-6 md:p-8 flex flex-col gap-5 font-barlow border border-electric-violet/30">
<p className="text-sm font-semibold">{plan.day}</p> <p className="text-sm font-semibold uppercase tracking-wide">{plan.day}</p>
<h3 className="text-6xl font-bold md:text-8xl">CHF {plan.price}</h3>
<p className="text-sm">{plan.date}</p> {/* Earlybird price block */}
<div className="flex flex-col gap-2">
<div className="flex items-end gap-3">
<h3 className="text-6xl font-bold md:text-8xl leading-none">CHF {plan.earlyBirdPrice}</h3>
<span className="mb-1 text-sm text-tech-navy/50 line-through">CHF {plan.regularPrice}</span>
</div>
<span className="inline-flex w-fit items-center gap-1.5 rounded-full bg-acid-lime px-3 py-1 text-xs font-semibold text-tech-navy">
<span className="h-1.5 w-1.5 rounded-full bg-tech-navy" />
Earlybird bis {plan.earlyBirdDeadline}
</span>
</div>
<p className="text-sm text-tech-navy/50">{plan.date}</p>
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
<button <button
@ -39,10 +54,11 @@ const TicketCardLime = ({ plan }: { plan: TicketPlan }) => {
</button> </button>
</div> </div>
<div className="text-right text-sm font-semibold"> <div className="text-right text-sm font-semibold">
<div>CHF {plan.price},-</div> <div className="text-tech-navy/50">CHF {plan.earlyBirdPrice},-</div>
<div>{total},-</div> <div>Total CHF {total},-</div>
</div> </div>
</div> </div>
<button className="w-full rounded-2xl bg-electric-violet text-cloud-white font-semibold py-3 text-sm"> <button className="w-full rounded-2xl bg-electric-violet text-cloud-white font-semibold py-3 text-sm">
Ticket sichern Ticket sichern
</button> </button>
@ -69,7 +85,7 @@ export const Pricing39Ticket = (props: Pricing39TicketProps) => {
}`} }`}
> >
{plans.map((plan, index) => ( {plans.map((plan, index) => (
<TicketCardLime key={index} plan={plan} /> <TicketCard key={index} plan={plan} />
))} ))}
</div> </div>
</div> </div>
@ -80,8 +96,8 @@ export const Pricing39Ticket = (props: Pricing39TicketProps) => {
export const Pricing39TicketDefaults: Props = { export const Pricing39TicketDefaults: Props = {
heading: "1-Tagespässe", heading: "1-Tagespässe",
plans: [ plans: [
{ day: "Donnerstag", price: 131, date: "25. Juni 2026" }, { day: "Donnerstag", date: "25. Juni 2026", earlyBirdPrice: 99, regularPrice: 131, earlyBirdDeadline: "31. Mai 2026" },
{ day: "Freitag", price: 131, date: "26. Juni 2026" }, { day: "Freitag", date: "26. Juni 2026", earlyBirdPrice: 99, regularPrice: 131, earlyBirdDeadline: "31. Mai 2026" },
], ],
}; };

View File

@ -81,17 +81,7 @@ const MemberCard = ({ member }: { member: TeamMember }) => (
<p className="text-lg text-tech-navy">{member.company}</p> <p className="text-lg text-tech-navy">{member.company}</p>
)} )}
</div> </div>
<p className="mb-4 text-base leading-relaxed text-tech-navy">{member.description}</p> <p className="text-base leading-relaxed text-tech-navy">{member.description}</p>
<div className="mb-4 flex flex-wrap gap-2">
{member.talks.map((talk, i) => (
<span
key={i}
className={`rounded-lg px-2 py-1 text-sm font-semibold whitespace-nowrap ${talkBadgeClass(talk.type)}`}
>
{talk.title}
</span>
))}
</div>
</div> </div>
); );

View File

@ -6,45 +6,61 @@ import { Event33 } from "../components/Event33";
import { Banner15 } from "../components/Banner15"; import { Banner15 } from "../components/Banner15";
import { Footer3 } from "../components/Footer"; import { Footer3 } from "../components/Footer";
import { ALL_EVENTS } from "../data/events"; import { ALL_EVENTS } from "../data/events";
import type { EventDay } from "../components/Event33";
const TYPE_TABS = ["Talk", "Workshop", "Interaction", "AI and Future"]; const DAY_VALUES = ["Donnerstag", "Freitag"];
const LEVEL_TABS: Record<string, string> = { const TYPE_VALUES = ["Talk", "Workshop", "Interaction", "AI and Future"];
"Senior": "Senior", const LEVEL_VALUES = ["Senior", "Mid-Level", "Junior"];
"Mid-Level": "Mid-Level",
"Junior": "Junior", const DAY_ORDER: Record<EventDay, number> = { Donnerstag: 0, Freitag: 1 };
const parseStartMinutes = (time: string) => {
const match = time.match(/(\d{2}):(\d{2})/);
return match ? parseInt(match[1]) * 60 + parseInt(match[2]) : 0;
}; };
const Programm = () => { const Programm = () => {
const [activeTab, setActiveTab] = useState("all"); const [activeTabs, setActiveTabs] = useState<string[]>([]);
const [searchQuery, setSearchQuery] = useState(""); const [searchQuery, setSearchQuery] = useState("");
const [activeDay, setActiveDay] = useState("all"); const [sortOrder, setSortOrder] = useState<"asc" | "desc">("asc");
const filteredEvents = useMemo(() => { const filteredEvents = useMemo(() => {
return ALL_EVENTS.filter((e) => { const filtered = ALL_EVENTS.filter((e) => {
if (activeTab !== "all") { if (activeTabs.length > 0) {
const matchesType = TYPE_TABS.includes(activeTab) && e.type === activeTab; const activeDays = activeTabs.filter((t) => DAY_VALUES.includes(t));
const matchesLevel = LEVEL_TABS[activeTab] && e.level === LEVEL_TABS[activeTab]; const activeTypes = activeTabs.filter((t) => TYPE_VALUES.includes(t));
if (!matchesType && !matchesLevel) return false; const activeLevels = activeTabs.filter((t) => LEVEL_VALUES.includes(t));
if (activeDays.length > 0 && !activeDays.includes(e.day)) return false;
if (activeTypes.length > 0 && !activeTypes.includes(e.type)) return false;
if (activeLevels.length > 0 && !activeLevels.includes(e.level)) return false;
} }
if (searchQuery) { if (searchQuery) {
const q = searchQuery.toLowerCase(); const q = searchQuery.toLowerCase();
if (!e.title.toLowerCase().includes(q) && !e.speaker.toLowerCase().includes(q)) return false; if (!e.title.toLowerCase().includes(q) && !e.speaker.toLowerCase().includes(q)) return false;
} }
if (activeDay !== "all" && e.day !== activeDay) return false;
return true; return true;
}); });
}, [activeTab, searchQuery, activeDay]);
return [...filtered].sort((a, b) => {
const dayDiff = DAY_ORDER[a.day] - DAY_ORDER[b.day];
if (dayDiff !== 0) return sortOrder === "asc" ? dayDiff : -dayDiff;
const timeDiff = parseStartMinutes(a.time) - parseStartMinutes(b.time);
return sortOrder === "asc" ? timeDiff : -timeDiff;
});
}, [activeTabs, searchQuery, sortOrder]);
const activeTags = [ const activeTags = [
searchQuery ? { label: searchQuery, onRemove: () => setSearchQuery("") } : null, searchQuery ? { label: searchQuery, onRemove: () => setSearchQuery("") } : null,
activeDay !== "all" ? { label: activeDay, onRemove: () => setActiveDay("all") } : null, ...activeTabs.map((tab) => ({
activeTab !== "all" ? { label: activeTab, onRemove: () => setActiveTab("all") } : null, label: tab,
onRemove: () => setActiveTabs((prev) => prev.filter((t) => t !== tab)),
})),
].filter(Boolean) as { label: string; onRemove: () => void }[]; ].filter(Boolean) as { label: string; onRemove: () => void }[];
const handleReset = () => { const handleReset = () => {
setActiveTab("all"); setActiveTabs([]);
setSearchQuery(""); setSearchQuery("");
setActiveDay("all"); setSortOrder("asc");
}; };
return ( return (
@ -52,14 +68,14 @@ const Programm = () => {
<Navbar3 /> <Navbar3 />
<Header23 /> <Header23 />
<Filters5 <Filters5
activeTab={activeTab} activeTabs={activeTabs}
onTabChange={setActiveTab} onTabsChange={setActiveTabs}
searchQuery={searchQuery} searchQuery={searchQuery}
onSearchChange={setSearchQuery} onSearchChange={setSearchQuery}
activeDay={activeDay}
onDayChange={setActiveDay}
activeTags={activeTags} activeTags={activeTags}
onReset={handleReset} onReset={handleReset}
sortOrder={sortOrder}
onSortChange={setSortOrder}
/> />
<Event33 events={filteredEvents} /> <Event33 events={filteredEvents} />
<Banner15 /> <Banner15 />

View File

@ -7,7 +7,6 @@ import { Team4Defaults } from "../components/Team4";
import { Footer3 } from "../components/Footer"; import { Footer3 } from "../components/Footer";
const SPEAKER_TABS = [ const SPEAKER_TABS = [
{ value: "all", label: "Alle ansehen" },
{ value: "Strategie", label: "Strategie" }, { value: "Strategie", label: "Strategie" },
{ value: "Research", label: "Research" }, { value: "Research", label: "Research" },
{ value: "Design / UX", label: "Design / UX" }, { value: "Design / UX", label: "Design / UX" },
@ -18,12 +17,12 @@ const SPEAKER_TABS = [
const ALL_SPEAKERS = Team4Defaults.teamMembers; const ALL_SPEAKERS = Team4Defaults.teamMembers;
const Speaker = () => { const Speaker = () => {
const [activeTab, setActiveTab] = useState("all"); const [activeTabs, setActiveTabs] = useState<string[]>([]);
const [searchQuery, setSearchQuery] = useState(""); const [searchQuery, setSearchQuery] = useState("");
const filteredSpeakers = useMemo(() => { const filteredSpeakers = useMemo(() => {
return ALL_SPEAKERS.filter((s) => { return ALL_SPEAKERS.filter((s) => {
if (activeTab !== "all" && s.category !== activeTab) return false; if (activeTabs.length > 0 && !activeTabs.includes(s.category)) return false;
if (searchQuery) { if (searchQuery) {
const q = searchQuery.toLowerCase(); const q = searchQuery.toLowerCase();
if ( if (
@ -35,11 +34,14 @@ const Speaker = () => {
} }
return true; return true;
}); });
}, [activeTab, searchQuery]); }, [activeTabs, searchQuery]);
const activeTags = [ const activeTags = [
searchQuery ? { label: searchQuery, onRemove: () => setSearchQuery("") } : null, searchQuery ? { label: searchQuery, onRemove: () => setSearchQuery("") } : null,
activeTab !== "all" ? { label: activeTab, onRemove: () => setActiveTab("all") } : null, ...activeTabs.map((tab) => ({
label: tab,
onRemove: () => setActiveTabs((prev) => prev.filter((t) => t !== tab)),
})),
].filter(Boolean) as { label: string; onRemove: () => void }[]; ].filter(Boolean) as { label: string; onRemove: () => void }[];
return ( return (
@ -49,20 +51,19 @@ const Speaker = () => {
heading="Unsere Speaker" 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." 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={[ buttons={[
{ title: "Zu den Speakern", variant: "primary", className: "bg-acid-lime text-neutral-dark border-acid-lime hover:bg-acid-lime/90 hover:border-acid-lime/90 rounded-2xl" }, { title: "Zum Programm", href: "/programm", className: "bg-transparent border-acid-lime text-acid-lime hover:bg-acid-lime/10" },
{ title: "Zum Programm", variant: "secondary", className: "bg-transparent border-acid-lime text-acid-lime hover:bg-acid-lime/10 hover:border-acid-lime hover:text-acid-lime rounded-2xl" },
]} ]}
/> />
<Filters5 <Filters5
tabs={SPEAKER_TABS} tabs={SPEAKER_TABS}
activeTab={activeTab} activeTabs={activeTabs}
onTabChange={setActiveTab} onTabsChange={setActiveTabs}
searchQuery={searchQuery} searchQuery={searchQuery}
onSearchChange={setSearchQuery} onSearchChange={setSearchQuery}
searchLabel="Stichwortsuche" searchLabel="Stichwortsuche"
resetLabel="Löschen" resetLabel="Löschen"
activeTags={activeTags} activeTags={activeTags}
onReset={() => { setSearchQuery(""); setActiveTab("all"); }} onReset={() => { setSearchQuery(""); setActiveTabs([]); }}
useFilterToggle={false} useFilterToggle={false}
showDayFilter={false} showDayFilter={false}
showSortButton={false} showSortButton={false}

View File

@ -2,6 +2,7 @@ import { useParams, Navigate } from "react-router-dom";
import { Navbar3 } from "../components/Navbar"; import { Navbar3 } from "../components/Navbar";
import { Team4, Team4Defaults } from "../components/Team4"; import { Team4, Team4Defaults } from "../components/Team4";
import { Footer3 } from "../components/Footer"; import { Footer3 } from "../components/Footer";
import { ALL_EVENTS } from "../data/events";
import webflowLogo from "../assets/webflow.png"; import webflowLogo from "../assets/webflow.png";
import relumeLogo from "../assets/relume.png"; import relumeLogo from "../assets/relume.png";
@ -38,6 +39,7 @@ const SpeakerDetail = () => {
if (!speaker) return <Navigate to="/speaker" replace />; if (!speaker) return <Navigate to="/speaker" replace />;
const speakerEvents = ALL_EVENTS.filter((e) => e.speaker === speaker.name);
const otherSpeakers = ALL_SPEAKERS.filter((s) => s.slug !== slug); const otherSpeakers = ALL_SPEAKERS.filter((s) => s.slug !== slug);
return ( return (
@ -95,6 +97,41 @@ const SpeakerDetail = () => {
</div> </div>
</section> </section>
{/* 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 */} {/* Other speakers */}
<div className="bg-cloud-white"> <div className="bg-cloud-white">
<div className="px-[5%] pt-16 font-barlow"> <div className="px-[5%] pt-16 font-barlow">

View File

@ -3,7 +3,6 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { Navbar3 } from "../components/Navbar"; import { Navbar3 } from "../components/Navbar";
import { Header23 } from "../components/Header23"; import { Header23 } from "../components/Header23";
import { Pricing1Ticket } from "../components/Pricing1Ticket";
import { Pricing39Ticket } from "../components/Pricing39Ticket"; import { Pricing39Ticket } from "../components/Pricing39Ticket";
import { Footer3 } from "../components/Footer"; import { Footer3 } from "../components/Footer";
import { Input } from "@relume_io/relume-ui"; import { Input } from "@relume_io/relume-ui";
@ -82,6 +81,7 @@ const Tickets = () => {
heading="Ticketshop" 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." 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={[]} buttons={[]}
showTicketButton={false}
> >
<div className="mt-5 font-semibold text-cloud-white md:mt-6 md:text-md"> <div className="mt-5 font-semibold text-cloud-white md:mt-6 md:text-md">
DO FR, 25. 26. Juni 2026 DO FR, 25. 26. Juni 2026
@ -120,17 +120,16 @@ const Tickets = () => {
</p> </p>
</div> </div>
</Header23> </Header23>
<Pricing1Ticket /> <Pricing39Ticket
<Pricing39Ticket
heading="1-Tagespässe" heading="1-Tagespässe"
plans={[ plans={[
{ day: "Donnerstag", price: 131, date: "25. Juni 2026" }, { day: "Donnerstag", date: "25. Juni 2026", earlyBirdPrice: 99, regularPrice: 131, earlyBirdDeadline: "31. Mai 2026" },
{ day: "Freitag", price: 131, date: "26. Juni 2026" }, { day: "Freitag", date: "26. Juni 2026", earlyBirdPrice: 99, regularPrice: 131, earlyBirdDeadline: "31. Mai 2026" },
]} ]}
/> />
<Pricing39Ticket <Pricing39Ticket
heading="2-Tagespässe" heading="2-Tagespass"
plans={[{ day: "Donnerstag Freitag", price: 236, date: "25. 26. Juni 2026" }]} plans={[{ day: "Donnerstag Freitag", date: "25. 26. Juni 2026", earlyBirdPrice: 179, regularPrice: 236, earlyBirdDeadline: "31. Mai 2026" }]}
/> />
<Footer3 /> <Footer3 />
</div> </div>