Repository: pandas-dev/pandas

Issue:
CSS selector for index cells
While using **Jupyter notebooks**, I like to change the **color of header cells** in Pandas dataframes (all index cells, up and left) for more **contrast between the indexes and the data itself** (especially when I'm dealing with multiple indexes after using `.groupby()` like in the picture below) :

![example](https://i.stack.imgur.com/BvvvM.png)

I used to achieve this using this CSS code :

```html
%%HTML
<style> 
.dataframe th {
background: #3f577c ;
font-family: monospace ;
color: white ;
border: 3px solid white ;
text-align: left !important ; } 
</style>
```

This worked because every index cell (in all indexes, up and left) used to be represented by a `<th> </th>` HTML tag.

However, after updating to Pandas version **`0.25.1`** from version **`0.21.x`**, the CSS stopped working because the HTML changed so only the upper index cells (column names) are wrapped in `<th> <th>` tags. The left index simply uses `<td> </td>` tags, just like the data cells (See image below. Note that both Genre and MediaTypeId are indexes):

![example](https://i.stack.imgur.com/9AeBd.jpg)

Was this intentional? I tried using `first-child` like below but it only worked with dataframes having only 2 indexes (up and left). For the dataframes with more than one index on the left, only the first index is affected :

```html
%%HTML
<style>
.dataframe th, td:first-child {
background: #3f577c ;
font-family: monospace ;
color: white ;
border: 3px solid white ;
text-align: left !important ; }                                             
</style>
```

Any ideas?


Discussion:
Can you go through the history and see when / why it was changed?
I went through all the changelogs from 0.21 to 0.25.1 but didn't find any mention of it.
I can confirm that the behaviour for _repr_html_ is incorrect. I think this is due to the default for bold_rows being False for DataFrameFormatter while the default being True for to_html.

will do some tests tomorrow but may be caused by #27991
Great! Thank you!

Resolving patch:
diff --git a/pandas/core/frame.py b/pandas/core/frame.py
--- a/pandas/core/frame.py
+++ b/pandas/core/frame.py
@@ -679,10 +679,25 @@ def _repr_html_(self):
 
             formatter = fmt.DataFrameFormatter(
                 self,
+                columns=None,
+                col_space=None,
+                na_rep="NaN",
+                formatters=None,
+                float_format=None,
+                sparsify=None,
+                justify=None,
+                index_names=True,
+                header=True,
+                index=True,
+                bold_rows=True,
+                escape=True,
                 max_rows=max_rows,
                 min_rows=min_rows,
                 max_cols=max_cols,
                 show_dimensions=show_dimensions,
+                decimal=".",
+                table_id=None,
+                render_links=False,
             )
             formatter.to_html(notebook=True)
             return formatter.buf.getvalue()


Test patch:
diff --git a/pandas/tests/io/formats/data/html/html_repr_max_rows_10_min_rows_12.html b/pandas/tests/io/formats/data/html/html_repr_max_rows_10_min_rows_12.html
new file mode 100644
--- /dev/null
+++ b/pandas/tests/io/formats/data/html/html_repr_max_rows_10_min_rows_12.html
@@ -0,0 +1,70 @@
+<div>
+<style scoped>
+    .dataframe tbody tr th:only-of-type {
+        vertical-align: middle;
+    }
+
+    .dataframe tbody tr th {
+        vertical-align: top;
+    }
+
+    .dataframe thead th {
+        text-align: right;
+    }
+</style>
+<table border="1" class="dataframe">
+  <thead>
+    <tr style="text-align: right;">
+      <th></th>
+      <th>a</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <th>0</th>
+      <td>0</td>
+    </tr>
+    <tr>
+      <th>1</th>
+      <td>1</td>
+    </tr>
+    <tr>
+      <th>2</th>
+      <td>2</td>
+    </tr>
+    <tr>
+      <th>3</th>
+      <td>3</td>
+    </tr>
+    <tr>
+      <th>4</th>
+      <td>4</td>
+    </tr>
+    <tr>
+      <th>...</th>
+      <td>...</td>
+    </tr>
+    <tr>
+      <th>56</th>
+      <td>56</td>
+    </tr>
+    <tr>
+      <th>57</th>
+      <td>57</td>
+    </tr>
+    <tr>
+      <th>58</th>
+      <td>58</td>
+    </tr>
+    <tr>
+      <th>59</th>
+      <td>59</td>
+    </tr>
+    <tr>
+      <th>60</th>
+      <td>60</td>
+    </tr>
+  </tbody>
+</table>
+<p>61 rows × 1 columns</p>
+</div>
diff --git a/pandas/tests/io/formats/data/html/html_repr_max_rows_10_min_rows_4.html b/pandas/tests/io/formats/data/html/html_repr_max_rows_10_min_rows_4.html
new file mode 100644
--- /dev/null
+++ b/pandas/tests/io/formats/data/html/html_repr_max_rows_10_min_rows_4.html
@@ -0,0 +1,46 @@
+<div>
+<style scoped>
+    .dataframe tbody tr th:only-of-type {
+        vertical-align: middle;
+    }
+
+    .dataframe tbody tr th {
+        vertical-align: top;
+    }
+
+    .dataframe thead th {
+        text-align: right;
+    }
+</style>
+<table border="1" class="dataframe">
+  <thead>
+    <tr style="text-align: right;">
+      <th></th>
+      <th>a</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <th>0</th>
+      <td>0</td>
+    </tr>
+    <tr>
+      <th>1</th>
+      <td>1</td>
+    </tr>
+    <tr>
+      <th>...</th>
+      <td>...</td>
+    </tr>
+    <tr>
+      <th>59</th>
+      <td>59</td>
+    </tr>
+    <tr>
+      <th>60</th>
+      <td>60</td>
+    </tr>
+  </tbody>
+</table>
+<p>61 rows × 1 columns</p>
+</div>
diff --git a/pandas/tests/io/formats/data/html/html_repr_max_rows_12_min_rows_None.html b/pandas/tests/io/formats/data/html/html_repr_max_rows_12_min_rows_None.html
new file mode 100644
--- /dev/null
+++ b/pandas/tests/io/formats/data/html/html_repr_max_rows_12_min_rows_None.html
@@ -0,0 +1,78 @@
+<div>
+<style scoped>
+    .dataframe tbody tr th:only-of-type {
+        vertical-align: middle;
+    }
+
+    .dataframe tbody tr th {
+        vertical-align: top;
+    }
+
+    .dataframe thead th {
+        text-align: right;
+    }
+</style>
+<table border="1" class="dataframe">
+  <thead>
+    <tr style="text-align: right;">
+      <th></th>
+      <th>a</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <th>0</th>
+      <td>0</td>
+    </tr>
+    <tr>
+      <th>1</th>
+      <td>1</td>
+    </tr>
+    <tr>
+      <th>2</th>
+      <td>2</td>
+    </tr>
+    <tr>
+      <th>3</th>
+      <td>3</td>
+    </tr>
+    <tr>
+      <th>4</th>
+      <td>4</td>
+    </tr>
+    <tr>
+      <th>5</th>
+      <td>5</td>
+    </tr>
+    <tr>
+      <th>...</th>
+      <td>...</td>
+    </tr>
+    <tr>
+      <th>55</th>
+      <td>55</td>
+    </tr>
+    <tr>
+      <th>56</th>
+      <td>56</td>
+    </tr>
+    <tr>
+      <th>57</th>
+      <td>57</td>
+    </tr>
+    <tr>
+      <th>58</th>
+      <td>58</td>
+    </tr>
+    <tr>
+      <th>59</th>
+      <td>59</td>
+    </tr>
+    <tr>
+      <th>60</th>
+      <td>60</td>
+    </tr>
+  </tbody>
+</table>
+<p>61 rows × 1 columns</p>
+</div>
diff --git a/pandas/tests/io/formats/data/html/html_repr_max_rows_None_min_rows_12.html b/pandas/tests/io/formats/data/html/html_repr_max_rows_None_min_rows_12.html
new file mode 100644
--- /dev/null
+++ b/pandas/tests/io/formats/data/html/html_repr_max_rows_None_min_rows_12.html
@@ -0,0 +1,269 @@
+<div>
+<style scoped>
+    .dataframe tbody tr th:only-of-type {
+        vertical-align: middle;
+    }
+
+    .dataframe tbody tr th {
+        vertical-align: top;
+    }
+
+    .dataframe thead th {
+        text-align: right;
+    }
+</style>
+<table border="1" class="dataframe">
+  <thead>
+    <tr style="text-align: right;">
+      <th></th>
+      <th>a</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <th>0</th>
+      <td>0</td>
+    </tr>
+    <tr>
+      <th>1</th>
+      <td>1</td>
+    </tr>
+    <tr>
+      <th>2</th>
+      <td>2</td>
+    </tr>
+    <tr>
+      <th>3</th>
+      <td>3</td>
+    </tr>
+    <tr>
+      <th>4</th>
+      <td>4</td>
+    </tr>
+    <tr>
+      <th>5</th>
+      <td>5</td>
+    </tr>
+    <tr>
+      <th>6</th>
+      <td>6</td>
+    </tr>
+    <tr>
+      <th>7</th>
+      <td>7</td>
+    </tr>
+    <tr>
+      <th>8</th>
+      <td>8</td>
+    </tr>
+    <tr>
+      <th>9</th>
+      <td>9</td>
+    </tr>
+    <tr>
+      <th>10</th>
+      <td>10</td>
+    </tr>
+    <tr>
+      <th>11</th>
+      <td>11</td>
+    </tr>
+    <tr>
+      <th>12</th>
+      <td>12</td>
+    </tr>
+    <tr>
+      <th>13</th>
+      <td>13</td>
+    </tr>
+    <tr>
+      <th>14</th>
+      <td>14</td>
+    </tr>
+    <tr>
+      <th>15</th>
+      <td>15</td>
+    </tr>
+    <tr>
+      <th>16</th>
+      <td>16</td>
+    </tr>
+    <tr>
+      <th>17</th>
+      <td>17</td>
+    </tr>
+    <tr>
+      <th>18</th>
+      <td>18</td>
+    </tr>
+    <tr>
+      <th>19</th>
+      <td>19</td>
+    </tr>
+    <tr>
+      <th>20</th>
+      <td>20</td>
+    </tr>
+    <tr>
+      <th>21</th>
+      <td>21</td>
+    </tr>
+    <tr>
+      <th>22</th>
+      <td>22</td>
+    </tr>
+    <tr>
+      <th>23</th>
+      <td>23</td>
+    </tr>
+    <tr>
+      <th>24</th>
+      <td>24</td>
+    </tr>
+    <tr>
+      <th>25</th>
+      <td>25</td>
+    </tr>
+    <tr>
+      <th>26</th>
+      <td>26</td>
+    </tr>
+    <tr>
+      <th>27</th>
+      <td>27</td>
+    </tr>
+    <tr>
+      <th>28</th>
+      <td>28</td>
+    </tr>
+    <tr>
+      <th>29</th>
+      <td>29</td>
+    </tr>
+    <tr>
+      <th>30</th>
+      <td>30</td>
+    </tr>
+    <tr>
+      <th>31</th>
+      <td>31</td>
+    </tr>
+    <tr>
+      <th>32</th>
+      <td>32</td>
+    </tr>
+    <tr>
+      <th>33</th>
+      <td>33</td>
+    </tr>
+    <tr>
+      <th>34</th>
+      <td>34</td>
+    </tr>
+    <tr>
+      <th>35</th>
+      <td>35</td>
+    </tr>
+    <tr>
+      <th>36</th>
+      <td>36</td>
+    </tr>
+    <tr>
+      <th>37</th>
+      <td>37</td>
+    </tr>
+    <tr>
+      <th>38</th>
+      <td>38</td>
+    </tr>
+    <tr>
+      <th>39</th>
+      <td>39</td>
+    </tr>
+    <tr>
+      <th>40</th>
+      <td>40</td>
+    </tr>
+    <tr>
+      <th>41</th>
+      <td>41</td>
+    </tr>
+    <tr>
+      <th>42</th>
+      <td>42</td>
+    </tr>
+    <tr>
+      <th>43</th>
+      <td>43</td>
+    </tr>
+    <tr>
+      <th>44</th>
+      <td>44</td>
+    </tr>
+    <tr>
+      <th>45</th>
+      <td>45</td>
+    </tr>
+    <tr>
+      <th>46</th>
+      <td>46</td>
+    </tr>
+    <tr>
+      <th>47</th>
+      <td>47</td>
+    </tr>
+    <tr>
+      <th>48</th>
+      <td>48</td>
+    </tr>
+    <tr>
+      <th>49</th>
+      <td>49</td>
+    </tr>
+    <tr>
+      <th>50</th>
+      <td>50</td>
+    </tr>
+    <tr>
+      <th>51</th>
+      <td>51</td>
+    </tr>
+    <tr>
+      <th>52</th>
+      <td>52</td>
+    </tr>
+    <tr>
+      <th>53</th>
+      <td>53</td>
+    </tr>
+    <tr>
+      <th>54</th>
+      <td>54</td>
+    </tr>
+    <tr>
+      <th>55</th>
+      <td>55</td>
+    </tr>
+    <tr>
+      <th>56</th>
+      <td>56</td>
+    </tr>
+    <tr>
+      <th>57</th>
+      <td>57</td>
+    </tr>
+    <tr>
+      <th>58</th>
+      <td>58</td>
+    </tr>
+    <tr>
+      <th>59</th>
+      <td>59</td>
+    </tr>
+    <tr>
+      <th>60</th>
+      <td>60</td>
+    </tr>
+  </tbody>
+</table>
+</div>
diff --git a/pandas/tests/io/formats/data/html/html_repr_min_rows_default_no_truncation.html b/pandas/tests/io/formats/data/html/html_repr_min_rows_default_no_truncation.html
new file mode 100644
--- /dev/null
+++ b/pandas/tests/io/formats/data/html/html_repr_min_rows_default_no_truncation.html
@@ -0,0 +1,105 @@
+<div>
+<style scoped>
+    .dataframe tbody tr th:only-of-type {
+        vertical-align: middle;
+    }
+
+    .dataframe tbody tr th {
+        vertical-align: top;
+    }
+
+    .dataframe thead th {
+        text-align: right;
+    }
+</style>
+<table border="1" class="dataframe">
+  <thead>
+    <tr style="text-align: right;">
+      <th></th>
+      <th>a</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <th>0</th>
+      <td>0</td>
+    </tr>
+    <tr>
+      <th>1</th>
+      <td>1</td>
+    </tr>
+    <tr>
+      <th>2</th>
+      <td>2</td>
+    </tr>
+    <tr>
+      <th>3</th>
+      <td>3</td>
+    </tr>
+    <tr>
+      <th>4</th>
+      <td>4</td>
+    </tr>
+    <tr>
+      <th>5</th>
+      <td>5</td>
+    </tr>
+    <tr>
+      <th>6</th>
+      <td>6</td>
+    </tr>
+    <tr>
+      <th>7</th>
+      <td>7</td>
+    </tr>
+    <tr>
+      <th>8</th>
+      <td>8</td>
+    </tr>
+    <tr>
+      <th>9</th>
+      <td>9</td>
+    </tr>
+    <tr>
+      <th>10</th>
+      <td>10</td>
+    </tr>
+    <tr>
+      <th>11</th>
+      <td>11</td>
+    </tr>
+    <tr>
+      <th>12</th>
+      <td>12</td>
+    </tr>
+    <tr>
+      <th>13</th>
+      <td>13</td>
+    </tr>
+    <tr>
+      <th>14</th>
+      <td>14</td>
+    </tr>
+    <tr>
+      <th>15</th>
+      <td>15</td>
+    </tr>
+    <tr>
+      <th>16</th>
+      <td>16</td>
+    </tr>
+    <tr>
+      <th>17</th>
+      <td>17</td>
+    </tr>
+    <tr>
+      <th>18</th>
+      <td>18</td>
+    </tr>
+    <tr>
+      <th>19</th>
+      <td>19</td>
+    </tr>
+  </tbody>
+</table>
+</div>
diff --git a/pandas/tests/io/formats/data/html/html_repr_min_rows_default_truncated.html b/pandas/tests/io/formats/data/html/html_repr_min_rows_default_truncated.html
new file mode 100644
--- /dev/null
+++ b/pandas/tests/io/formats/data/html/html_repr_min_rows_default_truncated.html
@@ -0,0 +1,70 @@
+<div>
+<style scoped>
+    .dataframe tbody tr th:only-of-type {
+        vertical-align: middle;
+    }
+
+    .dataframe tbody tr th {
+        vertical-align: top;
+    }
+
+    .dataframe thead th {
+        text-align: right;
+    }
+</style>
+<table border="1" class="dataframe">
+  <thead>
+    <tr style="text-align: right;">
+      <th></th>
+      <th>a</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <th>0</th>
+      <td>0</td>
+    </tr>
+    <tr>
+      <th>1</th>
+      <td>1</td>
+    </tr>
+    <tr>
+      <th>2</th>
+      <td>2</td>
+    </tr>
+    <tr>
+      <th>3</th>
+      <td>3</td>
+    </tr>
+    <tr>
+      <th>4</th>
+      <td>4</td>
+    </tr>
+    <tr>
+      <th>...</th>
+      <td>...</td>
+    </tr>
+    <tr>
+      <th>56</th>
+      <td>56</td>
+    </tr>
+    <tr>
+      <th>57</th>
+      <td>57</td>
+    </tr>
+    <tr>
+      <th>58</th>
+      <td>58</td>
+    </tr>
+    <tr>
+      <th>59</th>
+      <td>59</td>
+    </tr>
+    <tr>
+      <th>60</th>
+      <td>60</td>
+    </tr>
+  </tbody>
+</table>
+<p>61 rows × 1 columns</p>
+</div>
diff --git a/pandas/tests/io/formats/test_to_html.py b/pandas/tests/io/formats/test_to_html.py
--- a/pandas/tests/io/formats/test_to_html.py
+++ b/pandas/tests/io/formats/test_to_html.py
@@ -713,3 +713,42 @@ def test_to_html_with_col_space_units(unit):
     for h in hdrs:
         expected = '<th style="min-width: {unit};">'.format(unit=unit)
         assert expected in h
+
+
+def test_html_repr_min_rows_default(datapath):
+    # gh-27991
+
+    # default setting no truncation even if above min_rows
+    df = pd.DataFrame({"a": range(20)})
+    result = df._repr_html_()
+    expected = expected_html(datapath, "html_repr_min_rows_default_no_truncation")
+    assert result == expected
+
+    # default of max_rows 60 triggers truncation if above
+    df = pd.DataFrame({"a": range(61)})
+    result = df._repr_html_()
+    expected = expected_html(datapath, "html_repr_min_rows_default_truncated")
+    assert result == expected
+
+
+@pytest.mark.parametrize(
+    "max_rows,min_rows,expected",
+    [
+        # truncated after first two rows
+        (10, 4, "html_repr_max_rows_10_min_rows_4"),
+        # when set to None, follow value of max_rows
+        (12, None, "html_repr_max_rows_12_min_rows_None"),
+        # when set value higher as max_rows, use the minimum
+        (10, 12, "html_repr_max_rows_10_min_rows_12"),
+        # max_rows of None -> never truncate
+        (None, 12, "html_repr_max_rows_None_min_rows_12"),
+    ],
+)
+def test_html_repr_min_rows(datapath, max_rows, min_rows, expected):
+    # gh-27991
+
+    df = pd.DataFrame({"a": range(61)})
+    expected = expected_html(datapath, expected)
+    with option_context("display.max_rows", max_rows, "display.min_rows", min_rows):
+        result = df._repr_html_()
+    assert result == expected