Skip to content


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


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


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.


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.


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 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 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 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;


/* 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%;


# 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.