// el-style — The el-ui design system. // // Hierarchy: // 1. Theme — single source of truth, set at experience root // 2. Semantic tokens — Color::Primary, Spacing::Lg, Radius::Md, ... // 3. StyleModifier — fluent builder applied to any component // 4. StyleSheet — named rule sets, applied with .apply("card") // // Resolution flow: // component declares semantic token → theme resolves to RGBA / px → // compiler emits CSS-in-JS for the browser target / NSColor for macOS / ... // ── Color tokens ───────────────────────────────────────────────────────────── let COLOR_PRIMARY: String = "primary" let COLOR_ON_PRIMARY: String = "on_primary" let COLOR_PRIMARY_CONTAINER: String = "primary_container" let COLOR_SECONDARY: String = "secondary" let COLOR_ON_SECONDARY: String = "on_secondary" let COLOR_BACKGROUND: String = "background" let COLOR_ON_BACKGROUND: String = "on_background" let COLOR_SURFACE: String = "surface" let COLOR_ON_SURFACE: String = "on_surface" let COLOR_SURFACE_VARIANT: String = "surface_variant" let COLOR_ERROR: String = "error" let COLOR_ON_ERROR: String = "on_error" let COLOR_OUTLINE: String = "outline" let COLOR_OUTLINE_VARIANT: String = "outline_variant" // A resolved RGBA quadruple, encoded as JSON for transport across the FFI. type Rgba { r: Int // 0-255 g: Int // 0-255 b: Int // 0-255 a: Int // 0-255 (we store alpha *256 to keep everything integer in El today) } fn rgba(r: Int, g: Int, b: Int, a: Int) -> Rgba { { "r": r, "g": g, "b": b, "a": a } } fn rgba_to_css(c: Rgba) -> String { "rgba(" + int_to_str(c.r) + "," + int_to_str(c.g) + "," + int_to_str(c.b) + "," + int_to_str(c.a) + ")" } fn parse_hex(hex: String) -> Rgba { let s: String = hex if str_starts_with(s, "#") { let s = str_slice(s, 1, str_len(s)) } let r: Int = hex_to_int(str_slice(s, 0, 2)) let g: Int = hex_to_int(str_slice(s, 2, 4)) let b: Int = hex_to_int(str_slice(s, 4, 6)) let a: Int = 255 if str_len(s) > 6 { let a = hex_to_int(str_slice(s, 6, 8)) } { "r": r, "g": g, "b": b, "a": a } } // ── ColorScheme: semantic token -> Rgba ────────────────────────────────────── type ColorScheme { primary: String // hex on_primary: String primary_container: String secondary: String on_secondary: String background: String on_background: String surface: String on_surface: String surface_variant: String error: String on_error: String outline: String outline_variant: String } fn color_scheme_light() -> ColorScheme { { "primary": "#3b82f6", "on_primary": "#ffffff", "primary_container": "#dbeafe", "secondary": "#a855f7", "on_secondary": "#ffffff", "background": "#ffffff", "on_background": "#0f172a", "surface": "#f8fafc", "on_surface": "#0f172a", "surface_variant": "#e2e8f0", "error": "#dc2626", "on_error": "#ffffff", "outline": "#cbd5e1", "outline_variant": "#e2e8f0" } } fn color_scheme_dark() -> ColorScheme { { "primary": "#60a5fa", "on_primary": "#0f172a", "primary_container": "#1e3a8a", "secondary": "#c084fc", "on_secondary": "#0f172a", "background": "#0f172a", "on_background": "#f8fafc", "surface": "#1e293b", "on_surface": "#f8fafc", "surface_variant": "#334155", "error": "#f87171", "on_error": "#0f172a", "outline": "#475569", "outline_variant": "#334155" } } // Resolve a semantic color token to its hex value in the active scheme. fn resolve_color(scheme: ColorScheme, token: String) -> String { if str_eq(token, "primary") { return scheme.primary } if str_eq(token, "on_primary") { return scheme.on_primary } if str_eq(token, "primary_container") { return scheme.primary_container } if str_eq(token, "secondary") { return scheme.secondary } if str_eq(token, "on_secondary") { return scheme.on_secondary } if str_eq(token, "background") { return scheme.background } if str_eq(token, "on_background") { return scheme.on_background } if str_eq(token, "surface") { return scheme.surface } if str_eq(token, "on_surface") { return scheme.on_surface } if str_eq(token, "surface_variant") { return scheme.surface_variant } if str_eq(token, "error") { return scheme.error } if str_eq(token, "on_error") { return scheme.on_error } if str_eq(token, "outline") { return scheme.outline } if str_eq(token, "outline_variant") { return scheme.outline_variant } "#000000" } // ── Spacing scale ──────────────────────────────────────────────────────────── type SpacingScale { xs: Int sm: Int md: Int lg: Int xl: Int xxl: Int } fn spacing_default() -> SpacingScale { { "xs": 4, "sm": 8, "md": 16, "lg": 24, "xl": 32, "xxl": 48 } } fn resolve_spacing(s: SpacingScale, token: String) -> Int { if str_eq(token, "xs") { return s.xs } if str_eq(token, "sm") { return s.sm } if str_eq(token, "md") { return s.md } if str_eq(token, "lg") { return s.lg } if str_eq(token, "xl") { return s.xl } if str_eq(token, "xxl") { return s.xxl } s.md } // ── Radius / shadow ────────────────────────────────────────────────────────── type RadiusScale { none: Int sm: Int md: Int lg: Int full: Int } fn radius_default() -> RadiusScale { { "none": 0, "sm": 4, "md": 8, "lg": 16, "full": 9999 } } type ShadowScale { sm: String // CSS box-shadow string md: String lg: String } fn shadow_light() -> ShadowScale { { "sm": "0 1px 2px rgba(0,0,0,0.05)", "md": "0 4px 6px rgba(0,0,0,0.1)", "lg": "0 10px 15px rgba(0,0,0,0.1)" } } fn shadow_dark() -> ShadowScale { { "sm": "0 1px 2px rgba(0,0,0,0.4)", "md": "0 4px 6px rgba(0,0,0,0.5)", "lg": "0 10px 15px rgba(0,0,0,0.6)" } } // ── Typography ─────────────────────────────────────────────────────────────── type TextStyle { font_family: String font_size: Int font_weight: Int // 100..900 line_height: Int // px letter_spacing: Int // hundredths of em } fn text_style_default() -> TextStyle { { "font_family": "system-ui, -apple-system, sans-serif", "font_size": 16, "font_weight": 400, "line_height": 24, "letter_spacing": 0 } } // ── Theme ──────────────────────────────────────────────────────────────────── let THEME_MODE_LIGHT: String = "light" let THEME_MODE_DARK: String = "dark" let THEME_MODE_SYSTEM: String = "system" type Theme { mode: String // light | dark | system colors: ColorScheme spacing: SpacingScale radius: RadiusScale shadows: ShadowScale typography: TextStyle } fn theme_default_light() -> Theme { { "mode": "light", "colors": color_scheme_light(), "spacing": spacing_default(), "radius": radius_default(), "shadows": shadow_light(), "typography": text_style_default() } } fn theme_default_dark() -> Theme { { "mode": "dark", "colors": color_scheme_dark(), "spacing": spacing_default(), "radius": radius_default(), "shadows": shadow_dark(), "typography": text_style_default() } } // ── StyleSheet — named rule sets ───────────────────────────────────────────── // // A StyleSheet is a JSON object mapping selector -> declarations. // Components apply by name: element.apply("card") fn stylesheet_new() -> String { "{}" } fn stylesheet_define(sheet: String, name: String, css_decls: String) -> String { json_set(sheet, name, css_decls) } fn stylesheet_resolve(sheet: String, name: String) -> String { json_get(sheet, name) } // ── CSS emit ───────────────────────────────────────────────────────────────── // // Server-side render: theme → CSS custom properties block. // Emit as a " } fn text_style_to_css(ts: TextStyle) -> String { "font-family:" + ts.font_family + ";font-size:" + int_to_str(ts.font_size) + "px" + ";font-weight:" + int_to_str(ts.font_weight) + ";line-height:" + int_to_str(ts.line_height) + "px" } // ── Entry — smoke test ─────────────────────────────────────────────────────── let theme: Theme = theme_default_light() println("[el-style] light primary = " + theme.colors.primary) println("[el-style] css vars = " + theme_to_css_vars(theme))