File: design.md

package info (click to toggle)
textual 2.1.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 55,056 kB
  • sloc: python: 85,423; lisp: 1,669; makefile: 101
file content (341 lines) | stat: -rw-r--r-- 14,624 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
# Themes

Textual comes with several built-in *themes*, and it's easy to create your own.
A theme provides variables which can be used in the CSS of your app.
Click on the tabs below to see how themes can change the appearance of an app.

=== "nord"

    ```{.textual path="docs/examples/themes/todo_app.py"}
    ```

=== "gruvbox"

    ```{.textual path="docs/examples/themes/todo_app.py" press="ctrl+t"}
    ```

=== "tokyo-night"

    ```{.textual path="docs/examples/themes/todo_app.py" press="ctrl+t,ctrl+t"}
    ```

=== "textual-dark"

    ```{.textual path="docs/examples/themes/todo_app.py" press="ctrl+t,ctrl+t,ctrl+t"}
    ```

=== "solarized-light"

    ```{.textual path="docs/examples/themes/todo_app.py" press="ctrl+t,ctrl+t,ctrl+t,ctrl+t"}
    ```

## Changing the theme

The theme can be changed at runtime via the [Command Palette](./command_palette.md) (++ctrl+p++).

You can also programmatically change the theme by setting the value of `App.theme` to the name of a theme:

```python
class MyApp(App):
    def on_mount(self) -> None:
        self.theme = "nord"
```

A theme must be *registered* before it can be used.
Textual comes with a selection of built-in themes which are registered by default.

## Registering a theme

A theme is a simple Python object which maps variable names to colors.
Here's an example:

```python
from textual.theme import Theme

arctic_theme = Theme(
    name="arctic",
    primary="#88C0D0",
    secondary="#81A1C1",
    accent="#B48EAD",
    foreground="#D8DEE9",
    background="#2E3440",
    success="#A3BE8C",
    warning="#EBCB8B",
    error="#BF616A",
    surface="#3B4252",
    panel="#434C5E",
    dark=True,
    variables={
        "block-cursor-text-style": "none",
        "footer-key-foreground": "#88C0D0",
        "input-selection-background": "#81a1c1 35%",
    },
)
```

You can register this theme by calling `App.register_theme` in the `on_mount` method of your `App`.

```python
from textual.app import App

class MyApp(App):
    def on_mount(self) -> None:
        # Register the theme
        self.register_theme(arctic_theme)  # (1)!

        # Set the app's theme
        self.theme = "arctic"  # (2)!
```

1. Register the theme, making it available to the app (and command palette)
2. Set the app's theme. When this line runs, the app immediately refreshes to use the new theme.

## Theme variables

Themes consist of up to 11 *base colors*, (`primary`, `secondary`, `accent`, etc.), which Textual uses to generate a broad range of CSS variables.
For example, the `textual-dark` theme defines the *primary* base color as `#004578`.

Here's an example of CSS which uses these variables:

```css
MyWidget {
    background: $primary;
    color: $foreground;
}
```

On changing the theme, the values stored in these variables are updated to match the new theme, and the colors of `MyWidget` are updated accordingly.

## Base colors

When defining a theme, only the `primary` color is required.
Textual will attempt to generate the other base colors if they're not supplied.

The following table lists each of 11 base colors (as used in CSS) and a description of where they are used by default.

| Color                   | Description                                                                                                                                         |
| ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `$primary`              | The primary color, can be considered the *branding* color. Typically used for titles, and backgrounds for strong emphasis.                          |
| `$secondary`            | An alternative branding color, used for similar purposes as `$primary`, where an app needs to differentiate something from the primary color.       |
| `$foreground`           | The default text color, which should be legible on `$background`, `$surface`, and `$panel`.                                                         |
| `$background`           | A color used for the background, where there is no content. Used as the default background color for screens.                                       |
| `$surface`              | The default background color of widgets, typically sitting on top of `$background`.                                                                 |
| `$panel`                | A color used to differentiate a part of the UI form the main content. Used sparingly in Textual itself.                                             |
| `$boost`                | A color with alpha that can be used to create *layers* on a background.                                                                             |
| `$warning`              | Indicates a warning. Typically used as a background color. `$text-warning` can be used for foreground.                                                                                                            |
| `$error`                | Indicates an error. Typically used as a background color. `$text-error` can be used for foreground.                                                                                                             |
| `$success`              | Used to indicate success. Typically used as a background color. `$text-success` can be used for foreground.                                                                                                      |
| `$accent`               | Used sparingly to draw attention. Typically contrasts with `$primary` and `$secondary`.                                                                 |

## Shades

For every color, Textual generates 3 dark shades and 3 light shades.

- Add `-lighten-1`, `-lighten-2`, or `-lighten-3` to the color's variable name to get lighter shades (3 is the lightest).
- Add `-darken-1`, `-darken-2`, and `-darken-3` to a color to get the darker shades (3 is the darkest).

For example, `$secondary-darken-1` is a slightly darkened `$secondary`, and `$error-lighten-3` is a very light version of the `$error` color.

## Light and dark themes

Themes can be either *light* or *dark*.
This setting is specified in the `Theme` constructor via the `dark` argument, and influences how Textual
generates variables.
Built-in widgets may also use the value of `dark` to influence their appearance.

## Text color

The default color of text in a theme is `$foreground`.
This color should be legible on `$background`, `$surface`, and `$panel` backgrounds.

There is also `$foreground-muted` for text which has lower importance.
`$foreground-disabled` can be used for text which is disabled, for example a menu item which can't be selected.

You can set the text color via the [color](../styles/color.md) CSS property.

The available text colors are:

- `$text-primary`
- `$text-secondary`
- `$text-accent`
- `$text-warning`
- `$text-error`
- `$text-success`

### Ensuring text legibility

In some cases, the background color of a widget is unpredictable, so we cannot be certain our text will be readable against it.

The theme system defines three CSS variables which you can use to ensure that text is legible on any background.

- `$text` is set to a slightly transparent black or white, depending on which has better contrast against the background the text is on.
- `$text-muted` sets a slightly faded text color. Use this for text which has lower importance. For instance a sub-title or supplementary information.
- `$text-disabled` sets faded out text which indicates it has been disabled. For instance, menu items which are not applicable and can't be clicked.

### Colored text

Colored text is also generated from the base colors, which is guaranteed to be legible against a background of `$background`, `$surface`, and `$panel`.
For example, `$text-primary` is a version of the `$primary` color tinted to ensure legibility.

=== "Output (Theme: textual-dark)"

    ```{.textual path="docs/examples/themes/colored_text.py" lines="9" columns="30"}
    ```

=== "colored_text.py"

    ```python title="colored_text.py"
    --8<-- "docs/examples/themes/colored_text.py"
    ```

These colors are also be guaranteed to be legible when used as the foreground color of a widget with a *muted color* background.

## Muted colors

Muted colors are generated from the base colors by blending them with `$background` at 70% opacity.
For example, `$primary-muted` is a muted version of the `$primary` color.

Textual aims to ensure that the colored text it generates is legible against the corresponding muted color.
In other words, `$text-primary` text should be legible against a background of `$primary-muted`:

=== "Output (Theme: textual-dark)"

    ```{.textual path="docs/examples/themes/muted_backgrounds.py" lines="9" columns="40"}
    ```

=== "muted_backgrounds.py"

    ```python title="muted_backgrounds.py"
    --8<-- "docs/examples/themes/muted_backgrounds.py"
    ```

The available muted colors are:

- `$primary-muted`
- `$secondary-muted`
- `$accent-muted`
- `$warning-muted`
- `$error-muted`
- `$success-muted`

## Additional variables

Textual uses the base colors as default values for additional variables used throughout the framework.
These variables can be overridden by passing a `variables` argument to the `Theme` constructor.
This also allows you to override variables such as `$primary-muted`, described above.

In the Gruvbox theme, for example, we override the foreground color of the block cursor (the cursor used in widgets like `OptionList`) to be `$foreground`.

```python hl_lines="14-17"
Theme(
    name="gruvbox",
    primary="#85A598",
    secondary="#A89A85",
    warning="#fabd2f",
    error="#fb4934",
    success="#b8bb26",
    accent="#fabd2f",
    foreground="#fbf1c7",
    background="#282828",
    surface="#3c3836",
    panel="#504945",
    dark=True,
    variables={
        "block-cursor-foreground": "#fbf1c7",
        "input-selection-background": "#689d6a40",
    },
)
```

Here's a comprehensive list of these variables, their purposes, and default values:

### Border

| Variable | Purpose | Default Value |
|----------|---------|---------------|
| `$border` | The border color for focused widgets with a border | `$primary` |
| `$border-blurred` | The border color for unfocused widgets | Slightly darkened `$surface` |

### Cursor

| Variable | Purpose | Default Value |
|----------|---------|---------------|
| `$block-cursor-foreground` | Text color for block cursor (e.g., in OptionList) | `$text` |
| `$block-cursor-background` | Background color for block cursor | `$primary` |
| `$block-cursor-text-style` | Text style for block cursor | `"bold"` |
| `$block-cursor-blurred-foreground` | Text color for unfocused block cursor | `$text` |
| `$block-cursor-blurred-background` | Background color for unfocused block cursor | `$primary` with 30% opacity |
| `$block-cursor-blurred-text-style` | Text style for unfocused block cursor | `"none"` |
| `$block-hover-background` | Background color when hovering over a block | `$boost` with 5% opacity |

### Input

| Variable | Purpose | Default Value |
|----------|---------|---------------|
| `$input-cursor-background` | Background color of the input cursor | `$foreground` |
| `$input-cursor-foreground` | Text color of the input cursor | `$background` |
| `$input-cursor-text-style` | Text style of the input cursor | `"none"` |
| `$input-selection-background` | Background color of selected text | `$primary-lighten-1` with 40% opacity |

### Scrollbar

| Variable | Purpose | Default Value |
|----------|---------|---------------|
| `$scrollbar` | Color of the scrollbar | `$panel` |
| `$scrollbar-hover` | Color of the scrollbar when hovered | `$panel-lighten-1` |
| `$scrollbar-active` | Color of the scrollbar when active (being dragged) | `$panel-lighten-2` |
| `$scrollbar-background` | Color of the scrollbar track | `$background-darken-1` |
| `$scrollbar-corner-color` | Color of the scrollbar corner | Same as `$scrollbar-background` |
| `$scrollbar-background-hover` | Color of the scrollbar track when hovering over the scrollbar area | Same as `$scrollbar-background` |
| `$scrollbar-background-active` | Color of the scrollbar track when the scrollbar is active | Same as `$scrollbar-background` |

### Links

| Variable | Purpose | Default Value |
|----------|---------|---------------|
| `$link-background` | Background color of links | `"initial"` |
| `$link-background-hover` | Background color of links when hovered | `$primary` |
| `$link-color` | Text color of links | `$text` |
| `$link-style` | Text style of links | `"underline"` |
| `$link-color-hover` | Text color of links when hovered | `$text` |
| `$link-style-hover` | Text style of links when hovered | `"bold not underline"` |

### Footer

| Variable | Purpose | Default Value |
|----------|---------|---------------|
| `$footer-foreground` | Text color in the footer | `$foreground` |
| `$footer-background` | Background color of the footer | `$panel` |
| `$footer-key-foreground` | Text color for key bindings in the footer | `$accent` |
| `$footer-key-background` | Background color for key bindings in the footer | `"transparent"` |
| `$footer-description-foreground` | Text color for descriptions in the footer | `$foreground` |
| `$footer-description-background` | Background color for descriptions in the footer | `"transparent"` |
| `$footer-item-background` | Background color for items in the footer | `"transparent"` |

### Button

| Variable | Purpose | Default Value |
|----------|---------|---------------|
| `$button-foreground` | Foreground color for standard buttons | `$foreground` |
| `$button-color-foreground` | Foreground color for colored buttons | `$text` |
| `$button-focus-text-style` | Text style for focused buttons | `"bold reverse"` |

## App-specific variables

The variables above are defined and used by Textual itself.
However, you may also wish to expose other variables which are specific to your application.

You can do this by overriding `App.get_theme_variable_defaults` in your `App` subclass.

This method should return a dictionary of variable names and their default values.
If a variable defined in this dictionary is also defined in a theme's `variables` dictionary, the theme's value will be used.

## Previewing colors

Run the following from the command line to preview the colors defined in the color system:

```bash
textual colors
```

Inside the preview you can change the theme via the Command Palette (++ctrl+p++), and view the base variables and shades generated from the theme.