Skip to content

Border

The border style enables the drawing of a box around a widget.

Note

Due to a Textual limitation, border and outline cannot coexist in the same edge of a widget.

Syntax

border: [<border>] [<color>] [<percentage>];

border-top: [<border>] [<color>] [<percentage>];
border-right: [<border>] [<color> [<percentage>]];
border-bottom: [<border>] [<color> [<percentage>]];
border-left: [<border>] [<color> [<percentage>]];

The border style accepts an optional <border> that sets the visual style of the widget border, an optional <color> to set the color of the border, and an optional <percentage> to specify the color transparency.

Borders may also be set individually for the four edges of a widget with the border-top, border-right, border-bottom and border-left rules.

Multiple edge rules

If multiple border styles target the same edge, the last style that targets a specific edge is the one that is applied to that edge. For example, consider the CSS below:

Static {
    border-top: dashed red;
    border: solid green;  /* overrides the border-top rule above */
    /* Change the border but just for the bottom edge: */
    border-bottom: double blue;
}

The CSS snippet above will add a solid green border around Static widgets, except for the bottom edge, which will be double blue.

Defaults

If <color> is specified but <border> is not, it defaults to "solid". If <border> is specified but <color>is not, it defaults to green (RGB color "#00FF00"). If <percentage> is not specified it defaults to 100%.

Border command

The textual CLI has a subcommand which will let you explore the various border types interactively:

textual borders

Alternatively, you can see the examples below.

Examples

Basic usage

This examples shows three widgets with different border styles.

BorderApp ──────────────────────────────────────────────────────────────────────────── My border is solid red ──────────────────────────────────────────────────────────────────────────── ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ My border is dashed green ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ My border is tall blue ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

from textual.app import App
from textual.widgets import Label


class BorderApp(App):
    def compose(self):
        yield Label("My border is solid red", id="label1")
        yield Label("My border is dashed green", id="label2")
        yield Label("My border is tall blue", id="label3")


app = BorderApp(css_path="border.css")
#label1 {
    background: red 20%;
    color: red;
    border: solid red;
}

#label2 {
    background: green 20%;
    color: green;
    border: dashed green;
}

#label3 {
    background: blue 20%;
    color: blue;
    border: tall blue;
}

Screen {
    background: white;
}

Screen > Label {
    width: 100%;
    height: 5;
    content-align: center middle;
    color: white;
    margin: 1;
    box-sizing: border-box;
}

All border types

The next example shows a grid with all the available border types.

AllBordersApp +------------------+╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ |ascii|blankdashed +------------------+╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ ══════════════════━━━━━━━━━━━━━━━━━━ doubleheavyhidden/none ══════════════════━━━━━━━━━━━━━━━━━━ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ hkeyinnernone ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀──────────────────────────────────── outerroundsolid ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄──────────────────────────────────── ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ tallvkeywide ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔

from textual.app import App
from textual.containers import Grid
from textual.widgets import Label


class AllBordersApp(App):
    def compose(self):
        yield Grid(
            Label("ascii", id="ascii"),
            Label("blank", id="blank"),
            Label("dashed", id="dashed"),
            Label("double", id="double"),
            Label("heavy", id="heavy"),
            Label("hidden/none", id="hidden"),
            Label("hkey", id="hkey"),
            Label("inner", id="inner"),
            Label("none", id="none"),
            Label("outer", id="outer"),
            Label("round", id="round"),
            Label("solid", id="solid"),
            Label("tall", id="tall"),
            Label("vkey", id="vkey"),
            Label("wide", id="wide"),
        )

app = AllBordersApp(css_path="border_all.css")
#ascii {
    border: ascii $accent;
}

#blank {
    border: blank $accent;
}

#dashed {
    border: dashed $accent;
}

#double {
    border: double $accent;
}

#heavy {
    border: heavy $accent;
}

#hidden {
    border: hidden $accent;
}

#hkey {
    border: hkey $accent;
}

#inner {
    border: inner $accent;
}

#none {
    border: none $accent;
}

#outer {
    border: outer $accent;
}

#round {
    border: round $accent;
}

#solid {
    border: solid $accent;
}

#tall {
    border: tall $accent;
}

#vkey {
    border: vkey $accent;
}

#wide {
    border: wide $accent;
}

Grid {
    grid-size: 3 5;
    align: center middle;
    grid-gutter: 1 2;
}

Label {
    width: 20;
    height: 3;
    content-align: center middle;
}

Borders and outlines

The next example makes the difference between border and outline clearer by having three labels side-by-side. They contain the same text, have the same width and height, and are styled exactly the same up to their border and outline styles.

This example also shows that a widget cannot contain both a border and an outline:

OutlineBorderApp ─────────────────────────────────────────────────────────────────── ear is the mind-killer. ear is the little-death that brings total obliteration.  will face my fear.  will permit it to pass over me and through me. nd when it has gone past, I will turn the inner eye to see its path here the fear has gone there will be nothing. Only I will remain. ─────────────────────────────────────────────────────────────────── ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ I must not fear. Fear is the mind-killer. Fear is the little-death that brings total obliteration. I will face my fear. I will permit it to pass over me and through me. And when it has gone past, I will turn the inner eye to see its path. ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ───────────────────────────────────────────────────────────────────── I must not fear. Fear is the mind-killer. Fear is the little-death that brings total obliteration. I will face my fear. I will permit it to pass over me and through me. And when it has gone past, I will turn the inner eye to see its path. ─────────────────────────────────────────────────────────────────────

from textual.app import App
from textual.widgets import Label


TEXT = """I must not fear.
Fear is the mind-killer.
Fear is the little-death that brings total obliteration.
I will face my fear.
I will permit it to pass over me and through me.
And when it has gone past, I will turn the inner eye to see its path.
Where the fear has gone there will be nothing. Only I will remain."""


class OutlineBorderApp(App):
    def compose(self):
        yield Label(TEXT, classes="outline")
        yield Label(TEXT, classes="border")
        yield Label(TEXT, classes="outline border")


app = OutlineBorderApp(css_path="outline_vs_border.css")
Label {
    height: 8;
}

.outline {
    outline: $error round;
}

.border {
    border: $success heavy;
}

CSS

/* Set a heavy white border */
border: heavy white;

/* Set a red border on the left */
border-left: outer red;

/* Set a rounded orange border, 50% transparency. */
border: round orange 50%;

Python

# Set a heavy white border
widget.styles.border = ("heavy", "white")

# Set a red border on the left
widget.styles.border_left = ("outer", "red")

See also

  • box-sizing to specify how to account for the border in a widget's dimensions.
  • outline to add an outline around the content of a widget.