How can I use a custom CSS together with Bootstrap and Tabler CSS?

Hello!

How can I use a custom CSS together with:

My goal is to create various HTML components (pn.pane.HTML) which need Bootstrap + Tabler icons + custom CSS.

I could do this using the following approach, but I think there should be a better way to not duplicate/overwhelm the code.

custom_css = """
<style>
  some custom CSS
</style>
"""

HTML_TEXT = f"""
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@latest/tabler-icons.min.css">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
    {custom_css}
    <div class="custom_class1">
        <span class="bootstrap_class custom_class2"><strong>{custom_text1}</strong></span>
    </div>
    <div class="custom_class2">
        <a href="{custom_link}" class="btn btn-{button_style} btn-sm" role="button">
            <i class="ti ti-{custom_icon}"></i> {custom_text2}
        </a>
    </div> 
"""

html_component = pn.pane.HTML(HTML_TEXT)

In my case, I could use a way to have these CSS styles applied to the entire dashboard.

Are you using panel>1.0.0?

If so, what if you use styles or stylesheets?

Ref Apply CSS โ€” Panel v1.1.0

I am using panel 1.0.2 and tried the suggestion from your link.
However, when using

stylesheets=[
        custom_css,
        "https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@latest/tabler-icons.min.css",
        "https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"
    ]

only the Bootstrap stylesheet is taken into account, not also the tabler icons.
I can still use the SVG code, but I liked to use the webfont <i class="ti ti-360-view"></i>
Maybe there is a limitation in Panel to not be able to use two external stylesheets.

Can you try to upgrade to the latest version? I did a PR that fixed a problem with icons in buttons.

I updated to 1.1.0, and now the HTML
<i class="ti ti-settings-pause"></i>
will show
image
instead of
image

Can you share a minimal, reproducible example (MRE)?

Here is an MRE.

import panel as pn

pn.extension()

custom_css = """
.text-label {
    width: 140px;
    height: 50px;
    border: 1px solid #cfcfcf;
    border-radius: 5px 0 0 5px;
    background-color: #f8f9fa;
    padding: 10px;
    text-align: center;
    display: flex;
    align-items: center;
    justify-content: center;
}

.image-label {
    width: 140px;
    height: 50px;
    border: 1px solid #cfcfcf;
    border-left: 0;
    background-color: white;
    display: flex;
    align-items: center;
    justify-content: center;
}
"""

html_component = pn.pane.HTML(
        f"""
            <div class="input-group">
                <span class="text-label">
                    <i class="ti ti-settings-pause"></i>
                    <strong>Demo text</strong>
                </span>
                <span class="image-label">
                    <a href="#" target="_blank">
                        <img src="https://holoviz.org/assets/holoviews.png" width=40 alt="Image">
                    </a>
                    <button class="btn btn-primary" style="margin-left:5px;" type="submit">Button</button>
                </span> 
            </div> 
            
        """,
        stylesheets=[
            custom_css,
            "https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@latest/tabler-icons.min.css",
            "https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"
        ]
)

template = pn.template.MaterialTemplate(
    site="",
    title="",
    main=[html_component]
)

template.servable()

Run it with panel serve simple_debug.py (assuming simple_debug.py is the name of the file)

And the result is:
image

The followings can be observed:

You can embed it directly in the HTML like this:

import panel as pn

pn.extension()

custom_css = """
.text-label {
    width: 140px;
    height: 50px;
    border: 1px solid #cfcfcf;
    border-radius: 5px 0 0 5px;
    background-color: #f8f9fa;
    padding: 10px;
    text-align: center;
    display: flex;
    align-items: center;
    justify-content: center;
}

.image-label {
    width: 140px;
    height: 50px;
    border: 1px solid #cfcfcf;
    border-left: 0;
    background-color: white;
    display: flex;
    align-items: center;
    justify-content: center;
}
"""

html_component = pn.pane.HTML(
    """
    <link href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@latest/tabler-icons.min.css" rel="stylesheet"/>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"/>

    <div class="input-group">
        <span class="text-label">
            <i class="ti ti-settings-pause"></i>
            <strong>Demo text</strong>
        </span>
        <span class="image-label">
            <a href="#" target="_blank">
                <img src="https://holoviz.org/assets/holoviews.png" width=40 alt="Image">
            </a>
            <button class="btn btn-primary" style="margin-left:5px;" type="submit">Button</button>
        </span>
    </div>""",
    stylesheets=[custom_css],
)


html_component

image

Yes, I already mentioned this on my first post (HTML_TEXT component) - I just want to avoid lots of <link> duplicates across the dashboard source code.
For now, I am using the svg representation of the icons.

From: Upgrade Guide โ€” Panel v1.1.0

image

import panel as pn

custom_css = """
.text-label {
    width: 140px;
    height: 50px;
    border: 1px solid #cfcfcf;
    border-radius: 5px 0 0 5px;
    background-color: #f8f9fa;
    padding: 10px;
    text-align: center;
    display: flex;
    align-items: center;
    justify-content: center;
}

.image-label {
    width: 140px;
    height: 50px;
    border: 1px solid #cfcfcf;
    border-left: 0;
    background-color: white;
    display: flex;
    align-items: center;
    justify-content: center;
}
"""

css_files = [
    "https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@latest/tabler-icons.min.css",
    "https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css",
]
pn.extension()

html_component = pn.pane.HTML(
    """
    <div class="input-group">
        <span class="text-label">
            <i class="ti ti-settings-pause"></i>
            <strong>Demo text</strong>
        </span>
        <span class="image-label">
            <a href="#" target="_blank">
                <img src="https://holoviz.org/assets/holoviews.png" width=40 alt="Image">
            </a>
            <button class="btn btn-primary" style="margin-left:5px;" type="submit">Button</button>
        </span>
    </div>
    """,
)

pn.template.MaterialTemplate(
    main=[html_component], raw_css=raw_css, css_files=css_files
)
1 Like

If I use your code, as such (assuming raw_css = custom_css) as you probably know, I get ValueError: List parameter 'raw_css' must be a list, not an object of type <class 'str'>
However, if I wrap it in a list [custom_css] nothing loads - I have an empty webpage, completely blank (not even the Panel header).
For now, I will use the svg and maybe later on with future Panel releases, things will start working for me too. :slight_smile: