Plotly Dashboard : 12 columns for a row

Hello,

I’m trying to create a dashboard using the bootstrap 12 columnar grid layout. I have a row of dropdowns, each specified as “six columns” and these show up in a row just fine.

I then have 2 charts, each specified has 8 or 4 or 12 columns and these show up in a row just fine. However, the next row after these charts is giving me issues. I have 2 more charts, each specified as “six columns” but then end up stacked on top of each other. I’m not sure what’s wrong? Each of the divs are coded the same way for the charts. Please review my code and the snippet of what the Dashboard looks like:

import dash
from dash.dependencies import Input, Output, State, Event
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
import flask
import plotly.graph_objs as go
import os
import copy

server = flask.Flask(name)
app = dash.Dash(name, server=server)
app.config.suppress_callback_exceptions = True

Bootstrap CSS

app.css.append_css({‘external_url’: ‘https://codepen.io/chriddyp/pen/bWLwgP.css’})

Datasets

df = pd.read_csv(r’C:\Users\Sitara.Abraham\Downloads\Corporate Marketing Inbound Leads 2018 - 2018 Inbound Leads.csv’,encoding=‘cp1252’)
dailyLeads = pd.read_csv(r’C:\Users\Sitara.Abraham\Downloads\Corporate Marketing Inbound Leads 2018 - Leads per Day.csv’)
quarter = df.groupby([‘Quarter’,‘Industry Type’], as_index=False).count()
dailyLeads[‘Inbound Date’] = dailyLeads[‘Inbound Date’].str.replace(‘Total’,‘’)
businessType = set(df[‘Business Type’])
businessType = list(businessType)
verticals = set(df[‘Industry Type’])
verticals = list(verticals)
df[‘Inbound Date’] = pd.to_datetime(df[‘Inbound Date’])

df[‘Month’] = df[‘Inbound Date’].dt.month_name()

totalSQLs = len(
df[(df[“SQL”] == “Yes”)].index
)

q4dl = dailyLeads[dailyLeads[“Quarter”] == “Quarter 4”]
paceGoal = q4dl[“Target Inbounds”].max()
latestDate = q4dl[‘Inbound Date’].max()
behind = q4dl[q4dl[‘Inbound Date’] == latestDate]
behindBy = behind[‘Target Inbounds’] - behind[‘Running Total’]

Layout

app.layout = html.Div([
# Title - Row
html.Div(
[
html.Span(
‘CORPORATE MARKETING INBOUND LEADS’,
style={‘font-family’: ‘Arial Narrow’,
‘margin-top’: “25”,
‘margin-bottom’: “0”
},
className=“six columns”,
),
html.Img(
src=“https://www.scorpion.co/images/Corporate/logo.png”,
style={
‘height’: ‘100%’,
‘float’: ‘right’,},
className=“two columns”,
)
],
className=‘row header’
),
# tabs
html.Div([
dcc.Tabs(
id=“tabs”,
style={“height”:“20”,“verticalAlign”:“middle”},
children=[
dcc.Tab(label=“Conversion Funnel”, value=“conversion_funnel_tab”),
dcc.Tab(label=“Leads”, value=“leads_tab”),
dcc.Tab(label=“Social Leads”, value=“social_tab”),
],
value=“leads_tab”,
)],
className=‘row tabs_div’
),
# divs that save dataframe for each tab
html.Div(df.to_json(orient=“split”), id=“conversion_df”, style={“display”: “none”}), # conversion funnel df
html.Div(df.to_json(orient=“split”), id=“leads_df”, style={“display”: “none”}), # leads df
html.Div(df.to_json(orient=“split”), id=“social_df”, style={“display”: “none”}), # social df

     # Tab content
html.Div(id="tab_content", className="row", style={"margin": "1% 3%"}),

html.Link(href="https://use.fontawesome.com/releases/v5.2.0/css/all.css",rel="stylesheet"),
html.Link(href="https://cdn.rawgit.com/plotly/dash-app-stylesheets/2d266c578d2a6e8850ebce48fdb52759b2aef506/stylesheet-oil-and-gas.css",rel="stylesheet"),
html.Link(href="https://fonts.googleapis.com/css?family=Dosis", rel="stylesheet"),
html.Link(href="https://fonts.googleapis.com/css?family=Open+Sans", rel="stylesheet"),
html.Link(href="https://fonts.googleapis.com/css?family=Ubuntu", rel="stylesheet"),
html.Link(href="https://cdn.rawgit.com/amadoukane96/8a8cfdac5d2cecad866952c52a70a50e/raw/cd5a9bf0b30856f4fc7e3812162c74bfc0ebe011/dash_crm.css", rel="stylesheet"),


# top controls
html.Div(
    [
        html.Div(
            [
                html.P("Vertical"),
                dcc.Dropdown(
                    id="verticals",
                    options=[{'label': vertical, 'value': vertical} for vertical in verticals],
                    value=verticals,
                    multi=True,
                    clearable=False,
                ),
            ],
            className="eight columns",
        ),
       html.Div(
            [
                html.P('Quarter:'),
                dcc.Dropdown(
                    id='quarter',
                    options=[
                        {'label':'Q1','value':'Quarter 1'},
                        {'label':'Q2','value':'Quarter 2'},
                        {'label':'Q3','value':'Quarter 3'},
                        {'label':'Q4','value':'Quarter 4'}
                    ],
                    multi=True,
                    value=['Quarter 4'],
                    clearable=False,
                )
            ],
            className='four columns',
        ),
        html.Div(
            [
                html.P('Sub-Vertical:'),
                dcc.Dropdown(
                    id='subvertical',
                    options=[{'label': subvertical, 'value': subvertical} for subvertical in businessType],
                    value=['Criminal Defense','Healthcare', 'HVAC'],
                    multi=True,
                )
            ],
            className='twelve columns'
        ),
        
    ],
    className="row",
    style={"marginBottom": "10"},
),
# indicators row div
# charts row div
 html.Div([   
    html.Div(
            dcc.Graph(
            id='inbounds-vertical',
            style={
            "margin-top":"10",
            "margin-bottom":"10"
            }),
                
            className ="six columns"
    ),
    html.Div(
            dcc.Graph(
            id="lead-source-pie",
            style={
            "margin-top":"10",
            "margin-bottom":"10"
            }),
                
            className ="six columns"
            ),
    html.Div(
            dcc.Graph(
            id='sql-subvertical-month',
            style={
            "margin-top":"10",
            "margin-bottom":"10"
            }),
                
            className ="six columns"
            ),
    html.Div(
            dcc.Graph(
            id='monthly-inbounds',
            style={
            "margin-top":"10",
            "margin-bottom":"10"
            }),
                
            className ="six columns"
            ),
     ],
     className="row"         
     )

]
)

Hi @SitaraAbraham,

You’ll probably have better luck if you move this question to the “Dash” forum category (rather that API/Python).

-Jon

Bumping this because I moved it to a more appropriate category!

This is happening because the bWLwgP.css / stylesheet-oil-and-gas.css stylesheet you are using adds spacing between columns by adding a left margin to each column, except for the first one using the first-child CSS selector. If you open up the developer tools when running your app, you’ll see something like this:

This means the first column is left aligned. Subsequent columns all have the left margin, including the ones that overflow onto a new row. The size of the margin and the widths of the columns are carefully chosen so that the total width of the columns and margins are 100% of the width of the parent row. You can see in your screenshot that the final two plots are slightly indented compared to the first plot in the first row. This is because they are not the first child of the row, even though they are the first item on a new line of the row having overflowed / wrapped. This tiny bit of extra margin means that the two plots, with their margins, are slightly too wide to fit on a single row, and so they are wrapping onto separate rows.

The easiest way for you to fix this is to make two rows of two columns, i.e. structure like this

html.Div(
    [
        html.Div(graph1, className="six columns"),
        html.Div(graph2, className="six columns")
    ], 
    className="row"
),
html.Div(
    [
        html.Div(graph3, className="six columns"),
        html.Div(graph4, className="six columns")
    ], 
    className="row"
),

Another option that may interest you if you want to use Bootstrap is to check out dash-bootstrap-components (full disclosure, I’m one of the people working on this). Check out the docs for the layout components here. Then you can build this layout using the Row and Col components. Your layout would look something like

import dash_bootstrap_components as dbc

dbc.Row(
    [
        dbc.Col(graph1, width=6),
        dbc.Col(graph2, width=6),
        dbc.Col(graph3, width=6),
        dbc.Col(graph4, width=6),
    ]
)

with this method you wouldn’t need to worry about the left margin issue. Make sure you link to a Bootstrap stylesheet for this to work, you can do this in the dash.Dash constructor

app = dash.Dash(__name__, server=server, external_stylesheets=[dbc.themes.BOOTSTRAP])

or if you want to use only the grid system without other Bootstrap styling, replace dbc.themes.BOOTSTRAP with something like "https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap-grid.min.css" (once this PR gets merged that will be available as dbc.themes.GRID).

1 Like

Thank you so much!!! This worked, I just wrapped each actual row in its own “row” div like you said.

Similarly, I’m trying to have a row of indicators that are “four columns” each, but they are showing up like this (with extra space on the right instead of taking up the full width (i.e. 12 columns):

Could it be because, according to the stylesheet, columns are specified has having a left float? left_float

If so, how would I fix this? Currently I have each indicator wrapped in a “row” div, and each of them individually has a className equal to “four columns indicator.”

I don’t think it’s the float, though I could be wrong. Could you share the bit of code that is making the indicators?

Sure thing:

# indicators row div
html.Div([
    html.Div(
            [
            
            html.P(
                    "Inbound Goal",
                    className="twelve columns indicator_text"
            ),
            html.P(
                    615,
                    className="indicator_value"
            ),
            ],
            className="three columns indicator",
            
    ),
    html.Div(
            [
            
            html.P(
                    "Total SQL's",
                    className="twelve columns indicator_text"
            ),
            html.P(
                    totalSQLs,
                    className="indicator_value"
            ),
            ],
            className="three columns indicator",
            
    ),
    html.Div(
            [
            
            html.P(
                    "Total to be on Pace",
                    className="twelve columns indicator_text"
            ),
            html.P(
                    paceGoal,
                    className="indicator_value"
            ),
            ],
            className="three columns indicator",
            
    ),
    html.Div(
            [
            
            html.P(
                    "Total Behind",
                    className="twelve columns indicator_text"
                  ),
            html.P(
                    behindBy,
                    className="indicator_value"
                  ),
            ],
            className="three columns indicator",
            
    )
],
className="row",
style={
        "margin-left":"10",
        "margin-right":"10"
}
),

I looked into this, turns out it’s a problem with the stylesheet, the width for three columns is set incorrectly. Currently it’s 22%. Based on how the widths for the other classes are set though, it should be 24.625%. (You have four columns, three 0.5% spaces separating them, so the four columns should be (100 - 3 * 0.5) / 4 = 24.625% wide.)

The easiest way to fix this would probably just be to write your own bit of CSS to overrule what’s being set in the external stylesheet. Since what you specify will clash with what’s being specified in the external sheet, we need to make sure that your rule is the one that gets used. You can guarantee this by giving the top level html.Div in your layout an id like this

app.layout = html.Div([...], id="wrapper")

then linking the following bit of CSS (easiest to just save it to assets/style.css or something, see Dash docs)

#wrapper .three.columns {
  width: 24.625%;
}

This selector applies to any element that is in classes three and columns and is a child of the element with id wrapper. Because this is more specific than the selector in the external stylesheets (which only checks the classes) it will always get priority over that. If you aren’t familiar with the rules for how CSS decides which rules to apply when there’s a clash, this guide from Mozilla is great.

Anyway, that fixed the problem when I tried it:

THANK YOU!! This worked!

I really appreciate your thorough answer and you taking the time to figure out what my issue was! Have a great day!!

1 Like