Search code examples
htmljupyter-notebookipython

Formatting HTML Tables in Jupyter Notebook


I have a Jupyter notebook python cell where I'm trying to display some output in an HTML formatted table. I can't seem to be able to remove the border lines around internal cells.

Here's the HTML that I generate:

<html><style>
    table {border-style: solid; border-color: DodgerBlue;}
    .cp {background-color:DarkGreen; border-collapse:collapse;text-align:center;}
    .cf {background-color:DarkRed; border-collapse:collapse;text-align:center;}
</style>
<table>
<caption>Automaton 1 Evaluation</caption>
<caption style="background-color:DodgerBlue;">State Seen At Step</caption>
<thead style="background-color:DodgerBlue;"> 
    <tr><th>Trace #</th><th>1</th><th>2</th><th>3</th><th>4</th><th>5</th></tr></thead>
<tbody>
    <tr><td style="text-align:center;">0</td><td  class="cp">1</td><td  class="cf">3</td><td  class="cf">3</td><td  class="cf">3</td><td  class="cf">3</td></tr>
    <tr><td style="text-align:center;">1</td><td  class="cp">1</td><td  class="cp">0</td><td  class="cp">1</td><td  class="cp">1</td><td  class="cp">0</td></tr>
    <tr><td style="text-align:center;">2</td><td  class="cp">1</td><td  class="cp">0</td><td  class="cp">1</td><td  class="cp">0</td><td  class="cp">0</td></tr>
    <tr><td style="text-align:center;">3</td><td  class="cp">1</td><td >2</td><td  class="cp">0</td><td  class="cp">1</td><td >2</td></tr>
    <tr><td style="text-align:center;">4</td><td  class="cp">1</td><td >2</td><td  class="cp">1</td><td >2</td><td >2</td></tr>
</tbody>
</table></html>

and I display it in the cell using:

from IPython.display import HTML, display
tbl1 = auto1.traceListHTML(auto1.traceLog,title=f'Automaton 1 Evaluation')
tbl2 = auto2.traceListHTML(auto2.traceLog,title=f'Automaton 2 Evaluation')

box_layout = Layout(display='flex',
                    flex_flow='row',
                    justify_content='space-around',
                    width='450px'
                   )

#display(HTML(tbl1))
hbox1 = widgets.Box(children=[widgets.HTML(tbl1),widgets.HTML(tbl2)], layout=box_layout)
display(hbox1)

where auto1.traceListHTML generates the above HTML code.

What I get is: Display of above HTML in notebook cell

I'm trying to get rid of the hairlines between the internal cells. Even if I add border-style: solid to my .cp and.cf styles, those thin black lines remain as seen here:

.cf {background-color:DarkRed; border-collapse:collapse;text-align:center; border-style: solid}

Display of same HTML but with addition of border-style: solid

I'm wondering if this might be a problem with IPython.


Solution

  • Skip to the bottom section because I think I got it...

    Leaving this here though because maybe it will help someone else with less complicated needs. Or help others to see the journey.


    This doesn't quite work as I believe you want, but you didn't provide an minimal, reproducible example which I think mine does:

    Cell #1 content:

    # I added the `<table cellspacing="0" cellpadding="0">` from https://stackoverflow.com/a/71657476/8508004
    s='''<html><style>
        table {border-style: solid; border-color: DodgerBlue;}
        .cp {background-color:DarkGreen; text-align:center;}
        .cf {background-color:DarkRed; text-align:center;}
    </style>
    <table cellspacing="0" cellpadding="0">
    <caption>Automaton 1 Evaluation</caption>
    <caption style="background-color:DodgerBlue;">State Seen At Step</caption>
    <thead style="background-color:DodgerBlue;"> 
        <tr><th>Trace #</th><th>1</th><th>2</th><th>3</th><th>4</th><th>5</th></tr></thead>
    <tbody>
        <tr><td style="text-align:center;">0</td><td  class="cp">1</td><td  class="cf">3</td><td  class="cf">3</td><td  class="cf">3</td><td  class="cf">3</td></tr>
        <tr><td style="text-align:center;">1</td><td  class="cp">1</td><td  class="cp">0</td><td  class="cp">1</td><td  class="cp">1</td><td  class="cp">0</td></tr>
        <tr><td style="text-align:center;">2</td><td  class="cp">1</td><td  class="cp">0</td><td  class="cp">1</td><td  class="cp">0</td><td  class="cp">0</td></tr>
        <tr><td style="text-align:center;">3</td><td  class="cp">1</td><td >2</td><td  class="cp">0</td><td  class="cp">1</td><td >2</td></tr>
        <tr><td style="text-align:center;">4</td><td  class="cp">1</td><td >2</td><td  class="cp">1</td><td >2</td><td >2</td></tr>
    </tbody>
    </table></html>'''
    

    Cell #2 content:

    from IPython.display import HTML, display
    from ipywidgets import widgets, Layout, HTML
    tbl1 = s
    tbl2 = s
    
    box_layout = Layout(display='flex',
                        flex_flow='row',
                        justify_content='space-around',
                        width='450px'
                       )
    
    #display(HTML(tbl1))
    hbox1 = widgets.Box(children=[widgets.HTML(tbl1),widgets.HTML(tbl2)], layout=box_layout)
    display(hbox1)
    

    Result:

    enter image description here

    Key was I added the <table cellspacing="0" cellpadding="0"> from here, but you probably wanted the table itself to have a border? So that is why I suppose it isn't complete.

    So I need to go back to <table>....


    Maybe closer:

    s='''<html><style>
        table {
            border: 2px solid #222222;
            border-collapse: collapse;
        }
        td, th {
            border: none;
            padding: 5px;
        }
        caption {
            background-color: DodgerBlue;
            color: white;
            padding: 5px;
        }
        thead {
            background-color: DodgerBlue;
            color: white;
        }
        .cp {background-color:DarkGreen; color: white; text-align:center;}
        .cf {background-color:DarkRed; color: white; text-align:center;}
    </style>
    <table>
    <caption>Automaton 1 Evaluation</caption>
    <caption>State Seen At Step</caption>
    <thead> 
        <tr><th>Trace #</th><th>1</th><th>2</th><th>3</th><th>4</th><th>5</th></tr>
    </thead>
    <tbody>
        <tr><td style="text-align:center;">0</td><td class="cp">1</td><td class="cf">3</td><td class="cf">3</td><td class="cf">3</td><td class="cf">3</td></tr>
        <tr><td style="text-align:center;">1</td><td class="cp">1</td><td class="cp">0</td><td class="cp">1</td><td class="cp">1</td><td class="cp">0</td></tr>
        <tr><td style="text-align:center;">2</td><td class="cp">1</td><td class="cp">0</td><td class="cp">1</td><td class="cp">0</td><td class="cp">0</td></tr>
        <tr><td style="text-align:center;">3</td><td class="cp">1</td><td>2</td><td class="cp">0</td><td class="cp">1</td><td>2</td></tr>
        <tr><td style="text-align:center;">4</td><td class="cp">1</td><td>2</td><td class="cp">1</td><td>2</td><td>2</td></tr>
    </tbody>
    </table></html>'''
    from IPython.display import HTML, display
    from ipywidgets import widgets, Layout, HTML
    tbl1 = s
    tbl2 = s
    
    box_layout = Layout(display='flex',
                        flex_flow='row',
                        justify_content='space-around',
                        width='450px'
                        )
    
    #display(HTML(tbl1))
    hbox1 = widgets.Box(children=[widgets.HTML(tbl1),widgets.HTML(tbl2)], layout=box_layout)
    display(hbox1)
    

    enter image description here


    Or closer option #2:

    s='''<html><style>
        table {
            border: 4px solid DodgerBlue;
            border-collapse: separate;
            border-spacing: 0;
        }
        table::after {
            content: '';
            position: absolute;
            top: 1px;
            left: 1px;
            right: 1px;
            bottom: 1px;
            border: 1px solid #222222;
        }
        td, th {
            border: none;
            padding: 5px;
            position: relative;
        }
        caption {
            background-color: DodgerBlue;
            color: white;
            padding: 5px;
        }
        thead {
            background-color: DodgerBlue;
            color: white;
        }
        .cp {background-color:DarkGreen; color: white; text-align:center;}
        .cf {background-color:DarkRed; color: white; text-align:center;}
    </style>
    <table>
    <caption>Automaton 1 Evaluation</caption>
    <caption>State Seen At Step</caption>
    <thead> 
        <tr><th>Trace #</th><th>1</th><th>2</th><th>3</th><th>4</th><th>5</th></tr>
    </thead>
    <tbody>
        <tr><td style="text-align:center;">0</td><td class="cp">1</td><td class="cf">3</td><td class="cf">3</td><td class="cf">3</td><td class="cf">3</td></tr>
        <tr><td style="text-align:center;">1</td><td class="cp">1</td><td class="cp">0</td><td class="cp">1</td><td class="cp">1</td><td class="cp">0</td></tr>
        <tr><td style="text-align:center;">2</td><td class="cp">1</td><td class="cp">0</td><td class="cp">1</td><td class="cp">0</td><td class="cp">0</td></tr>
        <tr><td style="text-align:center;">3</td><td class="cp">1</td><td>2</td><td class="cp">0</td><td class="cp">1</td><td>2</td></tr>
        <tr><td style="text-align:center;">4</td><td class="cp">1</td><td>2</td><td class="cp">1</td><td>2</td><td>2</td></tr>
    </tbody>
    </table></html>'''
    from IPython.display import HTML, display
    from ipywidgets import widgets, Layout, HTML
    tbl1 = s
    tbl2 = s
    
    box_layout = Layout(display='flex',
                        flex_flow='row',
                        justify_content='space-around',
                        width='450px'
                        )
    
    #display(HTML(tbl1))
    hbox1 = widgets.Box(children=[widgets.HTML(tbl1),widgets.HTML(tbl2)], layout=box_layout)
    display(hbox1)
    

    enter image description here

    Or closer option #3:

    s='''<html><style>
        table {
            border: 4px solid DodgerBlue;
            border-top: 4px solid #333333;
            border-collapse: collapse;
        }
        td, th {
            border: none;
            padding: 5px;
        }
        caption {
            background-color: DodgerBlue;
            color: white;
            padding: 5px;
        }
        thead {
            background-color: DodgerBlue;
            color: white;
        }
        .cp {background-color:DarkGreen; color: white; text-align:center;}
        .cf {background-color:DarkRed; color: white; text-align:center;}
    </style>
    <table>
    <caption>Automaton 1 Evaluation</caption>
    <caption>State Seen At Step</caption>
    <thead> 
        <tr><th>Trace #</th><th>1</th><th>2</th><th>3</th><th>4</th><th>5</th></tr>
    </thead>
    <tbody>
        <tr><td style="text-align:center;">0</td><td class="cp">1</td><td class="cf">3</td><td class="cf">3</td><td class="cf">3</td><td class="cf">3</td></tr>
        <tr><td style="text-align:center;">1</td><td class="cp">1</td><td class="cp">0</td><td class="cp">1</td><td class="cp">1</td><td class="cp">0</td></tr>
        <tr><td style="text-align:center;">2</td><td class="cp">1</td><td class="cp">0</td><td class="cp">1</td><td class="cp">0</td><td class="cp">0</td></tr>
        <tr><td style="text-align:center;">3</td><td class="cp">1</td><td>2</td><td class="cp">0</td><td class="cp">1</td><td>2</td></tr>
        <tr><td style="text-align:center;">4</td><td class="cp">1</td><td>2</td><td class="cp">1</td><td>2</td><td>2</td></tr>
    </tbody>
    </table></html>'''
    from IPython.display import HTML, display
    from ipywidgets import widgets, Layout, HTML
    tbl1 = s
    tbl2 = s
    
    box_layout = Layout(display='flex',
                        flex_flow='row',
                        justify_content='space-around',
                        width='450px'
                        )
    
    #display(HTML(tbl1))
    hbox1 = widgets.Box(children=[widgets.HTML(tbl1),widgets.HTML(tbl2)], layout=box_layout)
    display(hbox1)
    

    enter image description here


    I think this does it?

    Made a secondary caption and applied styling to that to separate it from the top part of the table.

    (Plus I realized the top caption had gotten the dodger blue background along the way and OP wanted it dark.)

    s='''<html><style>
        table {
            border: 4px solid DodgerBlue;
            border-collapse: collapse;
            width: 100%;
        }
        td, th {
            border: none;
            padding: 5px;
        }
        caption {
            color: white;
            padding: 5px;
        }
        .primary-caption {
            background-color: black;
        }
        .secondary-caption {
            background-color: DodgerBlue;
            border-bottom: 1px solid black;
        }
        thead {
            background-color: DodgerBlue;
            color: white;
        }
        .table-header {
            border-top: 1px solid black;
        }
        .cp {background-color:DarkGreen; color: white; text-align:center;}
        .cf {background-color:DarkRed; color: white; text-align:center;}
    </style>
    <table>
    <caption class="primary-caption">Automaton 1 Evaluation</caption>
    <caption class="secondary-caption">State Seen At Step</caption>
    <thead class="table-header"> 
        <tr><th>Trace #</th><th>1</th><th>2</th><th>3</th><th>4</th><th>5</th></tr>
    </thead>
    <tbody>
        <tr><td style="text-align:center;">0</td><td class="cp">1</td><td class="cf">3</td><td class="cf">3</td><td class="cf">3</td><td class="cf">3</td></tr>
        <tr><td style="text-align:center;">1</td><td class="cp">1</td><td class="cp">0</td><td class="cp">1</td><td class="cp">1</td><td class="cp">0</td></tr>
        <tr><td style="text-align:center;">2</td><td class="cp">1</td><td class="cp">0</td><td class="cp">1</td><td class="cp">0</td><td class="cp">0</td></tr>
        <tr><td style="text-align:center;">3</td><td class="cp">1</td><td>2</td><td class="cp">0</td><td class="cp">1</td><td>2</td></tr>
        <tr><td style="text-align:center;">4</td><td class="cp">1</td><td>2</td><td class="cp">1</td><td>2</td><td>2</td></tr>
    </tbody>
    </table></html>'''
    from IPython.display import HTML, display
    from ipywidgets import widgets, Layout, HTML
    
    tbl1 = s
    tbl2 = s
    
    box_layout = Layout(display='flex',
                        flex_flow='row',
                        justify_content='space-around',
                        width='450px'
                       )
    
    hbox1 = widgets.Box(children=[widgets.HTML(tbl1),widgets.HTML(tbl2)], layout=box_layout)
    display(hbox1)
    

    enter image description here


    Update:
    I assumed since title was called caption that the original came from Python/Pandas and I have since added here more of that journey to make it easier to try different things, such as how IPython's HTML() seems to yield different look than ipywidget's HTML().
    The header details where you can get sessions in your browser to try it without touching your own system. Meant for JupyterLab Dark Theme.