"""Centralized handbook visual theme — single source of truth. All colour values, font sizes, spacing, and rendering tokens live here. Templates, CSS generation, and renderers reference this module instead of hardcoding visual rules. Spec source: ISP Handbook Enhancement Guidelines + sample PDF. """ from __future__ import annotations from dataclasses import dataclass, field # ── Colour palette ────────────────────────────────────────────── @dataclass(frozen=True) class Colors: """Every colour used in the handbook, named by purpose.""" heading_blue: str = "#1C75BC" heading_green: str = "#1A9970" body_text: str = "#000000" toc_text: str = "#111111" note_red: str = "#C00000" link_blue: str = "#1C75BC" benefits_header_bg: str = "#00F600" benefits_header_fg: str = "#FFFFFF" benefit_item_bg: str = "#00FCFC" benefit_item_fg: str = "#000000" school_info_green: str = "#1A9970" table_border: str = "#333333" table_header_bg: str = "#E6E6E6" table_header_fg: str = "#333333" toc_dots: str = "#777777" muted: str = "#666666" note_bg: str = "#F7F8FA" note_border: str = "#BBBBBB" page_bg: str = "#FFFFFF" # ── Typography ────────────────────────────────────────────────── @dataclass(frozen=True) class Typography: """Font families, sizes, weights, and line-heights.""" font_family: str = "'Century Gothic', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif" font_size_body: str = "10pt" font_size_h1: str = "12pt" font_size_h2: str = "12pt" font_size_h3: str = "10pt" font_size_toc_heading: str = "12pt" font_size_toc_item: str = "10pt" font_size_table: str = "9.5px" font_size_programs_table: str = "8.5px" font_size_career_list: str = "8.5px" font_size_note: str = "9.5px" font_size_benefits_header: str = "10.5px" font_size_benefit_item: str = "10px" font_size_school_name: str = "12pt" font_size_summary_label: str = "10.5px" font_size_summary_value: str = "9.5px" font_size_qualify: str = "10px" line_height_body: str = "1.4" line_height_heading: str = "1.2" line_height_table: str = "1.25" # ── Spacing / margins ────────────────────────────────────────── @dataclass(frozen=True) class Spacing: """Page geometry and element margins. All margins: 2.54cm.""" page_margin_top: str = "2.54cm" page_margin_right: str = "2.54cm" page_margin_bottom: str = "2.54cm" page_margin_left: str = "2.54cm" paragraph_margin: str = "2px 0 8px" heading_margin_h1: str = "12px 0 6px" heading_margin_h2: str = "10px 0 4px" list_margin: str = "2px 0 8px 18px" note_padding: str = "6px 8px" note_margin: str = "6px 0 8px" table_margin: str = "6px 0 10px" table_cell_padding: str = "5px 6px" benefits_margin: str = "4px 0 4px" school_top_summary_width: str = "58%" school_top_campus_width: str = "42%" # ── Table column widths ──────────────────────────────────────── @dataclass(frozen=True) class ProgramTableColumns: """Fixed widths for the 5-column programs table.""" program: str = "22%" designation: str = "14%" entrance_exam: str = "12%" career_pathways: str = "34%" funding: str = "18%" # ── Bullet characters ────────────────────────────────────────── @dataclass(frozen=True) class Bullets: """Bullet characters used throughout the handbook.""" primary: str = "\u27A2" # ➢ benefit: str = "\u2022" # • career: str = "disc" # CSS list-style-type for career lists # ── Render-block type registry ────────────────────────────────── BLOCK_TYPES = ( "heading_1", "heading_2", "paragraph", "bullet_list", "note", "table", "enrollment_steps", "school_profile", "university_summary", "toc", "cover", "full_page_image", ) # ── Composed theme object ────────────────────────────────────── @dataclass(frozen=True) class HandbookTheme: """Complete handbook theme — inject into renderers and templates.""" colors: Colors = field(default_factory=Colors) typography: Typography = field(default_factory=Typography) spacing: Spacing = field(default_factory=Spacing) program_columns: ProgramTableColumns = field(default_factory=ProgramTableColumns) bullets: Bullets = field(default_factory=Bullets) def css_vars(self) -> dict[str, str]: """Flatten theme to CSS custom properties (--hb-*).""" v: dict[str, str] = {} # Colors for fname in Colors.__dataclass_fields__: v[f"--hb-{fname.replace('_', '-')}"] = getattr(self.colors, fname) # Typography v["--hb-font-family"] = self.typography.font_family v["--hb-font-size-body"] = self.typography.font_size_body v["--hb-font-size-h1"] = self.typography.font_size_h1 v["--hb-font-size-h2"] = self.typography.font_size_h2 v["--hb-line-height-body"] = self.typography.line_height_body # Spacing v["--hb-page-margin-top"] = self.spacing.page_margin_top v["--hb-page-margin-right"] = self.spacing.page_margin_right v["--hb-page-margin-bottom"] = self.spacing.page_margin_bottom v["--hb-page-margin-left"] = self.spacing.page_margin_left # Bullet v["--hb-bullet-char"] = f'"{self.bullets.primary}"' return v # Module-level singleton THEME = HandbookTheme()