This is indeed due to another side effect of opacity
— elements it is applied to create a new stacking context, which is part of the system by which the browser decides which elements are drawn on top of which other elements (also affected by positioning, and a bunch of other things). So applying opacity
to an element can cause unintended layout effects.
Read this article to learn more: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context
I fixed your example by updating the markup like this:
<div class="main-img">
<button>Darken</button>
<img src="https://source.unsplash.com/random" alt="">
</div>
But I can’t really explain explain why 
I think it is something to do with the fact that originally, the button was separate from the <div>
, and applying opacity to it caused it to go into a separate stacking context, which then sat behind the <div>
, making it unclickable. The <div>
is later in the source order, so sits on top.
When it is inside the <div>
, it will go into a separate stacking context, but this is inside the <div>
s parent sacking context and so will sit on top of it.