Components
Shared primitives from @polystaq/ui plus product-specific UI patterns from apps/app.
Card
Anatomy
Input & Label
States
Inline title input
Used in the properties panel to edit block and connector names. Strips all borders and rings.
Alert
Variants
Product Patterns — Canvas Toolbar
Floating toolbar
The only dark surface in the product. Position: absolute inset-x-0 bottom-5, centered. Active tool uses #0c8ce9 electric blue.
Product Patterns — Canvas Block
Block element
rounded-[5px] tight radius. Inset shadow gives depth against the white canvas. Bold 18px white text.
Note element
Yellow sticky. All styling hardcoded — not theme tokens. Resizable via ReactFlow NodeResizer.
Product Patterns — Connector
FlowConnector
Custom @xyflow/react edge type. A bezier path with 28 px straight lead segments from each handle, strokeWidth 1.5, strokeLinecap round. Default: gradient color, arrow end, solid pattern.
• Lead segment: 28 px straight from handle before curve begins
• Bezier curve via getBezierPath() from @xyflow/react
• Hit area: 28 px wide (wider than the 3 px visible stroke)
• Default data: color "#5552E1", colorMode "gradient", end "arrow", start "none", pattern "solid"
Color modes
Gradient derives its start and end color from the connected block nodes — source color → target color. Solid uses a fixed hex from the same 4-color palette as canvas blocks.
Gradient
colorMode: "gradient" — source block color → target block color
Solid
colorMode: "solid" — fixed hex, same palette as canvas blocks
Stroke patterns
Three stroke patterns. Dashed uses strokeDasharray 10 8; dotted uses 2 8. All share strokeWidth 1.5 and strokeLinecap round.
Solid
Dashed
Dotted
Endpoint markers
Start and end are independently configurable. Arrow is the default end; none is the default start. Markers are SVG shapes drawn at the handle attachment point.
Arrow
Circle
Square
None
Arrow endpoint offsets the path by 13 px so the line meets the tip of the triangle rather than its center.
Label & icon
Label text sits at the bezier midpoint offset translate-y-3 below the path. Icon floats above: translate-y-[calc(100%+8px)]. Both use text-xs font-semibold italic with a bg-white/80 pill. Color matches the connector's labelColor (source color for gradient, solid color otherwise).
When the connector is steep (more vertical than horizontal) the label shifts to translate-y-3 only, and the icon and label stack together rather than splitting above/below.
Connection handles
Each block and note node renders four handles (top, right, bottom, left). Handles are opacity-0 by default and fade in on group hover. They act as both source and target.
• !size-3 !border-2 !border-white !bg-[#5552E1]
• opacity-0 group-hover:opacity-100
• Each handle is both isConnectableStart and isConnectableEnd
Node
A mid-connector node inserted by left-clicking on an existing connector. Lets users insert a step or extract a branch midway through a flow. Rendered as a small colored dot that inherits color from the incoming edge — solid color in solid mode, source block color in gradient mode. A minimal radial glow sits behind the dot.
• size-2.5 rounded-full — dot, colored from incoming edge
• Backdrop: size-5 radial-gradient(circle, color·30% 0%, transparent 70%)
• Gradient mode: uses source block color · Solid mode: uses edge color
• Selected ring: ring-2 ring-offset-1 in the edge color
• Inserted by left-clicking midway on an existing connector — splits it into two segments
Animations
Animated states communicate direction, activity level, and item throughput. Animate only connectors with live or in-progress data flow — static canvases stay still.
Flow
Dashes animate in the edge direction. Speed encodes activity level — fast (0.3s), normal (0.5s), slow (1s).
Pulse
Opacity fades 15 % → 100 % → 15 % over 2 s. Signals an idle but live connection — exists, nothing moving yet.
Particle
Dots travel along the bezier path via SVG animateMotion + mpath. Particle count and interval encode throughput — shown here: 2 on the top branch, 1 on bottom.
Counter
Badge at the bezier midpoint shows cumulative count or throughput rate. Updates live. Shares the same italic pill style as the connector label.
• Flow — strokeDasharray: "6 4" + strokeDashoffset 20→0 animating indefinitely. Duration encodes speed.
• Pulse — opacity 0.15 → 1 → 0.15 over 2 s ease-in-out. Path and arrowhead both animate together.
• Particle — <animateMotion> on a <circle> referencing the connector path via <mpath>. Stagger with begin offset.
• Counter — pill badge; same italic font + bg-white/80 backdrop as the connector label. Update from canvas page item counts.
Product Patterns — Emoji Picker
EmojiPicker
Inline picker for assigning an emoji, icon, or uploaded image to a canvas block. Tabbed interface with search, categorized emoji grid, and a category quick-jump bar.
Recent
• Tabs: Emoji (default) · Icons · Upload
• Active tab underline uses --brand-violet
• Category bar at the bottom quick-jumps between 8 categories + Recent
• Props: onSelect(emoji) · onRemove() · className
Product Patterns — Canvas Grid
Column & Row Grid
An invisible alignment layer over the canvas. Columns define X-axis snap positions; rows define Y-axis snap positions — similar to tab stops in a word processor. Blocks snap to the nearest column/row intersection when dragged.
• Columns — vertical snap rails set per-canvas, like tab stops. Blocks in the same column share an X-center.
• Rows — horizontal snap rails. Blocks in the same row share a Y-center (shown as dashed lines through block midpoints).
• Branching blocks stay in their column; diverging children each occupy their own row in the next column (Thing 3 & 4 above).
• Column and row positions are canvas-level settings — they persist across the full canvas, not per-block.
Spacing model
Column width and row height are the two configurable values, measured in canvas pixels. The UI for adjusting them mirrors a word processor ruler — drag to add or remove stops.
Column spacing
Row height
Rows snap to the nearest configured Y position. When a block is dragged vertically, it locks to the closest row rail.
Row height controls the minimum vertical spacing between block centers — preventing overlaps when blocks share a column.
Default column width: TBD px
Default row height: TBD px
Snap threshold: TBD px
Product Patterns — Properties Panel
Panel layout
max-w-[460px], absolute right-0 inset-y-0, border-l border-border, bg-background. Backdrop is bg-black/10.
Start event