It boils down to some number of iterations, where each iteration is addition of some shape (having some properties) to the canvas.
Those properties include the type of shape (dot, line, polygon), size, position, and color.
The number of iterations, along with the type of shape and its size determine the density of content on the canvas.
The other properties have their obvious effects.
The magic, if there is any, is in choosing ranges of acceptable property values (randomly). For example, if you make every property be a random value between the min and max acceptable for that property, you generally end up with some variation of noise. It's not really artistic in my opinion.
So what I did was play with narrowing ranges of acceptable properties, especially tying those ranges to the previous values of the previous shape (that was drawn on the previous iteration). Position is the best example. The first iteration would have no previous shape, so its position would be randomly somewhere on the canvas. But the next iteration would some random distance from the previous one, within a range that itself might have slightly randomized boundaries. That would result in clustering... which is generally pleasing, but ultimately monotonous. So I would provide some % chance of a "break out", such that the new position could be totally random rather than related to the previous.
I applied the same concepts of managed, previously-related randomization of value ranges to the other properties of the shape being drawn on the given iteration. For color, I might have it vary only one channel of the RGB, and only a small amount. But again, I would have a low % chance of a completely random/new color. And one of my favorite little additions was the complementary color chance. Once in a while, the next drawn shape would be close to the complementary color of the previous shape.
Finally, since "tuning" the randomization boundaries and exception chances was a bit tedious, I added a layer of similar randomization behaviors to the boundary rules themselves. That means that some images could be total chaos, and others could theoretically just be a single blob of garbage in one spot of the canvas.
Like a "good" photographer, I let it generate hundreds of images. Then I chose some that I thought were compelling. The rest went in the trash.
I have considered training a NN against famously popular and unpopular paintings, hoping to teach it how to recognize art that other people would like. Then I would setup a pipeline of generate-judge-keep/delete-repeat processes that should churn out abstract art that would make me famous and finally prove my value to the world. :)