<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/online-museum-framework-lxippeeb7" -->

---
title: Online Museum Framework | daily.dev
description: Q42 Engineering built the Online Museum Framework, a white-label platform that powers visually distinct museum websites from a single Next.js codebase. The...
canonical: https://daily.dev/posts/online-museum-framework-lxippeeb7
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: Online Museum Framework | daily.dev
og:description: Q42 Engineering built the Online Museum Framework, a white-label platform that powers visually distinct museum websites from a single Next.js codebase. The...
og:url: https://daily.dev/posts/online-museum-framework-lxippeeb7
og:image: https://api.daily.dev/og/posts/lxipPeeb7.png
og:image:alt: Online Museum Framework
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Online Museum Framework

**[Q42 Engineering](https://daily.dev/sources/q42)** · 16 min read · 9 upvotes · 0 comments

## Summary

Q42 Engineering built the Online Museum Framework, a white-label platform that powers visually distinct museum websites from a single Next.js codebase. The core innovation is a three-level theming system — global tokens, component-level theming with a layout system, and contextual theme overrides — all defined in TypeScript configs rather than custom CSS. Designers work within a Figma design system, and new museum sites can be deployed with zero CSS code by mapping designs to config options. The stack uses Next.js with SSG, Sanity CMS (per-museum deployments via Vercel), and Tailwind CSS with a custom whitelist generator to handle dynamic class generation. Key challenges included hitting TypeScript depth limits with Zod schema validation (resolved by switching to plain TypeScript types at compile time) and Tailwind's inability to detect dynamically concatenated class names. The platform launched with Frans Hals Museum and Teylers Museum, demonstrating that two visually unique sites can share one codebase.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://engineering.q42.nl/online-museum-framework>

## Questions this post answers

### Why did TypeScript type errors show up when using deeply nested Zod schemas?

Deeply nested Zod schema objects can generate TypeScript types so complex that they hit TypeScript's internal type-depth limits, producing excessive depth errors. This happened in a multi-tenant theming system where theme configurations were deeply nested. Zod version 4 significantly reduces this problem because the library became much more lightweight and efficient, though switching to plain TypeScript types with compile-time validation is another workaround.

_Anyone hitting TypeScript depth limits with schema validation can track fixes like this on daily.dev._

### How can I make Tailwind CSS include dynamically generated class names that are built via string concatenation?

Tailwind's compiler only bundles classes it can statically detect, so classnames built by concatenating strings at runtime based on configuration data get dropped from the build. The fix is writing a custom whitelist generator that, given a theme configuration, enumerates every possible Tailwind class that configuration could need and adds that list to Tailwind's safelist, forcing inclusion even though it increases bundle size with unused classes.

_Developers wrestling with dynamic Tailwind class generation can find practical fixes like this on daily.dev._

## Similar posts on daily.dev

- [My Frontend Stack In 2026](https://daily.dev/posts/my-frontend-stack-in-2026-waf9zkadc) · The T-Shaped Dev · 99 upvotes · 7 comments
- [Podium: Building a Website Where Running Becomes Storytelling](https://daily.dev/posts/podium-building-a-website-where-running-becomes-storytelling-axfh2vyo0) · Codrops · 18 upvotes · 1 comments
- [Frontend Design Patterns That Actually Work in 2026](https://daily.dev/posts/frontend-design-patterns-that-actually-work-in-2026-rk8av9x3v) · Netguru · 79 upvotes · 1 comments
- [Next.js but make it Angular](https://daily.dev/posts/next-js-but-make-it-angular-mfmn1zsv8) · Bytes by ui.dev · 10 upvotes · 0 comments

---

Tags: [#nextjs](https://daily.dev/tags/nextjs), [#tailwind-css](https://daily.dev/tags/tailwind-css)

[View this post on daily.dev](https://daily.dev/posts/online-museum-framework-lxippeeb7)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"Online Museum Framework","url":"https://daily.dev/posts/online-museum-framework-lxippeeb7","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/online-museum-framework-lxippeeb7"},"datePublished":"2026-07-20T15:19:35.175Z","dateModified":"2026-09-14T07:15:14.968Z","description":"Q42 Engineering built the Online Museum Framework, a white-label platform that powers visually distinct museum websites from a single Next.js codebase. The...","image":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/d1619b38671c0f3d15f112a3b60225ea?_a=AQAEuop","thumbnailUrl":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/d1619b38671c0f3d15f112a3b60225ea?_a=AQAEuop","isAccessibleForFree":true,"articleSection":"Q42 Engineering","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Organization","name":"Q42 Engineering","logo":"https://media.daily.dev/image/upload/t_logo,f_auto/v1/logos/44e9a37f672e4e34ba9dc82110de572c","url":"https://daily.dev/sources/q42"},"commentCount":0,"discussionUrl":"https://daily.dev/posts/online-museum-framework-lxippeeb7","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":9},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":0}],"keywords":"nextjs,tailwind-css","timeRequired":"PT16M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"Q42 Engineering","item":"https://daily.dev/sources/q42"},{"@type":"ListItem","position":3,"name":"Online Museum Framework"}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/online-museum-framework-lxippeeb7#faq","mainEntity":[{"@type":"Question","name":"Why did TypeScript type errors show up when using deeply nested Zod schemas?","acceptedAnswer":{"@type":"Answer","text":"Deeply nested Zod schema objects can generate TypeScript types so complex that they hit TypeScript's internal type-depth limits, producing excessive depth errors. This happened in a multi-tenant theming system where theme configurations were deeply nested. Zod version 4 significantly reduces this problem because the library became much more lightweight and efficient, though switching to plain TypeScript types with compile-time validation is another workaround. Anyone hitting TypeScript depth limits with schema validation can track fixes like this on daily.dev."}},{"@type":"Question","name":"How can I make Tailwind CSS include dynamically generated class names that are built via string concatenation?","acceptedAnswer":{"@type":"Answer","text":"Tailwind's compiler only bundles classes it can statically detect, so classnames built by concatenating strings at runtime based on configuration data get dropped from the build. The fix is writing a custom whitelist generator that, given a theme configuration, enumerates every possible Tailwind class that configuration could need and adds that list to Tailwind's safelist, forcing inclusion even though it increases bundle size with unused classes. Developers wrestling with dynamic Tailwind class generation can find practical fixes like this on daily.dev."}}]}
```

