Skip to content
Development
Skill

/makepad-2.0-dsl

CRITICAL: Use for Makepad 2.0 DSL syntax and property system. Triggers on: makepad dsl, script_mod!, makepad syntax, makepad property, makepad 2.0 syntax, colon syntax, merge operator, named instance, let binding, mod.widgets, register_widget, script_component, type_default,

From plugin
makepad-skills
74514 skills
Install
$ npx -y skills add ZhangHanDong/makepad-skills --skill makepad-2.0-dsl --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/makepad-2.0-dsl

Context preview

The summary Claude sees to decide when to auto-load this skill.

CRITICAL: Use for Makepad 2.0 DSL syntax and property system. Triggers on: makepad dsl, script_mod!, makepad syntax, makepad property, makepad 2.0 syntax, colon syntax, merge operator, named instance, let binding, mod.widgets, register_widget, script_component, type_default,

SKILL.md

makepad-2.0-dsl.SKILL.md
name: makepad-2.0-dsl
description: |
  CRITICAL: Use for Makepad 2.0 DSL syntax and property system. Triggers on:
  makepad dsl, script_mod!, makepad syntax, makepad property, makepad 2.0 syntax,
  colon syntax, merge operator, named instance, let binding, mod.widgets,
  register_widget, script_component, type_default, widgets_internal

Makepad 2.0 DSL Syntax Skill

Overview

Makepad 2.0 replaced the compile-time `live_design!` macro with the runtime `script_mod!` macro, powered by the Splash scripting language. This skill covers the complete DSL syntax, property system, registration patterns, and common pitfalls.

Key Syntax Rules

Property Assignment: Colon, NOT Equals

key: value          // CORRECT - colon syntax
key = value         // WRONG - old 1.x syntax, no longer works

Properties are whitespace/newline separated. No commas between siblings.

View{
    width: Fill
    height: Fit
    flow: Down
    spacing: 10
    padding: 15
}

Named Instances: `:=` Operator

Use `:=` to create addressable, named widget instances:

my_button := Button{ text: "Click me" }
title := Label{ text: "Hello" }

Named instances are:

  • Addressable from Rust code via `id!(my_button)` or `ids!(my_button)`
  • Overridable via dot-path syntax: `MyTemplate{ title.text: "New text" }`
  • Stored in the script object's `vec` (not `map`)

Regular properties use `:` and go into `map`:

width: Fill       // regular property -> map
label := Label{}  // named child -> vec

Merge Operator: `+:`

The `+:` operator extends/merges with the parent instead of replacing:

draw_bg +: {
    color: #f00    // Only overrides color, keeps all other draw_bg properties
}

Without `+:`, you REPLACE the entire property:

draw_bg: { color: #f00 }    // REPLACES all of draw_bg - loses hover, border, etc.
draw_bg +: { color: #f00 }  // MERGES - only changes color, keeps everything else

Dot-Path Shorthand

Dot-path is syntactic sugar for merge:

draw_bg.color: #f00
// is equivalent to:
draw_bg +: { color: #f00 }

draw_text.text_style.font_size: 14
// is equivalent to:
draw_text +: { text_style +: { font_size: 14 } }

Let Bindings: Local Templates

`let` creates local, reusable templates within a `script_mod!` block:

let MyCard = RoundedView{
    width: Fill height: Fit
    padding: 16 flow: Down spacing: 8
    draw_bg.color: #2a2a3d
    draw_bg.border_radius: 8.0
    title := Label{ text: "Default Title" draw_text.color: #fff }
    body := Label{ text: "" draw_text.color: #aaa }
}

// Instantiate and override:
MyCard{ title.text: "Card 1" body.text: "Content here" }
MyCard{ title.text: "Card 2" body.text: "More content" }

**IMPORTANT**: `let` bindings are LOCAL to the `script_mod!` block. They cannot be accessed from other `script_mod!` blocks. To share across modules, store in `mod.widgets.*`.

Spread Operator: `..`

Inherit all properties from another definition:

set_type_default() do #(DrawMyShader::script_shader(vm)){
    ..mod.draw.DrawQuad   // Inherit from DrawQuad
}

Script Module Structure

Basic App Structure

use makepad_widgets::*;

app_main!(App);

script_mod!{
    use mod.prelude.widgets.*

    load_all_resources() do #(App::script_component(vm)){
        ui: Root{
            main_window := Window{
                window.inner_size: vec2(800, 600)
                body +: {
                    // UI content here
                    my_button := Button{ text: "Click" }
                }
            }
        }
    }
}

impl App {
    fn run(vm: &mut ScriptVm) -> Self {
        crate::makepad_widgets::script_mod(vm);  // 1. Register base widgets
        App::from_script_mod(vm, self::script_mod)
    }
}

#[derive(Script, ScriptHook)]
pub struct App {
    #[source] source: ScriptObjectRef,   // REQUIRED for Script-derived structs
    #[live] ui: WidgetRef,
}

impl MatchEvent for App {
    fn handle_actions(&mut self, cx: &mut Cx, actions: &Actions) {
        if self.ui.button(ids!(my_button)).clicked(actions) {
            log!("Button clicked!");
        }
    }
}

impl AppMain for App {
    fn handle_event(&mut self, cx: &mut Cx, event: &Event) {
        self.match_event(cx, event);
        self.ui.handle_event(cx, event, &mut Scope::empty());
    }
}

Widget Definition Module

script_mod!{
    use mod.prelude.widgets_internal.*   // For widget library internals
    use mod.widgets.*                     // Access other registered widgets

    // Step 1: Register the Rust struct as a widget base
    mod.widgets.MyWidgetBase = #(MyWidget::register_widget(vm))

    // Step 2: Create a styled variant with default properties
    mod.widgets.MyWidget = set_type_default() do mod.widgets.MyWidgetBase{
        width: Fill
        height: Fit
        padding: theme.space_2

        draw_bg +: {
            color: theme.color_bg_app
        }
    }
}

Registration Patterns

Widget Registration

For structs that implement the `Widget` trait:

mod.widgets.MyWidgetBase = #(MyWidget::register_widget(vm))

Rust side:

#[derive(Script, ScriptHook, Widget)]
pub struct MyWidget {
    #[source] source: ScriptObjectRef,  // REQUIRED
    #[walk] walk: Walk,
    #[layout] layout: Layout,
    #[redraw] #[live] draw_bg: DrawQuad,
    #[live] draw_text: DrawText,
    #[rust] my_state: i32,  // Runtime-only, not exposed to script
}

Component Registration

For non-widget structs that need script integration:

mod.widgets.MyComponentBase = #(MyComponent::script_component(vm))

Draw Shader Registration

For custom draw types with shader fields:

set_type_default() do #(DrawMyShader::script_shader(vm)){
    ..mod.draw.DrawQuad   // Inherit from DrawQuad
}

Rust side:

#[derive(Script, ScriptHook)]
#[repr(C)]
pub struct DrawMyShader {
    #[deref] draw_super: DrawQuad,
    #[live] my_param: f32,
}

Setting Type Defaults

mod.wi
Read more
Ships withmakepad-skills

Skills for building cross-platform UI applications with Makepad 2.0.

Get the whole plugin

Other skills on makepad-skills.