Skip to content

Responsive sized charts #9

@ddanieltan

Description

@ddanieltan

Issue: The current altair chart does not resize responsively with the Div().

I found this discussion in Altair's issues that describe a fix by adding some CSS for the vega-embed class. vega/altair#2867

My suggestion to incorporate this fix:

  1. We add the additional CSS into altair_headers
  2. In altair2fasthtml(), we default to include .properties(width="container", height=240)

This allows the chart to resize responsively based on the width of it parent container, with a fixed height. I experimented with setting both width and height to "responsive" but I did not get the desired result.

Here's an example FastHTML app that reproduces this responsively sized chart

import altair as alt
from fasthtml.common import *
from vega_datasets import data

css = Style("""
.vega-embed {
  width: 100%;
  display: flex;
}
.vega-embed details,
.vega-embed details summary {
  position: relative;
}
""")

altair_headers = [
    Script(src="https://cdn.jsdelivr.net/npm/vega@5"),
    Script(src="https://cdn.jsdelivr.net/npm/vega-lite@5"),
    Script(src="https://cdn.jsdelivr.net/npm/vega-embed@6"),
]

hdrs = (
    picolink,
    css,
    altair_headers,
)

app, rt = fast_app(live=True, hdrs=hdrs, htmlkw={"data-theme": "light"})


def chart():
    source = data.seattle_weather()
    brush = alt.selection_interval(encodings=["x"])

    bars = (
        alt.Chart()
        .mark_bar()
        .encode(
            x="month(date):O",
            y="mean(precipitation):Q",
            opacity=alt.condition(brush, alt.OpacityValue(1), alt.OpacityValue(0.7)),
        )
        .add_params(brush)
    )

    line = (
        alt.Chart()
        .mark_rule(color="firebrick")
        .encode(y="mean(precipitation):Q", size=alt.SizeValue(3))
        .transform_filter(brush)
    )

    return alt.layer(bars, line, data=source)


def altair2html(chart):
    jsonstr = chart.properties(width="container", height=240).to_json()
    chart_id = f"uniq-{1}"
    settings = "{actions: false}"
    return Div(Script(f"vegaEmbed('#{chart_id}', {jsonstr}, {settings});"), id=chart_id)


@rt("/")
def get():
    return Title("Responsive Altair"), Container(
        altair2html(chart()),
    )


serve()

Happy to submit a PR if you like the idea

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions