@@ -1452,14 +1452,25 @@ def set_sticky(
14521452 Whether to make the index or column headers sticky.
14531453 pixel_size : int, optional
14541454 Required to configure the width of index cells or the height of column
1455- header cells when sticking a MultiIndex. Defaults to 75 and 25 respectively.
1455+ header cells when sticking a MultiIndex (or with a named Index).
1456+ Defaults to 75 and 25 respectively.
14561457 levels : list of int
14571458 If ``axis`` is a MultiIndex the specific levels to stick. If ``None`` will
14581459 stick all levels.
14591460
14601461 Returns
14611462 -------
14621463 self : Styler
1464+
1465+ Notes
1466+ -----
1467+ This method uses the CSS 'position: sticky;' property to display. It is
1468+ designed to work with visible axes, therefore both:
1469+
1470+ - `styler.set_sticky(axis="index").hide_index()`
1471+ - `styler.set_sticky(axis="columns").hide_columns()`
1472+
1473+ may produce strange behaviour due to CSS controls with missing elements.
14631474 """
14641475 if axis in [0 , "index" ]:
14651476 axis , obj , tag , pos = 0 , self .data .index , "tbody" , "left"
@@ -1471,15 +1482,42 @@ def set_sticky(
14711482 raise ValueError ("`axis` must be one of {0, 1, 'index', 'columns'}" )
14721483
14731484 if not isinstance (obj , pd .MultiIndex ):
1474- return self .set_table_styles (
1475- [
1485+ # handling MultiIndexes requires different CSS
1486+ props = "position:sticky; background-color:white;"
1487+
1488+ if axis == 1 :
1489+ # stick the first <tr> of <head> and, if index names, the second <tr>
1490+ # if self._hide_columns then no <thead><tr> here will exist: no conflict
1491+ styles : CSSStyles = [
14761492 {
1477- "selector" : f" { tag } th " ,
1478- "props" : f"position:sticky; { pos } : 0px; background-color:white ;" ,
1493+ "selector" : "thead tr:first-child " ,
1494+ "props" : props + "top: 0px; z-index:2 ;" ,
14791495 }
1480- ],
1481- overwrite = False ,
1482- )
1496+ ]
1497+ if not self .index .names [0 ] is None :
1498+ styles [0 ]["props" ] = (
1499+ props + f"top:0px; z-index:2; height:{ pixel_size } px;"
1500+ )
1501+ styles .append (
1502+ {
1503+ "selector" : "thead tr:nth-child(2)" ,
1504+ "props" : props
1505+ + f"top:{ pixel_size } px; z-index:2; height:{ pixel_size } px; " ,
1506+ }
1507+ )
1508+ else :
1509+ # stick the first <th> of each <tr> in both <thead> and <tbody>
1510+ # if self._hide_index then no <th> will exist in <tbody>: no conflict
1511+ # but <th> will exist in <thead>: conflict with initial element
1512+ styles = [
1513+ {
1514+ "selector" : "tr th:first-child" ,
1515+ "props" : props + "left:0px; z-index:1;" ,
1516+ }
1517+ ]
1518+
1519+ return self .set_table_styles (styles , overwrite = False )
1520+
14831521 else :
14841522 range_idx = list (range (obj .nlevels ))
14851523
0 commit comments