#Declarations

A declaration is a table. The only required key is ClassName.

{
    ClassName = "TextLabel",
    Text = "Hello",
    Size = UDim2.fromOffset(200, 40),
}

Every other string key is set on the instance. Nothing special so far.

#Children

Tables in the array part are children.

{
    ClassName = "Frame",
    Size = UDim2.fromOffset(300, 200),

    { ClassName = "UICorner", CornerRadius = UDim.new(0, 8) },

    {
        ClassName = "TextLabel",
        Text = "Nested",
    },
}

A child parents to the declaration containing it, so the shape of the table is the shape of the tree.

#Events are just keys

If a key names a signal instead of a property, the value is connected rather than assigned:

{
    ClassName = "TextButton",
    Text = "Click me",

    Activated = function(self)
        print("clicked", self.Name)
    end,
}

Text is assigned, Activated is connected, and you did not have to say which is which - Vision checks what the member actually is.

Notice the callback receives self. Every callback in Vision gets the instance first and the vision that built it second, so you can edit the instance and reach the vision's values without closing over either.

Activated = function(self, Panel)
    Panel.Count(Panel.Count() + 1)
end,

That second argument matters more than it looks: while you are writing a declaration, the vision it produces does not exist yet, so there is nothing to close over. This is how you get at it.

#Attributes and tags

{
    ClassName = "Frame",

    attributes = { Role = "Panel" },
    tags = { "Managed" },

    AttributeChanged = {
        Role = function(self, _, Role)
            print("role is now", Role)
        end,
    },
}

AttributeChanged connects after the declared attributes are applied, so setting up initial state does not fire your handler. The new value arrives as the third argument, so you rarely need GetAttribute inside the handler.

#Mounting somewhere else

mount says where a node goes. Given an Instance, it parents there:

mount(PlayerGui)

Given a string, it finds the node in the same tree with that Name:

{
    ClassName = "ScreenGui",
    mount(PlayerGui),

    { ClassName = "Frame", Name = "Panel" },

    {
        ClassName = "TextLabel",
        mount("Panel"),     -- lands inside Panel
    },
}

That is useful when a piece of UI is easier to write at the top level than where it belongs.

#Next

Staging and mounting - the part that makes this different.