use crate::config::ThemeConfig;
pub fn build_css(theme: &ThemeConfig) -> String {
fn compile_scss() -> (
&'static str,
std::collections::HashMap<&'static str, &'static str>,
) {
tairitsu_macros::scss! {
file: "styles/layout.scss",
no_hash
}
}
let core = compile_scss().0;
let components = hikari_component_css();
let overrides = theme_overrides(theme);
format!("{HIKARI_VARS}\n{core}\n{components}\n{overrides}")
}
fn hikari_component_css() -> String {
use hikari_components::styled::StyleRegistry;
use hikari_components::StyledComponent as _;
let mut registry = StyleRegistry::default();
registry.register_available();
hikari_components::basic::link::LinkComponent::register(&mut registry);
hikari_components::production::code_highlight::CodeHighlightComponent::register(&mut registry);
let all = registry.get_all();
let mut names: Vec<&'static str> = all.keys().copied().collect();
names.sort_unstable();
let css = names.iter().map(|n| all[n]).collect::<Vec<_>>().join("\n");
let mut bridge = String::new();
{
let mut depth: i32 = 0;
let mut in_comment = false;
let mut in_string: Option<char> = None;
let mut rule_start = 0usize;
let mut last_rule_end = 0usize;
let bytes = css.as_bytes();
let mut i = 0usize;
while i < bytes.len() {
let c = bytes[i] as char;
if in_comment {
if c == '*' && i + 1 < bytes.len() && bytes[i + 1] as char == '/' {
in_comment = false;
i += 2;
continue;
}
i += 1;
continue;
}
if let Some(q) = in_string {
if c == q && (i == 0 || bytes[i - 1] as char != '\\') {
in_string = None;
}
i += 1;
continue;
}
match c {
'/' if i + 1 < bytes.len() && bytes[i + 1] as char == '*' => {
in_comment = true;
i += 2;
continue;
}
'\'' | '"' => {
in_string = Some(c);
i += 1;
continue;
}
'{' => {
if depth == 0 {
rule_start = last_rule_end;
}
depth += 1;
i += 1;
continue;
}
'}' => {
depth -= 1;
if depth == 0 {
let rule = &css[rule_start..=i];
if rule.contains(".hk-code-highlight") {
let aliased = rule.replace(".hk-code-highlight", ".hi-code-highlight");
bridge.push_str(&aliased);
bridge.push('\n');
}
last_rule_end = i + 1;
}
i += 1;
continue;
}
_ => {
i += 1;
continue;
}
}
}
}
format!("{css}\n/* lagrange: hi-code-highlight aliases for hikari hk- classes */\n{bridge}")
}
const HIKARI_VARS: &str = r#":root {
--hi-radius-sm: 4px; --hi-radius-md: 8px; --hi-radius-lg: 12px; --hi-radius-full: 9999px;
--hi-blur-sm: 8px; --hi-blur-md: 16px;
--hi-ease-out-expo: cubic-bezier(.16, 1, .3, 1);
--hi-shadow-panel: 0 2px 12px rgba(0, 0, 0, .06);
--hi-shadow-elevated: 0 4px 20px rgba(0, 0, 0, .08);
--hi-shadow-focus: 0 0 0 3px rgba(58, 110, 165, .12);
--hi-z-modal: 1000; --hi-z-toast: 9999; --hi-z-tooltip: 10000;
--hi-scroll-size: 8px; --hi-scroll-thumb: 4px;
/* hikari component color tokens, mapped onto lagrange's palette so hi-*
markup (code blocks, alerts, tags, ...) renders on-palette even though
hikari-theme's own stylesheet is not shipped. The component CSS also
carries fallbacks, but they are light-scheme only. */
--hi-color-primary: var(--accent);
--hi-color-primary-glow: color-mix(in srgb, var(--accent) 35%, transparent);
--hi-color-bg-container: var(--code-bg);
--hi-color-bg-elevated: var(--bg-subtle);
--hi-color-border: var(--border);
--hi-color-text-primary: var(--fg);
--hi-color-text-secondary: var(--fg-sec);
--hi-color-text-tertiary: var(--fg-sec);
--hi-color-info: var(--accent);
--hi-color-success: #3fb950;
--hi-color-warning: #d29922;
--hi-color-danger: #f85149;
}
.hi-glass { background: var(--bg-subtle); backdrop-filter: blur(16px); }
.hi-glass-panel { background: var(--bg-subtle); border: 1px solid var(--border); border-radius: 12px; box-shadow: var(--hi-shadow-panel); }
.hi-scroll-container { overflow: auto; scrollbar-width: none; }
"#;
fn theme_overrides(theme: &ThemeConfig) -> String {
let mut s = String::new();
if let Some(v) = &theme.accent {
s.push_str(&format!(":root{{--accent:{v}}}\n"));
}
if let Some(v) = &theme.bg {
s.push_str(&format!(":root{{--bg:{v}}}\n"));
}
if let Some(v) = &theme.bg_subtle {
s.push_str(&format!(":root{{--bg-subtle:{v}}}\n"));
}
if let Some(v) = &theme.fg {
s.push_str(&format!(":root{{--fg:{v}}}\n"));
}
if let Some(v) = &theme.fg_sec {
s.push_str(&format!(":root{{--fg-sec:{v}}}\n"));
}
if let Some(v) = &theme.code_bg {
s.push_str(&format!(":root{{--code-bg:{v}}}\n"));
}
if let Some(v) = &theme.border {
s.push_str(&format!(":root{{--border:{v}}}\n"));
}
s
}