Path provides a fluent builder API for constructing custom shapes using lines, bezier curves, arcs, and geometric primitives. Shape is a View that renders a Path on screen, with support for fill colors, strokes, and gradient fills.

You can define custom shapes either inline by passing a Path to the Shape constructor, or by subclassing Shape and overriding build_path() for reusable shapes.

Constructor

nib.Path()
nib.Shape(
    path=None,
    view_box=None,
    fill=None,
    stroke=None,
    stroke_width=None,
    **modifiers,
)

Parameters

Shape

Parameter Type Default Description
path Path None A Path object with path operations. If not provided, build_path() is called to generate the path.
view_box tuple[float, float] None Coordinate system as (width, height). When set, the path is scaled to fit the view bounds while preserving aspect ratio. Overrides the class-level view_box if provided.
fill str \| Gradient None Fill color as a hex string, color name, or a gradient (LinearGradient, RadialGradient, etc.).
stroke str None Stroke color as a hex string or color name.
stroke_width float None Width of the stroke in points.
**modifiers Common view modifiers: width, height, padding, opacity, etc.

Path Methods

All Path methods return self for method chaining.

Method Description
move_to(x, y) Move to a point without drawing. Starts a new subpath.
line_to(x, y) Draw a straight line to a point.
curve_to(x, y, control1, control2) Draw a cubic bezier curve. control1 and control2 are (x, y) tuples.
quad_curve_to(x, y, control) Draw a quadratic bezier curve. control is an (x, y) tuple.
arc(center, radius, start_angle, end_angle, clockwise=True) Draw an arc. center is an (x, y) tuple. Angles are in radians.
close() Close the current subpath by drawing a line back to the starting point.
add_rect(x, y, width, height) Add a rectangle subpath.
add_rounded_rect(x, y, width, height, corner_radius) Add a rounded rectangle subpath.
add_ellipse(x, y, width, height) Add an ellipse subpath within the bounding rectangle.
add_circle(center_x, center_y, radius) Add a circle subpath.

Examples

Triangle with inline path

import nib

def main(app: nib.App):
    triangle = nib.Path()
    triangle.move_to(50, 0)
    triangle.line_to(100, 100)
    triangle.line_to(0, 100)
    triangle.close()

    app.build(
        nib.Shape(
            path=triangle,
            view_box=(100, 100),
            fill="#3B82F6",
            width=200,
            height=200,
        )
    )

nib.run(main)

Reusable shape via subclass

Subclass Shape and override build_path() for reusable custom shapes. Set view_box as a class attribute to define the coordinate system.

import nib
import math

class Star(nib.Shape):
    view_box = (100, 100)

    def build_path(self, path):
        cx, cy, outer, inner = 50, 50, 50, 20
        for i in range(10):
            angle = math.pi / 2 + i * math.pi / 5
            r = outer if i % 2 == 0 else inner
            x = cx + r * math.cos(angle)
            y = cy - r * math.sin(angle)
            if i == 0:
                path.move_to(x, y)
            else:
                path.line_to(x, y)
        return path.close()

def main(app: nib.App):
    app.build(
        nib.HStack(
            controls=[
                Star(fill="#FFD700", width=64, height=64),
                Star(fill="#FF6B6B", width=48, height=48),
                Star(
                    fill="#4ECDC4",
                    stroke="#FFFFFF",
                    stroke_width=2,
                    width=64,
                    height=64,
                ),
            ],
            spacing=12,
            padding=16,
        )
    )

nib.run(main)

Shape with gradient fill

import nib

def main(app: nib.App):
    path = (nib.Path()
        .move_to(50, 0)
        .line_to(100, 38)
        .line_to(81, 100)
        .line_to(19, 100)
        .line_to(0, 38)
        .close())

    app.build(
        nib.Shape(
            path=path,
            view_box=(100, 100),
            fill=nib.LinearGradient(
                colors=["#FF6B6B", "#4ECDC4"],
                start=(0, 0),
                end=(1, 1),
            ),
            width=200,
            height=200,
            padding=16,
        )
    )

nib.run(main)