The limitation is down to the existing arguments and how pretty much all widgets typically output content:
echo $args['before_widget'];
if ( $title ) {
echo $args['before_title'] . $title . $args['after_title'];
}
// Widget content
echo $args['after_widget'];
Some widgets may offer before/after content hooks, but if you want a “global” fix there’s no surefire way around it.
The only solution I can see is to add the wrap twice and use CSS :empty
to hide the leftovers:
register_sidebar(
array(
'before_widget' => '<div class="box box_push"><div class="box box-bd">',
'before_title' => '</div>', // If we have a title, close the wrap above
'after_title' => '<div class="box box-bd">', // Now open another one
'after_widget' => '</div></div>',
)
);
So with widgets that have titles, you’ll end up with:
<div class="box box_push">
<div class="box box-bd"></div>
Widget Title
<div class="box box-bd">
Widget content
</div>
</div>
Enter the aforementioned CSS:
.box-bd:empty {
display: none;
}
This will hide that first redundant wrap. A little dirty, I know, but it means title or no title, you’ll always have your content wrapped in .box-bd
.