Skip to content

SyRF Theming Guide

Comprehensive reference for the SyRF frontend theme system, design tokens, and styling standards.

Overview

SyRF uses Angular Material 21 with a custom Material Design 2 (M2) theme. The theme system consists of:

Layer Purpose Files
Color Palette Raw color definitions in Material hue format syrf-palette.scss
Design Tokens Semantic variables for spacing, typography, elevation, z-index _design-tokens.scss
Theme Config Material theme objects (light + dark) syrf-theme.scss
Component Themes Per-component color/typography mixins *-theme.scss files
Global Styles Overrides, utilities, legacy Bootstrap styles.scss

File Structure

src/global-styles/
  _design-tokens.scss        # Centralized design tokens (NEW)
  syrf-palette.scss          # SyRF primary + secondary Material palettes
  syrf-theme.scss            # Theme creation, component theme aggregation
  styles.scss                # Global overrides, utilities, MDC patches
  legacy-bootstrap/          # Bootstrap 3 CSS subset (deprecated, pending removal)
    buttons.scss
    panel.scss
    progress-bars.scss

src/app/
  **/[component]-theme.scss  # Component theme mixins (11 files)

Color System

Brand Palette

The SyRF brand uses a dark navy primary and cool gray secondary:

Role Hue Value Usage
Primary 500 Default #203457 Headers, nav, primary buttons
Primary 300 Lighter #637189 Hover states, accents
Primary 700 Darker #182746 Active states
Primary A100 Accent light #698dff Links
Secondary 500 Default #c6cad2 Borders, dividers
Secondary A200 Accent #2e4a7d Secondary actions
Warn 500 Default #f44336 Material red

Semantic Colors

Defined in _design-tokens.scss for consistent use across the app:

Token Value Usage
$color-success #4caf50 Success states, confirmations
$color-warning #ff9800 Warnings, staging indicators
$color-error #f44336 Errors, validation failures
$color-info #2196f3 Informational messages
$color-text-primary rgba(0,0,0,0.87) Primary text
$color-text-secondary rgba(0,0,0,0.6) Secondary/help text
$color-text-disabled rgba(0,0,0,0.38) Disabled text

Light and Dark Themes

Both themes are defined in syrf-theme.scss:

// Light (default)
$syrf-theme: mat.m2-define-light-theme((
  color: (primary: $syrf-app-primary, accent: $syrf-app-accent, warn: $syrf-app-warn)
));

// Dark (activated by .global-dark-theme class on root element)
$syrf-dark-theme: mat.m2-define-dark-theme((
  color: (primary: $syrf-app-primary, accent: $syrf-app-accent, warn: $syrf-app-warn)
));

Dark theme is applied via CSS class toggle:

<app-root [class.global-dark-theme]="isDarkMode">

Component Theme Pattern

All component themes follow the standard Angular Material mixin pattern:

@use 'sass:map';
@use '@angular/material' as mat;

@mixin color($config-or-theme) {
  $config: mat.m2-get-color-config($config-or-theme);
  $primary: map.get($config, primary);
  $accent: map.get($config, accent);
  $warn: map.get($config, warn);
  $background: map.get($config, background);
  $foreground: map.get($config, foreground);

  .my-element {
    color: mat.m2-get-color-from-palette($primary);
    background-color: mat.m2-get-color-from-palette($background, card);
  }
}

@mixin typography($config-or-theme) {
  // Typography overrides (optional)
}

@mixin density($config-or-theme) {
  // Density overrides (optional)
}

@mixin theme($theme-or-color-config) {
  $color: if(
    map.has-key($theme-or-color-config, color),
    mat.m2-get-color-config($theme-or-color-config),
    $theme-or-color-config
  );
  @if $color != null { @include color($color); }
}

Registration: Each component theme is imported and included in syrf-theme.scss:

@use '../app/my-component/my-component-theme' as my-component;

@include mat.all-component-themes($syrf-theme);
@include my-component.theme($syrf-theme);

.global-dark-theme {
  @include mat.all-component-themes($syrf-dark-theme);
  @include my-component.theme($syrf-dark-theme);
}

Existing Component Themes

Component File Status
Sidenav core/syrf-material/sidenav/_sidenav-theme.scss Good - proper theme usage
Banner info/banner/banner.component.theme.scss Good - includes typography mixin
Home info/home/home.component.theme.scss Good
Information Box shared/information-box/_information-box.component-theme.scss Good - handles dark theme
Membership Table project/project-admin/project-members/membership-table/_membership-table-theme.scss Stub - no active styles
Chips Email Input shared/chips-emails-input/_chips-email-input-theme.scss Needs fix - uses !important
Project Nav project/project-nav/_project-nav.component-theme.scss Needs fix - hardcoded colors
Incomplete Studies stage/stage-review/incomplete-studies/_incomplete-studies.component-theme.scss Good
Edit (Questions) project/project-admin/question-management/edit/_edit.component-theme.scss Good
Chip Label shared/chip-label/_chip-label.component-theme.scss Good

Design Tokens

Import from _design-tokens.scss:

@use 'src/global-styles/design-tokens' as tokens;

Spacing (4px grid)

Token Value Usage
$spacing-1 4px Tight spacing (icons, inline elements)
$spacing-2 8px Default component internal padding
$spacing-3 12px Moderate padding
$spacing-4 16px Standard padding (cards, sections)
$spacing-6 24px Section margins
$spacing-8 32px Large section gaps
$spacing-12 48px Page-level sections

Z-Index Scale

Token Value Usage
$z-index-base 0 Default
$z-index-dropdown 100 Dropdown menus
$z-index-sticky 200 Sticky headers
$z-index-nav 300 Navigation bar
$z-index-overlay 400 Overlays
$z-index-modal-backdrop 500 Modal backdrops
$z-index-modal 600 Modals/dialogs
$z-index-tooltip 800 Tooltips
$z-index-cover 1000 Full-page loading overlay
$z-index-impersonation 1040 Admin impersonation banner

Border Radius

Token Value Usage
$radius-xs 2px Subtle rounding
$radius-sm 4px Buttons, inputs (Material default)
$radius-md 8px Cards, dialogs
$radius-lg 16px Chips, pills
$radius-full 50% Avatars, circular badges

Typography Scale

Based on Material Design 2 type scale with Roboto:

Token Size Usage
$font-size-headline-5 24px Page titles
$font-size-headline-6 20px Section headers
$font-size-subtitle-1 16px Subtitles, form labels
$font-size-body-1 16px Primary body text
$font-size-body-2 14px Secondary body text
$font-size-caption 12px Captions, help text

Styling Rules

Do

  • Use theme mixins for colors that need to adapt to light/dark mode
  • Use design tokens for static semantic values (spacing, z-index, radii)
  • Use mat.m2-get-color-from-palette() inside theme mixins
  • Use CSS custom properties for values that change at runtime
  • Use mat.elevation() for shadows on Material components
  • Keep component styles scoped via Angular's ViewEncapsulation

Don't

  • Never hardcode hex colors in component SCSS - use palette functions or design tokens
  • Never use inline styles in HTML templates - move to component SCSS
  • Never use !important - fix specificity with better selectors
  • Never add arbitrary z-index values - use the z-index scale tokens
  • Never use Bootstrap classes for new development - use Angular Material
  • Never target internal Material class names (e.g., .mat-form-field-wrapper) - these change between versions

Legacy Bootstrap

Three Bootstrap 3 CSS files are included for backward compatibility:

File Classes Used Replacement
buttons.scss .btn-*, .btn-group mat-button, mat-raised-button
panel.scss .panel-* mat-card, mat-expansion-panel
progress-bars.scss .progress-bar-* mat-progress-bar

These are deprecated and should not be used in new components. They remain only for existing components that have not yet been migrated.

Known Issues (Technical Debt)

Hardcoded Colors (~80 instances)

Components with hardcoded colors that should use theme palette or design tokens:

  • app.component.scss - banner colors, link hover colors
  • project-nav.component-theme.scss - #ffffff, #dddddd, #f2f2f2, #ff6565
  • about.component.scss - multiple rgba and brand colors
  • error-recovery-dialog.component.scss - 9+ distinct hardcoded colors
  • study-table.component.scss - table row colors
  • design.component.scss - tree branch line colors

MDC Migration TODOs (~20 instances)

Selectors targeting pre-MDC internal classes like .mat-form-field-wrapper, .mat-form-field-infix. These need updating to target current MDC class names or use official component APIs.

!important Declarations (~42 instances)

Concentrated in question-management components. These indicate CSS specificity issues that should be resolved with proper selector structure.

Inline Styles (~61 instances)

HTML style="..." attributes that should be moved to component SCSS. Worst offenders are in review data upload, question management, and project overview components.