Bring Drag & Drop to Dash with Dashboard Engine. 💫 Learn how at our next webinar!

How styling headers in Multi-Headers DataTable?

My Table:

row_data={('user_profile_id', '', ''): {0: 984, 1: 985, 2: 986, 3: 987, 4: 988},
 ('email', '', ''): {0: 'egjkljgler@gmail.com',
  1: 'dasdadaddcd740@gmail.com',
  2: 'dasdadaddcd740@gmail.com',
  3: 'dasdadaddcd740@gmail.com',
  4: 'temp_da@dada.com'},
 ('feedback', 'AAA', 'Zaza'): {0: 'Perfect',
  1: 'Loose',
  2: 'nan',
  3: 'nan',
  4: 'nan'},
 ('feedback', 'AAA', 'Kuku'): {0: 'Perfect',
  1: 'Tight',
  2: 'nan',
  3: 'nan',
  4: 'nan'},
 ('feedback', 'AAA', 'Duku'): {0: 'Tight',
  1: 'Perfect',
  2: 'nan',
  3: 'nan',
  4: 'nan'},
 ('feedback', 'AAA', 'Sasa'): {0: 'Tight',
  1: 'Perfect',
  2: 'nan',
  3: 'nan',
  4: 'nan'},
 ('feedback', 'BBB', 'Rere'): {0: 'Perfect',
  1: 'nan',
  2: 'nan',
  3: 'nan',
  4: 'nan'},
 ('results', 'AAA', 'Zaza'): {0: 14,
  1: 12,
  2: 74,
  3: 87,
  4: 65},
 ('results', 'AAA', 'Kuku'): {0: 20,
  1: 'nan',
  2: 45,
  3: 52,
  4: 71},
 ('results', 'AAA', 'Duku'): {0: 25,
  1: 78,
  2: 25,
  3: 46,
  4: 89},
 ('results', 'AAA', 'Sasa'): {0: 45,
  1: 91,
  2: 45,
  3: 87,
  4: 85},
 ('results', 'BBB', 'Rere'): {0: 42,
  1: 77,
  2: 45,
  3: 12,
  4: 41}}
df= pd.DataFrame.from_dict(row_data)
df=df[['user_profile_id','email','results','feedback']]
df_columns = [{"name": col, "id": "_".join(col)} for col in df.columns]
df_data = [ {"_".join(col): val for col, val in row.items() } for row in df.to_dict('records')]

app = dash.Dash(__name__)
table=dash_table.DataTable(
                                    id='table',
                                    columns=df_columns,
                                    data=df_data,
                                    fixed_rows={'headers': False},
                                    page_size=20,
                                    sort_action='none',
                                    style_header={'backgroundColor': 'gray','fontSize':10,'color': 'black','fontWeight': 'bold','textAlign': 'center','border': '1px solid black'},
                                    style_cell={'textAlign': 'center','fontSize':15, 'font-family':'sans-serif','border': '1px solid black'},
                                    style_table={'overflowY': 'auto'},
                                    style_data={'color': 'black','backgroundColor': 'white', 'whiteSpace': 'normal'},
                                    fill_width=True,
                                    merge_duplicate_headers=True
)
app.layout = html.Div([ table])
if __name__ == '__main__':
    app.run_server(debug=True)

Result as Multi Headers Table:

Now I try to use style_header_conditional like in these examples
https://dash.plotly.com/datatable/conditional-formatting
to create styling for headers:


That is to say, a different color should be made to all the headings belonging to the main headline.
The problem is how to get to the main column? This doesn’t work :

                                    style_header_conditional=[
                                                    {
                                                        'if': {'column_id':'results'},
                                                        'backgroundColor': 'grey'
                                                    }
                                              ]

How can this be solved?
Thanks

Hi,

Please take a look on this old post for a longer discussion, but I believe you are looking for “header_index” in the if dictionary.

1 Like

@jlfsjunior Thanks for the reply,
It seems to me it does not so much work on a table with multi-headers:

row_data={('user_profile_id', '', ''): {0: 984, 1: 985, 2: 986, 3: 987, 4: 988},
 ('email', '', ''): {0: 'egjkljgler@gmail.com',
  1: 'dasdadaddcd740@gmail.com',
  2: 'dasdadaddcd740@gmail.com',
  3: 'dasdadaddcd740@gmail.com',
  4: 'temp_da@dada.com'},
 ('feedback', 'AAA', 'Zaza'): {0: 'Perfect',
  1: 'Loose',
  2: 'nan',
  3: 'nan',
  4: 'nan'},
 ('feedback', 'AAA', 'Kuku'): {0: 'Perfect',
  1: 'Tight',
  2: 'nan',
  3: 'nan',
  4: 'nan'},
 ('feedback', 'AAA', 'Duku'): {0: 'Tight',
  1: 'Perfect',
  2: 'nan',
  3: 'nan',
  4: 'nan'},
 ('feedback', 'AAA', 'Sasa'): {0: 'Tight',
  1: 'Perfect',
  2: 'nan',
  3: 'nan',
  4: 'nan'},
 ('feedback', 'BBB', 'Rere'): {0: 'Perfect',
  1: 'nan',
  2: 'nan',
  3: 'nan',
  4: 'nan'},
 ('results', 'AAA', 'Zaza'): {0: 14,
  1: 12,
  2: 74,
  3: 87,
  4: 65},
 ('results', 'AAA', 'Kuku'): {0: 20,
  1: 'nan',
  2: 45,
  3: 52,
  4: 71},
 ('results', 'AAA', 'Duku'): {0: 25,
  1: 78,
  2: 25,
  3: 46,
  4: 89},
 ('results', 'AAA', 'Sasa'): {0: 45,
  1: 91,
  2: 45,
  3: 87,
  4: 85},
 ('results', 'BBB', 'Rere'): {0: 42,
  1: 77,
  2: 45,
  3: 12,
  4: 41}}
df= pd.DataFrame.from_dict(row_data)
df=df[['user_profile_id','email','results','feedback']]
df_columns = [{"name": col, "id": "_".join(col)} for col in df.columns]
df_data = [ {"_".join(col): val for col, val in row.items() } for row in df.to_dict('records')]

app = dash.Dash(__name__)
table=dash_table.DataTable(
                                    id='table',
                                    columns=df_columns,
                                    data=df_data,
                                    fixed_rows={'headers': False},
                                    page_size=20,
                                    sort_action='none',
                                    style_header={'backgroundColor': 'gray','fontSize':10,'color': 'black','fontWeight': 'bold','textAlign': 'center','border': '1px solid black'},
                                    style_cell={'textAlign': 'center','fontSize':15, 'font-family':'sans-serif','border': '1px solid black'},
                                    style_table={'overflowY': 'auto'},
                                    style_data={'color': 'black','backgroundColor': 'white', 'whiteSpace': 'normal'},
                                    fill_width=True,
                                    merge_duplicate_headers=True,
    style_header_conditional=[
        { 'if': { 'column_type': 'numeric' }, 'backgroundColor': 'magenta' },
        { 'if': { 'header_index': 2 }, 'backgroundColor': 'orange' },
        { 'if': { 'column_id': 'results', 'header_index': 0 }, 'backgroundColor': 'red' },
        { 'if': { 'column_id': 'results', 'header_index': 1 }, 'backgroundColor': 'pink' },
        { 'if': { 'column_id': 'results', 'header_index': 2 }, 'backgroundColor': 'yellow' },
    ]
)
app.layout = html.Div([ table])
if __name__ == '__main__':
    app.run_server(debug=True)

This output :

this refers to header_index but ignores column_id :astonished:

I think the problem is that column_id has been redefined, so there is no id "results", just "results_AAA_Zizi" and so on… I am not sure if

{ 'if': { 'column_id': 'results_AAA_Zizi', 'header_index': 0 }, 'backgroundColor': 'red' },

would work either, as it is ambiguous. It could be related to this open issue , confirmed as a bug.

1 Like

@jlfsjunior
Yes, probably as of today, it’s a bug in Dash DataTable.
Maybe solution may be used css inside dash_table.DataTable() like here :

This example of borders styling with CSS, maybe this can be used for multi headers styling?

css=[
{'selector':'table', 'rule' : 'border-collapse: separate; border-spacing: 0 15px; margin-top: 5px;'},                                                                                                        
{'selector':'td', 'rule' : 'padding-top: 5px; padding-bottom : 5px;'},                                                                                                             
{'selector':'td:last-child', 'rule':'border-right : 2px solid #747678'},                                                                                                           
{'selector':'td:first-child', 'rule':'border-left : 2px solid #747678'}]

but I do not find any documentation about CSS parameters inside Dash DataTable

Hey there,

I haven’t run your example before, but I had to because I am not familiar with how the header is implemented in HTML by DataTable.

So, going back a bit, it seems that the bug you are seeing is slightly different than the one reported. I used:

style_header_conditional=[
        {"if": {"column_type": "numeric"}, "backgroundColor": "magenta"},
        {"if": {"header_index": 2}, "backgroundColor": "orange"},
        {"if": {"column_id": "results_AAA_Zaza", "header_index": 0}, "backgroundColor": "red"},
        {"if": {"column_id": "results_AAA_Zaza", "header_index": 1}, "backgroundColor": "pink"},
        {
            "if": {"column_id": "results_AAA_Zaza", "header_index": 2},
            "backgroundColor": "yellow",
        },
    ],

(Note the column_id being “results_AAA_Zaza”, not only "results)

Results:

So, basically it is targeting the next element in row for levels 1 and 2, regardless if the column_id actually matches (like for Kuku) or not (in BBB)… Not great!


Then you asked about the “css” props… It is mentioned in the component reference, but maybe there aren’t any examples out there in the rest of the docs, as it is recommended to start with “stlye_*”.

Two things to be aware of:

  1. the styles passed by “style_*” will take precedence over those in “css”, so you need to use “!important” in the rules in case you are e.g. setting the entire header background to gray using “style_header”.

  2. “css” rules are simply parsed by Dash and inserted in a style tag. Therefore the syntax is CSS (snake) and not CSS-in-React (camel). So, “background-color” needs to be used instead of “backgroundColor”.

Here’s one example of (what I believe) you were trying to reach:

    css=[
        {'selector':'tbody>tr:nth-of-type(1)>th[data-dash-column="results_AAA_Zaza"]', 'rule' : 'background-color: red'}, 
        {'selector': 'tbody>tr:nth-of-type(2)>th:nth-of-type(2)', 'rule': 'background-color: pink'},
        {'selector': 'tbody>tr:nth-of-type(2)>th:nth-of-type(3)', 'rule': 'background-color: pink'},
        {'selector': 'tbody>tr:nth-of-type(3)>th[data-dash-column^="results"]', 'rule': 'background-color: yellow'},
    ]

I mixed up some ways to target the header elements using both positional (nth-of-type) and attribute-based rules (here is more info in the latter), so you can adapt to different situations.

Another example, if you just want all the colors to be the same under “results”:

css=[{'selector': 'th[data-dash-column^="results"]', 'rule': 'background-color: yellow'}]

Apologies for the essay… :slight_smile: Hope this helps!

2 Likes

@jlfsjunior
Wow !!! You’re really crazy :slight_smile:
This works great, probably CSS today is a real solution for styling complex tables in Dash.

Many many thanks, friend !! :pray:

1 Like