Honored CSS
The authoritative list of properties the runtime interprets, plus selectors, pseudo-classes and transitions.
display (block|flex|grid|inline|none)
flex-direction, justify-content, align-items, gap, row-gap, column-gap
align-self, justify-self, justify-items, align-content
flex-grow, flex-shrink, flex-basis, flex-wrap, flex (shorthand)
grid-template-columns, grid-template-rows
grid-column, grid-row (+ -start/-end) (1 / 3, span 2, -1; no named lines)
grid-auto-flow, grid-auto-rows, grid-auto-columns
transform (translate/scale/rotate; visual only; hit regions aren't transformed)
position (static|relative|sticky|absolute|fixed) + top/right/bottom/left, aspect-ratio
width, height, min/max-width, min/max-height
padding, margin (shorthand 1–4 values + -top/-right/-bottom/-left)
border, border-width, border-color, border-<side>, border-<side>-width
background / background-color / background-image, opacity
(colour, linear-/radial-gradient, or url(…) image, cover-sized, clipped to corners)
box-shadow (single, outer; inset parsed but not drawn)
transition (property duration easing delay, comma-separated; see below)
border-radius (1–4 diagonal shorthand + per-corner -top-left/-top-right/…)
color, font-size, font-weight, font-family, font-style (italic), text-align
letter-spacing, word-spacing, line-height, white-space (nowrap|pre)
text-decoration (underline / line-through) (color: hex, rgb()/rgba(), CSS names)
overflow / overflow-x / overflow-y (hidden|clip = clip; auto|scroll = scroll;
both axes together; x and y can't differ)
overflow-wrap (break-word), word-break (break-all)
cursor (pointer, on @tap boxes only)
fill, fill-rule, stroke, stroke-width, stroke-linecap, stroke-linejoin
(<path> only; see above)
Selectors: tag, .class, #id, [role="…"], compounds, and all four
combinators: descendant (.a .b), child (.a > .b), next-sibling (.a + .b),
subsequent-sibling (.a ~ .b).
Pseudo-classes: :hover, :focus, :active, :checked, :current (a
link whose to names the path you are on), and :enter-from / :leave-to
(the two sides of an enter/leave swap, below). They stack
(.btn:hover:active), count as class-level specificity, and work anywhere in a
chain, .card:hover .title recolours the title while the pointer is over the
card. :hover/:active hold for the whole chain under the pointer, as in CSS;
:active is press-to-release and drops if you drag off the element; :focus
matches the input holding the caret. Driven in examples/pseudo.rux.
Any other pseudo-class (:disabled, :nth-child(…), ::selection) never
matches, and says so once on stderr. Before this existed the : was silently
dropped, so .box:hover parsed as .box and applied unconditionally, failing
closed is the safer half of that trade.
Transitions: transition walks a property to its new value instead of
jumping to it, whatever moved it: a signal, a :class, or a pseudo-class.
.card { transition: background-color 200ms ease-out, transform 200ms ease-out; }
.card:hover { background: #45475a; transform: translateY(-4px); }
.panel { height: 0; opacity: 0; transition: all 250ms ease-in-out 50ms; }
.panel.open { height: 60px; opacity: 1; }
Each entry is a property, a duration, an easing and a delay, in any order after
the property. As in CSS the first time is the duration and the second is the
delay, all stands for every animatable property, and a bare transition: 200ms
means all 200ms. Easings: linear, ease (the default), ease-in,
ease-out, ease-in-out, cubic-bezier(x1, y1, x2, y2). steps() is not
supported.
Animatable: opacity, background-color, color, border-color,
border-width, border-radius, width, height, padding, margin, gap,
font-size, transform, the insets (top/right/bottom/left, which
animate together), and on a <path>: fill, stroke, stroke-width and d,
the geometry itself. Naming anything else warns and lists what is animatable,
rather than leaving you with an element that silently never moves. A longhand of
an animatable shorthand (padding-left) is pointed at the shorthand: the four
sides animate as a unit.
Three limits, all of them deliberate:
- A value that has no midpoint jumps.
10px→50%needs a layout to resolve, and a colour becoming a gradient has no halfway. Same-unit lengths interpolate; anything else lands at once. - Enter and leave are opt-in. Unmarked, a node still arrives at its
authored style and a node the build stops reaching is simply gone. Add
r-transitionto animate the way in and out; see below. transitiondoes not inherit, exactly as in CSS. A parent'scolorchange does not animate a child's text; put the transition on the element whose style is moving.
An app with no transitions running is still fully event-driven: it sleeps
waiting for real events and renders nothing. Frames are scheduled only while
something is actually in flight, and stop the frame it lands. Driven in
examples/transition.rux.
Enter and leave: r-transition on an element with r-if (or on a keyed
r-for row) says that its arrival and departure are animated. What the two
sides look like is CSS, on :enter-from and :leave-to; how long the swap
lasts is the element's own transition.
<view class="panel" r-if="open" r-transition>…</view>.panel { opacity: 1; transform: translateY(0px);
transition: opacity 300ms ease-out, transform 300ms ease-out; }
.panel:enter-from { opacity: 0; transform: translateY(-16px); }
.panel:leave-to { opacity: 0; transform: translateY(-16px); }
The two sides are ordinary rules and the walk between them is the same
machinery a :hover uses. :enter-from is worn for one frame and dropped by
the next, which is what turns an arrival into an ordinary style change;
:leave-to is held from the moment the swap opens until it commits.
While a swap is pending both branches are really there: laid out, styled,
and still updating. That is why the condition changing back mid-swap reverses
the swap instead of stacking a second one, and why a departing component's
unmounted fires when the swap commits rather than when it starts. A
cancelled swap never fired one.