Skip to content

Latest commit

 

History

History
237 lines (171 loc) · 8.26 KB

File metadata and controls

237 lines (171 loc) · 8.26 KB

Styling

Perry widgets accept an inline style: { ... } object that maps to each platform's native styling APIs. The same shape works on every Widget constructor — Button, Text, Toggle, Slider, VStack/HStack, and friends — so cross-platform styling code stays the same regardless of target.

Inline style — recommended

Pass a StyleProps object as the trailing argument to any widget constructor. Codegen destructures the literal at HIR time into a sequence of native setter calls, so the runtime shape is the same as hand-writing the imperative pattern below — but the source is much shorter:

{{#include ../../examples/ui/styling/button_inline_style.ts:button-inline-full}}

The style arg is optional; widgets without it look identical to calls before this API existed. See docs/examples/ui/styling/button_inline_style.ts for the full file.

What style accepts

Prop Type Maps to
backgroundColor string | PerryColor widgetSetBackgroundColor
color string | PerryColor textSetColor / buttonSetTextColor
borderColor string | PerryColor widgetSetBorderColor
borderWidth number widgetSetBorderWidth
borderRadius number setCornerRadius
padding number | { top, right, bottom, left } widgetSetEdgeInsets
opacity number (0..=1) widgetSetOpacity
shadow { color, blur, offsetX, offsetY } widgetSetShadow
textDecoration "none" | "underline" | "strikethrough" textSetDecoration
gradient { angle, stops: [c1, c2] } widgetSetBackgroundGradient
fontSize, fontWeight, fontFamily number / string textSetFont*
tooltip string widgetSetTooltip
hidden boolean widgetSetHidden
enabled boolean widgetSetEnabled

Color values

Color props accept four interchangeable shapes:

backgroundColor: "#3B82F6"                                   // hex 6/8
backgroundColor: "#3B82F6FF"                                 // hex with alpha
backgroundColor: "blue"                                      // named color
backgroundColor: { r: 0.231, g: 0.510, b: 0.965, a: 1.0 }   // PerryColor object
backgroundColor: themeColor                                  // runtime variable

Named colors: white, black, red, green, blue, yellow, cyan, magenta, gray / grey, transparent. Hex forms supported: #RGB, #RGBA, #RRGGBB, #RRGGBBAA.

Literals (the first four forms) compile-time-fold into 4 baked-in float arguments — zero runtime cost. Runtime variables resolve through js_color_parse_channel (a small CSS color parser in perry-runtime) so backgroundColor: someStringVar works the same as the literal form.

Padding shapes

A single number applies to all four sides; an object picks per-side:

padding: 12                                       // all four sides 12
padding: { top: 8, right: 16, bottom: 8, left: 16 }  // per-side

Missing sides default to 0.

Container styling

VStack and HStack accept style after the children array:

{{#include ../../examples/ui/styling/stack_inline_style.ts:stack-inline-full}}

Both shapes work — VStack(children, style?) and VStack(spacing, children, style?).

Coming from CSS

If you're coming from web, the conceptual mapping is:

CSS Perry inline style
display: flex; flex-direction: column VStack(spacing, [...])
display: flex; flex-direction: row HStack(spacing, [...])
width: 100% widgetMatchParentWidth(widget)
padding: 10px 20px padding: { top: 10, right: 20, bottom: 10, left: 20 }
gap: 16px VStack(16, [...]) — first argument is the gap
CSS variables / design tokens perry-styling package
opacity: 0.5 opacity: 0.5
border-radius: 8px borderRadius: 8
background: #3B82F6 backgroundColor: "#3B82F6"
box-shadow: 0 4px 12px rgba(0,0,0,0.25) shadow: { color: "#0004", blur: 12, offsetY: 4 }
text-decoration: underline textDecoration: "underline"

See Layout for full details on alignment, distribution, overlays, and split views.

Imperative API (underlying)

The inline style object lowers to the same FFI calls as Perry's imperative free-function setters: widgetSet*, textSet*, buttonSet*. They take the widget handle as the first argument and remain available for cases where you want fine-grained control or need to mutate styles after creation. Colors here are RGBA floats in [0.0, 1.0] (divide each hex byte by 255 — 0xFF3B30(1.0, 0.231, 0.188, 1.0)).

Every snippet below is excerpted from docs/examples/ui/styling/snippets.ts, which CI compiles and runs on every PR — so the API drawn here is always the API the compiler accepts.

{{#include ../../examples/ui/styling/snippets.ts:imports}}

Colors

{{#include ../../examples/ui/styling/snippets.ts:colors}}

Fonts

{{#include ../../examples/ui/styling/snippets.ts:fonts}}

Use "monospaced" for the system monospaced font.

Corner Radius

{{#include ../../examples/ui/styling/snippets.ts:corner-radius}}

Borders

{{#include ../../examples/ui/styling/snippets.ts:borders}}

Padding and Insets

{{#include ../../examples/ui/styling/snippets.ts:padding}}

Sizing

{{#include ../../examples/ui/styling/snippets.ts:sizing}}

Opacity

{{#include ../../examples/ui/styling/snippets.ts:opacity}}

Background Gradient

{{#include ../../examples/ui/styling/snippets.ts:gradient}}

Control Size

{{#include ../../examples/ui/styling/snippets.ts:control-size}}

macOS: Maps to NSControl.ControlSize. Other platforms may interpret differently.

Tooltips

{{#include ../../examples/ui/styling/snippets.ts:tooltip}}

macOS/Windows/Linux: Native tooltips. iOS/Android: No tooltip support. Web: HTML title attribute.

Enabled/Disabled

{{#include ../../examples/ui/styling/snippets.ts:enabled}}

Complete Imperative Example

{{#include ../../examples/ui/styling/counter_card.ts}}

Composing Styles (imperative helper functions)

Reduce repetition by creating helper functions:

{{#include ../../examples/ui/styling/snippets.ts:card-helper}}

For larger apps, use the perry-styling package to define design tokens in JSON and generate a typed theme file. See Theming for the full workflow.

Platform support

Per-prop, per-platform support is tracked in the styling matrix — auto-generated from crates/perry-ui/src/styling_matrix.rs and CI-checked against each backend's lib.rs exports on every PR.

Current state (issue #185):

Platform Wired Stub Missing
macOS / iOS / tvOS / visionOS / watchOS / Android / Web 43/43 0 0
GTK4 (Linux) 39/43 0 4
Windows 38/43 5 0
  • GTK4 has 4 styling props (widget.on_click, button.content_tint_color, button.image_position, stack.detaches_hidden) that need a Linux contributor — tracked in issue #202. Inline style: {...} calls referencing only the wired props compile and run cleanly today; the missing props silently no-op until that issue lands.
  • Windows has 5 props in a "deferred-paint family" (shadow, opacity, border_color, border_width, text.decoration) where the FFI symbol exists and stores the requested params, but a custom WM_PAINT rendering pass is needed to make them visible — tracked in issue #210. User code authoring inline styles compiles and links cleanly on Windows; the visual rendering catches up when that issue lands.

Next Steps

  • Widgets — All available widgets
  • Layout — Layout containers
  • Animation — Animate style changes
  • Theming — Design tokens via the perry-styling package