w3c / w3c/css-validator

Not recognizing calc result as number, nor recognizing @property

Open
#518 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Java
Stars
250
Forks
123
PR merge metrics
No merged PRs in 30d

Description

Below is an excerpt of css being implemented in a WordPress mechanism to generate css-only Low Quality Image Placeholders (https://github.com/WordPress/performance/pull/2550/changes), inspired by this article https://leanrada.com/notes/css-only-lqip/

@property --lqip {
	syntax: "<number>";
	inherits: true;
	initial-value: 0;
}

[style*="--lqip:"] {
	--lqip-ca: mod(round(down, calc((var(--lqip) + 524288) / 262144)), 4);
	--lqip-cb: mod(round(down, calc((var(--lqip) + 524288) / 65536)), 4);
	--lqip-cc: mod(round(down, calc((var(--lqip) + 524288) / 16384)), 4);
	--lqip-cd: mod(round(down, calc((var(--lqip) + 524288) / 4096)), 4);
	--lqip-ce: mod(round(down, calc((var(--lqip) + 524288) / 1024)), 4);
	--lqip-cf: mod(round(down, calc((var(--lqip) + 524288) / 256)), 4);
	--lqip-ll: mod(round(down, calc((var(--lqip) + 524288) / 64)), 4);
	--lqip-aaa: mod(round(down, calc((var(--lqip) + 524288) / 8)), 8);
}

W3C CSS Validator shows these errors


1		Unrecognized at-rule @property
8	[style*="--lqip:"]	The types are incompatible ), 4)
9	[style*="--lqip:"]	The types are incompatible ), 4)
10	[style*="--lqip:"]	The types are incompatible ), 4)
11	[style*="--lqip:"]	The types are incompatible ), 4)
12	[style*="--lqip:"]	The types are incompatible ), 4)
13	[style*="--lqip:"]	The types are incompatible ), 4)
14	[style*="--lqip:"]	The types are incompatible ), 4)
15	[style*="--lqip:"]	The types are incompatible ), 8)
15	[style*="--lqip:"]	Parse Error

As noted in a code review comment there, this is what Claude Opus 5 said

Both tools are wrong, and about the same thing: the two‑argument round(down, A) form where the rounding interval B is omitted.

What the spec says

css-values-4 §10.3:

The round(<rounding-strategy>?, A, B?) function … If the type of A matches <number>, then B may be omitted, and defaults to 1. … In all other cases, omitting B is invalid.

--lqip is registered as syntax: "<number>" (lqip.css:12-16), so A is a <number> and omitting B is legal. The code is correct.

Why the error points at mod() instead of round()

Neither tool can establish a type for the round() result, so it falls through to mod()'s "both arguments must be the same type" check against the literal 4/8. That's also why lqip.css:27mod(calc(var(--lqip) + 524288), 8), no round() — is clean in both tools while lines 19‑26 are not.

The two tools fail for different reasons

I probed both with variants:

Expression PhpStorm W3C
round(down, 5) ok error
round(down, var(--lqip)) ok
round(down, calc(var(--lqip) + 1)) error error
round(down, …, 1) (explicit interval) ok ok
  • PhpStorm does implement the number default — it even reads your @property to type a bare var(--lqip). It just loses the type when the argument is a calc() containing a var(). Dropping the redundant calc() wrapper (round(down, (var(--lqip) + 524288) / 262144)) doesn't help.
  • W3C/Jigsaw doesn't implement the omitted-B rule at all — even round(down, 5) reports "The types are incompatible."

Browsers disagree with both

In Chrome 152, with --lqip: 192900, the omitted-interval and explicit-interval forms both compute --lqip-ca: 2, and CSS.supports('opacity', 'round(down, 0.5)') is true.

If you want the warnings gone

Adding the explicit , 1 to the eight round() calls is a semantic no-op that satisfies both tools. Verified on a copy: W3C goes from 9 errors to 1, PhpStorm to 0.

--lqip-ca: mod(round(down, calc((var(--lqip) + 524288) / 262144), 1), 4);

The remaining W3C error is Unrecognized at-rule "@property" at lqip.css:12 — a separate Jigsaw gap (it predates css-properties-values-api), and nothing you can or should work around.

So, there's two issues:

  1. not recognizing the calc as number
  2. Not recognizing @property

This resolves the calc/round error, but shouldn't be necessary (and isn't, in actual browsers)

	--lqip-ca: mod(round(down, calc((var(--lqip) + 524288) / 262144), 1), 4);
	--lqip-cb: mod(round(down, calc((var(--lqip) + 524288) / 65536), 1), 4);
	--lqip-cc: mod(round(down, calc((var(--lqip) + 524288) / 16384), 1), 4);
	--lqip-cd: mod(round(down, calc((var(--lqip) + 524288) / 4096), 1), 4);
	--lqip-ce: mod(round(down, calc((var(--lqip) + 524288) / 1024), 1), 4);
	--lqip-cf: mod(round(down, calc((var(--lqip) + 524288) / 256), 1), 4);
	--lqip-ll: mod(round(down, calc((var(--lqip) + 524288) / 64), 1), 4);
	--lqip-aaa: mod(round(down, calc((var(--lqip) + 524288) / 8), 1), 8);

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the supplied CSS in the validator, then trace how it parses @property and types calc(), round(), and mod(). Compare the results with the CSS Values specification and browser behavior. Done means valid @property declarations and number expressions validate without requiring an explicit rounding interval.

Written by the indexing model from the issue text.

Assessment

Tech stack
css, java
Domain
tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.