Styling Guide
Gymlium templates use CSS Modules for component-scoped styles and a global template.css file for theme-level custom properties. This guide covers everything you need to know about styling your template.
CSS Modules
Every component should have a co-located .module.css file:
Header/
├── Header.tsx
└── Header.module.cssImport and use styles as a typed object:
import styles from './Header.module.css'
export function Header() {
return <header className={styles.header}>...</header>
}CSS Modules automatically scope class names to the component, preventing style collisions between your template and the platform or other templates.
Naming conventions
- Use camelCase for class names:
.heroSection,.navLink,.primaryBtn - Avoid generic names like
.container— while CSS Modules scope them, descriptive names improve readability - Group related styles logically within the file
template.css — Global theme variables
The template.css file at your template’s root defines CSS custom properties that theme the entire site. The platform reads these variables to ensure visual consistency.
Required custom properties
:root {
/* Colours */
--color-accent: #e11d48;
--color-secondary: #9333ea;
/* Typography */
--font-heading: 'Sora', sans-serif;
--font-body: 'Inter', sans-serif;
/* Layout */
--border-radius: 12px;
--header-height: 72px;
}Theme mode variables
Define separate colour schemes for light and dark modes using the [data-theme] attribute selector:
[data-theme="light"] {
--bg-primary: #ffffff;
--bg-secondary: #f8fafc;
--text-primary: #0f172a;
--text-secondary: #64748b;
--border-color: #e2e8f0;
}
[data-theme="dark"] {
--bg-primary: #0f172a;
--bg-secondary: #1e293b;
--text-primary: #f8fafc;
--text-secondary: #94a3b8;
--border-color: #334155;
}You must define both light and dark mode variables. Templates that only support one mode will be rejected during review.
The data-brand attribute
The Gymlium platform sets a data-brand attribute on the <body> element containing the trainer’s chosen brand colour. You can use this to dynamically tint your template:
[data-brand="rose"] {
--color-accent: #e11d48;
}
[data-brand="blue"] {
--color-accent: #2563eb;
}
[data-brand="emerald"] {
--color-accent: #059669;
}This is optional — if you don’t use data-brand, your template uses its own --color-accent value regardless of what the trainer selects. However, supporting data-brand gives trainers more customisation options and makes your template more appealing.
Dark/light mode
The platform toggles theme mode by setting data-theme="light" or data-theme="dark" on the <html> element. Your component styles should reference theme variables rather than hardcoding colours:
/* ✅ Correct — uses theme variable */
.card {
background: var(--bg-secondary);
color: var(--text-primary);
}
/* ❌ Incorrect — hardcoded colours break theme switching */
.card {
background: #1e293b;
color: #f8fafc;
}For styles that need to differ between modes beyond colour variables, use the attribute selector within your CSS Module:
.hero {
background-image: url('/dark-hero-bg.jpg');
}
:global([data-theme="light"]) .hero {
background-image: url('/light-hero-bg.jpg');
}See Dark Mode Support for detailed implementation patterns.
Tailwind utilities
The Gymlium platform includes Tailwind CSS. You can use Tailwind utility classes alongside your CSS Modules:
export function Header() {
return (
<header className={`${styles.header} flex items-center justify-between`}>
...
</header>
)
}Available Tailwind features:
- Standard utility classes (spacing, flexbox, grid, typography, etc.)
- Responsive prefixes:
sm:,md:,lg:,xl: - Dark mode:
dark:prefix (maps to the platform’s theme system) - RTL:
rtl:prefix for RTL-specific overrides
Prefer CSS Modules for component-specific styles and Tailwind for quick layout utilities. This gives you the best of both worlds: scoped styles with the convenience of utility classes.
Global selectors in CSS Modules
When you need to target global attributes (like [dir="rtl"] or [data-theme]) from within a CSS Module, use the :global() wrapper:
.nav {
flex-direction: row;
}
:global([dir="rtl"]) .nav {
flex-direction: row-reverse;
}
:global([data-theme="light"]) .card {
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}Best practices
- Use CSS custom properties for all colours, spacing, and typography that should be theme-aware
- Avoid
!important— it makes styles difficult to override and can break platform styles - Avoid inline styles — they can’t be themed and are harder to maintain
- Keep specificity low — single class selectors are ideal
- Test both themes — always verify your template in both light and dark mode
- Test RTL — ensure your styles work correctly in right-to-left languages