swc-project/swc · error · Error
percentage, function (math functions) or ident (with 'none'
Error message
percentage, function (math functions) or ident (with 'none' value) token
What it means
Thrown by swc_css_parser when the SECOND channel of hsl()/hsla() (saturation) starts with a token other than percentage, math function, or ident (crates/swc_css_parser/src/parser/values_and_units/mod.rs:584-596). ErrorKind::Expected("percentage, function (math functions) or ident (with 'none' value) token") is reported at the current span. The most common hit is a bare NUMBER in the saturation slot — 'hsl(120, 50, 50%)' — because CSS requires saturation to be a percentage; dimensions, hashes, and strings also land here.
Source
Thrown at crates/swc_css_parser/src/parser/values_and_units/mod.rs:586
}
Token::Function { value, .. } if is_math_function(value) => {
Ok(Some(ComponentValue::Function(parser.parse()?)))
}
tok!("ident") => {
let ident: Box<Ident> = parser.parse()?;
if ident.value.eq_ignore_ascii_case("none") {
Ok(Some(ComponentValue::Ident(ident)))
} else {
Err(Error::new(
ident.span,
ErrorKind::Expected("'none' value of an ident token"),
))
}
}
_ => {
if !has_variable_before {
Err(Error::new(
parser.input.cur_span(),
ErrorKind::Expected(
"percentage, function (math functions) or ident \
(with 'none' value) token",
),
))
} else {
Ok(None)
}
}
},
&mut has_variable,
)?;
if let Some(percentage_or_none) = percentage_or_none {
values.push(percentage_or_none);
}
View on GitHub (pinned to 5176682b65)
Solutions
- Add '%' to the saturation (and lightness) values: 'hsl(120, 50%, 50%)' or 'hsl(120 50% 50%)'.
- Modern space syntax also requires percentages for s/l: 'hsl(120 100% 50%)'.
- For computed values, format with the unit: format!("hsl({}, {}%, {}%)", h, s, l).
- Use var() indirection for machine-generated channels to bypass strict checks.
Example fix
/* before */ color: hsl(120, 50, 50%); color: hsl(210, 70, 40); /* after */ color: hsl(120, 50%, 50%); color: hsl(210, 70%, 40%);
Defensive patterns
Strategy: try-catch
Validate before calling
fn hsl_saturation_token_ok(arg: &str) -> bool {
let a = arg.trim();
a.ends_with('%')
|| a.eq_ignore_ascii_case("none")
|| a.starts_with("var(")
|| a.ends_with(')') && ["calc(","min(","max(","clamp("].iter().any(|f| a.to_ascii_lowercase().starts_with(f)))
// 'hsl(120, 50, 50%)' fails: bare number 50 is rejected
} Type guard
fn is_percentage_or_none(text: &str) -> bool {
let t = text.trim();
t.ends_with('%') || t.eq_ignore_ascii_case("none") || t.starts_with("var(")
} Try / catch
if let Err(err) = swc_css_parser::parse_string::<Stylesheet>(css, config) {
if matches!(&err.kind(), ErrorKind::Expected(m) if m.contains("percentage, function (math functions) or ident")) {
let (span, _) = err.into_inner();
// saturation token invalid — most often a bare number; append '%' at the reported span
}
return Err(err.into());
} Prevention
- hsl()/hsla() saturation MUST be a percentage in both legacy and modern syntax — always append '%'.
- When formatting from numbers: format!("hsl({}, {}%, {}%)", h, s, l).
- Add a lint/regex in CI: /hsl\([^)]*,\s*\d+\s*,/ catches missing '%' in legacy syntax.
When it happens
Trigger: parse_string on 'hsl(120, 50, 50%)' or 'hsl(120 50 50%)' (number where percentage is required), 'hsl(120, 50px, 50%)', 'hsl(120, #fa, 50%)'. The '_' arm at line 584 fires when has_variable_before is false.
Common situations: Classic legacy-syntax mistake: hsl written with unit-less numbers (some tutorials and old design tools emit 'hsl(210, 70, 40)'); SCSS/JS color math emitting raw floats into hsl() strings; copy-paste from color pickers that expose slider values 0-100 without '%'.
Related errors
- number, dimension, function (math functions) or ident (with
- percentage, number, function (math functions) or ident (with
- comma token
- percentage, function (math functions) or number token
- internal error: entered unreachable code
AI-assisted analysis of swc-project/swc@5176682b65 (2026-08-17).
Data as JSON: /api/errors/c929fc61c5fad523.
Report an issue: GitHub.