Shiny for Python and SCSS: How to Style Your Dashboards with SASS

By:
Dario Radečić
January 15, 2023

There's a more powerful design kid on the block with the name of SASS (Syntactically Awesome Style Sheets), and it simplifies how app styles are written and maintained. Today you'll learn all about it in the context of Shiny for Python - also a new arrival to the web development scene. To be more precise, we'll write a Shiny for Python SCSS dashboard that displays earthquake data, both in table and chart form. The user can filter out quakes below a certain magnitude and can rename the title of the chart. It's simple but will be enough to demonstrate how SASS and Shiny for Python work. <blockquote>Want to dive deeper into Shiny for Python capabilities? <a href="https://appsilon.com/pyshiny-demo/" target="_blank" rel="noopener">Here's an entire demo written by an R Shiny professional</a>.</blockquote> Table of contents: <ul><li><a href="#dashboard">Let's Write a Basic Shiny for Python Dashboard</a></li><li><a href="#assets">How to Add a Static Asset Directory to Shiny</a></li><li><a href="#styles">How to Compile SCSS in Shiny for Python</a></li><li><a href="#summary">Summary of Shiny for Python SCSS</a></li></ul> <hr /> <h2 id="dashboard">Let's Write a Basic Shiny for Python Dashboard</h2> To explore SCSS stylings, we have to write the dashboard first. Download the <a href="https://www.kaggle.com/datasets/mathurinache/quakes" target="_blank" rel="nofollow noopener">Quakes dataset</a> from Kaggle and create an <code>app.py</code> file. Regarding the UI user inputs, the app will allow users to filter out the quakes based on the minimum magnitude and change the chart title. The app will display a table of 10 sample rows from the dataset and a histogram showing the distribution of the quake magnitudes. We won't dive deep into the code in this article, as the point is to discuss SCSS. Here's the entire snippet: <pre><code class="language-python">import pandas as pd import matplotlib.pyplot as plt from IPython import display from shiny import App, ui, render, reactive display.set_matplotlib_formats("svg") plt.rcParams["axes.spines.top"] = False plt.rcParams["axes.spines.right"] = False <br> df = pd.read_csv("quake.csv") <br> app_ui = ui.page_fluid(    ui.tags.h3("Earthquakes dataset visualizer", class_="app-heading"),    ui.tags.div(class_="input_container", children=[        ui.input_slider(            id="slider_min_magnitude",            label="Minimum magnitude:",            min=df["richter"].min(),            max=df["richter"].max(),            value=df["richter"].min(),            step=0.1        ),        ui.input_text(            id="in_histogram_title",            label="Histogram title",            value="Distribution of the Richter value"        )    ]),    # Table    ui.tags.div(class_="card", children=[        ui.tags.p("Sample of 10 earthquakes", class_="card_title"),        ui.output_table(id="quake_table", class_="quake_table")    ]),    # Histogram    ui.tags.div(class_="card", children=[        ui.output_text(id="out_histogram_title"),        ui.output_plot(id="quake_histogram")    ])     ) <br> def server(input, output, session):    @reactive.Calc    def data():        return df[df["richter"] &gt;= input.slider_min_magnitude()] <br>    @output    @render.table    def quake_table():        return data().sample(10, replace=True) <br>    @output    @render.text    def out_histogram_title():        return input.in_histogram_title() <br>    @output    @render.plot    def quake_histogram():        fig, ax = plt.subplots()        ax.hist(data()["richter"], ec="#000000", color="#0099F8")        return fig <br> app = App(ui=app_ui, server=server)</code></pre> You can launch the app by running the following shell command: <pre><code class="language-shell">shiny run --reload app.py</code></pre> Here's what it looks like: <img class="size-full wp-image-15608" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b7d2b075b180b12ad38de1_2e92464f_1-1.webp" alt="Image 1 - Quakes Shiny for Python dashboard" width="4336" height="2656" /> Image 1 - Quakes Shiny for Python dashboard You can play around with the input controls - here's an example: <img class="size-full wp-image-15610" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b7d2b15591af1433a8610d_4f6088ec_2-1.webp" alt="Image 2 - Quakes Shiny for Python dashboard (2)" width="4336" height="2656" /> Image 2 - Quakes Shiny for Python dashboard (2) And that's almost all we need to start styling the dashboard. The only prerequisite left is creating and linking a static assets directory. Let's cover that next. <h2 id="assets">How to Add a Static Asset Directory to Shiny</h2> The static files directory is used to host your images, scripts, styles, and everything else related to the app. A common practice is to create a <code>www</code> folder and put everything inside it. Create one yourself and make sure it's in the same folder as <code>app.py</code>. To connect Shiny for Python dashboard with the <code>www</code> folder, you'll have to do the following: <ul><li>Import the <code>Path</code> class from the <code>pathlib</code> library.</li><li>Create a variable <code>www_dir</code> that will store an absolute path to the <code>www</code> directory.</li><li>Inside <code>App()</code>, specify an additional parameter <code>static_assets</code> and set it to <code>www_dir</code>.</li></ul> Here's what your <code>app.py</code> file should look like after implementing the changes: <pre><code class="language-python">import pandas as pd import matplotlib.pyplot as plt from pathlib import Path from IPython import display from shiny import App, ui, render, reactive display.set_matplotlib_formats("svg") plt.rcParams["axes.spines.top"] = False plt.rcParams["axes.spines.right"] = False <br> df = pd.read_csv("quake.csv") <br> app_ui = ui.page_fluid(    ui.tags.head(        ui.tags.link(rel="stylesheet", href="styles.css")    ),    ui.tags.h3("Earthquakes dataset visualizer", class_="app-heading"),    ui.tags.div(class_="input_container", children=[        ui.input_slider( ... ),        ui.input_text( ... )    ]),    # Table    ui.tags.div(class_="card", children=[ ... ]),    # Histogram    ui.tags.div(class_="card", children=[ ... ])     ) <br> def server(input, output, session):    ... <br> www_dir = Path(__file__).parent / "www" app = App(ui=app_ui, server=server, static_assets=www_dir)</code></pre> The dots <code>...</code> are here just to make the snippet shorter - don't actually replace the code with them. Now we have everything we need to start writing some SCSS code. <h2 id="styles">How to Compile SCSS in Shiny for Python</h2> At the same level where your <code>app.py</code> is, create a new directory <code>styles</code> and two files inside it: <code>_variables.scss</code> and <code>styles.scss</code>. One of the neat things about SCSS is that you can separate the stylings into fragments (multiple files), and then include them in one main file. That's just what we did with the variables. This file will contain links to our fonts and declarations for different colors, sizes, and similar - everything you want to reuse in multiple places: <pre><code class="language-css">@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;700&amp;display=swap'); <br>$font-stack: Poppins, sans-serif; $bg-color: #F4F4F4; $card-color: #FFFFFF; $border-color: #EEEEEE; $text-color: #333333; $text-color-light: #505050; $card-border: 1px solid $border-color; $card-border-radius: 5px;</code></pre> The <code>styles.scss</code> will import the variables fragment and declare styles for the entire app. Since the app is simple, the resulting file is quite short: <pre><code class="language-css">@use 'variables'; <br>body {    font-family: variables.$font-stack;    background-color: variables.$bg-color;    color: variables.$text-color;    box-sizing: border-box;    margin: 0;    padding: 0; } <br>.container-fluid {    max-width: 1280px;    margin: 0 auto; } <br>.card {    background-color: variables.$card-color;    padding: 1rem 1.25rem;    margin: 1rem auto;    border: variables.$card-border;    border-radius: variables.$card-border-radius;    box-shadow: 5px 5px 15px 0px rgba(0,0,0,0.2);    transition: all 0.2s; <br>    .card_title, #out_histogram_title {        color: variables.$text-color-light;        font-weight: bold;        text-transform: uppercase;    } } <br>.card:hover {    box-shadow: 5px 5px 15px 0px rgba(0,0,0,0.4); } <br>.app-heading {    text-transform: uppercase;    font-weight: bold;    margin-bottom: 1rem; } <br>.input_container {    display: flex;    flex-direction: row;    background-color: variables.$card-color;    padding: 1rem 1.25rem;    margin: 1rem auto; <br>    .shiny-input-container:first-child {        margin-right: 3rem;    } } <br>table.shiny-table {    width: 100% !important;    text-align: center; <br>    thead {        text-align: center !important; <br>        tr {            text-align: center !important;        }    } }</code></pre> That's SCSS taken care of, but what do you actually do with it? The <code>styles.css</code> is linked in the dashboard, and we don't have that file in the <code>www</code> directory. Well, assuming you have <a href="https://sass-lang.com/install" target="_blank" rel="nofolow noopener">SASS installed</a>, the compilation boils down to a single shell command: <pre><code class="language-shell">sass &lt;path-to-scss-file&gt; &lt;path-to-css-file&gt;</code></pre> Here's what it looks like in our case: <img class="size-full wp-image-15618" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b7d2b24d0cd7b298100cc4_309b285a_3-1.webp" alt="Image 3 - Compiling SCSS into CSS" width="2092" height="1096" /> Image 3 - Compiling SCSS into CSS And that's it! The <code>styles.css</code> file is now present in the <code>www</code> folder: <img class="size-full wp-image-15614" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b7d2b406181de50dbf9d20_2fb0bd45_4-1.webp" alt="Image 4 - Compiled CSS file" width="4336" height="2656" /> Image 4 - Compiled CSS file You can now refresh the app and see the styles in action: <img class="size-full wp-image-15616" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b7d2b4f1dfa1404cc85648_2e1e78d9_5-1.webp" alt="Image 5 - Shiny for Python dashboard with SCSS styles" width="4336" height="2656" /> Image 5 - Shiny for Python dashboard with SCSS styles And that's how you can work with SASS/SCSS in Shiny for Python. Let's wrap things up next. Want to see an earthquake dashboard design in R Shiny? <a href="https://connect.appsilon.com/quakes_explorer/" target="_blank" rel="noopener">Explore our Quakes Explorer</a> built with <a href="https://appsilon.com/professional-shiny-app-ui/" target="_blank" rel="noopener">shiny.fluent and imola</a>. <hr /> <h2 id="summary">Summary of Shiny for Python SCSS</h2> There's no one stopping you from using vanilla CSS in your Shiny for Python projects, but it's always a good idea to keep up with the trends and best practices in the industry. SASS has been one for years, and it doesn't seem like it's going anywhere. It's an excellent skill to learn and it will definitely make your job easier. <blockquote>Curious about <a href="https://appsilon.com/shiny-application-layouts/" target="_blank" rel="noopener">where Shiny application layouts are trending</a>? We've got you covered.</blockquote> <i>What are your thoughts on Shiny for Python and SCSS integration? Do you use some alternative method for compiling SCSS?</i> Please let us know in the comment section below. Also, feel free to continue the discussion on Twitter - <a href="https://twitter.com/appsilon" target="_blank" rel="nofollow noopener">@appsilon</a> - We'd love to hear from you. <blockquote>Need to make some reports quickly? <a href="https://appsilon.com/quarto-and-jupyter-notebooks/" rel="noopener">Try Jupyter Notebook and Quarto integration.</a></blockquote>

Have questions or insights?

Engage with experts, share ideas and take your data journey to the next level!

Is Your Software GxP Compliant?

Download a checklist designed for clinical managers in data departments to make sure that software meets requirements for FDA and EMA submissions.
Explore Possibilities

Share Your Data Goals with Us

From advanced analytics to platform development and pharma consulting, we craft solutions tailored to your needs.

Talk to our Experts
shiny
shiny dashboards
UI/UX
sass
tutorials
shiny for python