Colour Schemes
Define named visual schemes with Tailwind semantic tokens.
Use colour schemes when a section, card, page, or route needs to switch the
same component between named visual themes. The Tailwind helper implements this
with subthemes: a scheme class changes semantic CSS variables, while
component code keeps using bg-bg, text-fg, border-bg-low, and similar
semantic classes.
Define Schemes
Define the possible scheme classes in tailwind.config.ts.
responsiveTheme({
colors: {
base: {
black: "#202020",
white: "#ffffff",
silver: {
DEFAULT: "#d8d8d8",
light: "#f1f1f1",
dark: "#aaaaaa",
},
orange: "#ea5e42",
blue: "#2f99d4",
green: "#4a9f62",
card: "#dec99c",
yellow: "#f8d44f",
},
semantic: {
bg: "silver.light",
fg: "black",
"bg-low": "silver",
},
subthemes: {
"theme-black": {
bg: "black",
fg: "silver.light",
"bg-low": "silver.dark",
},
"theme-orange": {
bg: "orange",
fg: "black",
"bg-low": "white",
},
"theme-blue": {
bg: "blue",
fg: "black",
"bg-low": "white",
},
"theme-green": {
bg: "green",
fg: "black",
"bg-low": "white",
},
"theme-card": {
bg: "card",
fg: "black",
"bg-low": "white",
},
"theme-yellow": {
bg: "yellow",
fg: "black",
"bg-low": "white",
},
},
},
})The key is the class name. Use a theme-* prefix for scheme classes unless a
project has already established different naming.
Apply A Scheme
Apply the class at the smallest useful boundary. A whole page can set a scheme:
import { pageColorClass } from "@features/brand/usePageColor"
import { cn } from "@utils/tw"
import { defineView } from "eddev/views"
export default defineView("single-case-study", (props) => {
return (
<div className={cn(pageColorClass(props.caseStudy?.info?.pageColor))}>
<SubpageContentBlocks blocks={props.caseStudy?.contentBlocks} />
</div>
)
})A single block or card can set its own scheme:
const SCHEME_CLASSES = {
black: "theme-black",
orange: "theme-orange",
blue: "theme-blue",
} as const
export function Card(props: {
color?: keyof typeof SCHEME_CLASSES
children: React.ReactNode
}) {
return (
<article className={`${SCHEME_CLASSES[props.color ?? "black"]} bg-bg text-fg p-4 rounded-md`}>
{props.children}
</article>
)
}Use generated variants when a component needs to adjust itself based on an ancestor theme:
<div className="bg-orange text-fg is-theme-black:theme-orange">
...
</div>The helper creates is-${themeName}: variants for every configured subtheme.
Let Editors Choose
For editor-controlled schemes, register a small enum field in PHP and select it in the block or view GraphQL file.
<?php
ED()->registerEnumFieldType("page-scheme", [
"label" => "Page Scheme",
"type" => "select",
"allow_null" => 1,
"options" => [
"silver" => "Silver",
"green" => "Green",
"orange" => "Orange",
"blue" => "Blue",
"card" => "Card",
"yellow" => "Yellow",
],
]);query {
block {
pages_highlight_section {
theme
}
}
}Map the generated enum values to classes in one helper:
import { PageSchemeOption, SectionSchemeOption } from "@generated-types"
const SCHEME_CLASSES: Record<PageSchemeOption, string> = {
silver: "theme-silver",
green: "theme-green",
orange: "theme-orange",
blue: "theme-blue",
card: "theme-card",
yellow: "theme-yellow",
}
export function pageColorClass(
color: PageSchemeOption | SectionSchemeOption | null | undefined,
) {
return SCHEME_CLASSES[color || "silver"] || SCHEME_CLASSES.silver
}Keep the enum small. Most projects only need page-level schemes and a shorter section-level set for blocks.
Route-Level Themes
For route-driven themes, derive the current theme from RouteState and apply
the scheme around RouteDisplay.
import { RouteDisplay, RouteState } from "eddev/routing"
export function ThemedRouteDisplay(props: { route: RouteState }) {
const dark = props.route.view === "template-home"
return (
<div className={dark ? "theme-dark bg-bg text-fg" : "theme-light bg-bg text-fg"}>
<RouteDisplay route={props.route} />
</div>
)
}Use route-level themes for global surfaces such as headers, drawers, page transitions, and footers. Use block fields for author-controlled content sections.