// Broadway Interiors — main app, navigation, home, footer
const { useState: useS, useEffect: useE, useRef: useR, useMemo: useM } = React;
// ──────────────────────────────────────────────────────────────────────
// Tweaks defaults — wired through useTweaks() in the panel
// ──────────────────────────────────────────────────────────────────────
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
"accent": "#A98B53",
"type": "instrument",
"heroTreatment": "still",
"darkChrome": false
}/*EDITMODE-END*/;
// ──────────────────────────────────────────────────────────────────────
// Tiny hash router
// ──────────────────────────────────────────────────────────────────────
function useRoute() {
const [route, setRoute] = useS(() => parseHash(location.hash));
useE(() => {
const onHash = () => setRoute(parseHash(location.hash));
window.addEventListener('hashchange', onHash);
return () => window.removeEventListener('hashchange', onHash);
}, []);
const go = (path) => { location.hash = '#' + path; window.scrollTo(0, 0); };
return [route, go];
}
function parseHash(hash) {
const path = (hash || '#/').replace(/^#/, '') || '/';
const parts = path.split('/').filter(Boolean);
if (parts.length === 0) return { page: 'home', path: '/' };
if (parts[0] === 'projects' && parts.length === 1) return { page: 'projects', path };
if (parts[0] === 'projects' && parts[1]) return { page: 'case', slug: parts[1], path };
if (parts[0] === 'sectors' && parts.length === 1) return { page: 'sectors', path };
if (parts[0] === 'sectors' && parts[1]) return { page: 'sector', id: parts[1], path };
if (parts[0] === 'about') return { page: 'about', path };
if (parts[0] === 'awards') return { page: 'awards', path };
if (parts[0] === 'contact') return { page: 'contact', path };
return { page: '404', path };
}
// ──────────────────────────────────────────────────────────────────────
// Navbar
// ──────────────────────────────────────────────────────────────────────
function Navbar({ route, go, scrolled, darkChrome }) {
const [open, setOpen] = useS(false);
const items = [
{ id: 'projects', label: 'Projects' },
{ id: 'sectors', label: 'Sectors' },
{ id: 'about', label: 'Studio' },
{ id: 'awards', label: 'Awards' },
{ id: 'contact', label: 'Contact' },
];
// Dark chrome when explicitly forced, or on home (scrolled toggles)
const isHome = route.page === 'home';
const isCase = route.page === 'case';
const transparent = (isHome || isCase) && !scrolled;
return (
{open && (
)}
);
}
// ──────────────────────────────────────────────────────────────────────
// Home
// ──────────────────────────────────────────────────────────────────────
function Home({ go, heroTreatment }) {
const featured = PROJECTS.filter(p => p.featured);
const [marqueeIdx, setMarqueeIdx] = useS(0);
useE(() => {
const i = setInterval(() => setMarqueeIdx(x => (x + 1) % featured.length), 4500);
return () => clearInterval(i);
}, [featured.length]);
return (
{/* Manifesto strip */}
An award-winning, design-and-build consultancy in Dubai. Established 1999.
We design it
to be built.
And then we build it.
One studio. One project manager. One quantity surveyor. The drawings
and the workshop sit four metres apart — which is why the rooms we
deliver still look the way we drew them.
{/* Sectors */}
Five sectors · 70+ projects
Rooms we draw and build.
{/* Featured work — big editorial spreads */}
{featured.slice(0, 4).map((p, i) => (
))}
{/* Design-and-build differentiator strip */}
The difference
01.
One studio for design and delivery.
Most fit-out projects involve at least three companies. Ours involve one. The
programme tightens and the change orders disappear.
02.
In-house QS, on day one.
Our Managing QS sits in design reviews. The concept already knows what it costs
— and how it will be procured — before you see it.
03.
A 70-project rolodex of trades.
Twenty-five years of vetted joiners, MEP partners and finish suppliers — picked
per project, not assigned by default.
{/* Awards strip — marquee */}
25+ design awards · 2010 — 2025
Including a few we’re especially proud of.
{AWARDS.filter(a => a.flagship).concat(AWARDS.filter(a => !a.flagship).slice(0, 4)).map((a, i) => (
{a.year}
{a.flagship && '★ '}{a.title}
{a.issuer}{a.project ? ` · ${a.project}` : ''}
{a.flagship &&
Flagship
}
))}
{/* Trusted by */}
Public, private & operator clients · selected
{['DEWA', 'KHDA', 'DIFC', 'MBRSC', 'Asia Asia', 'STK', 'Black Tap', 'Lock, Stock & Barrel',
'Viacom', 'Anantara', 'Make Up For Ever', 'Rixos', 'Grand Millennium', 'twofour54', 'DAFZA', 'HIPA'].map(c => (
{c}
))}
Some government and operator names withheld pending publication clearance.
);
}
function HeroBlock({ treatment, go }) {
const hero = PROJECTS.find(p => p.hero) || PROJECTS[0];
if (treatment === 'split') return ;
if (treatment === 'noir') return ;
return ;
}
function HeroStill({ project, go }) {
return (
Award-winning interior design + build · Dubai · since 1999
Intelligent,
innovative
design.
A boutique design and build consultancy — drawing and delivering F&B,
office, government, leisure and residential interiors from a single studio
in Barsha Heights.
go('/projects')}>See the work →
go('/contact')}>Start a project
№ 01 / Asia Asia
Pier 7, Dubai Marina · 2013 · World’s Best Leisure Interior, 2021
);
}
function HeroSplit({ project, go }) {
return (
Dubai · since 1999
Intelligent,
innovative
design.
Award-winning interior design + build consultancy. One studio. Five sectors.
Seventy delivered projects.
go('/projects')}>See the work →
go('/contact')}>Start a project
№ 01 · Asia Asia
Pier 7, Dubai Marina · 2013
);
}
function HeroNoir({ project, go }) {
return (
);
}
function FeatureSpread({ project, index, go }) {
const sector = SECTORS.find(s => s.id === project.sector);
return (
{ e.preventDefault(); go(`/projects/${project.slug}`); }}>
№ {String(index + 1).padStart(2, '0')}
{sector ? sector.label : project.sector} · {project.location}
{project.name}.
{project.tagline &&
{project.tagline}
}
{project.summary}
{project.scope} · {project.year}
Read the case study →
);
}
// ──────────────────────────────────────────────────────────────────────
// Footer
// ──────────────────────────────────────────────────────────────────────
function Footer({ go }) {
return (
);
}
// ──────────────────────────────────────────────────────────────────────
// Tweaks
// ──────────────────────────────────────────────────────────────────────
function TweaksUI() {
const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
useE(() => {
const root = document.documentElement;
root.style.setProperty('--accent', t.accent);
root.dataset.type = t.type;
root.dataset.darkChrome = t.darkChrome ? '1' : '0';
}, [t.accent, t.type, t.darkChrome]);
return (
setTweak('accent', v)} />
setTweak('type', v)} />
setTweak('heroTreatment', v)} />
setTweak('darkChrome', v)} />
);
}
// ──────────────────────────────────────────────────────────────────────
// Root
// ──────────────────────────────────────────────────────────────────────
function App() {
const [route, go] = useRoute();
const [scrolled, setScrolled] = useS(false);
const [projFilter, setProjFilter] = useS('all');
const [t] = useTweaks(TWEAK_DEFAULTS);
useE(() => {
const onScroll = () => setScrolled(window.scrollY > 24);
window.addEventListener('scroll', onScroll, { passive: true });
onScroll();
return () => window.removeEventListener('scroll', onScroll);
}, []);
let body = null;
if (route.page === 'home') body = ;
else if (route.page === 'projects') body = ;
else if (route.page === 'case') body = ;
else if (route.page === 'sectors') body = ;
else if (route.page === 'sector') body = ;
else if (route.page === 'about') body = ;
else if (route.page === 'awards') body = ;
else if (route.page === 'contact') body = ;
else body = ;
return (
{body}
);
}
ReactDOM.createRoot(document.getElementById('root')).render( );