How to optimize Dash App for mobile phone view

Hey Everyone,

I spent a good amount of time building a Dash app and am to the point that I am comfortable with how it looks on a web browser. However, I came to the realization that after deployment that most users will use the app on their mobile phones. I’ve added meta tags and have set sizes using Dash Bootstrap components (XS, M, XL, etc.) with the goal of adjusting plot sizes to fit different size screens. Additionally, I’ve tried to remove unnecessary graph components to allow more space for key “signal” components.

Unfortunately, I’m not happy with the results and have not made much progress in optimizing the web app for mobile use. As you can see, many of the plots I have are horizontal focused, and while they look fine on the web version of the app, I’d say the mobile version is un-viewable. The mobile screenshot is using the IPhone SE view on Google Chrome developer tools.

Does anyone have ideas or guidance on how to improve the app so that it looks passable on mobile? I’m running pretty low on ideas and would appreciate any help!

1 Like

Wanted to bump this post on account of my stuckness

Hello @clevercolt,

Welcome to the community!

Mobile friendliness is something that definitely needs focus more and more.

What I do is use css along with media queries with screen size and pointer types to determine how I want things to display.

@media only screen and max-width (920px) {
     .dash-graph {
        width: 100% !important;
        height: 65% !important;
        position: absolute !important;
    }
}

This is just an example, it may not work 100% for you, but could get you pointed in the right direction.

The !important tells the browser to ignore all other styling and apply it, even overriding the inline styling.

Hope this helps.

If you need to, you can also replace the % with vw and vh as needed.

1 Like

Thanks so much for the reply! I’ll look into this and report back once I have time to test it out.

1 Like

@clevercolt

In the screenshot on the bottom, it appears that the content only takes up half the screen. Is that the case? If so, the layout may not be set up correctly. If you can make a minimal example, I might be able to help.

2 Likes

@AnnMarieW Thanks for the reply! And sorry for the slow response on my part, haven’t had much free time the last couple of days. It’s possible that you are correct. I attached a skeleton script (Github gist) of how I have the layout of my app setup. I’d love any feedback to see if I have actually setup the layout incorrectly.

Also, I’m attaching a screenshot of another section of the dashboard, a table that for some reason does take up the width of the screen. The screenshot is also on the IPhone SE setting on developer tools.

I am not sure who to credit for originally posting this on these forums but I found it here. My graphs looked terrible on a phone like yours until I added a css stylesheet to the assets folder. Inside of this css sheet I added the following code.

.dash-graph {
    zoom: 0.45;
  }
  
  @media (min-width: 475px) {
    .dash-graph {
      zoom: 0.6;
    }
  }
  
  @media (min-width: 768px) {
    .dash-graph {
      zoom: 0.85;
    }
  }
  
  @media (min-width: 1024px) {
    .dash-graph {
      zoom: 1;
    }
  }

Well, sure you could add some CSS for the figures that will override what you specified in the layout. This was suggested earlier too. But that won’t fix all the other elements that only take up half the screen.

Lets’ look at the layout for this heading.

h3_ = html.H3("Select Artist(s)", style={"paddingRight": "30px"})
dbc.Row(
    [
        dbc.Col(h3_, width={"size": 4, "offset": 1}),
    ]
)  

This will make the heading fit in 4 columns on all screen sizes, which is fine on a big screen, but on a small one, it will display on two lines to fit. If it was a figure, it would take up less than half the screen too.

Bootstrap is designed for building the responsive, mobile-first web apps. The best way to start is to keep things really simple and let the framework do it’s magic.

Let’s start with a couple things from your app, a heading, an accordion with a button to the right, and a figure.


import dash
import dash_bootstrap_components as dbc
from dash import dcc, html
from dash_bootstrap_templates import load_figure_template
import plotly.express as px


load_figure_template("slate")

app = dash.Dash(external_stylesheets=[dbc.themes.SLATE])

h3_ = html.H3("Select Artist(s)")

button = dbc.Button(
    id="submit_button",
    n_clicks=0,
    children="Submit",
)

accordion =dbc.Accordion(
    [
        dbc.AccordionItem([], title="Billy Strings"),
        dbc.AccordionItem([], title="Goose"),
        dbc.AccordionItem([], title="Grateful Dead"),
        dbc.AccordionItem([], title="Phish"),
        dbc.AccordionItem([], title="Widespread Panic"),
    ],
)


app.layout = dbc.Container(
    [
        h3_,
        dbc.Row(
            [
                dbc.Col(accordion, md=6),
                dbc.Col(button),
            ],
            className="my-4"
        ),
        dcc.Graph(figure=px.scatter()),
    ]
)


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



Here’s a summary:

  • Dash has good default meta tags for mobile design, so I removed what you have for now.

  • Used Bootstrap classes for spacing instead of the style prop or html.Div(html.Br()). This makes for a more consistent, cleaner and efficient design. In the example above className="my-4" adds margin in the y axis. See all the classes at Dash Bootstrap Cheatsheet

  • Changed the html.Button to a dbc.Button so the button is styled according to your selected Bootstrap theme

  • Removed the Fluid=True from the dbc.Container. This will add more padding on larger screens automatically, which I’m assuming was the purpose of offset prop.

  • Used dbc.Row(dbc.Col() only when it’s necessary to put things into more than one column. (The default is full width.

  • For the multi column row, made the columns responsive by specified the width for different screen sizes, (ie md=9). This makes it so that the column width is 9 on screens larger than medium( about an ipad), otherwise it’s full width ( the default).

There is more info in the dash-bootstrap-components layout docs. You can also find lots of good minimal examples at the Dash Example Index.

Hopefully, this is enough info so you can apply it to the rest of the app (which looks cool btw!)

2 Likes

Dash bootstrap and Dash mantine both have the ability to feed these settings when everything is set in a grid layout. This works well in a lot of cases.

But sometimes the effort of trying to have things in a grid layout is too difficult to implement (custom dashboard layout where the users can drag the graphs anywhere) this is where css can apply.

Take my reference earlier:

@media only screen and max-width (920px) {
     .dash-graph {
        width: 100% !important;
        height: 65% !important;
        position: relative !important;
    }
}

#design-area .dash-graph[style] {
    flex-grow: 1 !important;
    flex-shrink: 0 !important;
    position: relative !important;
    width: 100% !important;
    height: 50vh !important;
    display: flex !important;
    top: 0px !important;
    left: 0px !important;
}

Width 100% obviously takes up the full width of the container it is positioned in. Height, 65% of the container it is in. Position relative means that if there are multiple graphs in the same container, they will now stack regardless of position.

The design-area is a real world example of me overwriting the graph when styling is provided under a specific screen query. I provide everything that is normally present within the inline-styling for the design. (design-area is from my Dashboard-Helper where you can create custom layouts).

The thing I like about css, is the ability to apply settings with broad strokes via classnames, this isnt just about applying settings to how the element looks, but also to how the element can be displayed in the layout.

With that said, can you reuse things like @AnnMarieW mentioned, and the answer is yes.

Let’s take her app that she demonstrated with, its very easy, I made some adjustments, and then repeated the layout of the accordion and graph a second time.

import dash
import dash_bootstrap_components as dbc
from dash import dcc, html
from dash_bootstrap_templates import load_figure_template
import plotly.express as px


load_figure_template("slate")

app = dash.Dash(external_stylesheets=[dbc.themes.SLATE])

h3_ = html.H3("Select Artist(s)")

uxSz = {}
uxSz['acc'] = {'md': 9, 'lg': 6}

button = dbc.Button(
    id="submit_button",
    n_clicks=0,
    children="Submit",
)

accordion =dbc.Accordion(
    [
        dbc.AccordionItem([], title="Billy Strings"),
        dbc.AccordionItem([], title="Goose"),
        dbc.AccordionItem([], title="Grateful Dead"),
        dbc.AccordionItem([], title="Phish"),
        dbc.AccordionItem([], title="Widespread Panic"),
    ],
)


app.layout = dbc.Container(
    [
        h3_,
        dbc.Row(
            [
                dbc.Col(accordion, **uxSz['acc']),
                dbc.Col(button),
            ],
            className="my-4"
        ),
        dcc.Graph(figure=px.scatter()),
        dbc.Row(
            [
                dbc.Col(accordion, **uxSz['acc']),
                dbc.Col(button),
            ],
            className="my-4"
        ),
        dcc.Graph(figure=px.scatter()),
    ]
)


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

Now, we could type in md=9, lg=6 across each time that we wanted to specify the layout, but once this gets spread out among multiple elements and layouts, if you found an issue and wanted to update, you’d have to go through each instance and update it. This is where the dictionary comes into play, you can save your settings as key: value pairs, then wherever you want this specific design, you can call it by using ** in front of the specific dictionary you want to reference, in this case it was uxSz['acc'] which when used with the ** it broke out into md=9, lg=6.

If you use pages and multiple layouts, using a dictionary from a utils directory that you import gives you the opportunity to save these settings in one location and be able to adjust them simply. Also note, they can apply to any settings across the board (className, color, etc).

1 Like

@AnnMarieW @jinnyzor @ lykolate Thanks a bunch to the 3 of you for your detailed replies! I haven’t had a ton of free time to work on the app lately… but I have implemented some of what y’all have shared and these changes have already made a big difference!

Two things I need to sort out:

  1. The DBC table (from dataframe) does not responsively adjust to different screen sizes. It is much wider than the rest of the elements for some reason. I’m going to try to figure out a way to wrap text or potentially try a dcc table.
  2. Horizontal bar graph still doesn’t look great. May just find an alternative.

You all collectively helped me get unstuck - thanks again!

1 Like

For you tables, you could consider using AG grid instead. It is responsive in nature and has a slew of cool things that are innately available.