Prompt Template 15: Multi-Theme Override
Template for handling theme-specific visual overrides when a component needs to look different for specific hotels.
1. Context Loading
Section titled “1. Context Loading”Read these files before starting:
docs/dev-frontend-guides/05_BEM_SASS_THEMING.md (BEM + SASS theming guide)docs/prd/05_DESIGN_SYSTEM.md (design system specification)packages/themes/src/_base.css (base CSS custom properties)packages/themes/src/{AFFECTED_THEME_FILES} (theme files needing overrides)packages/ui/src/modules/{COMPONENT_NAME}/{COMPONENT_NAME}.module.scss2. Prompt Template
Section titled “2. Prompt Template”I need to implement theme-specific visual overrides for the {COMPONENT_NAME}module ({MODULE_ALIAS}).
## Component Overview
{COMPONENT_DESCRIPTION}
## Which Hotels Need Overrides
| Hotel (siteKey) | What Differs ||---------------------|----------------------------------------------------|| {SITE_KEY_1} | {DIFFERENCE_DESCRIPTION_1} || {SITE_KEY_2} | {DIFFERENCE_DESCRIPTION_2} || All others | Default appearance (no override needed) |
## Figma References
- Default design: {FIGMA_LINK_DEFAULT}- {SITE_KEY_1} variant: {FIGMA_LINK_VARIANT_1}- {SITE_KEY_2} variant: {FIGMA_LINK_VARIANT_2}
## Override Classification
For each hotel override, classify the type:
### {SITE_KEY_1}: {TOKEN_LEVEL / SCSS_MODIFIER / COMPONENT_VARIANT}
{DETAILED_DESCRIPTION_OF_DIFFERENCES}
Differences:- Layout: {SAME / DIFFERENT — describe}- Spacing: {SAME / DIFFERENT — describe}- Colors: {SAME / DIFFERENT — describe}- Typography: {SAME / DIFFERENT — describe}- Shadows/Borders: {SAME / DIFFERENT — describe}- Structure (HTML): {SAME / DIFFERENT — describe}
### {SITE_KEY_2}: {TOKEN_LEVEL / SCSS_MODIFIER / COMPONENT_VARIANT}
{DETAILED_DESCRIPTION_OF_DIFFERENCES}
## Strategy Hierarchy
Apply the LEAST invasive approach:
1. **Token-level override (preferred)** — Change CSS custom property values in the hotel's theme file. No component SCSS changes needed. Use when only colors, spacing values, or font sizes differ.
2. **SCSS theme modifier** — Use `[data-theme="{site-key}"] .component` or `.component--theme-{site-key}` selector in the component SCSS. Use when the CSS rules differ (e.g., different background type, border vs no border) but the HTML structure is the same.
3. **Component prop variant** — Pass a `variant` prop that changes the rendered HTML structure. Use only when the structural difference is significant (different number of elements, different layout grid).
## Requirements
- Use the least invasive override approach- No hardcoded color/spacing values in component SCSS (always use CSS custom properties)- All 8 hotel themes must still render correctly after changes- Each theme variant has a dedicated Storybook story- Colour contrast passes WCAG AA in all variants- Override is documented in the component's story file3. Acceptance Criteria
Section titled “3. Acceptance Criteria”- Override uses the least invasive approach from the strategy hierarchy
- No hardcoded colour, spacing, or font values in component SCSS
- All values reference CSS custom properties (e.g.,
var(--color-primary)) - All 8 hotel themes render correctly (not just the overridden ones)
- Token-level overrides are placed in the correct theme file under
[data-theme="{site-key}"] - SCSS modifiers (if used) follow BEM convention or
[data-theme]attribute selector - Component prop variants (if used) are typed in the props interface
- Dedicated Storybook story for each theme variant
- Colour contrast meets WCAG AA (4.5:1 for text, 3:1 for large text) in all variants
- No visual regression in unaffected themes (test by switching
data-themeattribute) - Override approach documented in a comment in the SCSS file
4. Common Pitfalls
Section titled “4. Common Pitfalls”-
Hardcoding hotel colours in component SCSS. Never write
color: #8B6F47in component styles. Always use CSS custom properties likevar(--color-primary). The theme file sets the property value. -
Using JavaScript to detect theme. Theme detection belongs in CSS via
[data-theme]selectors. Do not readsiteKeyin a component to conditionally apply classes for visual styling. -
Creating separate components per hotel. Do not create
M15CTABannerSavoy.tsxandM15CTABannerRoyal.tsx. Use variants, modifiers, or token overrides on a single component. -
Forgetting to test OTHER themes after adding an override. Adding
[data-theme="savoy-palace"]rules can inadvertently affect specificity for other themes. Always verify all 8 themes still render correctly. -
Over-qualifying selectors.
[data-theme="savoy-palace"] .m15-cta-banneris sufficient. Do not add unnecessary parent selectors that increase specificity beyond what is needed. -
Mixing override strategies. Pick one strategy per override. Do not combine token-level changes with SCSS modifiers for the same property on the same hotel — it creates confusion about which layer controls the value.
-
Forgetting dark/light mode interaction. Some themes may have light and dark variants. Ensure overrides work in both if applicable.
-
Not checking colour contrast after override. A colour that passes WCAG on a white background may fail on the hotel’s specific background colour. Always verify contrast in context.
5. Example
Section titled “5. Example”M15 CTA Banner — Multi-Theme Override
Section titled “M15 CTA Banner — Multi-Theme Override”Component description: A call-to-action banner with heading, body text, CTA button, and background treatment. Appears on landing pages.
Hotels needing overrides:
| Hotel (siteKey) | What Differs |
|---|---|
| savoy-palace | Full-width gradient background (gold to transparent) |
| royal-savoy | Bordered card style with shadow, not full-width |
| All others | Default solid background using --color-surface-accent |
Override classification:
- savoy-palace: SCSS modifier (different background type — gradient vs solid)
- royal-savoy: SCSS modifier (border + shadow + constrained width)
Implementation:
Step 1: Verify base component uses tokens only
Section titled “Step 1: Verify base component uses tokens only”.m15-cta-banner { padding: var(--spacing-xl) var(--spacing-lg); background-color: var(--color-surface-accent); text-align: center;
&__heading { font-family: var(--font-heading); font-size: var(--font-size-2xl); color: var(--color-text-primary); margin-bottom: var(--spacing-sm); }
&__body { font-family: var(--font-body); font-size: var(--font-size-md); color: var(--color-text-secondary); margin-bottom: var(--spacing-lg); max-width: 60ch; margin-inline: auto; }
&__cta { // Uses shared button component, no styles here }}Step 2: Add theme-specific SCSS modifiers
Section titled “Step 2: Add theme-specific SCSS modifiers”// Theme overrides — Savoy Palace// Uses gradient background instead of solid colour[data-theme="savoy-palace"] { .m15-cta-banner { background: linear-gradient( 135deg, var(--color-accent-gold) 0%, transparent 60% ), var(--color-surface-accent); }}
// Theme overrides — Royal Savoy// Uses bordered card style with constrained width[data-theme="royal-savoy"] { .m15-cta-banner { background-color: var(--color-surface-primary); border: 1px solid var(--color-border-subtle); border-radius: var(--radius-lg); box-shadow: var(--shadow-md); max-width: 800px; margin-inline: auto; }}Step 3: Add Storybook stories for each variant
Section titled “Step 3: Add Storybook stories for each variant”export const Default: Story = { decorators: [withTheme('default')],};
export const SavoyPalace: Story = { decorators: [withTheme('savoy-palace')], parameters: { docs: { description: { story: 'Savoy Palace uses a gold gradient background.', }, }, },};
export const RoyalSavoy: Story = { decorators: [withTheme('royal-savoy')], parameters: { docs: { description: { story: 'Royal Savoy uses a bordered card style with shadow.', }, }, },};
// Verify all other themes still look correctexport const AllThemes: Story = { decorators: [withAllThemes()], parameters: { docs: { description: { story: 'All 8 hotel themes side by side for visual regression check.', }, }, },};Key decisions in this example:
- Both overrides are SCSS modifiers (not token-level) because the background type changes, not just the colour value
[data-theme]attribute selector used instead of BEM modifier — the component does not need to know which theme is active- No JavaScript involved — pure CSS handles the visual differentiation
- Gradient in Savoy Palace still references
--color-accent-goldtoken, not a hardcoded hex value - Royal Savoy border uses
--color-border-subtleand--shadow-mdtokens AllThemesstory enables quick visual regression checking across all hotels- No structural HTML changes needed, so component prop variant was not used