Col width is always 50-50 even when specified differently

Hello to everyone !

I run dash on a jupyter notebook google chrome windows 11 set up. The idea is to talk to a chatbot and when the user says a word like “!col” a second col appears on the screen next to the chatbot, aligned on the same row.

The issue is that the width is always 50-50 even if I specified width = 20%

Here I can provided you a minimal example.

import dash
import dash_bootstrap_components as dbc
from dash import html, dcc, Input, Output, State, callback

def get_app_background_style():
    """
    """
    # Set a unified background for the whole app or container.
    BACKGROUND_COLOR = '#FAFAFA' # '#f8f9fa'
    # 'height': f'{height}vh', 'display': 'flex', 'flexDirection': 'row'
    app_background_style = {
        'backgroundColor': BACKGROUND_COLOR,  # Example background color, should be changed based on your design
        'height': '100vh',  # 100% of the viewport height
        'padding': '20px',
        'width': '100vw'
    }
    return app_background_style

def get_second_col_style():
    """
    """
    return {
        'padding': '10px',
        'display': 'none',  # Initially hidden
        'marginLeft': '5px',
        'marginRight': '7px',
        'verticalAlign': 'top',
        'width': "0%",  # Define width only when the column is shown
    }

def get_chatbot_column_style():
    """
    """
    return {
        'padding': '7px',
        'marginLeft': '7px',
        'marginRight': '7px',
        'width': '100%',  # Default width
        'display': 'inline-block',
        'verticalAlign': 'top',
    }
def get_row_style():
    # Use flex properties on the container of the columns to align them at the top.
    return {
        'display': 'flex',
        'flexWrap': 'nowrap',  # Prevents wrapping of columns
        'alignItems': 'flex-start'  # Align children to the start of the cross axis
    }

# Initialize the Dash app
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])


def get_app_layout_fake(CHAT_LAYOUT, MENU_LAYOUT):
    chatbot_column_style = get_chatbot_column_style()
    second_col_style = get_second_col_style()
    row_style = get_row_style()
    app_background_style = get_app_background_style()
    header = html.Div()  # Define your header here

    APP_LAYOUT = html.Div([
                    header,
                    dbc.Row([
                        dbc.Col(id='chatbot_column', children=[CHAT_LAYOUT], style=chatbot_column_style),
                        dbc.Col(id='menu_column', children=[MENU_LAYOUT], style= second_col_style),
                    ], style=row_style),
                    dbc.Row([
                        html.Div(id='profile-recipe-div'),
                    ], className='h-100', style={'marginTop': '20px', 'marginBottom': '20px'}),
                    dcc.Store(id='profile-store'),
                ], style=app_background_style)
    
    return APP_LAYOUT

# Chatbot Layout
CHAT_LAYOUT_FAKE = html.Div([
    dbc.Textarea(id="chat-area", className="mb-3", style={"width": "100%", "height": "300px"}),
    dbc.Input(id="chat-input", placeholder="Type a message...", type="text"),
    dbc.Button("Send", id="send-button", n_clicks=0, className="ms-2"),
    html.Div(id="chat-output")
])

# Menu Layout (Placeholder for your menu content)
MENU_LAYOUT_FAKE = html.Div([html.H3("Menu Area"), html.P("Menu content goes here...")])

app.layout = get_app_layout_fake(CHAT_LAYOUT_FAKE, MENU_LAYOUT_FAKE)

# Callback to update the chat and possibly show the second column
@app.callback(
    [Output("chat-output", "children"),
     Output("menu_column", "style")],
    [Input("send-button", "n_clicks")],
    [State("chat-input", "value"),
     State("menu_column", "style")]
)
def update_output(n_clicks, value, style):
    if n_clicks > 0:
        # Check if the user typed "!col"
        if value == "!col":
            # Update style to show the second column
            style["display"] = "inline-block"
            style["width"] = "20%"  # Adjust width as needed
            return "Showing menu...", style
        else:
            return f"You said: {value}", style
    return "", style


# Callback for the chatbot (This is a placeholder for the actual logic)
@app.callback(
    Output("chat-area", "value"),
    Input("send-button", "n_clicks"),
    State("chat-input", "value"),
    State("chat-area", "value")
)
def update_chat(n_clicks, user_input, chat_history):
    if n_clicks > 0:
        new_message = f"User: {user_input}\nChatbot: (Your response logic here)\n"
        return chat_history + new_message if chat_history else new_message
    return chat_history

# Run the app
if __name__ == '__main__':
    app.run_server("localhost", 560, debug=True)

Thank you for your help !

Hello @Plotlyoko,

Pretty sure the default setting if to split columns evenly, since you are not providing a span.

To do this properly, you should have the chatbot_column start at span=12 and the other column stay at span=2 or something, with style={'display':'none'}

Then when the user says !col, your callback updates the chatbot_column to span 10 and updates the style of the other to {'display':'block'} and obviously to turn off, it resets to the original values.

1 Like

@jinnyzor thank you for your answer. If I recall properly, in Bootstrap, the grid system is max 12. So with your method the other columns would be below however we don’t mind because it’s not displayed. And in the callback I will adapt the size so the sum is 12 to achieve the two cols on the same row and aligned.
If it’s correct it’s very convenient because I can have more than one column not displayed with span = whatever. Which I plan in the final projet.

This is correct, for both dmc and dbc the total span is 12, anything beyond that will be dropped to the next line.

I tried to implement it, and this is what I’ve fathom so far, maybe I can do better, with your solution I have to add at least two components in the callback to handle the display and the span.

 Output("menu_column", "className"),
 Output("menu_column", "style")

While with the previous code (which was not working) I could handle everything just with :
Output("menu_column", "style")

And let’s say I want to update the right and left margin in the callback. Will I have 4 components per second col. Or maybe there is a better way to do it !