ESC
Type to search...
S
Soli Docs

Styling

There is no cascade and no selector. A style is a hash of the protocol's own vocabulary — display, gap, pad, bg, fg, size, weight, radius, width, align, justify, cursor, position — and colours are roles.

The colour roles

There are 33. The client resolves a role against the viewer's light or dark mode, density and font scale, so the same view is right in both without the server knowing which one it is. A literal "#RRGGBB" is available for a brand mark and wrong for a surface.

RolesFor
surface.base surface.raised surface.sunken surface.overlayBackgrounds, from the page itself to an overlay.
text.default text.muted text.inverted text.disabledForeground text, including disabled and inverted.
accent.base accent.hover accent.active accent.onThe application's own colour, and its interaction states.
success.base success.subtle success.onAn outcome that went well.
warning.base warning.subtle warning.onSomething the viewer should look at.
danger.base danger.subtle danger.onDestruction, or an error.
info.base info.subtle info.onNeutral information.
border.subtle border.default border.strongRules and outlines, by weight.
focus.ringThe keyboard focus ring.
series.1 series.2 series.3 series.4 series.5The categorical series of a chart, in fixed order.

The series.* family exists so a chart names a categorical series rather than hard-coding a hex, and so two charts agree on which colour the second series is. A view naming a role that does not exist is refused — EUI: unknown colour role 'series.9' — rather than quietly drawn in a default.

Transitions

transition names how long a node takes to settle into a change of style, in the client's own scale rather than milliseconds — the viewer's reduced-motion setting is the client's to honour.

transitionFor
"none"The default: the change is immediate.
"fast"A hover or a press — an answer to something the viewer just did.
"base"A panel opening, a row highlighting.
"slow"A change the viewer did not ask for and should notice.
"slower" "slowest"Ambient movement — a level meter settling, a background easing between states.
box({"bg": lit ? "accent.base" : "surface.sunken", "transition": "fast"}, [])

Arriving and leaving

transition is a duration and never a direction. What a node does when it is grafted or released is animation, which is a list, and motion, which says which way.

KeyValues
animation"spin" "enter" "exit" — a list, so ["enter", "exit"] is the ordinary spelling of a page.
motion"fade" "leading" "trailing" "top" "bottom" "scale" "paired"

Only the arriving side names a direction. Whatever is leaving beside it takes the mirror — leading against trailing, top against bottom — so a push and a pop are one sentence read in the two directions, and “which way is back” is never asked. A motion with neither an entrance nor an exit is refused, because it is a direction with nothing to direct.

nav_page("detail", customer_page(state), {"motion": "trailing"})

A shared element

"paired" is not a direction: it is one thing on two pages. Put it, with the same key, on the node that is leaving and on the node taking its place, and the arriving one flies out of the box its partner had — a row's avatar becoming a header's avatar, a thumbnail becoming a hero. Both ends are boxes the client already laid out, so nothing is laid out again for it.

# In the list, on every row: which row is about to be the one is not
# known until it is tapped.
shared_element("cust:" + one["id"], initial_avatar(one["initial"], one["tone"], 24))

# And in the detail, under the same name, half again as large.
shared_element("cust:" + one["id"], initial_avatar(one["initial"], one["tone"], 36))

A name that resolves to nothing is the ordinary case and not an error — a panel is built and torn down as it opens — so the node simply takes the motion of the page it is on. Which is also the one way to get this wrong silently: a name spelt two ways is a page where nothing moves and nothing complains. Run the client with EUI_TRACE=1 and it prints a line per pair, resolved or not, and says why.

Placement

positionMeaning
"flow"The default — laid out in the parent's flow.
"stack"Positioned within a stack parent, so siblings overlap.
"pointer"Placed where the pointer is — a context menu, or a tooltip that follows the cursor.