If I add your code to _ready, I don’t see a panel, as you said. But, looking at the instantiated panel in the Remote scene tree, I found all I had to do was set its size in the inspector to see it.
Armed with that info, I added one line to your code, which renders the panel correctly. Here’s my working test code:
func _ready():
var panel = Panel.new()
panel.rect_size = Vector2(200, 200) # <-- added this
self.add_child(panel)
var new_style = StyleBoxFlat.new()
new_style.set_bg_color(Color(1, 0, 0, 1))
panel.set("custom_styles/panel", new_style)
Thanks, it seems specifying the sizes is the key!
I was initially creating the panel under a MarginContainer, also created through code.
It turns out this MarginContainer also had a size of Vector2(0, 0) as default.
Therefore no matter what I set as the size of the panel, it would not be rendered.
Specifying the size of the MarginContainer first results in the panel being scaled to be the size of the MarginContainer.
What confused me was that I assumed the default sizes for these nodes would be the same created through inspector or code.