Normative behaviour of the value layer of RecursiveAI.OneHtml.Css, written so another language can reproduce it. It
sits on the tokens and component values of css-syntax.md. The algorithms are those of CSS Values and Units 4,
CSS Color 4 (with the Color 5 relative-color and color-mix() parts), CSS Variables 1, Media Queries 5 and CSS
Conditional 3 as of 20 October 2026; this file records what those texts leave open and what is not implemented.
Property table
- Source.
ed/css.jsonof the@webref/css@latestbranch of <https://github.com/w3c/webref>, commitbb927df3d6cc86f1c0beca204844a170a0843a18(committed 2026-10-22), SHA-25630cb8daa786d6c27a1f74c5d413311c785ee0eec1dd34c65e8a4e6681c86baca. This is webref's merged extract of every CSS specification's property definitions, not the per-spec curated files. - Generator.
tools/generate-css-properties.py <css.json>writessrc/OneHtml/Css/Generated/PropertyTableData.g.cs. It checks the input against the pinned SHA-256, is deterministic, and records commit and date in the generated file. The fetch commands are in the script's header.tools/generate-css-colors.pydoes the same for the named-color table of CSS Color 4 (Generated/NamedColors.g.cs). - Content. 822 properties, of which 170 are shorthands, 64 are logical-property members (20 logical groups), and 64 are legacy aliases. Each entry has its name, inheritance (yes, no, "see longhands", unspecified), initial value as text, applies-to, percentages, computed-value text, value syntax, alias target, longhands, reset-only longhands and logical group. The initial value is parsed to component values on first use.
- Prose values. An initial value is "text" only when the source gives a value that is CSS. Otherwise the entry says
which kind of prose it is and the initial value is not guessed:
- see longhands (98):
all,animation,background,borderand its sides,color-adjust,columns,container,font,gap,list-style,margin-block,margin-inline,mask,offset,outline,padding-block,padding-inline,text-decoration,transition, therule*andcolumn-rule*families, thecorner-*-shapefamily, thetimeline-trigger*and*-timelineshorthands,place-items,cue,pause,rest, and the others the generator reports withPropertyInitialKind.SeeLonghands; - depends on the user agent (1):
font-family; - not defined for shorthand properties (1):
marker; - missing in the source (73): the
-webkit-aliases of the flexbox, animation, transition, transform, mask, box and appearance properties, andcolumn-width,font-stretch,grid-column-gap,grid-gap,grid-row-gap,stop-color,stop-opacity,word-wrap.PropertyTable.All.Where(p => p.InitialKind != PropertyInitialKind.Value)lists them.
- see longhands (98):
- Typed properties. 33 properties have a typed parser (
TypedProperties.IsTyped): the accessibility set (display,visibility,content,colorand the other color properties,background-image,opacity,font-size,font-weight,font-style,font-family,outline-*,clip,clip-path,positionand the offsets,overflow-*,speak,speak-as,pointer-events). Every other property keeps its value as component values. - Licences. See
THIRD-PARTY-NOTICES.md.
CSS-wide keywords and shorthands
initial,inherit,unset,revertandrevert-layerare recognised only when they are the whole value (CssWideKeywords.Detect).Shorthands.Expand(name, value)returns the leaf longhands in table order; nested shorthands are flattened and reset-only longhands areinitial. Status isExpanded,Invalid(the value does not match the grammar),PendingSubstitution(the value containsvar()and cannot be split yet),NotExpanded(a shorthand without a grammar here: keep the value as written) orNotShorthand.- Grammars exist for: shorthands whose value syntax ends in
{1,4}or{1,2}with that many longhands (margin,padding,inset,scroll-margin,border-color,border-style,border-widthand the logical pairs) and the two-longhand pair shorthands; shorthands with exactly a-width,-styleand-colorlonghand (borderand its sides,outline,column-rule); andtext-decoration,list-style,font,background,background-position,transitionandanimation.Shorthands.HasGrammar(name)says which. Every other shorthand isNotExpanded. - Where the table's syntax ends in
{1,4}or{1,2}the expansion uses the CSS rule for omitted sides.
Numbers, units and math
- A
CssQuantityis a number, percentage or dimension as written, or the result of a math function. A literal too large for a double is held at the largest finite value; only a math function yields infinity. - Units: absolute lengths, font-relative (
em ex cap ch ic lh rem rex rcap rch ric rlh), viewport (`vw vh vi vb vmin vmaxand thes,l,dvariants), container (cqw cqh cqi cqb cqmin cqmax`), angle, time, frequency, resolution and flex units.LengthContextsupplies the inputs for relative units; the library never measures fonts. When font metrics are unknown,ex=0.5em,ch=0.5em,cap=0.7em andic=1em (Values 4 §6.1.1 fallbacks; cap is this library's choice). Absolute units use the CSS reference pixel. - Math functions:
calc min max clamp round mod rem sin cos tan asin acos atan atan2 pow sqrt hypot log exp abs sign. Parsing uses an explicit stack, so nesting never recurses; the expression is capped at 65,536 nodes.- Type checking follows Values 4 §10.1: sums need the same type; a percentage in a calculation is invalid where the
property takes no percentage, even when it cancels (
sign(50%)). - Simplification: constants fold;
min/max/clampof constants with the same unit fold, percentages included; a sum is sorted number, percentage, then dimensions by unit name (§10.13). Aclamp()bound writtennonestands alone and takes no part in arithmetic. - Serialization: a math function that folds to a constant serializes as
calc(<value><unit>); NaN and infinity ascalc(NaN),calc(infinity). - Not implemented: products of two dimensions (
1px * 1px) are kept as written rather than simplified,progress(),random(),random-item(),calc-mix(),inherit()andattr()in math (Values 5).
- Type checking follows Values 4 §10.1: sums need the same type; a percentage in a calculation is invalid where the
property takes no percentage, even when it cancels (
Colors
- Forms: named colors, hex (3, 4, 6, 8 digits),
rgb() rgba() hsl() hsla() hwb() lab() lch() oklab() oklch(),color()with the predefined spaces exceptdisplay-p3-linear,color-mix(), relative color syntax,light-dark(),currentcolor,transparentand the system colors. - Legacy serialization (hex, named,
rgb(),hsl(),hwb(), with nonone):rgb(r, g, b)orrgba(r, g, b, a)with channels rounded half up to integers 0..255 and the alpha stored as 8 bits and written with the two-decimal rule of Color 4 §16.1.1 (#00000088isrgba(0, 0, 0, 0.53)).- css-parsing-tests expects unrounded channels and a six-decimal alpha for these (it predates the 8-bit rule). The suite runner accepts a difference within the rounding; the current text wins.
- Missing components.
rgb()withnoneserializes ascolor(srgb ...)withnonekept;hsl()andhwb()stay in their own function withnonekept (§16.2.2). - Modern forms print their own function with numbers rounded to six decimals, shortest form. Color 4 requires at
least round-trip precision, so six decimals is allowed; tests that expect six significant digits (
73.3386) differ. Numbers of magnitude 1e15 or more use an exponent. - Parse-time clamps:
lab/lchlightness to 0..100 andoklab/oklchto 0..1, chroma at least 0, hue taken modulo 360,hslsaturation and lightness to 0..100,hwbwhiteness and blackness at least 0 (a sum of 100% or more is a gray), NaN from math becomes 0, an infinite hue becomes 0.rgb()channels clamp to 0..255. color-mix()inhslorhwbreturns sRGB. Hues interpolate with the four methods. Missing components take the other operand's value.- Relative colors: channel keywords are numbers (
rgb0..255,hslandhwbpercentages as numbers 0..100). The result ofrgb(),hsl()orhwb()relative syntax iscolor(srgb ...)unless a component is missing, whenhslandhwbkeep their function. A keyword used alone keeps its component missing. - Powerless hues. Converting into
hsl,hwb,lchoroklchmarks the hue missing when the source chroma is at most 0.0015 (lab, lch) or 0.000004 (oklab, oklch), the converted saturation is under 0.001%, or whiteness plus blackness is within 0.002% of 100%. An authored color already in the mix space keeps its hue. - System colors take fixed light-scheme sRGB values (dark with
ColorParseOptions.DarkScheme) and keep their keyword inSystemName;ToCss()prints the keyword. - Gamut mapping to sRGB (
ToSrgb) is the CSS Gamut Mapping algorithm in OKLCH with the JND of 0.02. - Not implemented:
alpha(),contrast-color(),color-layers(),display-p3-linear, single-operandcolor-mix(),color-mix()with 0% weights, and the specified-value serialization ofcolor-mix(), relative colors and math functions inside colors (ToCss()returns the resolved color;lab(calc(0.5) ...)prints the number). A font-relative or container length inside a color calculation does not parse, because the color parser has no length context.
var()
VarSubstitution.ResolveCustomPropertiescomputes custom properties (cycles and references to invalid properties become guaranteed-invalid and are left out).Substitutereplacesvar()in a value. Both are iterative.- A cycle is a strongly connected component (Tarjan, iterative) of the graph whose edges are the
var()references in a value, fallbacks included, whether or not the fallback would be used. - A fallback has its leading whitespace dropped. A substituted value gets
/**/between two pieces only where two name-like characters would fuse into one token. - Limits, checked before the work they guard (
VarLimits): 1,048,576 characters in one substituted value, 2,097,152 token visits (every token of every expanded fallback counts, so an empty expansion is bounded too), and 16,777,216 characters over oneResolveCustomPropertiescall. Exceeding one givesSubstitutionStatus.LimitExceeded, and the declaration is invalid at computed-value time. A billion-laughs input fails in milliseconds. IsValidSyntaxchecksvar()at parse time (Variables 2): the first argument is one or more tokens, or a single non-empty{}block; a{}block cannot be mixed with other tokens there; whatever follows the first top-level comma is the fallback and may be empty. Whether the first argument names a custom property is checked at substitution, which accepts only a--nameidentifier (Variables 1).
Media queries
MediaQueryList.Parsefollows Media Queries 5: media types,not/only/and/or, range syntax (width >= 400px,400px < width <= 800px),<general-enclosed>(unknown).ToCss()is the canonical text; an invalid query serializes and evaluates asnot all.- Evaluation is three-valued. A query whose condition is unknown does not match, and
notof unknown is unknown, sonot all and (unknown-feature)does not match. - Unclosed blocks, functions and strings are closed at the end of the input, and the canonical text writes those closers out so that it parses back to itself.
- Features: `width height device-width device-height aspect-ratio device-aspect-ratio resolution color color-index
monochrome grid (0 or 1) orientation hover any-hover pointer any-pointer update overflow-block overflow-inline scan
color-gamut prefers-color-scheme prefers-reduced-motion prefers-reduced-transparency prefers-reduced-data
prefers-contrast forced-colors scripting display-mode dynamic-range video-dynamic-range inverted-colors nav-controls`
and
-webkit-device-pixel-ratio. Other names, and values outside a feature's grammar, are unknown. @containerqueries are parsed as at-rules and are not evaluated (they need layout).
StyleContext
StyleContext is an immutable record. Today it holds MediaFeatures: media type, width, height, device pixel ratio,
prefers-color-scheme, prefers-reduced-motion, -transparency, -data, prefers-contrast, forced-colors,
hover/any-hover, pointer/any-pointer, color bits, and scripting, which is always none because the library
never runs script. The defaults are a 1280x720 light-scheme screen. New members get defaults, so existing callers keep
working when the interaction state (focus, hover, :target, :active) is added.
@supports
SupportsCondition parses not, and, or, parentheses, (property: value), selector(...), and
<general-enclosed> (false). DeclarationSupport.IsSupported(property, value) is true when:
- the property is a custom property with a name; or
- it is in the property table (aliases included) and the value is non-empty and syntactically acceptable (no bad
strings or URLs, no stray closers, no top-level braces or semicolons), and
- the value is a CSS-wide keyword or contains
var(); or - the property is typed: its typed parser accepts the value; or
- the property is a shorthand with a grammar: it expands; or
- the property's value syntax is only keywords and simple types (
<length>,<color>, ...): the value matches it; or - otherwise: nothing more is checked (the value is accepted).
- the value is a CSS-wide keyword or contains
selector(...) is decided by SupportsOptions.SelectorSupported, which gets the selector text; without a callback the
text only has to look like one complex selector (selector-shaped tokens, no top-level commas). The selectors
component is meant to supply the real check.
Linked stylesheets
WhatwgCssEncodingProvider.Instance implements the CSS Syntax §3.2 byte-to-text steps with the WHATWG decoders: the
protocol label, the @charset rule, then the referrer's encoding and UTF-8. The decoder replaces CR and CRLF with LF.
Deviations the test suites show
The replay of the web-platform-tests files (testdata/css/wpt-expectations.txt) and of css-parsing-tests lists every known
failure with its reason. In short: the specified value of color-mix(), relative colors and math in colors is not
kept; Values 5 and Color 6 functions are missing; typed arithmetic on dimensions is not simplified; the analogous-
component carry-forward of Color 4 §12.4 between different spaces is incomplete.