solid-fuse

UI & Layout

Build interfaces with views, text, and flex

View

<view> is the workhorse layout container. It maps to a composition of Flutter widgets — Container, Padding, SizedBox, Opacity, Transform, and more — depending on which props you set.

<view
  padding={16}
  decoration={{ color: "white", borderRadius: 12, shadow: [{ blurRadius: 8, color: "#0002" }] }}
>
  <text fontSize={18}>Card content</text>
</view>

View props

PropTypeDescription
flexFlexInputLayout children (see Flex below)
paddingEdgeInsetsInputInner spacing
marginEdgeInsetsInputOuter spacing
width / heightnumberFixed dimensions
minWidth / maxWidthnumberSize constraints
minHeight / maxHeightnumberSize constraints
aspectRationumberWidth-to-height ratio
alignmentAlignmentStringAlign child within view
decorationDecorationInputBackground, borders, shadows, gradients
foregroundDecorationDecorationInputDecoration painted on top
opacitynumber0–1 transparency
transformTransformInputRotate, scale, translate
clipBehaviorstringClip behavior ("hardEdge", "antiAlias", "none")
grownumberFlex grow factor (when inside a flex parent)
fitstring"tight" or "loose" (with grow)
visiblebooleanShow/hide without removing from tree
ignorePointerbooleanDisable hit testing

Text

<text> renders Flutter Text widgets.

<text fontSize={24} fontWeight="bold" color="blue">
  Hello world
</text>

Text props

PropTypeDescription
fontSizenumberFont size in logical pixels
fontWeightFontWeight100900 or "thin", "light", "regular", "medium", "semiBold", "bold", "extraBold", "black"
fontFamilystringFont family name
fontStylestring"normal" or "italic"
colorColorInputText color
lineHeightnumberLine height multiplier
letterSpacingnumberSpace between characters
textAlignstring"left", "right", "center", "justify", "start", "end"
maxLinesnumberMaximum number of lines
overflowstring"clip", "fade", "ellipsis", "visible"
textDecorationstring"underline", "overline", "lineThrough", "none"
backgroundColorColorInputHighlight behind text
shadowsShadowInput[]Text shadows

Stack and Positioned

<stack> layers children on top of each other. <positioned> places children at specific coordinates within the stack.

<stack alignment="center">
  <view width={200} height={200} decoration={{ color: "blue" }} />
  <positioned top={10} right={10}>
    <text color="white">Badge</text>
  </positioned>
</stack>
Stack propTypeDescription
alignmentAlignmentStringDefault alignment for children
fitstring"loose", "expand", "passthrough"
clipBehaviorstringClip overflowing children
Positioned propTypeDescription
top / left / right / bottomnumberOffset from stack edges
width / heightnumberFixed dimensions

ScrollView

<scrollView> creates a scrollable container.

<scrollView padding={16} physics="bouncing">
  <view flex={{ gap: 8 }}>
    <For each={items()}>{(item) => <text>{item}</text>}</For>
  </view>
</scrollView>
PropTypeDescription
scrollDirectionstring"vertical" (default) or "horizontal"
physicsstring"bouncing", "clamping", "always", "never", "page"
reversebooleanReverse scroll direction
paddingEdgeInsetsInputInner padding
controllerScrollControllerProgrammatic scroll control (see Controllers)
clipBehaviorstringClip behavior
keyboardDismissBehaviorstring"manual" or "onDrag"

Flex

Every element that accepts children supports flex — this is intentional. JSX can't enforce "exactly one child" vs "many children" at the type level, so instead of separate <row> and <column> components, Fuse uses flex as a universal layout config.

{/* Horizontal row with spacing */}
<view flex={{ direction: "horizontal", gap: 12 }}>
  <text>Left</text>
  <text>Right</text>
</view>

{/* Centered content filling available space */}
<view flex={{ align: "center", justify: "center", expand: true }}>
  <text>Centered</text>
</view>

{/* Space between items */}
<view flex={{ justify: "spaceBetween" }}>
  <text>Start</text>
  <text>End</text>
</view>

FlexInput

PropertyTypeDefaultDescription
directionstring"vertical""horizontal" or "vertical"
gapnumber0Spacing between children (pixels)
alignstring"start"Cross-axis: "start", "center", "end", "stretch"
justifystring"start"Main-axis: "start", "center", "end", "spaceBetween", "spaceAround", "spaceEvenly"
expandbooleanfalseFill available space on main axis

Grow

Use grow on children to distribute extra space, similar to CSS flex-grow:

<view flex={{ direction: "horizontal", expand: true }}>
  <view grow={1} decoration={{ color: "red" }}>
    <text>Takes remaining space</text>
  </view>
  <view width={100} decoration={{ color: "blue" }}>
    <text>Fixed 100px</text>
  </view>
</view>

GestureDetector

<gestureDetector> wraps content and handles touch, mouse, and trackpad events. It maps to Flutter's GestureDetector.

<gestureDetector onTap={() => setCount((c) => c + 1)}>
  <view padding={16} decoration={{ color: "blue", borderRadius: 8 }}>
    <text color="white">Tap me</text>
  </view>
</gestureDetector>

All standard Flutter gesture callbacks are available: onTap, onTapDown, onTapUp, onDoubleTap, onLongPress, onVerticalDragUpdate, onHorizontalDragUpdate, onPanUpdate, onScaleUpdate, and their variants. TypeScript autocomplete will show the full list.

Event data

Events include both global and local positions:

<gestureDetector
  onTapDown={(e) => console.log(e.localX, e.localY)}
  onPanUpdate={(e) => {
    // e.dx, e.dy — delta since last update
    setX((x) => x + e.dx);
    setY((y) => y + e.dy);
  }}
  onPanEnd={(e) => {
    // e.vx, e.vy — velocity in pixels per second
    console.log("flung at", e.vx, e.vy);
  }}
>
  {/* ... */}
</gestureDetector>
FieldPresent onDescription
x, yAll position eventsGlobal position
localX, localYAll position eventsPosition relative to the widget
dx, dyDrag/pan/scale updateDelta since last update
vx, vyEnd eventsVelocity (pixels/sec)
scale, rotationScale updatePinch scale factor and rotation (radians)
kindStart/down events"touch", "mouse", "stylus", "trackpad"

Hit test behavior

The behavior prop controls how the detector responds to touches:

ValueDescription
"deferToChild"Default. Only the child's area is tappable.
"opaque"Entire bounds tappable, blocks gestures behind.
"translucent"Entire bounds tappable, gestures pass through.

<gestureDetector> also supports flex for layout, so you can use it as a tappable container directly.

Input types

These types are used across multiple elements for colors, spacing, borders, and more.

ColorInput

Colors can be specified as named strings, hex codes, or RGB/HSL objects:

// Named
<text color="red" />
<text color="transparent" />

// Hex (3, 6, or 8 character)
<view decoration={{ color: "#F00" }} />
<view decoration={{ color: "#FF0000" }} />
<view decoration={{ color: "#80FF0000" }} />  {/* with alpha */}

// RGB object (r/g/b: 0-255, a: 0-1)
<view decoration={{ color: { r: 255, g: 0, b: 0 } }} />
<view decoration={{ color: { r: 255, g: 0, b: 0, a: 0.5 } }} />

// HSL object (h: 0-360, s/l: 0-100, a: 0-1)
<view decoration={{ color: { h: 0, s: 100, l: 50 } }} />

EdgeInsetsInput

Padding and margin accept a number (all sides) or an object:

<view padding={16} />                                    {/* all sides */}
<view padding={{ horizontal: 16, vertical: 8 }} />       {/* symmetric */}
<view padding={{ top: 10, left: 20, bottom: 10 }} />     {/* individual */}

BorderRadiusInput

<view decoration={{ borderRadius: 12 }} />                          {/* all corners */}
<view decoration={{ borderRadius: { topLeft: 12, topRight: 12 } }} /> {/* individual */}

DecorationInput

Combines background, borders, shadows, gradients, and images:

<view decoration={{
  color: "white",
  borderRadius: 12,
  border: { width: 1, color: "#E0E0E0" },
  shadow: [{ blurRadius: 8, spreadRadius: 1, color: "#0001", offsetY: 2 }],
  gradient: {
    type: "linear",
    colors: ["blue", "purple"],
    begin: "topLeft",
    end: "bottomRight",
  },
}} />

TransformInput

<view transform={{ rotate: 0.1 }} />           {/* radians */}
<view transform={{ scale: 1.5 }} />
<view transform={{ translateX: 10, translateY: -5 }} />

On this page