Skip to content

LMS course project theme

CMS authors configure a project theme per course (preset + colour/typography overrides). The LMS applies that theme on all course routes so the course sidebar, landing page, and entry content share the same branding.

Data flow

  1. CMS stores projects.theme_settings and resolves CSS via generateThemeCss (see CMS theme system in apps/cms/docs/reference/theme-system.md).
  2. LMS calls GET /projects/{id}/page-hierarchy?expand=projectTheme once per getCourseBySlug.
  3. The API returns projectThemeCss with LMS-safe scoping: colours under :root:not(.dark) [data-course-project-theme], and typography/custom CSS on [data-course-project-theme] (including mirrored --sidebar* tokens for the shadcn course sidebar).
  4. CourseProjectTheme injects the style block and wraps course UI in data-course-project-theme.
  5. Entry lesson/quiz/assignment bodies use CmsAuthoredPagePreviewBody with includeProjectThemeStyle={false} so theme is not duplicated.

Routes

Route Wrapper
/learn/course/\[slug\] (landing) CourseProjectTheme on the page
/learn/course/\[slug\]/entry/* CourseProjectTheme in the entry layout (sidebar + main)

Site chrome (header, footer, light/dark theme toggle, and font-size toggle) is outside the scoped wrapper and keeps tenant-level styling.

Site project theme

The LMS site project (CMS_PROJECT_UUID) also exposes projectThemeCss through GET /projects/{id}/pages/{pageId}?expand=projectTheme.

  • getSiteProjectTheme resolves the site hierarchy, picks home (fallback first top-level page), and reads projectThemeCss.
  • SiteProjectTheme injects that CSS once in src/app/[locale]/layout.tsx, before site chrome.
  • Public and learner routes now share one app-wide site theme source (including /learn), while course-specific branding remains isolated to [data-course-project-theme].
  • LMS dark-mode compatibility is intentional: site colours are scoped to :root:not(.dark), so dark mode falls back to shared design-token dark values while CMS typography and custom CSS still apply in both modes.

User font-size preference: learners adjust reading scale from the site header font-size menu. The LMS applies a multiplier at the <html> root on top of --font-size-base when the site theme defines one; Medium equals that CMS base (or the browser default). Course-scoped [data-course-project-theme] branding is unchanged — only the learner's reading scale changes. See Learner Experience — Site header preferences.

  • entry-pagination.mdcourse-sidebar test IDs and shell layout
  • CMS theme system (apps/cms/docs/reference/theme-system.md) — token mirroring and API expand