# Theme Configuration Defaults and Helpers # # Provides base theme with full configuration and helpers for creating variants. let contracts = import "contracts.ncl" in { # Base theme with complete default configuration # All theme variants should extend this base_theme = { typography = { font_family_sans = "Inter, ui-sans-serif, system-ui, sans-serif", font_family_mono = "JetBrains Mono, ui-monospace, 'Cascadia Code', monospace", font_size_xs = "0.75rem", font_size_sm = "0.875rem", font_size_base = "1rem", font_size_lg = "1.125rem", font_size_xl = "1.25rem", font_size_2xl = "1.5rem", font_size_3xl = "1.875rem", line_height_tight = "1.25", line_height_normal = "1.5", line_height_relaxed = "1.75", }, spacing = { xs = "0.25rem", sm = "0.5rem", md = "1rem", lg = "1.5rem", xl = "2rem", "2xl" = "2.5rem", "3xl" = "3rem", }, radius = { sm = "0.25rem", md = "0.375rem", lg = "0.5rem", xl = "0.75rem", "2xl" = "1rem", full = "9999px", }, shadows = { sm = "0 1px 2px 0 rgb(0 0 0 / 0.05)", base = "0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)", md = "0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)", lg = "0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)", xl = "0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)", }, components = { button = { border_radius = "0.5rem", padding_x_sm = "0.75rem", padding_y_sm = "0.375rem", padding_x_md = "1rem", padding_y_md = "0.5rem", padding_x_lg = "1.5rem", padding_y_lg = "0.75rem", font_weight = "500", transition = "all 200ms ease-in-out", }, card = { border_radius = "0.75rem", padding = "1.5rem", shadow = "sm", border_width = "1px", }, input = { border_radius = "0.375rem", padding_x = "0.75rem", padding_y = "0.5rem", border_width = "1px", transition = "border-color 200ms ease-in-out, box-shadow 200ms ease-in-out", }, navbar = { height = "4rem", padding_x = "1rem", backdrop_blur = "true", border_bottom_width = "1px", }, footer = { padding_y = "2rem", border_top_width = "1px", }, }, assets = { logo_light = "/logos/jesusperez-logo-b.png", logo_dark = "/logos/jesusperez-logo-w.png", logo_icon = "/logos/logo-icon.svg", favicon = "/images/favicon.ico", favicon_dark = "/images/favicon.ico", logo_alt = "Jesús Pérez Lorenzo", logo_href = "/", logo_show_in_nav = true, logo_show_in_footer = true, logo_width = "auto", logo_height = "2.5rem", logo_height_sm = "2rem", logo_height_md = "2.5rem", logo_height_lg = "3rem", }, animations = { duration_fast = "150ms", duration_normal = "200ms", duration_slow = "300ms", easing = "cubic-bezier(0.4, 0, 0.2, 1)", }, breakpoints = { sm = "640px", md = "768px", lg = "1024px", xl = "1280px", "2xl" = "1536px", }, }, # Helper function: Create a color variant theme # # Usage: # make_color_variant { # name = "Dark", # description = "Dark theme with high contrast", # colors = { primary = "#60a5fa", ... }, # } # # Parameters: # config: Record with name, description, colors, and optional assets # # Returns: Complete theme with colors overridden make_color_variant | Dyn -> Dyn = fun config => # For assets, rebuild the entire record with conditional field selection # This avoids merge conflicts when overriding string fields let final_assets = if std.record.has_field "assets" config then { logo_light = if std.record.has_field "logo_light" config.assets then config.assets.logo_light else base_theme.assets.logo_light, logo_dark = if std.record.has_field "logo_dark" config.assets then config.assets.logo_dark else base_theme.assets.logo_dark, logo_icon = if std.record.has_field "logo_icon" config.assets then config.assets.logo_icon else base_theme.assets.logo_icon, favicon = if std.record.has_field "favicon" config.assets then config.assets.favicon else base_theme.assets.favicon, favicon_dark = if std.record.has_field "favicon_dark" config.assets then config.assets.favicon_dark else base_theme.assets.favicon_dark, logo_alt = if std.record.has_field "logo_alt" config.assets then config.assets.logo_alt else base_theme.assets.logo_alt, logo_href = if std.record.has_field "logo_href" config.assets then config.assets.logo_href else base_theme.assets.logo_href, logo_show_in_nav = if std.record.has_field "logo_show_in_nav" config.assets then config.assets.logo_show_in_nav else base_theme.assets.logo_show_in_nav, logo_show_in_footer = if std.record.has_field "logo_show_in_footer" config.assets then config.assets.logo_show_in_footer else base_theme.assets.logo_show_in_footer, logo_width = if std.record.has_field "logo_width" config.assets then config.assets.logo_width else base_theme.assets.logo_width, logo_height = if std.record.has_field "logo_height" config.assets then config.assets.height else base_theme.assets.logo_height, logo_height_sm = if std.record.has_field "logo_height_sm" config.assets then config.assets.logo_height_sm else base_theme.assets.logo_height_sm, logo_height_md = if std.record.has_field "logo_height_md" config.assets then config.assets.logo_height_md else base_theme.assets.logo_height_md, logo_height_lg = if std.record.has_field "logo_height_lg" config.assets then config.assets.logo_height_lg else base_theme.assets.logo_height_lg, } else base_theme.assets in # Explicitly construct new record to avoid merge conflicts # Cannot use & merge because assets field has conflicting string values { metadata = { name = config.name, description = config.description, version = "1.0.0", author = "Rustelo Contributors", extends = "default", }, colors = config.colors, typography = base_theme.typography, spacing = base_theme.spacing, radius = base_theme.radius, shadows = base_theme.shadows, components = base_theme.components, assets = final_assets, animations = base_theme.animations, breakpoints = base_theme.breakpoints, }, # Default light colors (Blue theme) light_colors = { primary = "#3b82f6", primary_hover = "#2563eb", secondary = "#6b7280", secondary_hover = "#4b5563", accent = "#f59e0b", success = "#10b981", warning = "#f59e0b", error = "#ef4444", info = "#06b6d4", surface = "#ffffff", surface_dark = "#1f2937", surface_hover = "#f9fafb", surface_hover_dark = "#374151", text = "#111827", text_dark = "#f9fafb", text_secondary = "#6b7280", text_secondary_dark = "#9ca3af", border = "#e5e7eb", border_dark = "#374151", }, # Dark theme colors dark_colors = { primary = "#60a5fa", primary_hover = "#3b82f6", secondary = "#9ca3af", secondary_hover = "#d1d5db", accent = "#fbbf24", success = "#34d399", warning = "#fbbf24", error = "#f87171", info = "#22d3ee", surface = "#111827", surface_dark = "#030712", surface_hover = "#1f2937", surface_hover_dark = "#374151", text = "#f9fafb", text_dark = "#111827", text_secondary = "#d1d5db", text_secondary_dark = "#6b7280", border = "#374151", border_dark = "#4b5563", }, # Corporate colors (Professional blue-gray) corporate_colors = { primary = "#1e40af", primary_hover = "#1e3a8a", secondary = "#64748b", secondary_hover = "#475569", accent = "#0ea5e9", success = "#059669", warning = "#d97706", error = "#dc2626", info = "#0284c7", surface = "#ffffff", surface_dark = "#1e293b", surface_hover = "#f8fafc", surface_hover_dark = "#334155", text = "#0f172a", text_dark = "#f8fafc", text_secondary = "#64748b", text_secondary_dark = "#94a3b8", border = "#e2e8f0", border_dark = "#334155", }, # Purple colors (Creative purple theme) purple_colors = { primary = "#9333ea", primary_hover = "#7e22ce", secondary = "#71717a", secondary_hover = "#52525b", accent = "#f59e0b", success = "#10b981", warning = "#f59e0b", error = "#ef4444", info = "#06b6d4", surface = "#ffffff", surface_dark = "#18181b", surface_hover = "#fafafa", surface_hover_dark = "#27272a", text = "#09090b", text_dark = "#fafafa", text_secondary = "#71717a", text_secondary_dark = "#a1a1aa", border = "#e4e4e7", border_dark = "#3f3f46", }, }