Amey Changes + Mine

This commit is contained in:
Ravi Gulhane 2026-07-17 13:03:53 +05:30
parent d9270632dc
commit d9843a5fb1
8 changed files with 1254 additions and 366 deletions

1
help.txt Normal file
View File

@ -0,0 +1 @@
URL: http://localhost:5175/event/6a43e05fdc940a0d6e057f7b

View File

@ -1,9 +1,295 @@
import { apiGet } from './client'; import { apiGet } from './client';
import type { PublicMicrositePayload, RegistrationInfo } from '../types'; import type { PublicMicrositePayload, RegistrationInfo } from '../types';
export const micrositeApi = { export const micrositeApi = {
get(eventId: string): Promise<PublicMicrositePayload> { get(eventId: string): Promise<PublicMicrositePayload> {
//Real API call
return apiGet<PublicMicrositePayload>(`/public/events/${eventId}/microsite`); return apiGet<PublicMicrositePayload>(`/public/events/${eventId}/microsite`);
//Mock: Testing data
// return Promise.resolve({
// "event": {
// "_id": "6a43e05fdc940a0d6e057f7b",
// "name": "NOVEL Lung Health Summit - Goa - 7th & 8th August 2026",
// "subtitle": "",
// "description": "The summit aims to be one of the most sought-after programs in India which will cover topics on contemporary and advanced diagnostics, and management strategies in Respiratory medicine including asthma, COPD, tuberculosis, ILD, airway diseases, and practical respiratory care through concise, high-impact interactive sessions.\nThe summit is curated for pulmonologists and consultant physicians with a special interest in respiratory medicine across India. The program will feature expert-led case discussions, interactive panel sessions, and practical clinical insights focused on evidence-based respiratory care.",
// "eventType": "summit",
// "startDate": "2026-08-07T12:00:00.000Z",
// "endDate": "2026-08-08T12:00:00.000Z",
// "date": "Aug 7, 2026 – Aug 8, 2026",
// "location": "DoubleTree by Hilton Panjim, Goa",
// "locationType": "physical",
// "logoUrl": "https://did94hhhcgq1d.cloudfront.net/eventbuzz/6a43dfeddc940a0d6e057eef/6a43e05fdc940a0d6e057f7b/event/logo/event-logo-1782833860045.png",
// "imageUrl": "https://did94hhhcgq1d.cloudfront.net/eventbuzz/6a43dfeddc940a0d6e057eef/6a43e05fdc940a0d6e057f7b/event/logo/event-logo-1782833860045.png"
// },
// "website": {
// "enabled": false,
// "title": "",
// "description": "",
// "logo": "",
// "banners": [],
// "favicon": "",
// "primaryColor": "",
// "accentColor": "",
// "fontPair": "Playfair + Source Sans",
// "layoutTemplate": "conference",
// "seoDescription": "",
// "theme": {
// "theme": "simple",
// "color": "yellow",
// "font": "Raleway"
// }
// },
// "title": "NOVEL Lung Health Summit - Goa - 7th & 8th August 2026",
// "sections": [
// {
// "id": "header-0",
// "type": "header",
// "order": 1,
// "config": {
// "logoText": "NOVEL Lung Health Summit - Goa - 7th & 8th August 2026",
// "logoUrl": "",
// "menuItems": [
// {
// "label": "About",
// "url": "#about"
// },
// {
// "label": "Speakers",
// "url": "#speakers"
// },
// {
// "label": "Schedule",
// "url": "#schedule"
// },
// {
// "label": "Register",
// "url": "https://eventbuzz.live/event/6a43e05fdc940a0d6e057f7b/register"
// }
// ]
// }
// },
// {
// "id": "banner-1",
// "type": "banner",
// "order": 0,
// "config": {
// "headline": "NOVEL Lung Health Summit - Goa - 7th & 8th August 2026",
// "subtext": "The summit aims to be one of the most sought-after programs in India which will cover topics on contemporary and advanced diagnostics, and management strategies in Respiratory medicine including asthma, COPD, tuberculosis, ILD, airway diseases, and practical respiratory care through concise, high-impact interactive sessions.\nThe summit is curated for pulmonologists and consultant physicians with a special interest in respiratory medicine across India. The program will feature expert-led case discussions, interactive panel sessions, and practical clinical insights focused on evidence-based respiratory care.",
// "ctaText": "Register",
// "ctaUrl": "https://eventbuzz.live/event/6a43e05fdc940a0d6e057f7b/register",
// "secondaryCtaText": "View schedule",
// "secondaryCtaUrl": "#schedule",
// "backgroundClass": "bg-primary",
// "backgroundImage": "https://did94hhhcgq1d.cloudfront.net/eventbuzz/6a43dfeddc940a0d6e057eef/6a43e05fdc940a0d6e057f7b/event/banner/event-banner-1782833860270.png",
// "heroMetaLine": "Aug 7, 2026 – Aug 8, 2026 · DoubleTree by Hilton Panjim, Goa"
// }
// },
// {
// "id": "countdown-2",
// "type": "countdown",
// "order": 2,
// "config": {
// "headingLine": "Event starts · Aug 7, 2026, 12:00 PM"
// }
// },
// {
// "id": "about-3",
// "type": "about",
// "order": 3,
// "config": {
// "title": "About the event",
// "lead": "The summit aims to be one of the most sought-after programs in India which will cover topics on contemporary and advanced diagnostics, and management strategies in Respiratory medicine including asthma, COPD, tuberculosis, ILD, airway diseases, and practical respiratory care through concise, high-impact interactive sessions.\nThe summit is curated for pulmonologists and consultant physicians with a special interest in respiratory medicine across India. The program will feature expert-led case discussions, interactive panel sessions, and practical clinical insights focused on evidence-based respiratory care.",
// "bulletsTitle": "Why attend",
// "bullets": [
// "Hands-on sessions and practical takeaways",
// "Networking with peers and sponsors",
// "Product tips from the EventBuzz team"
// ],
// "ctaLabel": "Reserve your seat",
// "ctaUrl": "https://eventbuzz.live/event/6a43e05fdc940a0d6e057f7b/register"
// }
// },
// {
// "id": "agenda-4",
// "type": "agenda",
// "order": 4,
// "config": {
// "title": "Schedule"
// }
// },
// {
// "id": "speakers-5",
// "type": "speakers",
// "order": 5,
// "config": {
// "title": "Speakers",
// "limit": 12
// }
// },
// {
// "id": "register_links-6",
// "type": "register_links",
// "order": 6,
// "config": {
// "title": "Register",
// "primaryLabel": "Register now",
// "primaryUrl": "https://eventbuzz.live/event/6a43e05fdc940a0d6e057f7b/register"
// }
// },
// {
// "id": "sponsors-7",
// "type": "sponsors",
// "order": 7,
// "config": {
// "title": "Sponsors & partners",
// "sponsors": [],
// "partnerCtaLabel": "Become a partner",
// "partnerCtaUrl": "#"
// }
// },
// {
// "id": "footer-8",
// "type": "footer",
// "order": 8,
// "config": {
// "copyrightText": "© 2026 NOVEL Lung Health Summit - Goa - 7th & 8th August 2026. All rights reserved."
// }
// }
// ],
// "agenda": [
// {
// "title": "Precision Management with SITT in OADs",
// "date": "Aug 7, 2026",
// "time": "1:30 PM – 2:15 PM",
// "track": "DoubleTree by Hilton Panjim, Goa",
// "description": "The summit aims to be one of the most sought-after programs in India which will cover topics on contemporary and advanced diagnostics, and management strategies in Respiratory medicine including asthma, COPD, tuberculosis, ILD, airway diseases, and practical respiratory care through concise, high-impact interactive sessions.\nThe summit is curated for pulmonologists and consultant physicians with a special interest in respiratory medicine across India. The program will feature expert-led case dis",
// "speakerName": "",
// "speakerPhotoUrl": ""
// },
// {
// "title": "Indian Innovations in Aerosol Therapy",
// "date": "Aug 7, 2026",
// "time": "2:15 PM – 2:55 PM",
// "track": "Main programme",
// "description": "",
// "speakerName": "",
// "speakerPhotoUrl": ""
// },
// {
// "title": "JAMA: Is it really the End of Blue Inhalers? (15-min)",
// "date": "Aug 7, 2026",
// "time": "2:55 PM – 3:10 PM",
// "track": "Main programme",
// "description": "",
// "speakerName": "",
// "speakerPhotoUrl": ""
// },
// {
// "title": "Concepts in Pulmonary Diagnostics: Practice-changing directions",
// "date": "Aug 7, 2026",
// "time": "3:10 PM – 3:55 PM",
// "track": "Main programme",
// "description": "",
// "speakerName": "",
// "speakerPhotoUrl": ""
// },
// {
// "title": "Transforming Respiratory Medicine Landscape: Era of Biologics",
// "date": "Aug 7, 2026",
// "time": "4:15 PM – 4:45 PM",
// "track": "Main programme",
// "description": "",
// "speakerName": "",
// "speakerPhotoUrl": ""
// },
// {
// "title": "Novel Screening and assessment tools: Closing the Care Gap for India",
// "date": "Aug 7, 2026",
// "time": "4:45 PM – 5:30 PM",
// "track": "Main programme",
// "description": "",
// "speakerName": "",
// "speakerPhotoUrl": ""
// },
// {
// "title": "Complex cases from Respiratory clinics",
// "date": "Aug 7, 2026",
// "time": "5:30 PM – 6:05 PM",
// "track": "Main programme",
// "description": "04 cases of 5 mins each + 3 mins discussion on each case",
// "speakerName": "",
// "speakerPhotoUrl": ""
// },
// {
// "title": "Tuberculosis Associated Lung Disease (TBaLD): India's Hidden Respiratory Epidemic?",
// "date": "Aug 8, 2026",
// "time": "9:00 AM – 9:45 AM",
// "track": "Main programme",
// "description": "15-min Talk\t| 30-min Panel Discussion",
// "speakerName": "",
// "speakerPhotoUrl": ""
// },
// {
// "title": "Untangling complex scenarios of ILD & OAD",
// "date": "Aug 8, 2026",
// "time": "9:45 AM – 10:15 AM",
// "track": "Main programme",
// "description": "15-min Talk | 15-min Panel Discussion",
// "speakerName": "",
// "speakerPhotoUrl": ""
// },
// {
// "title": "Prevention of Future Exacerbations (Discharge Bundle)",
// "date": "Aug 8, 2026",
// "time": "10:15 AM – 11:00 AM",
// "track": "Main programme",
// "description": "5-min Intro |\t40-min Panel Discussion",
// "speakerName": "",
// "speakerPhotoUrl": ""
// },
// {
// "title": "Pulmonary Rehabilitation in Chronic Lung diseases: Experiences from India",
// "date": "Aug 8, 2026",
// "time": "11:15 AM – 11:40 AM",
// "track": "Main programme",
// "description": "20-min Presentation | 5-min Q&A",
// "speakerName": "",
// "speakerPhotoUrl": ""
// },
// {
// "title": "JAMA: Clinical applications of Lung Oscillometry (15-min)",
// "date": "Aug 8, 2026",
// "time": "11:40 AM – 11:55 AM",
// "track": "Main programme",
// "description": "",
// "speakerName": "",
// "speakerPhotoUrl": ""
// },
// {
// "title": "Sponsored Session (20-mins)",
// "date": "Aug 8, 2026",
// "time": "11:55 AM – 12:15 PM",
// "track": "Main programme",
// "description": "",
// "speakerName": "",
// "speakerPhotoUrl": ""
// },
// {
// "title": "Respiratory practice of tomorrow: What lies on the horizon?",
// "date": "Aug 8, 2026",
// "time": "12:15 PM – 12:30 PM",
// "track": "Main programme",
// "description": "15-min Panel Discussion",
// "speakerName": "",
// "speakerPhotoUrl": ""
// }
// ],
// "speakers": [],
// "registrationFormId": "6a43e4e9dc940a0d6e058466",
// "registrationUrl": "https://eventbuzz.live/event/6a43e05fdc940a0d6e057f7b/form/6a43e4e9dc940a0d6e058466"
// });
}, },
getRegistration(eventId: string): Promise<RegistrationInfo> { getRegistration(eventId: string): Promise<RegistrationInfo> {

View File

@ -1,5 +1,5 @@
import { useEffect, type CSSProperties, type ReactNode } from 'react'; import { useEffect, type CSSProperties, type ReactNode } from 'react';
import { conferenceCssVars, loadConferenceFont } from '../../theme/conferenceTheme'; import { conferenceCssVars, loadConferenceFont, resolveThemeClass } from '../../theme/conferenceTheme';
import type { PublicMicrositePayload } from '../../types'; import type { PublicMicrositePayload } from '../../types';
type Props = { type Props = {
@ -9,7 +9,7 @@ type Props = {
export function MicrositeLayout({ data, children }: Props) { export function MicrositeLayout({ data, children }: Props) {
useEffect(() => { useEffect(() => {
loadConferenceFont(data.website.fontPair); loadConferenceFont(data.website);
const title = data.website.title || data.event.name || 'Event'; const title = data.website.title || data.event.name || 'Event';
document.title = title; document.title = title;
if (data.website.favicon) { if (data.website.favicon) {
@ -24,13 +24,17 @@ export function MicrositeLayout({ data, children }: Props) {
}, [data]); }, [data]);
const style = conferenceCssVars(data.website) as CSSProperties; const style = conferenceCssVars(data.website) as CSSProperties;
const themeClass = resolveThemeClass(data.website.theme);
return ( return (
<div className="eb-microsite-root" style={style}> <div className={['eb-microsite-root', themeClass].filter(Boolean).join(' ')} style={style}>
{children} <main className="eb-microsite-main">
<footer className="eb-static-footer"> {children}
Powered by <strong>EventBuzz</strong> <div className="eb-microsite-footer-spacer"></div>
</footer> <footer className="eb-static-footer">
Powered by <strong>EventBuzz</strong>
</footer>
</main>
</div> </div>
); );
} }

View File

@ -47,10 +47,11 @@ function HeaderSection({ section, ctx }: { section: MicrositeSection; ctx: Ctx }
{/* <a className="navbar-brand" href="#hero" style={{ textDecoration: 'none', color: '#fff' }}> {/* <a className="navbar-brand" href="#hero" style={{ textDecoration: 'none', color: '#fff' }}>
{logoUrl ? <img src={logoUrl} alt={logoText} className="eb-header-logo" /> : <span>{logoText}</span>} {logoUrl ? <img src={logoUrl} alt={logoText} className="eb-header-logo" /> : <span>{logoText}</span>}
</a> */} </a> */}
<a className="navbar-brand" href="#hero" style={{ display: 'flex', alignItems: 'center', gap: '12px', textDecoration: 'none', color: '#fff',}}> {logoUrl && ( <img src={logoUrl} alt={logoText} className="eb-header-logo" /> )} <a className="navbar-brand" href="#hero" >
<span>{logoText}</span> <div className="eb-header-logo-container">{logoUrl && ( <img src={logoUrl} alt={logoText} className="eb-header-logo" /> )}</div>
<span>{logoText}</span>
</a> </a>
<ul className="navbar-nav" style={{ display: 'flex',alignItems: 'center', gap: '0.75rem', listStyle: 'none', margin: 0, padding: 0, flexWrap: 'wrap' }}> {/* <ul className="navbar-nav" style={{ display: 'flex',alignItems: 'center', gap: '0.75rem', listStyle: 'none', margin: 0, padding: 0, flexWrap: 'wrap' }}>
{menuItems.map((item, i) => { {menuItems.map((item, i) => {
const label = str(item.label); const label = str(item.label);
const url = str(item.url, '#'); const url = str(item.url, '#');
@ -68,7 +69,7 @@ function HeaderSection({ section, ctx }: { section: MicrositeSection; ctx: Ctx }
</li> </li>
); );
})} })}
</ul> </ul> */}
</div> </div>
</nav> </nav>
</header> </header>
@ -91,9 +92,9 @@ function BannerSection({ section, ctx }: { section: MicrositeSection; ctx: Ctx }
{bgImage ? <div className="eb-hero-bg" style={{ backgroundImage: `url(${bgImage})` }} /> : null} {bgImage ? <div className="eb-hero-bg" style={{ backgroundImage: `url(${bgImage})` }} /> : null}
<div className="eb-hero-mask" /> <div className="eb-hero-mask" />
<div className="container eb-hero-inner"> <div className="container eb-hero-inner">
<h1 className="eb-hero-heading mb-4">{headline}</h1> {/* <h1 className="eb-hero-heading mb-4">{headline}</h1> */}
{heroMetaLine ? <div className="eb-hero-meta mb-3">{heroMetaLine}</div> : null} {/* {heroMetaLine ? <div className="eb-hero-meta mb-3">{heroMetaLine}</div> : null} */}
{subtext ? <p className="eb-hero-intro mb-4">{subtext}</p> : null} {/* {subtext ? <p className="eb-hero-intro mb-4">{subtext}</p> : null} */}
{(ctaText || secText) && ( {(ctaText || secText) && (
<div className="eb-hero-cta"> <div className="eb-hero-cta">
{ctaText && ctaUrl ? ( {ctaText && ctaUrl ? (
@ -101,11 +102,11 @@ function BannerSection({ section, ctx }: { section: MicrositeSection; ctx: Ctx }
{ctaText} {ctaText}
</a> </a>
) : null} ) : null}
{secText ? ( {/* {secText ? (
<a className="btn btn-outline-light btn-lg" href={secUrl}> <a className="btn btn-outline-light btn-lg" href={secUrl}>
{secText} {secText}
</a> </a>
) : null} ) : null} */}
</div> </div>
)} )}
</div> </div>
@ -146,14 +147,15 @@ function CountdownSection({ section, ctx }: { section: MicrositeSection; ctx: Ct
}, [start, ctx.data.event.name]); }, [start, ctx.data.event.name]);
return ( return (
<section className="eb-static-stats py-5 text-center" aria-label="Countdown to event"> // <section className="eb-static-stats py-5 text-center" aria-label="Countdown to event">
<div className="container"> // <div className="container">
{headingLine ? <p className="eb-countdown-heading small text-uppercase mb-3">{headingLine}</p> : null} // {headingLine ? <p className="eb-countdown-heading small text-uppercase mb-3">{headingLine}</p> : null}
<p className="eb-countdown-phrase mb-0" role="status" aria-live="polite"> // <p className="eb-countdown-phrase mb-0" role="status" aria-live="polite">
{phrase} // {phrase}
</p> // </p>
</div> // </div>
</section> // </section>
<></>
); );
} }
@ -166,24 +168,26 @@ function AboutSection({ section, ctx }: { section: MicrositeSection; ctx: Ctx })
const ctaUrl = resolveRegistrationCtaUrl(ctaText, c.ctaUrl, ctx.registrationUrl); const ctaUrl = resolveRegistrationCtaUrl(ctaText, c.ctaUrl, ctx.registrationUrl);
return ( return (
<section id="about" className="py-5 eb-section-light"> <section id="about" className="about-section section eb-section-ligh bg-transparent">
<div className="container" style={{ maxWidth: 820 }}> {/* <div className="container" style={{ maxWidth: 820 }}> */}
<h2 className="eb-section-heading text-center mb-4">{title}</h2> <div className="container">
{body ? <p style={{ lineHeight: 1.7, color: '#475569' }}>{body}</p> : null} {/* <h2 className="eb-section-heading text-center mb-4">{title}</h2> */}
{/* {body ? <p style={{ lineHeight: 1.7, color: '#475569' }}>{body}</p> : null} */}
{body ? <p style={{ lineHeight: 1.7 }}>{body}</p> : null}
{bullets.length > 0 ? ( {bullets.length > 0 ? (
<ul style={{ lineHeight: 1.8, color: '#475569' }}> <ul className="theme-list" style={{ lineHeight: 1.8 }}>
{bullets.map((b, i) => ( {bullets.map((b, i) => (
<li key={i}>{b}</li> <li key={i}>{b}</li>
))} ))}
</ul> </ul>
) : null} ) : null}
{ctaText && ctaUrl ? ( {/* {ctaText && ctaUrl ? (
<div className="text-center mt-4"> <div className="text-center mt-4">
<a className="btn btn-primary btn-lg" href={ctaUrl}> <a className="btn btn-primary btn-lg" href={ctaUrl}>
{ctaText} {ctaText}
</a> </a>
</div> </div>
) : null} ) : null} */}
</div> </div>
</section> </section>
); );
@ -465,18 +469,18 @@ function AgendaSection({
const filteredList = dates.length ? list.filter((item) => item.date === selectedDate) : list; const filteredList = dates.length ? list.filter((item) => item.date === selectedDate) : list;
return ( return (
<section id="schedule" className="eb-schedule-section section py-5"> <section id="schedule" className="eb-schedule-section section py-5 bg-transparent">
<div className="container"> <div className="container">
<h2 className="eb-section-heading text-center mb-4">{title}</h2> <h2 className="eb-section-heading text-start mb-4">{title}</h2>
{/* Day Selector */} {/* Day Selector */}
<div <div
className="d-flex justify-content-center flex-wrap gap-2 mb-4" className="d-flex justify-content-center flex-wrap gap-2 mb-3"
> >
{dates.map((date) => ( {dates.map((date) => (
<button <button
key={date} key={date}
className={`btn ${ className={`btn btn-theme-tab ${
selectedDate === date selectedDate === date
? "btn-primary" ? "btn-primary"
: "btn-outline-primary" : "btn-outline-primary"
@ -495,14 +499,15 @@ function AgendaSection({
{/* Agenda */} {/* Agenda */}
<div <div
style={{ style={{
maxWidth: "900px" , // maxWidth: "900px" ,
margin: "0 auto", // margin: "0 auto",
background: "#fff", // background: "#fff",
borderRadius: "12px", // borderRadius: "12px",
overflow: "hidden", // overflow: "hidden",
boxShadow: "0 1px 3px rgba(0,0,0,.05)", // boxShadow: "0 1px 3px rgba(0,0,0,.05)",
border: "0.5px solid #e2e8f0", // border: "0.5px solid #e2e8f0",
}} }}
className='theme-accordion-wrapper'
> >
{filteredList.map((item, index) => { {filteredList.map((item, index) => {
const isExpanded = expandedIndex === index; const isExpanded = expandedIndex === index;
@ -511,51 +516,54 @@ function AgendaSection({
return ( return (
<div <div
key={index} key={index}
style={{ // style={{
borderBottom: // borderBottom:
index < filteredList.length - 1 // index < filteredList.length - 1
? "0.5px solid #e2e8f0" // ? "0.5px solid #e2e8f0"
: "none", // : "none",
}} // }}
className={`theme-accordion-item ${index < filteredList.length - 1 ? 'border-bottom' : ''}`}
> >
{/* Your existing accordion code */} {/* Your existing accordion code */}
<button <button
onClick={() => setExpandedIndex(isExpanded ? null : index)} onClick={() => setExpandedIndex(isExpanded ? null : index)}
style={{ style={{
width: '100%', // width: '100%',
background: isExpanded ? '#f8fafc' : '#fff', // background: isExpanded ? '#f8fafc' : '#fff',
border: 'none', // border: 'none',
padding: '1rem 1.5rem', // padding: '1rem 1.5rem',
textAlign: 'left', // textAlign: 'left',
cursor: 'pointer', // cursor: 'pointer',
transition: 'background-color 0.3s, padding 0.3s', // transition: 'background-color 0.3s, padding 0.3s',
display: 'flex', // display: 'flex',
alignItems: 'center', // alignItems: 'center',
gap: '1rem', // gap: '1rem',
justifyContent: 'space-between', // justifyContent: 'space-between',
}} }}
onMouseEnter={(e) => { // onMouseEnter={(e) => {
e.currentTarget.style.backgroundColor = '#f1f5f9'; // e.currentTarget.style.backgroundColor = '#f1f5f9';
}} // }}
onMouseLeave={(e) => { // onMouseLeave={(e) => {
e.currentTarget.style.backgroundColor = isExpanded ? '#f8fafc' : '#fff'; // e.currentTarget.style.backgroundColor = isExpanded ? '#f8fafc' : '#fff';
}} // }}
className='theme-accordion-button'
> >
{/* Left: Time and Title */} {/* Left: Time and Title */}
<div style={{ display: 'flex', alignItems: 'center', gap: '1rem', flex: 1, minWidth: 0, overflow: 'hidden'}}> <div style={{ display: 'flex', alignItems: 'center', gap: '1rem', flex: 1, minWidth: 0, overflow: 'hidden'}}>
{/* Time Badge */} {/* Time Badge */}
<span <span
style={{ style={{
background: '#0f3072', // background: '#0f3072',
color: '#fff', // color: '#fff',
padding: '0.5rem 0.75rem', // padding: '0.5rem 0.75rem',
borderRadius: '4px', // borderRadius: '4px',
fontSize: '0.8rem', // fontSize: '0.8rem',
fontWeight: 600, // fontWeight: 600,
whiteSpace: 'nowrap', // whiteSpace: 'nowrap',
flexShrink: 0, // flexShrink: 0,
}} }}
className='theme-accordion-time'
> >
{item.time || 'TBA'} {item.time || 'TBA'}
</span> </span>
@ -564,30 +572,32 @@ function AgendaSection({
<div style={{ minWidth: 0, flex: 1 }}> <div style={{ minWidth: 0, flex: 1 }}>
<div <div
style={{ style={{
fontSize: '1rem', // fontSize: '1rem',
fontWeight: 700, // fontWeight: 700,
color: '#0f172a', // color: '#0f172a',
margin: '0 0 0.25rem 0', // margin: '0 0 0.25rem 0',
overflow: 'hidden', // overflow: 'hidden',
textOverflow: 'ellipsis', // textOverflow: 'ellipsis',
whiteSpace: 'nowrap', // whiteSpace: 'nowrap',
}} }}
className='theme-accordion-title'
> >
{item.title || 'Untitled Session'} {item.title || 'Untitled Session'}
</div> </div>
{item.track && ( {item.track && (
<span <span
style={{ style={{
background: 'rgba(0, 194, 209, 0.1)', // background: 'rgba(0, 194, 209, 0.1)',
color: '#0891b2', // color: '#0891b2',
padding: '0.25rem 0.6rem', // padding: '0.25rem 0.6rem',
borderRadius: '3px', // borderRadius: '3px',
fontSize: '0.65rem', // fontSize: '0.65rem',
fontWeight: 600, // fontWeight: 600,
textTransform: 'uppercase', // textTransform: 'uppercase',
letterSpacing: '0.3px', // letterSpacing: '0.3px',
display: 'inline-block', // display: 'inline-block',
}} }}
className='theme-accordion-track'
> >
{item.track} {item.track}
</span> </span>
@ -598,19 +608,20 @@ function AgendaSection({
{/* Right: Expand/Collapse Icon */} {/* Right: Expand/Collapse Icon */}
<div <div
style={{ style={{
display: 'flex', // display: 'flex',
alignItems: 'center', // alignItems: 'center',
justifyContent: 'center', // justifyContent: 'center',
width: '26px', // width: '26px',
height: '26px', // height: '26px',
background: isExpanded ? '#e0f2fe' : '#f1f5f9', // background: isExpanded ? '#e0f2fe' : '#f1f5f9',
borderRadius: '6px', // borderRadius: '6px',
transition: 'all 0.3s', // transition: 'all 0.3s',
color: isExpanded ? '#0891b2' : '#64748b', // color: isExpanded ? '#0891b2' : '#64748b',
fontWeight: 600, // fontWeight: 600,
fontSize: '1.2rem', // fontSize: '1.2rem',
flexShrink: 0, // flexShrink: 0,
}} }}
className='theme-accordion-icon'
> >
{/* {isExpanded ? '−' : '+'} */} {/* {isExpanded ? '−' : '+'} */}
<i className={`bi ${isExpanded ? 'bi-chevron-up' : 'bi-chevron-down'}`}></i> <i className={`bi ${isExpanded ? 'bi-chevron-up' : 'bi-chevron-down'}`}></i>
@ -620,17 +631,18 @@ function AgendaSection({
{/* Accordion Body/Content */} {/* Accordion Body/Content */}
{isExpanded && ( {isExpanded && (
<div <div
style={{ // style={{
background: '#f8fafc', // background: '#f8fafc',
padding: '1.5rem', // padding: '1.5rem',
borderTop: '1px solid #e2e8f0', // borderTop: '1px solid #e2e8f0',
animation: 'slideDown 0.3s ease-out', // animation: 'slideDown 0.3s ease-out',
}} // }}
className='theme-accordion-content'
> >
{/* Description/ Session Details*/} {/* Description/ Session Details*/}
{item.description && ( {item.description && (
<div style={{ marginBottom: '1.5rem' }}> <div style={{ marginBottom: '1.5rem' }}>
<h4 {/* <h4
style={{ style={{
fontSize: '0.85rem', fontSize: '0.85rem',
fontWeight: 600, fontWeight: 600,
@ -642,8 +654,8 @@ function AgendaSection({
}} }}
> >
Session Details Session Details
</h4> </h4> */}
<h3 {/* <h3
style={{ style={{
margin: '0.75rem 0 0', margin: '0.75rem 0 0',
fontSize: '0.95rem', fontSize: '0.95rem',
@ -652,8 +664,8 @@ function AgendaSection({
}} }}
> >
{item.title} {item.title}
</h3> </h3> */}
{item.track && ( {/* {item.track && (
<span <span
style={{ style={{
background: 'rgba(0, 194, 209, 0.1)', background: 'rgba(0, 194, 209, 0.1)',
@ -669,15 +681,15 @@ function AgendaSection({
> >
{item.track} {item.track}
</span> </span>
)} )} */}
<p <p
style={{ // style={{
margin: '0.75rem 0 0', // margin: '0.75rem 0 0',
fontSize: '0.95rem', // fontSize: '0.95rem',
color: '#475569', // color: '#475569',
lineHeight: 1.6, // lineHeight: 1.6,
}} // }}
> >
{item.description} {item.description}
</p> </p>
@ -732,13 +744,13 @@ function AgendaSection({
{/* Action Buttons */} {/* Action Buttons */}
<div className="text-center mt-4"> <div className="text-center mt-4">
{ctx.registrationUrl ? ( {ctx.registrationUrl ? (
<a className="btn btn-primary btn-lg me-2" href={ctx.registrationUrl}> <a className="btn btn-primary btn-lg me-2" href={ctx.registrationUrl} style={{ minWidth: '220px' }}>
Register Register
</a> </a>
) : null} ) : null}
<a className="btn btn-outline-secondary btn-lg" href="#hero"> {/* <a className="btn btn-outline-secondary btn-lg" href="#hero">
Back to top Back to top
</a> </a> */}
</div> </div>
</div> </div>
@ -765,32 +777,34 @@ function SpeakersSection({ section, speakers }: { section: MicrositeSection; spe
const list = speakers.slice(0, limit); const list = speakers.slice(0, limit);
return ( return (
<section id="speakers" className="eb-speakers-section section py-5"> <>{list?.length > 0 && (
<div className="container"> <section id="speakers" className="eb-speakers-section section py-5">
<h2 className="eb-section-heading text-center mb-5">{title}</h2> <div className="container">
<div className="row"> <h2 className="eb-section-heading text-center mb-5">{title}</h2>
{list.length ? ( <div className="row">
list.map((s, i) => ( {list.length ? (
<div key={i} className="col-6 col-lg-3 mb-4"> list.map((s, i) => (
<div className="card h-100 shadow-sm"> <div key={i} className="col-6 col-lg-3 mb-4">
{s.photoUrl ? ( <div className="card h-100 shadow-sm">
<img src={s.photoUrl} className="card-img-top" alt={s.name} /> {s.photoUrl ? (
) : ( <img src={s.photoUrl} className="card-img-top" alt={s.name} />
<div className="card-img-top" style={{ background: '#cbd5e1', minHeight: 120 }} /> ) : (
)} <div className="card-img-top" style={{ background: '#cbd5e1', minHeight: 120 }} />
<div className="card-body"> )}
<h5 className="card-title">{s.name}</h5> <div className="card-body">
{s.designation ? <p className="card-text text-muted small">{s.designation}</p> : null} <h5 className="card-title">{s.name}</h5>
{s.designation ? <p className="card-text text-muted small">{s.designation}</p> : null}
</div>
</div> </div>
</div> </div>
</div> ))
)) ) : (
) : ( <div className="col-12 text-center text-muted py-4">No speakers added yet.</div>
<div className="col-12 text-center text-muted py-4">No speakers added yet.</div> )}
)} </div>
</div> </div>
</div> </section>
</section> )}</>
); );
} }
@ -805,19 +819,20 @@ function RegisterLinksSection({ section, ctx }: { section: MicrositeSection; ctx
const secondaryUrl = str(c.secondaryUrl, '#'); const secondaryUrl = str(c.secondaryUrl, '#');
return ( return (
<section id="register" className="py-5"> // <section id="register" className="py-5">
<div className="container text-center"> // <div className="container text-center">
<h2 className="eb-section-heading mb-4">{title}</h2> // <h2 className="eb-section-heading mb-4">{title}</h2>
<a className="btn btn-primary btn-lg" href={primaryUrl}> // <a className="btn btn-primary btn-lg" href={primaryUrl}>
{primaryLabel} // {primaryLabel}
</a> // </a>
{secondaryLabel ? ( // {secondaryLabel ? (
<a className="btn btn-outline-secondary btn-lg ms-2" href={secondaryUrl} style={{ marginLeft: '0.5rem' }}> // <a className="btn btn-outline-secondary btn-lg ms-2" href={secondaryUrl} style={{ marginLeft: '0.5rem' }}>
{secondaryLabel} // {secondaryLabel}
</a> // </a>
) : null} // ) : null}
</div> // </div>
</section> // </section>
<></>
); );
} }
@ -913,12 +928,12 @@ function FooterSection({ section, ctx }: { section: MicrositeSection; ctx: Ctx }
const links = (Array.isArray(c.links) ? c.links : []) as { label?: string; url?: string }[]; const links = (Array.isArray(c.links) ? c.links : []) as { label?: string; url?: string }[];
return ( return (
<footer id="footer" className="py-4 mt-5" style={{ background: 'var(--eb-conf-footer-bg)', color: '#fff' }}> <footer id="footer" className="mt-5" style={{ color: 'inherit' }}>
<div className="container" style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: '1rem', textAlign: 'center' }}> <div className="container" style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', textAlign: 'center' }}>
<span style={{ opacity: 0.85 }}>{copyrightText}</span> <span style={{ opacity: 0.85 }}>{copyrightText}</span>
<div> <div>
{links.map((link, i) => ( {links.map((link, i) => (
<a key={i} href={str(link.url, '#')} style={{ color: 'rgba(255,255,255,0.8)', marginRight: '1rem' }}> <a key={i} href={str(link.url, '#')} style={{ marginRight: '1rem' }}>
{str(link.label)} {str(link.label)}
</a> </a>
))} ))}

View File

@ -16,6 +16,7 @@ export function EventMicrositePage() {
setError(null); setError(null);
try { try {
const payload = await micrositeApi.get(eventId); const payload = await micrositeApi.get(eventId);
console.log('payload', payload);
setData(payload); setData(payload);
} catch (e) { } catch (e) {
setError(e instanceof Error ? e.message : 'Could not load event microsite.'); setError(e instanceof Error ? e.message : 'Could not load event microsite.');

View File

@ -19,66 +19,507 @@ html {
body { body {
margin: 0; margin: 0;
font-family: var(--eb-conf-body-font, -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif); font-family: var(--eb-conf-body-font, -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif);
color: #0f172a; color: #0f172a;
background: #ffffff; background: #ffffff;
line-height: 1.6; line-height: 1.6;
} }
.eb-microsite-root { .eb-microsite-root {
--text-content-color: rgba(255, 255, 255, 0.9);
--navbar-brand-color: rgba(255, 255, 255, 0.9);
--eb-conf-primary-dark: rgba(72,83,153,1) !important;
--eb-conf-primary-light: rgba(158,168,254,1);
--btn-primary-start: rgba(72,83,153,0.33) !important;
--btn-primary-end: rgba(72,83,153,0.83) !important;
--btn-primary-border: rgba(72,83,153,0.23) !important;
--btn-primary-border-hover: rgba(72,83,153,0.93) !important;
--section-heading-color: rgba(255,255,255,0.45);
--section-text-color: rgba(255,255,255,0.3);
--list-bullet-icon-bg: rgba(255,255,255,0.18);
--list-bullet-icon-border: rgba(255,255,255,0.6);
--list-bullet-icon-outline: rgba(255,255,255,0.15);
--btn-primary-text-color: rgba(255, 255, 255, 0.85);
--btn-border: rgba(255,255,255,0.06);
--btn-bg: rgba(255,255,255,0.03);
--btn-text-color: rgba(255,255,255,0.95);
--theme-accordion-title: rgba(255,255,255,0.73);
--theme-accordion-button-bg: rgba(255, 255, 255, 0.06);
--theme-accordion-time-bg: rgba(255,255,255,0.15);
--theme-accordion-time-text: rgba(255,255,255,0.63);
--theme-accordion-content-bg: rgba(255, 255, 255, 0.1);
--theme-accordion-content-text: rgba(255,255,255,0.73);
--theme-accordion-icon-bg: rgba(255,255,255,0.15);
--theme-accordion-track-text: rgba(158,168,254,1);
min-height: 100vh; min-height: 100vh;
background: #fff;
overflow-x: hidden; overflow-x: hidden;
background: var(--eb-conf-primary-dark);
color: var(--text-content-color);
} }
.eb-microsite-root.theme-simple-green {
--text-content-color: rgba(255, 255, 255, 0.9);
--navbar-brand-color: rgba(255, 255, 255, 0.9);
--eb-conf-primary-dark: rgba(13, 103, 76, 1) !important;
--btn-primary-start: rgba(13,103,76,0.33) !important;
--btn-primary-end: rgba(13,103,76,0.83) !important;
--btn-primary-border: rgba(13,103,76,0.23) !important;
--btn-primary-border-hover: rgba(13,103,76,0.93) !important;
--section-heading-color: rgba(255, 255, 255, 0.45) !important;
--section-text-color: rgba(255,255,255,0.3);
--list-bullet-icon-bg: rgba(255,255,255,0.18);
--list-bullet-icon-border: rgba(255,255,255,0.6);
--list-bullet-icon-outline: rgba(255,255,255,0.15);
--btn-primary-text-color: rgba(255, 255, 255, 0.85);
--btn-border: rgba(255,255,255,0.06);
--btn-bg: rgba(255,255,255,0.03);
--btn-text-color: rgba(255,255,255,0.95);
--theme-accordion-title: rgba(255,255,255,0.73);
--theme-accordion-button-bg: rgba(255, 255, 255, 0.06);
--theme-accordion-time-bg: rgba(255,255,255,0.15);
--theme-accordion-time-text: rgba(255,255,255,0.63);
--theme-accordion-content-bg: rgba(255, 255, 255, 0.1);
--theme-accordion-content-text: rgba(255,255,255,0.73);
--theme-accordion-icon-bg: rgba(255,255,255,0.15);
--theme-accordion-track-text: rgb(125, 201, 178);
/* --text-white: rgba(92,106,196,0.5) !important; */
background: var(--eb-conf-primary-dark);
color: var(--text-content-color);
}
.eb-microsite-root.theme-simple-purple {
--text-content-color: rgba(255, 255, 255, 0.9);
--navbar-brand-color: rgba(255, 255, 255, 0.9);
--eb-conf-primary-dark: rgba(49, 27, 146, 1) !important;
--btn-primary-start: rgba(49, 27, 146,0.33) !important;
--btn-primary-end: rgba(49, 27, 146,0.83) !important;
--btn-primary-border: rgba(49, 27, 146,0.23) !important;
--btn-primary-border-hover: rgba(49, 27, 146,0.93) !important;
--section-heading-color: rgba(255, 255, 255, 0.45) !important;
--section-text-color: rgba(255,255,255,0.3);
--list-bullet-icon-bg: rgba(255,255,255,0.18);
--list-bullet-icon-border: rgba(255,255,255,0.6);
--list-bullet-icon-outline: rgba(255,255,255,0.15);
--btn-primary-text-color: rgba(255, 255, 255, 0.85);
--btn-border: rgba(255,255,255,0.06);
--btn-bg: rgba(255,255,255,0.03);
--btn-text-color: rgba(255,255,255,0.95);
--theme-accordion-title: rgba(255,255,255,0.73);
--theme-accordion-button-bg: rgba(255, 255, 255, 0.06);
--theme-accordion-time-bg: rgba(255,255,255,0.15);
--theme-accordion-time-text: rgba(255,255,255,0.63);
--theme-accordion-content-bg: rgba(255, 255, 255, 0.1);
--theme-accordion-content-text: rgba(255,255,255,0.73);
--theme-accordion-icon-bg: rgba(255,255,255,0.15);
--theme-accordion-track-text: rgb(164, 150, 230);
/* --text-white: rgba(92,106,196,0.5) !important; */
background: var(--eb-conf-primary-dark);
color: var(--text-content-color);
}
.eb-microsite-root.theme-simple-gray {
--text-content-color: rgba(255, 255, 255, 0.9);
--navbar-brand-color: rgba(255, 255, 255, 0.9);
--eb-conf-primary-dark: rgba(55, 71, 79, 1) !important;
--btn-primary-start: rgba(55, 71, 79,0.33) !important;
--btn-primary-end: rgba(55, 71, 79,0.83) !important;
--btn-primary-border: rgba(55, 71, 79,0.23) !important;
--btn-primary-border-hover: rgba(55, 71, 79,0.93) !important;
--section-heading-color: rgba(255, 255, 255, 0.45) !important;
--section-text-color: rgba(255,255,255,0.3);
--list-bullet-icon-bg: rgba(255,255,255,0.18);
--list-bullet-icon-border: rgba(255,255,255,0.6);
--list-bullet-icon-outline: rgba(255,255,255,0.15);
--btn-primary-text-color: rgba(255, 255, 255, 0.85);
--btn-border: rgba(255,255,255,0.06);
--btn-bg: rgba(255,255,255,0.03);
--btn-text-color: rgba(255,255,255,0.95);
--theme-accordion-title: rgba(255,255,255,0.73);
--theme-accordion-button-bg: rgba(255, 255, 255, 0.06);
--theme-accordion-time-bg: rgba(255,255,255,0.15);
--theme-accordion-time-text: rgba(255,255,255,0.63);
--theme-accordion-content-bg: rgba(255, 255, 255, 0.1);
--theme-accordion-content-text: rgba(255,255,255,0.73);
--theme-accordion-icon-bg: rgba(255,255,255,0.15);
--theme-accordion-track-text: rgb(187, 206, 215);
/* --text-white: rgba(92,106,196,0.5) !important; */
background: var(--eb-conf-primary-dark);
color: var(--text-content-color);
}
.eb-microsite-root.theme-simple-yellow {
--text-content-color: rgba(55, 55, 55, 0.9);
--navbar-brand-color: rgba(55, 55, 55, 0.9);
--eb-conf-primary-dark: rgb(255, 236, 179, 1) !important;
--btn-primary-start: rgba(255, 236, 179,0.33) !important;
--btn-primary-end: rgba(255, 236, 179,0.83) !important;
--btn-primary-border: rgba(255, 236, 179,0.23) !important;
--btn-primary-border-hover: rgba(255, 236, 179,0.93) !important;
--section-heading-color: rgba(55, 55, 55, 0.45) !important;
--section-text-color: rgba(55,55,55,0.3);
--list-bullet-icon-bg: rgba(55,55,55,0.18);
--list-bullet-icon-border: rgba(55,55,55,0.6);
--list-bullet-icon-outline: rgba(55,55,55,0.15);
--btn-primary-text-color: rgba(55, 55, 55, 0.75);
--btn-border: rgba(55,55,55,0.06);
--btn-bg: rgba(55,55,55,0.03);
--btn-text-color: rgba(55,55,55,0.95);
--theme-accordion-title: rgba(55,55,55,0.73);
--theme-accordion-button-bg: rgba(55, 55, 55, 0.06);
--theme-accordion-time-bg: rgba(55,55,55,0.15);
--theme-accordion-time-text: rgba(55,55,55,0.63);
--theme-accordion-content-bg: rgba(55, 55, 55, 0.1);
--theme-accordion-content-text: rgba(55,55,55,0.73);
--theme-accordion-icon-bg: rgba(55,55,55,0.15);
--theme-accordion-track-text: rgb(160, 134, 57);
/* --text-white: rgba(92,106,196,0.5) !important; */
background: var(--eb-conf-primary-dark);
color: var(--text-content-color);
}
.eb-microsite-root.theme-gradient-green {
--text-content-color: rgba(55, 55, 55, 1);
--navbar-brand-color: rgba(42, 123, 155, 1);
--eb-conf-primary-dark: rgba(42, 123, 155, 1) !important;
--eb-conf-primary-light: rgb(93, 179, 212);
--btn-primary-start: rgba(42, 123, 155,0.33) !important;
--btn-primary-end: rgba(42, 123, 155,0.83) !important;
--btn-primary-border: rgba(42, 123, 155,0.23) !important;
--btn-primary-border-hover: rgba(42, 123, 155,0.93) !important;
--section-heading-color: rgba(42, 123, 155, 0.8) !important;
--section-text-color: rgba(42, 123, 155, 1) !important;
--list-bullet-icon-bg: rgba(42, 123, 155,0.38);
--list-bullet-icon-border: rgba(42, 123, 155,0.6);
--list-bullet-icon-outline: rgba(42, 123, 155,0.3);
--btn-primary-text-color: rgba(255, 255, 255, 0.95);
--btn-border: rgba(42,123,155,0.05);
--btn-bg: rgba(42,123,155,0.15);
--btn-text-color: rgba(42,123,155,0.95);
--theme-accordion-title: rgba(55,55,55,0.73);
--theme-accordion-button-bg: rgba(55, 55, 55, 0.1);
--theme-accordion-time-bg: rgba(55,55,55,0.03);
--theme-accordion-time-text: rgba(55, 55, 55, 0.75);
--theme-accordion-content-bg: rgba(55, 55, 55, 0.12);
--theme-accordion-content-text: rgba(55,55,55,0.73);
--theme-accordion-icon-bg: rgba(55,55,55,0.1);
--theme-accordion-track-text: rgb(93, 179, 212);
background: #2A7B9B;
/* background: linear-gradient(90deg, rgba(42, 123, 155, 1) 0%, rgba(87, 199, 133, 1) 50%, rgba(237, 221, 83, 1) 100%) !important; */
background:linear-gradient(-45deg, rgba(42, 123, 155, 0.8) 0%, rgba(87, 199, 133, 0.32) 50%, rgba(237, 221, 83, 0.87) 100%);
/* background: radial-gradient(circle,rgba(238, 174, 202, 1) 0%, rgba(148, 187, 233, 1) 100%); */
background-size: 400% 400%;
animation: gradient 12s ease infinite;
color: var(--text-content-color);
}
.eb-microsite-root.theme-gradient-warm {
--text-content-color: rgba(55, 55, 55, 1);
--navbar-brand-color: rgba(131, 58, 180, 1);
--eb-conf-primary-dark: rgba(131, 58, 180, 1) !important;
--eb-conf-primary-light: rgb(202, 155, 234);
--btn-primary-start: rgba(131, 58, 180,0.33) !important;
--btn-primary-end: rgba(131, 58, 180,0.83) !important;
--btn-primary-border: rgba(131, 58, 180,0.23) !important;
--btn-primary-border-hover: rgba(131, 58, 180,0.93) !important;
--section-heading-color: rgba(131, 58, 180, 0.8) !important;
--section-text-color: rgba(131, 58, 180, 1) !important;
--list-bullet-icon-bg: rgba(131, 58, 180,0.38);
--list-bullet-icon-border: rgba(131, 58, 180,0.6);
--list-bullet-icon-outline: rgba(131, 58, 180,0.3);
--btn-primary-text-color: rgba(255, 255, 255, 0.95);
--btn-border: rgba(131, 58, 180,0.05);
--btn-bg: rgba(131, 58, 180,0.15);
--btn-text-color: rgba(131, 58, 180,0.95);
--theme-accordion-title: rgba(55,55,55,0.73);
--theme-accordion-button-bg: rgba(55, 55, 55, 0.1);
--theme-accordion-time-bg: rgba(55,55,55,0.03);
--theme-accordion-time-text: rgba(55, 55, 55, 0.75);
--theme-accordion-content-bg: rgba(55, 55, 55, 0.12);
--theme-accordion-content-text: rgba(55,55,55,0.73);
--theme-accordion-icon-bg: rgba(55,55,55,0.1);
--theme-accordion-track-text: rgb(202, 155, 234);
background: #2A7B9B;
/* background: linear-gradient(90deg, rgba(42, 123, 155, 1) 0%, rgba(87, 199, 133, 1) 50%, rgba(237, 221, 83, 1) 100%) !important; */
/* background:linear-gradient(-45deg, rgba(42, 123, 155, 0.8) 0%, rgba(87, 199, 133, 0.32) 50%, rgba(237, 221, 83, 0.87) 100%); */
background: linear-gradient(-45deg,rgba(131, 58, 180, 0.82) 0%, rgba(253, 29, 29, 0.1) 50%, rgba(252, 176, 69, 0.79) 100%);
/* background: radial-gradient(circle,rgba(238, 174, 202, 1) 0%, rgba(148, 187, 233, 1) 100%); */
background-size: 400% 400%;
animation: gradient 12s ease infinite;
color: var(--text-content-color);
}
.eb-microsite-root.theme-gradient-warm2 {
--text-content-color: rgba(55, 55, 55, 1);
--navbar-brand-color: rgba(194, 147, 82, 1);
--eb-conf-primary-dark: rgba(194, 147, 82, 1) !important;
--eb-conf-primary-light: rgb(229, 183, 119);
--btn-primary-start: rgba(194, 147, 82,0.33) !important;
--btn-primary-end: rgba(194, 147, 82,0.83) !important;
--btn-primary-border: rgba(194, 147, 82,0.23) !important;
--btn-primary-border-hover: rgba(194, 147, 82,0.93) !important;
--section-heading-color: rgba(194, 147, 82, 0.8) !important;
--section-text-color: rgba(194, 147, 82, 1) !important;
--list-bullet-icon-bg: rgba(194, 147, 82,0.38);
--list-bullet-icon-border: rgba(194, 147, 82,0.6);
--list-bullet-icon-outline: rgba(194, 147, 82,0.3);
--btn-primary-text-color: rgba(255, 255, 255, 0.95);
--btn-border: rgba(194, 147, 82,0.05);
--btn-bg: rgba(194, 147, 82,0.15);
--btn-text-color: rgba(194, 147, 82,0.95);
--theme-accordion-title: rgba(55,55,55,0.73);
--theme-accordion-button-bg: rgba(55, 55, 55, 0.1);
--theme-accordion-time-bg: rgba(55,55,55,0.03);
--theme-accordion-time-text: rgba(55, 55, 55, 0.75);
--theme-accordion-content-bg: rgba(55, 55, 55, 0.12);
--theme-accordion-content-text: rgba(55,55,55,0.73);
--theme-accordion-icon-bg: rgba(55,55,55,0.1);
--theme-accordion-track-text: rgb(143, 102, 45);
background: #2A7B9B;
/* background: linear-gradient(90deg, rgba(42, 123, 155, 1) 0%, rgba(87, 199, 133, 1) 50%, rgba(237, 221, 83, 1) 100%) !important; */
/* background:linear-gradient(-45deg, rgba(42, 123, 155, 0.8) 0%, rgba(87, 199, 133, 0.32) 50%, rgba(237, 221, 83, 0.87) 100%); */
background: linear-gradient(-45deg,rgba(131, 58, 180, 0.82) 0%, rgba(253, 29, 29, 0.1) 50%, rgba(252, 176, 69, 0.79) 100%);
/* background: radial-gradient(circle,rgba(238, 174, 202, 1) 0%, rgba(148, 187, 233, 1) 100%); */
background-size: 400% 400%;
animation: gradient 12s ease infinite;
color: var(--text-content-color);
}
@keyframes gradient{
0%{background-position:0% 50%;}
50%{background-position:100% 50%;}
100%{background-position:0% 50%;}
}
.eb-microsite-main {
max-width: 1000px;
margin: 0 auto;
}
/* =====================
UTILITIES
===================== */
.container {
width: 100%;
max-width: 1140px;
margin: 0 auto;
padding: 0 1.5rem;
}
.container-fluid {
width: 100%;
padding: 0 1.5rem;
}
.row {
display: flex;
flex-wrap: wrap;
margin: 0 -0.75rem;
}
.col-6,
.col-md-3,
.col-md-4,
.col-md-9,
.col-lg-2,
.col-lg-3 {
padding: 0 0.75rem;
margin-bottom: 1.5rem;
}
.col-6 {
flex: 0 0 50%;
max-width: 50%;
}
@media (min-width: 768px) {
.col-md-3 {
flex: 0 0 25%;
max-width: 25%;
}
.col-md-4 {
flex: 0 0 33.333%;
max-width: 33.333%;
}
.col-md-9 {
flex: 0 0 75%;
max-width: 75%;
}
}
@media (min-width: 992px) {
.col-lg-2 {
flex: 0 0 16.667%;
max-width: 16.667%;
}
.col-lg-3 {
flex: 0 0 25%;
max-width: 25%;
}
}
.g-3 > * {
margin-bottom: 1rem;
}
.mx-auto {
margin-left: auto;
margin-right: auto;
}
.mt-4 {
margin-top: 1.5rem;
}
.me-2 {
margin-right: 0.5rem;
}
.ms-2 {
margin-left: 0.5rem;
}
.mb-3 {
margin-bottom: 1rem;
}
.mb-4 {
margin-bottom: 1.5rem;
}
.mb-5 {
margin-bottom: 3rem;
}
.py-3 {
padding-top: 0.75rem;
padding-bottom: 0.75rem;
}
.py-4 {
padding-top: 1rem;
padding-bottom: 1rem;
}
.py-5 {
padding-top: 3rem;
padding-bottom: 3rem;
}
.px-lg-4 {
padding-left: 1.5rem !important;
padding-right: 1.5rem !important;
}
.text-center {
text-align: center;
}
.text-muted {
color: #64748b !important;
}
.small {
font-size: 0.875rem;
}
.rounded-circle {
border-radius: 50%;
}
.h-100 {
height: 100%;
}
.shadow-sm {
box-shadow: var(--shadow-sm);
}
.img-fluid {
max-width: 100%;
height: auto;
display: block;
}
.align-items-start {
align-items: flex-start;
}
.align-items-center {
align-items: center;
}
.justify-content-center {
justify-content: center;
}
/* ===================== /* =====================
HEADER & NAVIGATION HEADER & NAVIGATION
===================== */ ===================== */
.eb-conference-header { .eb-conference-header {
background: var(--eb-conf-nav-bg); /* background: var(--eb-conf-nav-bg); */
padding: 0.75rem 0; padding: 0rem 0;
position: sticky; /* position: relative; */
top: 0; /* top: 0; */
z-index: 1000; /* z-index: 1000; */
box-shadow: var(--shadow-md); /* box-shadow: var(--shadow-md); */
transition: box-shadow var(--transition-smooth); transition: box-shadow var(--transition-smooth);
} }
.eb-conference-header .navbar { .eb-conference-header .navbar {
padding: 0.5rem 0; padding: 0rem 0 1rem;
position: relative;
min-height: 70px;
} }
.eb-conference-header .navbar-brand { .eb-conference-header .navbar-brand {
color: #fff !important; color: var(--navbar-brand-color) !important;
font-family: var(--eb-conf-heading-font); font-family: var(--eb-conf-heading-font);
font-weight: 700; font-weight: 700;
font-size: 1.25rem; font-size: 1.25rem;
transition: opacity var(--transition-smooth); transition: opacity var(--transition-smooth);
position: absolute;
top: -3rem;
display: flex;
text-decoration: none;
flex-direction: column;
} }
.eb-conference-header .navbar-brand:hover { /* .eb-conference-header .navbar-brand:hover {
opacity: 0.85; opacity: 0.85;
} } */
.eb-conference-header .nav-link { .eb-conference-header .nav-link {
color: rgba(255, 255, 255, 0.9) !important; /* color: rgba(255, 255, 255, 0.9) !important; */
transition: color var(--transition-smooth); transition: color var(--transition-smooth);
font-weight: 500; font-weight: 500;
letter-spacing: 0.3px; letter-spacing: 0.3px;
} }
.eb-conference-header .nav-link:hover { /* .eb-conference-header .nav-link:hover {
color: #fff !important; color: #fff !important;
} */
.eb-header-logo-container {
position: relative;
z-index: 999;
} }
.eb-header-logo { .eb-header-logo {
max-height: 40px; border-radius: 6px;
width: auto; max-height: 100px;
width: 150px;
transition: transform var(--transition-smooth); transition: transform var(--transition-smooth);
border: 6px solid var(--eb-conf-primary-dark);
box-shadow: -2px -2px 0px var(--eb-conf-primary-dark), 2px -2px 0px var(--eb-conf-primary-dark);
margin-left: -8px;
} }
.eb-header-logo-container span { padding-left: 10px;}
.eb-header-logo:hover { .eb-header-logo:hover {
transform: scale(1.05); transform: scale(1.02);
} }
/* ===================== /* =====================
@ -86,14 +527,33 @@ body {
===================== */ ===================== */
.eb-hero-block { .eb-hero-block {
min-height: 500px; min-height: 400px;
display: flex; display: flex;
align-items: center; align-items: end;
justify-content: center; justify-content: center;
padding: 6rem 1rem 4rem; padding: 6rem 1rem 1rem;
background: var(--eb-conf-hero-gradient); background: var(--eb-conf-hero-gradient);
position: relative; position: relative;
overflow: hidden; overflow: hidden;
border-radius: 15px;
margin-top: 1.5rem;
margin-left: -1rem;
margin-right: -1rem;
/* clip-path: polygon(
0 0,
100% 0,
100% 100%,
200px 100%,
200px calc(100% - 90px),
0 calc(100% - 90px)
); */
/* -webkit-mask:
radial-gradient(90px 60px at 110px 100%, transparent 99%, #000 100%),
linear-gradient(#000 0 0); */
/* -webkit-mask-composite: source-over; */
/* mask-composite: add; */
} }
.eb-hero-bg { .eb-hero-bg {
@ -118,7 +578,8 @@ body {
.eb-hero-inner { .eb-hero-inner {
position: relative; position: relative;
z-index: 3; z-index: 3;
max-width: 820px; /* max-width: 820px; */
padding: 0;
animation: fadeInUp 0.8s ease-out; animation: fadeInUp 0.8s ease-out;
} }
@ -164,7 +625,7 @@ body {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: 1rem; gap: 1rem;
justify-content: center; justify-content: flex-end;
} }
/* ===================== /* =====================
@ -172,16 +633,17 @@ body {
===================== */ ===================== */
.section { .section {
padding: 4rem 1rem; padding: 4rem 0rem;
transition: background-color var(--transition-smooth); transition: background-color var(--transition-smooth);
} }
.eb-section-heading { .eb-section-heading {
font-family: var(--eb-conf-heading-font); font-family: var(--eb-conf-heading-font);
font-weight: 700; font-weight: 700;
font-size: clamp(1.5rem, 4vw, 2.5rem); font-size: clamp(1.5rem, 2.5vw, 2rem);
color: var(--eb-conf-primary-dark); /* color: var(--eb-conf-primary-dark); */
margin: 0 0 1rem; color: var(--section-heading-color);
margin: 0 0 2rem;
letter-spacing: -0.01em; letter-spacing: -0.01em;
} }
@ -190,8 +652,9 @@ body {
display: block; display: block;
width: 60px; width: 60px;
height: 4px; height: 4px;
background: linear-gradient(90deg, var(--eb-conf-accent) 0%, var(--eb-conf-primary) 100%); /* background: linear-gradient(90deg, var(--eb-conf-accent) 0%, var(--eb-conf-primary) 100%); */
margin: 1rem auto 0; background: linear-gradient(135deg, var(--btn-primary-start) 0%, var(--btn-primary-end) 100%);
margin: 0rem 0 0;
border-radius: 2px; border-radius: 2px;
} }
@ -221,12 +684,152 @@ body {
margin: 0.5rem 0 0; margin: 0.5rem 0 0;
} }
.about-section {
padding-top: 1rem;
padding-bottom: 2rem;
}
.about-section p {
font-size: 1rem;
font-weight: 300;
/* color: var(--section-text-color); */
color: inherit;
}
/* ======
LIST STYLES
============= */
.theme-list {
list-style: none;
padding: 0;
margin: 0;
}
.theme-list li {
position: relative;
padding-left: 1.5rem;
margin-bottom: 0.5rem;
}
.theme-list li::before {
content: '';
display: block;
width: 8px;
height: 8px;
background: var(--list-bullet-icon-bg);
border: 0.5px solid var(--list-bullet-icon-border);
outline: 2px solid var(--list-bullet-icon-outline);
outline-offset: 1px;
border-radius: 50%;
position: absolute;
left: 0;
top: 0.7rem;
}
/** ==============
Theme Accordion Styles
============== */
.theme-accordion-wrapper {
border-radius: 12px;
overflow: hidden;
}
.theme-accordion-item.border-bottom {
/* border-bottom: 0.5px solid rgba(255,255,255,0.3); */
margin-bottom: 0.05rem;
}
.theme-accordion-button {
width: 100%;
background: var(--theme-accordion-button-bg);
backdrop-filter: blur(5px);
border: none;
padding: 1rem 1.5rem;
text-align: left;
cursor: pointer;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
display: flex;
/* flex-direction: column; */
align-items: center;
justify-content: space-between;
gap: 1rem;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.03);
cursor: pointer;
}
.theme-accordion-time {
background: var(--theme-accordion-time-bg);
backdrop-filter: blur(5px);
color: var(--theme-accordion-time-text);
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.15);
padding: 0.5rem 0.75rem;
border-radius: 4px;
font-size: 0.8rem;
font-weight: 600;
white-space: nowrap;
flex-shrink: 0;
align-self: flex-start;
}
.theme-accordion-title {
font-size: 1.1rem;
line-height: 1.5;
font-weight: 600;
color: var(--theme-accordion-title);
margin: 0 0 0.25rem 0;
overflow: hidden;
text-align: ellipsis;
/* white-space: nowrap; */
letter-spacing: 0.35px;
}
.theme-accordion-track {
/* background: rgba(0, 194, 209, 0.1); */
/* color: #0891b2; */
color: var(--theme-accordion-track-text);
padding: 0.25rem 0rem;
border-radius: 3px;
font-size: 0.8rem;
font-weight: 500;
/* text-transform: uppercase; */
letter-spacing: 0.5px;
display: inline-block;
}
.theme-accordion-icon {
display: flex;
align-Items: center;
justify-content: center;
width: 26px;
height: 26px;
background: var(--theme-accordion-icon-bg);
border-Radius: 6px;
transition: all 0.3s;
color: #3d4249;
font-weight: 600;
font-size: 1.2rem;
flex-shrink: 0;
backdrop-filter: blur(5px);
}
.theme-accordion-content {
background: var(--theme-accordion-content-bg);
backdrop-filter: blur(5px);
padding: 1.5rem;
/* border-top: 0.5px solid rgba(255,255,255,0.3); */
color: inherit;
animation: 0.2s ease-out 0s 1 normal both running slideDown;
/* border-bottom-left-radius: 12px; */
/* border-bottom-right-radius: 12px; */
}
.theme-accordion-content p {
color: inherit;
margin: 0 0 0.5rem;
}
/* ===================== /* =====================
SCHEDULE SECTION SCHEDULE SECTION
===================== */ ===================== */
.eb-schedule-section { .eb-schedule-section {
background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%); /* background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%); */
/* border-top: 1px solid rgba(255,255,255,0.05); */
padding-top: 2rem;
box-shadow: 0 -1px 0px rgba(250, 250, 250, 0.05);
} }
/* New horizontal card layout */ /* New horizontal card layout */
@ -256,6 +859,11 @@ body {
border-color: #cbd5e1; border-color: #cbd5e1;
} }
.eb-schedule-section .btn-theme-tab.btn {
padding: 0.6rem 0.85rem;
font-size: 0.85rem;
}
/* Old list layout - kept for backward compatibility */ /* Old list layout - kept for backward compatibility */
.eb-schedule-list { .eb-schedule-list {
background: #fff; background: #fff;
@ -412,7 +1020,7 @@ body {
display: inline-block; display: inline-block;
padding: 0.75rem 1.5rem; padding: 0.75rem 1.5rem;
border-radius: 8px; border-radius: 8px;
border: 1px solid transparent; border: 1px solid var(--btn-border);
font-weight: 600; font-weight: 600;
text-decoration: none; text-decoration: none;
cursor: pointer; cursor: pointer;
@ -422,18 +1030,25 @@ body {
letter-spacing: 0.3px; letter-spacing: 0.3px;
text-transform: uppercase; text-transform: uppercase;
font-size: 0.95rem; font-size: 0.95rem;
background: var(--btn-bg);
color: var(--btn-text-color);
} }
.btn-primary { .btn-primary {
background: linear-gradient(135deg, var(--eb-conf-accent) 0%, var(--eb-conf-primary) 100%); /* background: linear-gradient(135deg, var(--eb-conf-accent) 0%, var(--eb-conf-primary) 100%); */
border-color: var(--eb-conf-accent) !important; /* background: linear-gradient(135deg, rgba(92,106,196,0.33) 0%, rgba(92,106,196,0.83) 100%); */
color: var(--eb-conf-accent-foreground) !important; background: linear-gradient(135deg, var(--btn-primary-start) 0%, var(--btn-primary-end) 100%);
border-color: var(--btn-primary-border) !important;
/* color: var(--eb-conf-accent-foreground) !important; */
color: var(--btn-primary-text-color) !important;
box-shadow: var(--shadow-md); box-shadow: var(--shadow-md);
backdrop-filter: blur(5px);
} }
.btn-primary:hover { .btn-primary:hover {
background: linear-gradient(135deg, var(--eb-conf-accent-hover) 0%, var(--eb-conf-primary) 100%); /* background: linear-gradient(135deg, var(--eb-conf-accent-hover) 0%, var(--eb-conf-primary) 100%); */
border-color: var(--eb-conf-accent-hover) !important; /* background: linear-gradient(135deg, rgba(92,106,196,0.33) 0%, rgba(92,106,196,0.83) 100%); */
border-color: var(--btn-primary-border-hover) !important;
box-shadow: var(--shadow-lg); box-shadow: var(--shadow-lg);
transform: translateY(-2px); transform: translateY(-2px);
} }
@ -520,160 +1135,6 @@ textarea.form-control {
margin-top: 0.25rem; margin-top: 0.25rem;
} }
/* =====================
UTILITIES
===================== */
.container {
width: 100%;
max-width: 1140px;
margin: 0 auto;
padding: 0 1rem;
}
.container-fluid {
width: 100%;
padding: 0 1rem;
}
.row {
display: flex;
flex-wrap: wrap;
margin: 0 -0.75rem;
}
.col-6,
.col-md-3,
.col-md-4,
.col-md-9,
.col-lg-2,
.col-lg-3 {
padding: 0 0.75rem;
margin-bottom: 1.5rem;
}
.col-6 {
flex: 0 0 50%;
max-width: 50%;
}
@media (min-width: 768px) {
.col-md-3 {
flex: 0 0 25%;
max-width: 25%;
}
.col-md-4 {
flex: 0 0 33.333%;
max-width: 33.333%;
}
.col-md-9 {
flex: 0 0 75%;
max-width: 75%;
}
}
@media (min-width: 992px) {
.col-lg-2 {
flex: 0 0 16.667%;
max-width: 16.667%;
}
.col-lg-3 {
flex: 0 0 25%;
max-width: 25%;
}
}
.g-3 > * {
margin-bottom: 1rem;
}
.mx-auto {
margin-left: auto;
margin-right: auto;
}
.mt-4 {
margin-top: 1.5rem;
}
.me-2 {
margin-right: 0.5rem;
}
.ms-2 {
margin-left: 0.5rem;
}
.mb-4 {
margin-bottom: 1.5rem;
}
.mb-5 {
margin-bottom: 3rem;
}
.py-3 {
padding-top: 0.75rem;
padding-bottom: 0.75rem;
}
.py-4 {
padding-top: 1rem;
padding-bottom: 1rem;
}
.py-5 {
padding-top: 3rem;
padding-bottom: 3rem;
}
.px-lg-4 {
padding-left: 1.5rem !important;
padding-right: 1.5rem !important;
}
.text-center {
text-align: center;
}
.text-muted {
color: #64748b !important;
}
.small {
font-size: 0.875rem;
}
.rounded-circle {
border-radius: 50%;
}
.h-100 {
height: 100%;
}
.shadow-sm {
box-shadow: var(--shadow-sm);
}
.img-fluid {
max-width: 100%;
height: auto;
display: block;
}
.align-items-start {
align-items: flex-start;
}
.align-items-center {
align-items: center;
}
.justify-content-center {
justify-content: center;
}
.card { .card {
background: #fff; background: #fff;
border-radius: 12px; border-radius: 12px;
@ -708,16 +1169,16 @@ textarea.form-control {
===================== */ ===================== */
.eb-static-footer { .eb-static-footer {
background: linear-gradient(135deg, var(--eb-conf-footer-bg) 0%, rgba(0, 0, 0, 0.8) 100%); /* background: linear-gradient(135deg, var(--eb-conf-footer-bg) 0%, rgba(0, 0, 0, 0.8) 100%); */
color: rgba(255, 255, 255, 0.85); color: inherit;
padding: 2rem 0; padding: 1rem 0;
font-size: 0.875rem; font-size: 0.875rem;
text-align: center; text-align: center;
border-top: 1px solid rgba(255, 255, 255, 0.1); /* border-top: 1px solid rgba(255, 255, 255, 0.1); */
} }
.eb-static-footer strong { .eb-static-footer strong {
color: #fff; color: inherit;
font-weight: 700; font-weight: 700;
} }

View File

@ -1,8 +1,82 @@
import type { MicrositeThemeConfig } from '../types';
const DEFAULT_PRIMARY = '#5c6ac4'; const DEFAULT_PRIMARY = '#5c6ac4';
const DEFAULT_ACCENT = '#00c2d1'; const DEFAULT_ACCENT = '#00c2d1';
const DEFAULT_FONT_PAIR = 'Montserrat + Roboto'; const DEFAULT_FONT_PAIR = 'Montserrat + Roboto';
const DEFAULT_THEME_FONT = 'Open Sans';
export const FONT_SPECS: Record<string, { href: string; heading: string; body: string }> = { /** Colors that have matching `.theme-simple-*` rules in conference.css */
const SIMPLE_COLORS = new Set(['green', 'purple', 'gray', 'yellow']);
/** Colors that have matching `.theme-gradient-*` rules in conference.css */
const GRADIENT_COLORS = new Set(['green', 'warm', 'warm2']);
type FontFaceSpec = { href: string; heading: string; body: string };
/**
* Maps API theme config → a single class on `.eb-microsite-root`.
* Unknown mode/color returns '' so the default root styles apply.
*/
export function resolveThemeClass(config?: MicrositeThemeConfig | null): string {
if (!config?.theme || !config?.color) return '';
const mode = String(config.theme).trim().toLowerCase();
const color = String(config.color).trim().toLowerCase();
if (!mode || !color) return '';
if (mode === 'simple' && SIMPLE_COLORS.has(color)) {
return `theme-simple-${color}`;
}
if (mode === 'gradient' && GRADIENT_COLORS.has(color)) {
return `theme-gradient-${color}`;
}
return '';
}
/** Single-family fonts for theme.font (heading + body share the same family). */
export const THEME_FONT_SPECS: Record<string, FontFaceSpec> = {
'Open Sans': {
href: 'https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;600;700;800&display=swap',
heading: '"Open Sans", system-ui, sans-serif',
body: '"Open Sans", system-ui, sans-serif',
},
Montserrat: {
href: 'https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;600;700;800&display=swap',
heading: '"Montserrat", system-ui, sans-serif',
body: '"Montserrat", system-ui, sans-serif',
},
Roboto: {
href: 'https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap',
heading: '"Roboto", system-ui, sans-serif',
body: '"Roboto", system-ui, sans-serif',
},
Poppins: {
href: 'https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600;700;800&display=swap',
heading: '"Poppins", system-ui, sans-serif',
body: '"Poppins", system-ui, sans-serif',
},
Raleway: {
href: 'https://fonts.googleapis.com/css2?family=Raleway:wght@300;400;600;700;800&display=swap',
heading: '"Raleway", system-ui, sans-serif',
body: '"Raleway", system-ui, sans-serif',
},
};
const THEME_FONT_ALIASES: Record<string, string> = Object.fromEntries(
Object.keys(THEME_FONT_SPECS).map((name) => [name.toLowerCase(), name]),
);
/**
* Resolves theme.font → font face. Defaults to Open Sans.
* Unknown values fall back to Open Sans.
*/
export function resolveThemeFont(config?: MicrositeThemeConfig | null): FontFaceSpec {
const raw = config?.font != null ? String(config.font).trim() : '';
const key = raw ? THEME_FONT_ALIASES[raw.toLowerCase()] : DEFAULT_THEME_FONT;
return THEME_FONT_SPECS[key] || THEME_FONT_SPECS[DEFAULT_THEME_FONT];
}
export const FONT_SPECS: Record<string, FontFaceSpec> = {
'Montserrat + Roboto': { 'Montserrat + Roboto': {
href: 'https://fonts.googleapis.com/css2?family=Montserrat:wght@600;700;800&family=Roboto:wght@300;400;700&display=swap', href: 'https://fonts.googleapis.com/css2?family=Montserrat:wght@600;700;800&family=Roboto:wght@300;400;700&display=swap',
heading: '"Montserrat", system-ui, sans-serif', heading: '"Montserrat", system-ui, sans-serif',
@ -87,7 +161,31 @@ export function fontSpec(fontPair?: string) {
return FONT_SPECS[key] || FONT_SPECS[DEFAULT_FONT_PAIR]; return FONT_SPECS[key] || FONT_SPECS[DEFAULT_FONT_PAIR];
} }
export function conferenceCssVars(website: { primaryColor?: string; accentColor?: string; fontPair?: string }) { /**
* Typography resolution:
* 1. When website.theme is present → theme.font (default Open Sans) for heading + body
* 2. Else → website.fontPair (heading/body pair)
*/
export function resolveWebsiteFont(website: {
fontPair?: string;
theme?: MicrositeThemeConfig | null;
}): FontFaceSpec {
if (website.theme) {
return resolveThemeFont(website.theme);
}
const pair = website.fontPair?.trim();
if (pair && FONT_SPECS[pair]) {
return FONT_SPECS[pair];
}
return THEME_FONT_SPECS[DEFAULT_THEME_FONT];
}
export function conferenceCssVars(website: {
primaryColor?: string;
accentColor?: string;
fontPair?: string;
theme?: MicrositeThemeConfig | null;
}) {
const primary = normalizeHex(website.primaryColor) || DEFAULT_PRIMARY; const primary = normalizeHex(website.primaryColor) || DEFAULT_PRIMARY;
const accent = normalizeHex(website.accentColor) || DEFAULT_ACCENT; const accent = normalizeHex(website.accentColor) || DEFAULT_ACCENT;
const primaryDark = darken(primary, 0.22); const primaryDark = darken(primary, 0.22);
@ -96,7 +194,7 @@ export function conferenceCssVars(website: { primaryColor?: string; accentColor?
const gradStop2 = primaryDark; const gradStop2 = primaryDark;
const navBg = darken(primary, 0.42); const navBg = darken(primary, 0.42);
const footerBg = darken(primary, 0.48); const footerBg = darken(primary, 0.48);
const spec = fontSpec(website.fontPair); const spec = resolveWebsiteFont(website);
return { return {
'--eb-conf-primary': primary, '--eb-conf-primary': primary,
@ -114,13 +212,20 @@ export function conferenceCssVars(website: { primaryColor?: string; accentColor?
} as Record<string, string>; } as Record<string, string>;
} }
export function loadConferenceFont(fontPair?: string) { export function loadConferenceFont(website: {
const spec = fontSpec(fontPair); fontPair?: string;
theme?: MicrositeThemeConfig | null;
}) {
const spec = resolveWebsiteFont(website);
const id = 'eb-conf-fonts'; const id = 'eb-conf-fonts';
if (document.getElementById(id)) return; let link = document.getElementById(id) as HTMLLinkElement | null;
const link = document.createElement('link'); if (!link) {
link.id = id; link = document.createElement('link');
link.rel = 'stylesheet'; link.id = id;
link.href = spec.href; link.rel = 'stylesheet';
document.head.appendChild(link); document.head.appendChild(link);
}
if (link.href !== spec.href) {
link.href = spec.href;
}
} }

View File

@ -76,6 +76,19 @@ export interface AgendaItem {
speakerPhotoUrl?: string; speakerPhotoUrl?: string;
} }
/** Page background preset. Pattern / confetti / emoji come later. */
export type MicrositeThemeMode = 'simple' | 'gradient';
/** Single-family fonts driven by theme.font (separate from website.fontPair). */
export type MicrositeThemeFont = 'Open Sans' | 'Montserrat' | 'Roboto' | 'Poppins' | 'Raleway';
export interface MicrositeThemeConfig {
theme: MicrositeThemeMode;
color: string;
/** Optional; defaults to Open Sans when theme is present. */
font?: MicrositeThemeFont | string | null;
}
export interface PublicMicrositePayload { export interface PublicMicrositePayload {
event: PublicFormEventSummary & { _id: string }; event: PublicFormEventSummary & { _id: string };
website: { website: {
@ -90,6 +103,8 @@ export interface PublicMicrositePayload {
fontPair: string; fontPair: string;
layoutTemplate: string; layoutTemplate: string;
seoDescription: string; seoDescription: string;
/** Luma-style background preset: maps to theme-{simple|gradient}-{color} on the root. */
theme?: MicrositeThemeConfig | null;
}; };
title: string; title: string;
sections: MicrositeSection[]; sections: MicrositeSection[];