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