So I began with the code from https://github.com/facultyai/dash-bootstrap-components/blob/master/examples/multi-page-apps/simple_sidebar.py and ran with it. I have a good start but it needs some CSS tweaks so that the footer does not hard-align to the bottom of the browser, but the whole page expands correctly as content is added to the content and footer sections. You can see from this image that I have 25 lines of content which are visible by scrollbar that only scrolls the Content section. I have five lines added to the footer which roll off and are not visible. Of course this makes sense to me based how the _STYLE definitions seem hard and fixed.
Here is an image of the finished product with me scrolling a little to capture the Content frame overflow:
And here is my code. I am hoping and experienced CSSer can make the tweaks so that the page size expands to handle a variable amount of content within the Content and Footer frames. The footer frame should have whatever variable sizing is needed to fit its content and if the Content frame has many lines of content, the page would expand, the footer would be pushed off the bottom and the scrollbar would apply to the entire page.
I hope this would be a helpful addition to https://github.com/facultyai/dash-bootstrap-components/tree/master/examples/multi-page-apps once these final adjustments are made. I believe it would probably be using something besides position: fixed on the bottom settings and a hard footer_height definition, but everything I experimented with made the format worse.
import dash
import dash_bootstrap_components as dbc
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
app = dash.Dash(external_stylesheets=[dbc.themes.BOOTSTRAP])
header_height, footer_height = "6rem", "10rem"
sidebar_width, adbar_width = "12rem", "12rem"
HEADER_STYLE = {
"position": "fixed",
"top": 0,
"left": 0,
"right": 0,
"height": header_height,
"padding": "2rem 1rem",
"background-color": "white",
}
SIDEBAR_STYLE = {
"position": "fixed",
"top": header_height,
"left": 0,
"bottom": footer_height,
"width": sidebar_width,
"padding": "1rem 1rem",
"background-color": "lightgreen",
}
ADBAR_STYLE = {
"position": "fixed",
"top": header_height,
"right": 0,
"bottom": footer_height,
"width": adbar_width,
"padding": "1rem 1rem",
"background-color": "lightblue",
}
FOOTER_STYLE = {
"position": "fixed",
"bottom": 0,
"left": 0,
"right": 0,
"height": footer_height,
"padding": "1rem 1rem",
"background-color": "gray",
}
CONTENT_STYLE = {
"margin-top": header_height,
"margin-left": sidebar_width,
"margin-right": adbar_width,
"margin-bottom": footer_height,
"padding": "1rem 1rem",
}
header = html.Div([
html.H2("Header")], style=HEADER_STYLE
)
fdivs = [html.H2("Footer")]
for f in range(5):
fdivs.append(html.P(f'Footer line {f}'))
footer = html.Div(fdivs, style=FOOTER_STYLE)
adbar = html.Div([
html.H2("Adbar"),
html.P('Advertisements go here')], style=ADBAR_STYLE
)
sidebar = html.Div([
html.H2("Sidebar"),
html.Hr(),
html.P("A simple sidebar layout with navigation links", className="lead"),
dbc.Nav([
dbc.NavLink("Page 1", href="/page-1", id="page-1-link"),
dbc.NavLink("Page 2", href="/page-2", id="page-2-link"),
dbc.NavLink("Page 3", href="/page-3", id="page-3-link"),
], vertical=True, pills=True,
)],
style=SIDEBAR_STYLE
)
cdivs = [html.H2("Content"),
html.Div(id="page-content")]
for n in range(25):
cdivs.append(html.P(f'Content line {n}'))
content = html.Div(cdivs, style=CONTENT_STYLE)
app.layout = html.Div([dcc.Location(id="url"),
header, sidebar, adbar, content, footer])
# this callback uses the current pathname to set the active state of the
# corresponding nav link to true, allowing users to tell see page they are on
@app.callback(
[Output(f"page-{i}-link", "active") for i in range(1, 4)],
[Input("url", "pathname")],
)
def toggle_active_links(pathname):
if pathname == "/":
# Treat page 1 as the homepage / index
return True, False, False
return [pathname == f"/page-{i}" for i in range(1, 4)]
@app.callback(Output("page-content", "children"), [Input("url", "pathname")])
def render_page_content(pathname):
if pathname in ["/", "/page-1"]:
return html.P("This is the content of page 1!")
elif pathname == "/page-2":
return html.P("This is the content of page 2. Yay!")
elif pathname == "/page-3":
return html.P("Oh cool, this is page 3!")
# If the user tries to reach a different page, return a 404 message
return dbc.Jumbotron(
[
html.H1("404: Not found", className="text-danger"),
html.Hr(),
html.P(f"The pathname {pathname} was not recognised..."),
]
)
if __name__ == "__main__":
app.run_server(port=8888)