pub const BREAKPOINTS: &[(&str, f32)] = &[
("sm", 640.0),
("md", 768.0),
("lg", 1024.0),
("xl", 1280.0),
("2xl", 1536.0),
];
pub const STATES: &[&str] = &[
"disabled",
"hover",
"focus",
"active",
"focus-visible",
"focus-within",
];
pub const DEFAULT_KEY: &str = "default";
pub fn is_breakpoint(tok: &str) -> bool {
BREAKPOINTS.iter().any(|(name, _)| *name == tok)
}
pub fn is_state(tok: &str) -> bool {
STATES.contains(&tok)
}
pub fn breakpoint_min_width(bp: &str) -> Option<f32> {
BREAKPOINTS
.iter()
.find(|(name, _)| *name == bp)
.map(|(_, w)| *w)
}
pub fn is_variant_token(tok: &str) -> bool {
tok == DEFAULT_KEY || is_breakpoint(tok) || is_state(tok)
}
fn state_rank(state: &str) -> u32 {
match state {
"disabled" => 1,
"hover" => 2,
"focus" | "focus-visible" | "focus-within" => 3,
"active" => 4,
_ => 0,
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ParsedKey {
pub base: String,
pub breakpoint: Option<String>,
pub state: Option<String>,
pub arg: Option<String>,
}
pub fn parse_prop_key(key: &str) -> ParsedKey {
let (mut work, arg) = match key.rfind('.') {
Some(idx) => (key[..idx].to_string(), Some(key[idx + 1..].to_string())),
None => (key.to_string(), None),
};
let mut state = None;
if let Some(idx) = work.find(':') {
let candidate = work[idx + 1..].to_string();
if is_state(&candidate) {
state = Some(candidate);
work.truncate(idx);
}
}
let mut breakpoint = None;
if let Some(idx) = work.find('@') {
let candidate = work[idx + 1..].to_string();
if is_breakpoint(&candidate) {
breakpoint = Some(candidate);
work.truncate(idx);
}
}
ParsedKey {
base: work,
breakpoint,
state,
arg,
}
}
fn decorated_base(parsed: &ParsedKey) -> String {
let mut out = parsed.base.clone();
if let Some(bp) = &parsed.breakpoint {
out.push('@');
out.push_str(bp);
}
if let Some(st) = &parsed.state {
out.push(':');
out.push_str(st);
}
out
}
pub fn pick_variant_base(
base: &str,
candidate_keys: &[&str],
viewport_w: f32,
active_states: &[&str],
) -> Option<String> {
const STATE_BASE: f32 = 100_000.0;
const STATE_STEP: f32 = 10_000.0;
let mut best: Option<(f32, String)> = None;
for key in candidate_keys {
let parsed = parse_prop_key(key);
if parsed.base != base {
continue;
}
if let Some(bp) = &parsed.breakpoint {
match breakpoint_min_width(bp) {
Some(min_w) if viewport_w >= min_w => {}
_ => continue, }
}
if let Some(st) = &parsed.state {
if !active_states.contains(&st.as_str()) {
continue;
}
}
let bp_weight = parsed
.breakpoint
.as_deref()
.and_then(breakpoint_min_width)
.unwrap_or(0.0);
let score = match (&parsed.breakpoint, &parsed.state) {
(None, None) => 0.0,
(Some(_), None) => bp_weight,
(_, Some(st)) => STATE_BASE + state_rank(st) as f32 * STATE_STEP + bp_weight,
};
let decorated = decorated_base(&parsed);
match &best {
Some((best_score, _)) if *best_score >= score => {}
_ => best = Some((score, decorated)),
}
}
best.map(|(_, decorated)| decorated)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn predicates() {
assert!(is_breakpoint("md"));
assert!(is_breakpoint("2xl"));
assert!(!is_breakpoint("hover"));
assert!(is_state("hover"));
assert!(is_state("focus-visible"));
assert!(!is_state("md"));
assert_eq!(breakpoint_min_width("md"), Some(768.0));
assert_eq!(breakpoint_min_width("2xl"), Some(1536.0));
assert_eq!(breakpoint_min_width("nope"), None);
assert!(is_variant_token("default"));
assert!(is_variant_token("md"));
assert!(is_variant_token("hover"));
assert!(!is_variant_token("top"));
assert!(!is_variant_token("padding"));
}
#[test]
fn parse_plain() {
let p = parse_prop_key("padding.0");
assert_eq!(p.base, "padding");
assert_eq!(p.breakpoint, None);
assert_eq!(p.state, None);
assert_eq!(p.arg.as_deref(), Some("0"));
}
#[test]
fn parse_breakpoint() {
let p = parse_prop_key("padding@md.0");
assert_eq!(p.base, "padding");
assert_eq!(p.breakpoint.as_deref(), Some("md"));
assert_eq!(p.state, None);
assert_eq!(p.arg.as_deref(), Some("0"));
}
#[test]
fn parse_state() {
let p = parse_prop_key("backgroundColor:hover.0");
assert_eq!(p.base, "backgroundColor");
assert_eq!(p.breakpoint, None);
assert_eq!(p.state.as_deref(), Some("hover"));
assert_eq!(p.arg.as_deref(), Some("0"));
}
#[test]
fn parse_combined() {
let p = parse_prop_key("backgroundColor@md:hover.0");
assert_eq!(p.base, "backgroundColor");
assert_eq!(p.breakpoint.as_deref(), Some("md"));
assert_eq!(p.state.as_deref(), Some("hover"));
assert_eq!(p.arg.as_deref(), Some("0"));
}
#[test]
fn parse_named_arg_no_variant() {
let p = parse_prop_key("padding.top");
assert_eq!(p.base, "padding");
assert_eq!(p.breakpoint, None);
assert_eq!(p.state, None);
assert_eq!(p.arg.as_deref(), Some("top"));
}
#[test]
fn parse_bare_no_arg() {
let p = parse_prop_key("padding");
assert_eq!(p.base, "padding");
assert_eq!(p.breakpoint, None);
assert_eq!(p.state, None);
assert_eq!(p.arg, None);
}
#[test]
fn parse_hyphenated_state() {
let p = parse_prop_key("color:focus-visible.0");
assert_eq!(p.base, "color");
assert_eq!(p.state.as_deref(), Some("focus-visible"));
assert_eq!(p.arg.as_deref(), Some("0"));
}
#[test]
fn parse_2xl_breakpoint() {
let p = parse_prop_key("padding@2xl.0");
assert_eq!(p.base, "padding");
assert_eq!(p.breakpoint.as_deref(), Some("2xl"));
assert_eq!(p.arg.as_deref(), Some("0"));
}
#[test]
fn parse_invalid_markers_stay_in_base() {
let bp = parse_prop_key("padding@invalid.0");
assert_eq!(bp.base, "padding@invalid");
assert_eq!(bp.breakpoint, None);
assert_eq!(bp.state, None);
assert_eq!(bp.arg.as_deref(), Some("0"));
let st = parse_prop_key("color:bogus.0");
assert_eq!(st.base, "color:bogus");
assert_eq!(st.state, None);
assert_eq!(st.breakpoint, None);
}
#[test]
fn pick_base_only() {
let keys = ["padding.0"];
assert_eq!(
pick_variant_base("padding", &keys, 1000.0, &[]),
Some("padding".to_string())
);
}
#[test]
fn pick_no_match_returns_none() {
let keys = ["margin.0"];
assert_eq!(pick_variant_base("padding", &keys, 1000.0, &[]), None);
}
#[test]
fn pick_breakpoint_active() {
let keys = ["padding.0", "padding@md.0"];
assert_eq!(
pick_variant_base("padding", &keys, 1000.0, &[]),
Some("padding@md".to_string())
);
}
#[test]
fn pick_breakpoint_inactive() {
let keys = ["padding.0", "padding@md.0"];
assert_eq!(
pick_variant_base("padding", &keys, 500.0, &[]),
Some("padding".to_string())
);
}
#[test]
fn pick_ascending_breakpoint_order() {
let keys = ["padding.0", "padding@md.0", "padding@lg.0", "padding@xl.0"];
assert_eq!(
pick_variant_base("padding", &keys, 1100.0, &[]),
Some("padding@lg".to_string())
);
}
#[test]
fn pick_hover_overrides_breakpoint() {
let keys = ["backgroundColor.0", "backgroundColor@md.0", "backgroundColor:hover.0"];
assert_eq!(
pick_variant_base("backgroundColor", &keys, 1000.0, &["hover"]),
Some("backgroundColor:hover".to_string())
);
}
#[test]
fn pick_active_overrides_hover() {
let keys = ["c.0", "c:hover.0", "c:active.0"];
assert_eq!(
pick_variant_base("c", &keys, 800.0, &["hover", "active"]),
Some("c:active".to_string())
);
}
#[test]
fn pick_disabled_lowest_of_states() {
let keys = ["c.0", "c:disabled.0", "c:hover.0"];
assert_eq!(
pick_variant_base("c", &keys, 800.0, &["disabled", "hover"]),
Some("c:hover".to_string())
);
assert_eq!(
pick_variant_base("c", &keys, 800.0, &["disabled"]),
Some("c:disabled".to_string())
);
}
#[test]
fn pick_combined_requires_both() {
let keys = ["c.0", "c@md:hover.0"];
assert_eq!(
pick_variant_base("c", &keys, 1000.0, &[]),
Some("c".to_string())
);
assert_eq!(
pick_variant_base("c", &keys, 500.0, &["hover"]),
Some("c".to_string())
);
assert_eq!(
pick_variant_base("c", &keys, 1000.0, &["hover"]),
Some("c@md:hover".to_string())
);
}
#[test]
fn pick_state_only_inactive_falls_to_base() {
let keys = ["c.0", "c:hover.0"];
assert_eq!(
pick_variant_base("c", &keys, 800.0, &[]),
Some("c".to_string())
);
}
#[test]
fn pick_combined_outranks_plain_state_via_breakpoint_tiebreak() {
let keys = ["c:hover.0", "c@md:hover.0"];
assert_eq!(
pick_variant_base("c", &keys, 1000.0, &["hover"]),
Some("c@md:hover".to_string())
);
}
#[test]
fn pick_high_breakpoint_lower_state_does_not_leak_across_state_bands() {
let keys = ["c:hover.0", "c@2xl:disabled.0"];
assert_eq!(
pick_variant_base("c", &keys, 1536.0, &["disabled", "hover"]),
Some("c:hover".to_string())
);
}
#[test]
fn pick_focus_visible_ranks_as_focus_not_above_active() {
let keys = ["c:focus-visible.0", "c:active.0"];
assert_eq!(
pick_variant_base("c", &keys, 0.0, &["focus-visible", "active"]),
Some("c:active".to_string())
);
}
}