If I'm not mistaken, widget.layout.border gets translated into adding the CSS border property. This property is short-hand for three separate border properties: border-width, border-style, and border-color. By forcing someone to use the shorthand border property, rather than the individual properties, there is no way to set different border types for the different border edges. There is no way (AFAICT), for instance, to build the equivalent of a CSS body like:
#mywidget {
border-style: none none solid none; /* I just want a border at the bottom of my widget */
border-width: 2px;
border-color: black;
}
My use case is that I'm trying to build a table of widgets. Each row is an HBox of several widgets. It would be useful to specify that each row should have a bottom border.
I understand that I can obviously do this with a custom widget. Nevertheless, this seems like a fairly common use case. We could add three additional unicode traitlets to the Layout widget: border_style, border_width and border_color. We could also raise an exception if the user specifies both these and the shorthand border.
I'm very happy to submit a PR if people think this would be a useful addition.
@pbugnion thanks for opening the issue.
Whenever there is a shorthand for multiple properties (like border / margin / padding) this is the one that we have been exposing for the sake of simplicity. (So no border-[top/right/bottom/left]) properties were exposed.
Overall, I think that at least border-width should be added because it is part of the css box model.
@jasongrout thoughts?
Some quick technical background on this.
If you set border to 1px solid blue and then border-width to 2px, the border property is changed to 2px solid blue. If you modify border-width again to 2px 4px, the border property is changed to ''.
So shorthand properties are bound to properties in a complex fashion so that we cannot expose them both through traitlets.
Therefore, exposing border-width would require removing the border property... This would be a backward incompatible change...
Hmm... CSS is complicated. Thanks a lot for your help.
Would there be scope for making the backward incompatible change in the next release?
Alternatively, could we overload the border traitlet? For instance:
widget.layout.border = "2px solid blue"
#聽=> results in css like { border: 2px solid blue; }
widget.layout.border = { "style": "solid", "width" : "2px 4px", "color": "blue" }
# => results in css like { border-style: solid ; border-width: 2px 4px ; border-color : blue }
#聽OR
widget.layout.border = Border(style="solid", width="2px 4px", color="blue")
#聽where `Border` is a new Widget type.
I appreciate that this would be different to the current layout traitlets, which have a near 1-to-1 mapping with underlying CSS properties.
A very different option which would address my problem (but not the underlying issue) would be a new widget type for tables: something like the HBox / VBox widget, but aimed specially at creating tabular layouts of widgets. I suspect this would probably live in a separate repository, at least initially? For reference, this is my current widget -- it does everything I want, except I would like a black line under the headers:

Would there be scope for making the backward incompatible change in the next release?
Yes, since it is a major release. However, I think we've been changing the way widgets can be styled pretty much every time we could for a long time - no doubt some people are getting tired of changing their code. But I think you're right that we don't have it right yet.
a new widget type for tables: something like the HBox / VBox widget, but aimed specially at creating tabular layouts of widgets.
I think this makes a lot of sense on its own. It's fragile to get a table layout from hbox/vbox combinations, I think.
It's a tricky thing to get the needs balanced between ease of use and power with regards to the styling.
Thanks for your help!
Yes, since it is a major release. However, I think we've been changing the way widgets can be styled pretty much every time we could for a long time - no doubt some people are getting tired of changing their code. But I think you're right that we don't have it right yet.
Is that a tentative +1 for a PR deprecating border in favour of border_style, border_width and border_color?
For the tabular arrangement of widgets, would there be interest in including this in core ipywidgets, or should I set up another repository?
I just don't know how to deprecate without conflicting between the two implementations.
I guess one way would be to keep the border property synchronised with border_style, border_width and border_color at the traitlets level. Something like this:
import traitlets
class Layout(traitlets.HasTraits):
border = traitlets.Unicode(None, allow_none=True)
border_width = traitlets.Unicode(None, allow_none=True)
border_style = traitlets.Unicode(None, allow_none=True)
border_color = traitlets.Unicode(None, allow_none=True)
@traitlets.validate('border')
def _validate_border(self, proposal):
#聽validate that this is a valid border property
return proposal['value']
@traitlets.observe('border')
def _observe_border(self, change):
new_border = change['new']
width, style, color = new_border.split(' ')
self.border_width = width
self.border_style = style
self.border_color = color
@traitlets.observe('border_width')
def _observe_width(self, change):
new_width = change['new']
new_width = new_width.strip()
if len(new_width.split(" ")) > 1:
# invalidate shorthand border property
self.border = ""
else:
self.border = "{} {} {}".format(self.border_width, self.border_style, self.border_color)
l = Layout()
l.border = "2px solid blue"
l.border_width, l.border_style, l.border_color
#聽=> (u'2px', u'solid', u'blue')
This is somewhat ugly since we would be re-implementing a lot of CSS validation at the widget layer. I suspect this would be a nightmare to maintain, but it would only need to be maintained correctly during the deprecation period.
@jasongrout I would like to postpone this to after 6.0.
I'm really interested in a table like widget and was attempting to make my own with HBox VBox rather unsuccessfully. I need to embed a button next to each row and I can't do that with a pandas dataframe.

@AlJohri Thanks for the comment -- I've done something like that in a conference demo.
The demo stacks a bunch of widgets into HBoxes to make rows, then stacks the rows into a VBox to make a full table. It doesn't have buttons specifically, but should be easy enough to adapt.
Some third party table widget that takes care of the layout would be a welcome addition to the ecosystem.
Thanks @pbugnion! I saw your repository after commenting. It looks like a great start. Were you able to figure out how to get the borders?
No, I don't think that's possible in pure Python. Depending on your level of commitment, you could add CSS classes to each HBox, and inject the CSS into the notebook directly.
import ipywidgets as widgets
from IPython.display import HTML
# In a separate cell so it gets displayed:
HTML("""
<style>
.bottom-border {
border-bottom: 1px solid black
}
</style>
""")
#聽Build your widget and add a CSS class to it:
w = widgets.HBox([
widgets.Label('t1'),
widgets.Label('t2')
])
w.add_class('bottom-border')
w

A few more thoughts:
now that CSS grid (https://caniuse.com/#feat=css-grid) is making its way into browsers, I think we could have (in core) a GridBox widget that places its widgets in a grid using the CSS grid spec.
Perhaps the column heading underline can be part of the gridbox widget as a .style property?
A GridBox widget is now #1942.
Closing in favour of #2689. Thanks @zerline !
Most helpful comment
Yes, since it is a major release. However, I think we've been changing the way widgets can be styled pretty much every time we could for a long time - no doubt some people are getting tired of changing their code. But I think you're right that we don't have it right yet.
I think this makes a lot of sense on its own. It's fragile to get a table layout from hbox/vbox combinations, I think.
It's a tricky thing to get the needs balanced between ease of use and power with regards to the styling.