Skip to content

Supported RCSS matrix -- generated, keyed to the vendored RmlUi

Vendored RmlUi SHA: 0ae381e00d7426762bb5ed897973366358b16642 (Source/ThirdParty/RmlUi/VENDORED_SHA.txt)

GENERATED FILE -- do not edit the tables by hand. Regenerate after any RmlUi bump with python3 Tools/gen_rcss_matrix.py (run from the plugin root). The property and shorthand tables are parsed mechanically from StyleSheetSpecification::RegisterDefaultProperties (StyleSheetSpecification.cpp:248-438 in this vendored tree); the decorator/filter/font-effect names from the Factory::RegisterDefault*Instancers registrations in Factory.cpp. The Notes column is hand-maintained inside the generator script (plugin-side truth with source cites) and survives regeneration.

The value grammar per property is its parser chain: keyword: a, b accepts those keywords; length, length_percent, number, color, string etc. are the RCSS value parsers registered in RegisterDefaultParsers (same file). A property with multiple parsers accepts any of them, tried in order.

Properties (99)

PropertyDefaultInheritedAffects layoutAccepted valuesNotes
margin-top0pxnoyeskeyword: auto; length_percent
margin-right0pxnoyeskeyword: auto; length_percent
margin-bottom0pxnoyeskeyword: auto; length_percent
margin-left0pxnoyeskeyword: auto; length_percent
padding-top0pxnoyeslength_percent
padding-right0pxnoyeslength_percent
padding-bottom0pxnoyeslength_percent
padding-left0pxnoyeslength_percent
border-top-width0pxnoyeslength
border-right-width0pxnoyeslength
border-bottom-width0pxnoyeslength
border-left-width0pxnoyeslength
border-top-colorblacknonocolor
border-right-colorblacknonocolor
border-bottom-colorblacknonocolor
border-left-colorblacknonocolor
border-top-left-radius0pxnonolength
border-top-right-radius0pxnonolength
border-bottom-right-radius0pxnonolength
border-bottom-left-radius0pxnonolength
displayinlinenoyeskeyword: none, block, inline, inline-block, flow-root, flex, inline-flex, table, inline-table, table-row, table-row-group, table-column, table-column-group, table-cellNo CSS Grid in RmlUi (flex-first is the market bar, arch spec 1); the keyword list here is exhaustive.
positionstaticnoyeskeyword: static, relative, absolute, fixedabsolute resolves against the nearest positioned ancestor; there is no browser-style default-positioned root chain past the document (Content/DevUI/M5Hud/vacuus-base.rcss:29-31).
topautononokeyword: auto; length_percent
rightautononokeyword: auto; length_percent
bottomautononokeyword: auto; length_percent
leftautononokeyword: auto; length_percent
floatnonenoyeskeyword: none, left, right
clearnonenoyeskeyword: none, left, right, both
box-sizingcontent-boxnoyeskeyword: content-box, border-box
z-indexautononokeyword: auto; number
widthautonoyeskeyword: auto; length_percent
min-width0pxnoyeslength_percent
max-widthnonenoyeskeyword: none; length_percent
heightautonoyeskeyword: auto; length_percent
min-height0pxnoyeslength_percent
max-heightnonenoyeskeyword: none; length_percent
line-height1.2yesyesnumber_length_percent
vertical-alignbaselinenoyeskeyword: baseline, middle, sub, super, text-top, text-bottom, top, center, bottom; length_percent
overflow-xvisiblenoyeskeyword: visible, hidden, auto, scroll
overflow-yvisiblenoyeskeyword: visible, hidden, auto, scroll
clipautononokeyword: auto, none, always; number
visibilityvisiblenonokeyword: visible, hidden
text-overflowclipnonokeyword: clip, ellipsis; string
background-colortransparentnonocolor
colorwhiteyesnocolor
caret-colorautoyesnokeyword: auto; color
image-colorwhitenonocolor
opacity1yesnonumber
font-family`` (empty)yesyesstring
font-stylenormalyesyeskeyword: normal, italic
font-weightnormalyesyeskeyword: normal=400, bold=700; number
font-size12pxyesyeslength; length_percent
font-kerningautoyesyeskeyword: auto, normal, none
letter-spacingnormalyesyeskeyword: normal; length
text-alignleftyesyeskeyword: left, right, center, justify
text-decorationnoneyesnokeyword: none, underline, overline, line-through
text-transformnoneyesyeskeyword: none, capitalize, uppercase, lowercase
white-spacenormalyesyeskeyword: normal, pre, nowrap, pre-wrap, pre-line
word-breaknormalyesyeskeyword: normal, break-all, break-word
row-gap0pxnoyeslength_percent
column-gap0pxnoyeslength_percent
cursor`` (empty)yesnostring
dragnonenonokeyword: none, drag, drag-drop, block, clone
tab-indexnonenonokeyword: none, auto
focusautoyesnokeyword: none, auto
nav-upnonenonokeyword: none, auto, horizontal, vertical, tree-order; stringThe nav-* family is NOT inherited: spatial-nav rules must sit on the focusable element itself, and targets use #id syntax (rmlui-input.md:190-207, :450).
nav-rightnonenonokeyword: none, auto, horizontal, vertical, tree-order; stringSee nav-up.
nav-downnonenonokeyword: none, auto, horizontal, vertical, tree-order; stringSee nav-up.
nav-leftnonenonokeyword: none, auto, horizontal, vertical, tree-order; stringSee nav-up.
scrollbar-margin0nonolength
overscroll-behaviorautononokeyword: auto, contain
pointer-eventsautoyesnokeyword: none, autoInherited, but there is no subtree pruning -- none on a parent still walks children during hit tests (docs/research/m2-api-notes/rmlui-input.md:198).
perspectivenonenonokeyword: none; length
perspective-origin-x50%nonokeyword: left, center, right; length_percent
perspective-origin-y50%nonokeyword: top, center, bottom; length_percent
transformnonenonotransform
transform-origin-x50%nonokeyword: left, center, right; length_percent
transform-origin-y50%nonokeyword: top, center, bottom; length_percent
transform-origin-z0nonolength
transitionnonenonotransitionTHERE IS NO ease FAMILY. The complete tween table is eleven families -- back bounce circular cubic elastic exponential linear quadratic quartic quintic sine -- each with -in/-out/-in-out (PropertyParserAnimation.cpp:27-77). ease-in-out is not in it, and an unrecognized token drops the WHOLE declaration with one generic "Syntax error parsing property declaration" (:240 misses the map, :267 fails the duration sscanf, :301-315 returns false). Use cubic-in-out for CSS's ease-in-out. Keywords must be LOWERCASE here: transition does not lowercase its token (:240) while animation does (:133). gotchas.md #3. Timing tokens MAY come from var(), but only because the vendored tree is patched for it (VENDORED_TAG.txt patch #4, bead VaCuus-6gj): upstream reads this property before computed values exist (ElementStyle.cpp:388) and drops the whole declaration with no diagnostic at all. gotchas.md #3b.
animationnonenonoanimationSame tween table as transition (PropertyParserAnimation.cpp:27-77), but this parser LOWERCASES each token before the lookup (:133), so Cubic-Out works here and silently kills a transition. Keyframe values may contain var() (Element.cpp:2784-2798), and so may this property's own value -- it is read through ComputedValues::animation(), which resolves variables (ComputedValues.cpp:8-16), so unlike transition it never needed a patch.
decorator`` (empty)nonodecoratorRenders through the recorder; shader(...) values resolve builtin names then registered UMaterial style keys (see the decorators section below).
mask-image`` (empty)nonodecoratorPARSES BUT DOES NOT MASK in v1 -- masking needs the mask layer captured as a filter (ElementEffects.cpp:306 -> RenderInterface::SaveLayerAsMaskImage) and the replayer has no layer render targets. It is REFUSED, once per view, with a Warning (bead VaCuus-iuv). The element renders UNMASKED and the mask artwork is drawn over it, because the layer the decorators were drawn into is not a real render target -- verified on screen, not inferred. Substitute: bake the alpha into the image asset and use decorator: image/ninepatch, or clip with overflow: hidden plus border-radius.
font-effect`` (empty)yesnofont_effectGlyph generation for effects (glow/outline) is the measured spike class -- ~4.2 ms on the reference HUD's FIRST Record, UI thread, before first publish (perf-guide.md, Exp-GLYPH-WARMUP). Budget it at load, not per frame.
filter`` (empty)nonofilter: filterv1 compiles blur only; the other nine types are refused (one Warning per type, effect dropped per element -- VaCuusRecordingRenderInterface.cpp:866-907). Per-element filter blur is not a shipped v1 surface (arch spec 5, M5 amendment): the verified blur consumer is backdrop-filter.
backdrop-filter`` (empty)nonofilterbackdrop-filter: blur(...) is the shipped glass path -- distilled at record time and re-blurred every engine frame at composite time (arch spec 5, M5 amendment). Non-blur backdrop filters are refused like element filters.
box-shadownonenonobox_shadowDOES NOT RENDER in v1 -- the shadow needs the current layer captured to a texture (GeometryBoxShadow.cpp:235 -> RenderInterface::SaveLayerAsTexture) and the replayer has no layer render targets. It is REFUSED, once per view, with a Warning naming the property and the substitute; the element then renders its normal background and border with the shadow dropped (bead VaCuus-u0q; VaCuus patch #3 to the vendored RmlUi is what makes the failure harmless -- before it the element rendered as an opaque WHITE rectangle and republished every frame). Substitute: decorator: ninepatch(...) with a pre-blurred shadow image, or font-effect: glow for text. Also NOT animatable even where it renders: RmlUi refuses the key at animation start with a Warning (ElementAnimation.cpp:640-648); vacuus lint flags it at authoring time (Web/packages/cli/lib/lint.mjs:68-99).
fill-image`` (empty)nonostring
align-contentstretchnoyeskeyword: flex-start, flex-end, center, space-between, space-around, space-evenly, stretch
align-itemsstretchnoyeskeyword: flex-start, flex-end, center, baseline, stretch
align-selfautonoyeskeyword: auto, flex-start, flex-end, center, baseline, stretch
flex-basisautonoyeskeyword: auto; length_percent
flex-directionrownoyeskeyword: row, row-reverse, column, column-reverse
flex-grow0noyesnumber
flex-shrink1noyesnumber
flex-wrapnowrapnoyeskeyword: nowrap, wrap, wrap-reverse
justify-contentflex-startnoyeskeyword: flex-start, flex-end, center, space-between, space-around, space-evenly
-rmlui-language`` (empty)yesyesstring
-rmlui-directionautoyesyeskeyword: auto, ltr, rtl

Shorthands (20)

ShorthandExpands toExpansion typeNotes
marginmargin-top, margin-right, margin-bottom, margin-leftBox
paddingpadding-top, padding-right, padding-bottom, padding-leftBox
border-widthborder-top-width, border-right-width, border-bottom-width, border-left-widthBox
border-colorborder-top-color, border-right-color, border-bottom-color, border-left-colorBox
border-topborder-top-width, border-top-colorFallThrough
border-rightborder-right-width, border-right-colorFallThrough
border-bottomborder-bottom-width, border-bottom-colorFallThrough
border-leftborder-left-width, border-left-colorFallThrough
borderborder-top, border-right, border-bottom, border-leftRecursiveRepeat
border-radiusborder-top-left-radius, border-top-right-radius, border-bottom-right-radius, border-bottom-left-radiusBox
insettop, right, bottom, leftBox
overflowoverflow-x, overflow-yReplicate
backgroundbackground-colorFallThrough
fontfont-style, font-weight, font-size, font-familyFallThrough
gaprow-gap, column-gapReplicate
navnav-up, nav-right, nav-down, nav-leftBoxShorthand for the nav-* family; the non-inheritance note on nav-up applies.
perspective-originperspective-origin-x, perspective-origin-yFallThrough
transform-origintransform-origin-x, transform-origin-y, transform-origin-zFallThrough
flexflex-grow, flex-shrink, flex-basisFlex
flex-flowflex-direction, flex-wrapFallThrough

Decorators (16 registered names)

Registered in Factory.cpp (line numbers in this vendored tree). All render through the record/replay path. shader(<name-or-key>) resolves builtin names first (glass-panel ships), then UMaterial style keys registered by the game (UVaCuusStyleSet -- the M5 material-decorator tier; unknown keys are refused once with a Warning naming both halves of what would have worked).

The six gradient decorators are antialiased in screen space, which is a deliberate deviation from RmlUi's reference backend. A hard colour break -- two stops at the same position, which is how a segmented dial ring or a hazard hatch is written -- is rendered as a one-pixel ramp instead of a step, so a diagonal break does not staircase. The width comes from the analytic screen-space derivative of the gradient parameter, so it is one pixel at any size, scale or transform; a stop pair that is already more than a pixel apart is left bit-for-bit alone, and a smooth gradient is unchanged. A repeating gradient gets the same treatment at its period edge as at its interior stops, and once its bands shrink past about two pixels it fades to the period's mean colour rather than aliasing (VaCuusGradient.usf).

This covers the gradient FILL only. Element outlines -- notably border-radius arcs -- are tessellated geometry, and geometry is antialiased by a separate, OPT-IN knob: vacuus.ViewSampleCount (1 by default, 2/4/8) multisamples the per-view render target. Off, a curve's outline is a hard staircase -- a 206x206 border-radius disc measures exactly 0 partially covered pixels, because a single-sampled rasterizer produces a step function; at 4x it measures 432, and text, axis-aligned edges and gradient fills come back byte-identical. The knob costs one extra multisampled target per view (N x 7.91 MiB at 1080p, on top of the RT it resolves into), which is why it is off: see perf-guide.md's cost table and docs/research/proofs/3tg-view-msaa.

border-radius takes a LENGTH ONLY, not a percentage -- all four longhands register the "length" parser and nothing else (StyleSheetSpecification.cpp:300-303), so border-radius: 50% does not parse and the corner stays square. Spell a circle as half its own side (width: 64px; border-radius: 32px).

DecoratorFactory.cpp line
text:196
tiled-horizontal:197
tiled-vertical:198
tiled-box:199
image:200
ninepatch:201
shader:202
gradient:204
horizontal-gradient:205
vertical-gradient:206
linear-gradient:208
repeating-linear-gradient:209
radial-gradient:210
repeating-radial-gradient:211
conic-gradient:212
repeating-conic-gradient:213

Filters (10 registered names) -- blur-only in v1

RmlUi registers ten filter instancers; the VaCuus recorder compiles exactly ONE -- blur. Every other type is refused with one Warning per type and the effect dropped per element (VaCuusRecordingRenderInterface.cpp:866-907; returning handle 0 is RmlUi's own safe-refusal contract, and RmlUi then warns per element, ElementEffects.cpp:161-165). The shipped, verified blur consumer is backdrop-filter (glass); per-element filter: blur is a v1.x item (arch spec 5, M5 amendment).

box-shadow's blur never reaches this table at all: the shadow is refused a step earlier, at the layer capture it is built on (see the box-shadow row above and Layer capture below), so its CompileFilter("blur") call is never made.

FilterFactory.cpp linev1 status
hue-rotate:216refused (one Warning per type; effect dropped per element)
brightness:217refused (one Warning per type; effect dropped per element)
contrast:218refused (one Warning per type; effect dropped per element)
grayscale:219refused (one Warning per type; effect dropped per element)
invert:220refused (one Warning per type; effect dropped per element)
opacity:221refused (one Warning per type; effect dropped per element)
saturate:222refused (one Warning per type; effect dropped per element)
sepia:223refused (one Warning per type; effect dropped per element)
blur:225compiled; shipped consumer is backdrop-filter (glass)
drop-shadow:226refused (one Warning per type; effect dropped per element)

Font effects (4 registered names)

All four render. Effect-glyph generation is the measured load-spike class: ~4.2 ms on the reference HUD's first Record (UI thread, before first publish, never the game thread) -- see perf-guide.md, Exp-GLYPH-WARMUP, before shipping effect-heavy styles.

Font effectFactory.cpp line
blur:229
glow:230
outline:231
shadow:232

Layer capture -- the two properties v1 refuses outright

Rml::RenderInterface has 21 virtuals and the VaCuus recorder now overrides all 21, but two of them are REFUSALS rather than recordings -- SaveLayerAsTexture and SaveLayerAsMaskImage (RenderInterface.h:112-116). Both mean "hand me the current layer back", and this renderer has no layer to hand back: PushLayer, CompositeLayers and PopLayer are recorded and then skipped at replay, so every draw between a push and a pop lands directly in the base render target. Glass (backdrop-filter) does not need them -- it is distilled from the buffer and composited per engine frame -- which is why it ships and these do not.

Each is refused with one Warning per view, latched, naming the property and its substitute. A document with two hundred shadowed elements logs one line.

PropertyReachesv1 behaviour
box-shadowSaveLayerAsTextureShadow dropped; the element renders its normal background and border. No per-frame cost.
mask-imageSaveLayerAsMaskImageElement renders unmasked, and the mask artwork is drawn over it.

Both are pinned by automation: VaCuus.Render.LayerCapture.Refused and VaCuus.Render.LayerCapture.RestyleChurn.

At-rules

Dispatched in StyleSheetParser.cpp on at_rule_identifier (:783-869 in this tree): @keyframes (:786), @decorator (:790), @spritesheet (:798), @media (:835), @font-face (:855). Anything else logs "Invalid at-rule identifier" (:869). @media supports the query grammar parsed by ParseMediaFeatureMap (:567-684 -- width/height/resolution/theme et al.).

@font-face -- and its src is ROOT-relative, unlike every other path

There is NO public C++ font-loading API (Rml::LoadFontFace is called only inside the plugin, VaCuusEngine.cpp:138-153), so @font-face is the ONE route for a project shipping its own faces. It works.

css
@font-face {
	font-family: "Michroma";                  /* REQUIRED, a quoted string */
	src: myapp/fonts/Michroma-Regular.ttf;    /* REQUIRED. ROOT-relative. Bare path, NO url() */
	font-weight: normal;                     /* all | normal | bold | <number>; default `all` */
	font-style: normal;                      /* normal | italic; default normal */
	-rmlui-fallback-face: false;             /* RmlUi extension; default false */
	-rmlui-face-index: 0;                    /* RmlUi extension, for collections; default 0 */
}

Grammar at StyleSheetParser.cpp:294-330 (the property set) and :525-564 (the block: both required properties checked, then one LoadFontFace per src). src is COMMA-EXPANDED into a list of bare paths -- url() is not part of this grammar at all.

ROOT-relative, not document-relative. src is passed verbatim with no JoinPath (:561), handed straight to the file interface (FontEngineDefault/FontProvider.cpp:94) and resolved against the ordered document roots (Source/VaCuus/Private/VaCuusContentPaths.cpp:92-103). <link> and <script src> are the opposite -- document-relative (gotchas.md #12) -- so a sheet at Content/DevUI/myapp/app.rcss links its neighbours bare but must spell its fonts myapp/fonts/Face.ttf.

Variable fonts render at their default weight for every requested weight: the default font engine calls FT_New_Face without setting a variation axis. Ship static instances, one file per weight.

Custom properties and var() -- the only theming layer

Fully implemented in this vendored tree, and worth knowing because there is no calc() (see below): var() is the whole of RCSS's computed-value story.

css
body { --accent: #38BDF8; --pad: 12px; }
#panel { color: var(--accent); padding: var(--pad); }
#panel.alt { color: var(--missing, #F87171); }   /* fallback after the comma */
  • Declared like any property whose name starts with --; the value is stored unparsed as Unit::STRING, or as Unit::VAR_EXPRESSION when it itself contains a var() (PropertySpecification.cpp:233-248). A normal property whose value contains var() is likewise stored as VAR_EXPRESSION and resolved at compute time, not parse time (:260-300).
  • Substituted with fallbacks (var(--x, <fallback>), nested parens counted) and with CYCLE DETECTION that logs an Error naming the variable and the element (ElementStyle.cpp:162-243).
  • Inherited: an undefined name walks up the parent chain (ElementStyle.cpp:115-134), so body { --accent: … } themes the document.
  • Writable from JS through the facade's Element::SetProperty, which routes --* names to SetCustomProperty (Element.cpp:590-614, the loop at :605-608) -- so a one write, not a stylesheet swap.
  • Works inside @keyframes: keyframe properties are resolved through the same substitution path (Element.cpp:2784-2798).
  • Works in transition and animation timing -- transition only because the vendored tree is patched for it. Resolution happens at COMPUTE time, and those two are the only properties read before that: upstream's transition read (ElementStyle.cpp:388) saw the unresolved string and dropped the entire declaration with no warning, no error and no log line, so a theme that put its durations in custom properties -- the obvious thing to do, and what this section recommends -- simply never animated. animation was never affected (ComputedValues.cpp:8-16). VENDORED_TAG.txt patch #4, bead VaCuus-6gj, pinned by VaCuus.Core.Style.TransitionVariable; gotchas.md #3b.

Unknown variable with no fallback is an Error and the declaration is dropped, so LogVaCuus: Error: [Rml] Invalid substitution, variable '--x' not defined is the line to grep for (gotchas.md #14).

What is NOT here

No CSS Grid (flex-first; arch spec 1 non-goals). No calc() -- there is no such parser anywhere in the vendored Core, so lengths cannot be computed and var() above is the only indirection RCSS offers. No UA stylesheet -- link vacuus-base.rcss first (gotchas.md #1). transform: scale() on a root is the usual substitute for the missing calc(), and it DOES now clip correctly -- that cost every scroll container in the document until the clip-mask stencil pass landed, so check gotchas.md #8a for what it costs in memory before adopting it. Selector support, data-* binding attributes and element tags are RmlUi-documented surface, not RCSS properties, and are out of this matrix's scope.