Styling dash Tabs with css classes -- missing option for tab-selected?

#1

Hi,

I am trying to style some dash Tabs, and think that there is a missing option in dcc.Tabs, or I might not be aware of. I would like to pass own css styles to a dash Tabs element, from inspection I see that the renderer loads the following:

  • tab-parent
  • tab-container
  • tab
  • tab-selected

I tried to add own styles to the Tabs using the usual className=‘os-tab’ (etc) attribute. I want to be able to use different tabs in different pages and provide each its own css style. I can style the elements, but the user provided os-tab-selected class is not active, as it is set after the element is ‘pre’?-rendered, when the tab is set to active by react.

I am aware of the following similar posts: adjusting-height-of-tabs how-to-customize-tabs-dcc-tab

I think that the dcc.Tabs element should take an further option classNameSelected=’…’ to allow to pass the css style to the react backend.

Here is some code and css for demonstration, see below the code why this still works due to a workaround in the css, but this is not satisfactory as I can only fix one dcc.Tabs element this way.

My dash and related packages are at the following release levels:

  • dash==0.41.0
  • dash-core-components==0.46.0
  • dash-html-components==0.15.0
  • dash-renderer==0.22.0

Follows: dash_server_with_own_css_for_tabs.py

# -*- coding: utf-8 -*-
import dash
import dash_html_components as html
import dash_core_components as dcc
import plotly.graph_objs as go
import time

from dash.dependencies import Input, Output, State
    
app = dash.Dash(
    __name__,
)

app.index_string = '''
    <!DOCTYPE html>
    <html>
    <head>
        {%metas%}
        <title>{%title%}</title>
        {%css%}
    {%favicon%}
    </head>
    <body>
        {%app_entry%}
        <footer>
            {%config%}
            {%scripts%}
            {%renderer%}
        </footer>
    </body>
</html>
'''

app.scripts.config.serve_locally = True
app.title = "Testing Tab's css"

app.layout = html.Div(
    children=[
        html.Div(
            children = [
                html.H4('Example loading tabs with own css (class) style'),
            ]
        ),
    dcc.Tabs(
            className="os-tab-container",
            id="tabs",
            value="tab-1",
	children=[
                dcc.Tab(label="Bar Graph", value="tab-1", className = 'os-tab'),
                dcc.Tab(label="Scatter Graph", value="tab-2", className='os-tab'),
	]),
    html.Div(id='tabs-content'),
    ],
)


@app.callback(Output('tabs-content', 'children'),
    [Input('tabs', 'value')])
def render_content(tab):
    time.sleep(2)
    if tab == 'tab-1':
        _layout = html.Div(id='loading-1', children=[
            dcc.Graph(
                id='graph-2-tabs',
                figure=go.Figure(
                    data=[
                        go.Bar(
                            x=[1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
                               2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012],
                            y=[219, 146, 112, 127, 124, 180, 236, 207, 236, 263,
                               350, 430, 474, 526, 488, 537, 500, 439],
                            name='Rest of world',
                            marker=go.bar.Marker(
                            color='rgb(55, 83, 109)'
                            )
                        ),
                        go.Bar(
                            x=[1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
                               2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012],
                            y=[16, 13, 10, 11, 28, 37, 43, 55, 56, 88, 105, 156, 270,
                               299, 340, 403, 549, 499],
                            name='China',
                            marker=go.bar.Marker(
                                color='rgb(26, 118, 255)'
                            )
                        )
                    ],
                    layout=go.Layout(
                        title='US Export of Plastic Scrap',
                        showlegend=True,
                        legend=go.layout.Legend(
                            x=0,
                            y=1.0
                        ),
                        margin=go.layout.Margin(l=40, r=0, t=40, b=30)
                    )
                ),
            )
        ])
    elif tab == 'tab-2':
        _layout = html.Div(id='loading-2', children=[
            dcc.Graph(
                id='graph-1-tabs',
                figure=go.Figure(
                    data=[
                        go.Scatter(
                            x=[1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
                               2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012],
                            y=[219, 146, 112, 127, 124, 180, 236, 207, 236, 263,
                               350, 430, 474, 526, 488, 537, 500, 439],
                            name='Rest of world',
                            marker=go.Marker(
                               color='hotpink'
                            )
                        ),
                        go.Scatter(
                            x=[1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
                               2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012],
                            y=[16, 13, 10, 11, 28, 37, 43, 55, 56, 88, 105, 156, 270,
                               299, 340, 403, 549, 499],
                            name='China',
                            marker=go.Marker(
                                color='gold'
                            )
                        )
                    ],
                    layout=go.Layout(
                        title='US Export of Plastic Scrap',
                        showlegend=True,
                        legend=go.layout.Legend(
                            x=0,
                            y=1.0
                        ),
                        margin=go.layout.Margin(l=40, r=0, t=40, b=30)
                    )
                ),
            )
        ])
    print(_layout)
    return _layout	

if __name__ == "__main__":
    app.run_server(debug=False)

The css (to be placed in assets) is: typography.css

.os-tab-parent {
width: auto;
}

.os-tab-container {
  width: auto;
  height: 40px;
  display: inline-block;
  margin-top: 2px;
}

.os-tab {
  width: auto;
  background-color: #abc432 !important;
  color: #323232;
  vertical-align: middel !important;
  padding: 8px 10px 2px 10px !important;
}

.tab--selected{
  background-color: #323232 !important;
  border-top-color: #abc432 !important; 
  color: #abc432 !important;
  padding: 8px 10px 2px 10px !important;
}

.tab:hover{
  color: white;
}

Note that e.g. in .tab-selected I used the ‘!important’ flag to prevend react from overwriting my css settings. This is bad css style and should be avoided. Also it prevent me from having different classes for different tabs on several (dash) pages.

Code is taken from another post and slightly adapted loading-states-api-and-a-loading-component-prerelease

Any help how to do this better or in a canonical way would be welcome, and perhaps a new option to dcc.Tabs is actualy missing here?