The differences between Plots.jl and Makie.jl can sometimes be a bit confusing, especially because both use a function called plot. But Plots’s plot creates abstract objects which are descriptions of figures with possibly multiple subplots arranged in layouts. When you plot! into those, you just add to the description, which is at the end converted into a complete visualization in whatever backend you have activated.
In Makie, there’s Figure (a Scene with a GridLayout), which can contain stuff like Axis, Colorbar, Legend, etc, and all of these objects are built up out of Scenes which contain “plot primitives” such as Lines, Scatter, Heatmap, etc. All plotting functions at their core create such “plot primitives”, but, as a convenience, the containers for these are created as well in the default case. Usually, that’s a Figure with an Axis inside. So heatmap(...) makes a Heatmap plot object, which is stored inside an Axis, which is placed inside a Figure. To pass keyword arguments to the Axis, you can do heatmap(...; axis = (; title = ... and for the Figure you do heatmap(...; figure = (; resolution = ....
Remember that in Plots.jl, you don’t control all these different objects, you just make an abstract “plot description”, therefore all keywords are top-level keywords, and Plots.jl sorts out which relate to what would be the Figure in Makie, or the Axis, or the Plot. But in Makie, it’s completely separated, that’s why all Figure keywords have to be sent in via the special figure keyword. For example, in principle some plot might have a title attribute, and this way you could set that plus the axis title via some_plot(...; title = "Plot title", axis = (; title = "Axis title"....
If, in Makie, you go the mutation route, the plotting function loses its ability to pass Figure and Axis information on, because those have already been created beforehand:
f = Figure(resolution = ...)
ax = Axis(f[1, 1], title = ...)
heatmap!(ax, ...; # no figure or axis keyword allowed here, those already exist