Effect Modifiers

Effect modifiers add visual effects and transformations to views, including shadows, borders, blend modes, scaling, offsets, and animations.

import nib

nib.VStack(
    controls=[nib.Text("Card")],
    shadow_color="black",
    shadow_radius=10,
    shadow_y=4,
    border_color="#CCCCCC",
    border_width=1,
    animation=nib.Animation.spring(),
)

Shadow

Drop shadows are controlled by four parameters that work together. At least one shadow parameter must be set for the shadow to render.

shadow_color

The color of the drop shadow.

Type Default
Color | str None

shadow_radius

The blur radius of the shadow in points. Larger values produce softer shadows.

Type Default (when shadow is active)
float 4.0

shadow_x

Horizontal offset of the shadow in points. Positive values shift the shadow right.

Type Default (when shadow is active)
float 0.0

shadow_y

Vertical offset of the shadow in points. Positive values shift the shadow down.

Type Default (when shadow is active)
float 2.0
# Basic shadow
nib.Rectangle(fill="white", shadow_color="black", shadow_radius=8)

# Customized shadow
nib.VStack(
    controls=[nib.Text("Elevated Card")],
    padding=16,
    background="#FFFFFF",
    corner_radius=12,
    shadow_color="black",
    shadow_radius=12,
    shadow_x=0,
    shadow_y=6,
)

# Shadow with only radius (no color)
nib.Rectangle(fill="white", shadow_radius=4)

Border

Border modifiers draw an outline around a view's bounds. Unlike stroke (which is for shapes), borders work on any view type.

border_color

The color of the border. Required for the border to render.

Type Default
Color | str None

border_width

The width of the border in points.

Type Default (when border_color is set)
float 1.0
# Default 1pt border
nib.Rectangle(fill="white", border_color="#CCCCCC")

# Custom border width
nib.VStack(
    controls=[nib.Text("Bordered content")],
    padding=12,
    border_color=nib.Color.RED,
    border_width=2,
    corner_radius=8,
)

Note: border_width without border_color has no effect.


blend_mode

Controls how a view is composited with the content behind it. See BlendMode for all available modes.

Type Default
BlendMode | str None
nib.Rectangle(fill=nib.Color.BLUE, blend_mode=nib.BlendMode.MULTIPLY)
nib.Image(src="overlay.png", blend_mode="screen")

scale

Applies a uniform scale transformation to a view. The view is scaled from its center point. A value of 1.0 is the original size.

Type Default
float None
nib.SFSymbol("star.fill", scale=2.0)   # Double size
nib.Text("Small", scale=0.75)                     # 75% size

offset

Shifts a view from its natural position. The view still occupies its original space in the layout. See Offset for constructor details.

Type Default
Offset None
nib.Circle(
    fill=nib.Color.BLUE,
    width=50,
    height=50,
    offset=nib.Offset(10, -5),
)

Animation

animation

Configures how property changes on this view are animated. Once set, the animation is "sticky". All future property mutations on the view animate using this configuration. See Animation for factory methods and presets.

Type Default
Animation None
# Spring animation on all property changes
counter = nib.Text("0", animation=nib.Animation.spring())

def increment():
    counter.content = str(int(counter.content) + 1)  # Animates automatically
# Ease-in-out opacity toggle
box = nib.Rectangle(
    fill="blue",
    width=100,
    height=100,
    animation=nib.Animation.easeInOut(0.3),
)

content_transition

Controls how a view's content animates when it changes. This is separate from view transitions. It affects the content within a view that remains visible. See ContentTransition for all values.

Type Default
ContentTransition | str None
# Rolling numeric digits
counter = nib.Text(
    "0",
    content_transition=nib.ContentTransition.NUMERIC_TEXT,
    animation=nib.Animation.spring(),
)

# Fading content swap
label = nib.Text("Status", content_transition=nib.ContentTransition.OPACITY)

transition

Controls how a view animates when it appears in or disappears from the view hierarchy. See Transition for all values, asymmetric transitions, and custom keyframes.

Type Default
Transition | str | TransitionConfig None
# Simple fade
panel = nib.VStack(controls=[...], transition=nib.Transition.OPACITY)

# Asymmetric: slide in, fade out
panel = nib.VStack(
    controls=[...],
    transition=nib.Transition.asymmetric(
        insertion=nib.Transition.SLIDE,
        removal=nib.Transition.OPACITY,
    ),
)

# Combined: fade and scale simultaneously
badge = nib.Text(
    "New",
    transition=nib.Transition.combined(nib.Transition.OPACITY, nib.Transition.SCALE),
)

Examples

Elevated card with shadow and border

import nib

def main(app: nib.App):
    app.build(
        nib.VStack(
            controls=[
                nib.Text("Notification", font=nib.Font.HEADLINE),
                nib.Text(
                    "You have 3 new messages.",
                    foreground_color=nib.Color.SECONDARY,
                ),
            ],
            spacing=6,
            padding=16,
            background="#FFFFFF",
            corner_radius=12,
            border_color="#E0E0E0",
            border_width=1,
            shadow_color="black",
            shadow_radius=10,
            shadow_y=4,
        )
    )

nib.run(main)

Animated counter with effects

import nib

def main(app: nib.App):
    count = 0
    label = nib.Text(
        "0",
        font=nib.Font.system(48, nib.FontWeight.BOLD),
        content_transition=nib.ContentTransition.NUMERIC_TEXT,
        animation=nib.Animation.spring(response=0.4, damping=0.6),
    )

    def increment():
        nonlocal count
        count += 1
        label.content = str(count)

    app.build(
        nib.VStack(
            controls=[
                label,
                nib.Button(
                    "Add",
                    action=increment,
                    style=nib.ButtonStyle.BORDERED_PROMINENT,
                ),
            ],
            spacing=16,
            padding=24,
        )
    )

nib.run(main)