Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Ozcaar/real-estate-template/llms.txt

Use this file to discover all available pages before exploring further.

The template’s theme system is built around a single principle: every visual decision that could vary between agencies is expressed as a named design token, never as a hardcoded Tailwind class inside a component. A ThemeConfig TypeScript object is the single source of truth. At startup, the theme.ts Nuxt plugin calls themeToCssVars() to serialize the entire object into a :root { … } CSS block and inject it into the page <head> — both during SSR and on the client. Components then consume tokens exclusively through CSS custom properties (var(--color-primary), var(--radius-md), etc.), which means changing the entire visual identity of the site requires editing one TypeScript file with zero component modifications.

How the pipeline works

AgencyConfig.theme (id string)


resolveTheme(id)          ← app/themes/index.ts


ThemeConfig object        ← app/themes/default.theme.ts  (or your theme)


themeToCssVars(theme)     ← app/core/utils/theme-to-css-vars.ts


:root { --color-primary: #0F766E; … }   (injected via useHead in theme.ts plugin)


Components use var(--color-primary) via Tailwind arbitrary values
The theme.ts plugin runs on both server and client, so the correct branding is embedded in the SSR response with no flash of unstyled content:
app/plugins/theme.ts
export default defineNuxtPlugin(() => {
  const site = useSiteConfig()

  useHead({
    htmlAttrs: {
      'data-theme': site.value.theme.id,
    },
    link: [
      { rel: 'icon', href: site.value.agency.favicon || '/favicon.ico' },
    ],
    style: [
      {
        id: 'agency-theme',
        innerHTML: themeToCssVars(site.value.theme),
      },
    ],
  })
})

The default theme

The template ships with a clean, neutral default theme. These are the exact values from app/themes/default.theme.ts:
app/themes/default.theme.ts
import type { ThemeConfig } from '~/types/theme.types'

export const defaultTheme: ThemeConfig = {
  id: 'default',
  name: 'Default Theme',
  colors: {
    background: '#FFFFFF',
    foreground: '#111827',
    surface: '#FFFFFF',
    surfaceMuted: '#F5F5F4',
    primary: '#0F766E',
    primaryForeground: '#FFFFFF',
    secondary: '#F5F5F4',
    secondaryForeground: '#111827',
    accent: '#D97706',
    accentForeground: '#FFFFFF',
    muted: '#6B7280',
    border: '#E5E7EB',
    card: '#FFFFFF',
    cardForeground: '#111827',
    success: '#16A34A',
    warning: '#D97706',
    error: '#DC2626',
  },
  fonts: {
    heading: 'Inter, system-ui, sans-serif',
    body: 'Inter, system-ui, sans-serif',
    serif: 'Georgia, "Times New Roman", serif',
  },
  radius: {
    sm: '0.375rem',
    md: '0.75rem',
    lg: '1rem',
    xl: '1.5rem',
    full: '9999px',
  },
  shadow: {
    sm: '0 1px 2px rgb(0 0 0 / 0.08)',
    md: '0 8px 24px rgb(0 0 0 / 0.10)',
    lg: '0 16px 48px rgb(0 0 0 / 0.14)',
  },
  layout: {
    containerMaxWidth: '1280px',
    sectionSpacing: '5rem',
  },
}

CSS variable reference

themeToCssVars() maps every token in the ThemeConfig to a CSS custom property on :root. The table below shows every variable name and its corresponding TypeScript field.

Colors

CSS variableTypeScript fieldDefault value
--color-backgroundcolors.background#FFFFFF
--color-foregroundcolors.foreground#111827
--color-surfacecolors.surface#FFFFFF
--color-surface-mutedcolors.surfaceMuted#F5F5F4
--color-primarycolors.primary#0F766E
--color-primary-foregroundcolors.primaryForeground#FFFFFF
--color-secondarycolors.secondary#F5F5F4
--color-secondary-foregroundcolors.secondaryForeground#111827
--color-accentcolors.accent#D97706
--color-accent-foregroundcolors.accentForeground#FFFFFF
--color-mutedcolors.muted#6B7280
--color-bordercolors.border#E5E7EB
--color-cardcolors.card#FFFFFF
--color-card-foregroundcolors.cardForeground#111827
--color-successcolors.success#16A34A
--color-warningcolors.warning#D97706
--color-errorcolors.error#DC2626

Typography

CSS variableTypeScript fieldDefault value
--font-headingfonts.headingInter, system-ui, sans-serif
--font-bodyfonts.bodyInter, system-ui, sans-serif
--font-seriffonts.serifGeorgia, "Times New Roman", serif

Radius

CSS variableTypeScript fieldDefault value
--radius-smradius.sm0.375rem
--radius-mdradius.md0.75rem
--radius-lgradius.lg1rem
--radius-xlradius.xl1.5rem
--radius-fullradius.full9999px

Shadows

CSS variableTypeScript fieldDefault value
--shadow-smshadow.sm0 1px 2px rgb(0 0 0 / 0.08)
--shadow-mdshadow.md0 8px 24px rgb(0 0 0 / 0.10)
--shadow-lgshadow.lg0 16px 48px rgb(0 0 0 / 0.14)

Layout

CSS variableTypeScript fieldDefault value
--container-max-widthlayout.containerMaxWidth1280px
--section-spacinglayout.sectionSpacing5rem

Using tokens in components

Reference design tokens in Vue components using Tailwind CSS’s arbitrary value syntax. This keeps all visual decisions in the theme object and ensures that changing a color token updates every component that references it.
<!-- ✅ Correct — consumes the theme token -->
<button class="bg-[var(--color-primary)] text-[var(--color-primary-foreground)] rounded-[var(--radius-md)]">
  {{ $t('common.contact') }}
</button>

<!-- ❌ Incorrect — hardcodes a Tailwind palette class that bypasses the theme -->
<button class="bg-teal-700 text-white rounded-xl">
  Contact
</button>
Hardcoded Tailwind palette classes (bg-teal-700, text-gray-900, rounded-xl, etc.) are fine for layout, spacing, grid, and responsiveness. They become a problem only when used for brand colors, font families, border radii, or shadows — those must always come from CSS variables.
You can also use CSS variables directly in a <style> block:
<style scoped>
.hero-section {
  background-color: var(--color-surface-muted);
  font-family: var(--font-heading);
  max-width: var(--container-max-width);
  padding-top: var(--section-spacing);
}
</style>

Creating a new theme

To create a theme for a luxury agency with dark backgrounds and gold accents, follow these three steps:
1

Create the theme file

Create app/themes/luxury.theme.ts exporting a ThemeConfig object. You only need to define the tokens you want to differ from the default — but all fields are required by the TypeScript interface.
app/themes/luxury.theme.ts
import type { ThemeConfig } from '~/types/theme.types'

export const luxuryTheme: ThemeConfig = {
  id: 'luxury',
  name: 'Luxury Theme',
  colors: {
    background: '#0F0F0F',
    foreground: '#F5F0E8',
    surface: '#1A1A1A',
    surfaceMuted: '#242424',
    primary: '#C9A84C',
    primaryForeground: '#0F0F0F',
    secondary: '#242424',
    secondaryForeground: '#F5F0E8',
    accent: '#E8C87A',
    accentForeground: '#0F0F0F',
    muted: '#888888',
    border: '#333333',
    card: '#1A1A1A',
    cardForeground: '#F5F0E8',
    success: '#4CAF7D',
    warning: '#C9A84C',
    error: '#E05252',
  },
  fonts: {
    heading: '"Playfair Display", Georgia, serif',
    body: '"Lato", system-ui, sans-serif',
    serif: '"Playfair Display", Georgia, serif',
  },
  radius: {
    sm: '0.125rem',
    md: '0.25rem',
    lg: '0.5rem',
    xl: '0.75rem',
    full: '9999px',
  },
  shadow: {
    sm: '0 1px 3px rgb(0 0 0 / 0.4)',
    md: '0 8px 32px rgb(0 0 0 / 0.5)',
    lg: '0 20px 64px rgb(0 0 0 / 0.6)',
  },
  layout: {
    containerMaxWidth: '1200px',
    sectionSpacing: '6rem',
  },
}
2

Register the theme in the index

Add the new theme to the registry in app/themes/index.ts:
app/themes/index.ts
import type { ThemeConfig } from '~/types/theme.types'
import { defaultTheme } from './default.theme'
import { luxuryTheme } from './luxury.theme'

export const themes: Record<string, ThemeConfig> = {
  [defaultTheme.id]: defaultTheme,
  [luxuryTheme.id]: luxuryTheme,
}

export function resolveTheme(id: string): ThemeConfig {
  return themes[id] ?? defaultTheme
}
3

Set the theme in the agency config

Update the theme field in the agency config to reference the new ID:
app/config/agencies/default.agency.ts
export const defaultAgencyConfig: AgencyConfig = {
  id: 'default',
  name: 'Prestige Properties',
  theme: 'luxury',   // ← was 'default'
  // … rest of config
}

resolveTheme() fallback behavior

If the theme field in the agency config contains an ID that is not registered in the themes registry, resolveTheme() falls back to defaultTheme rather than crashing:
app/themes/index.ts
export function resolveTheme(id: string): ThemeConfig {
  return themes[id] ?? defaultTheme
}
While resolveTheme() prevents a runtime crash, the validateAgencyConfig() call in site.config.ts will still throw a ZodError if theme is not a registered key, because cross-config coherence is checked at module load. The fallback is a safety net for edge cases such as dynamic theme IDs loaded from environment variables — not a substitute for registering the theme properly.
If you just want to change a few colors for a single-agency deployment, you can edit app/themes/default.theme.ts directly instead of creating a new theme file. For example, to change the primary color from teal to indigo:
primary: '#4F46E5',
primaryForeground: '#FFFFFF',
This is the simplest path for minor rebranding. Create a new named theme only when you need to switch between multiple distinct visual identities (e.g. a multi-agency white-label deployment).

Build docs developers (and LLMs) love