Keyline rendering appears intermittent under scrolling.
Have you checked closed issues? (https://github.com/Textualize/textual/issues?q=is%3Aissue+is%3Aclosed) yes
Have you checked against the most recent version of Textual? (https://pypi.org/search/?q=textual) yes
The bug
Please give a brief but clear explanation of the issue. If you can, include a complete working example that demonstrates the bug. Check it can run without modifications.
The following simple program demonstrates oddities in rendering as the container scrolls.
from textual.app import App, ComposeResult
from textual.widgets import Input
from textual.containers import Horizontal, Vertical, VerticalScroll
class MyApp(App):
DEFAULT_CSS = """\
#my-container {
keyline: heavy blue;
Vertical {
margin: 1;
width: 1fr;
height: 3;
}
}
"""
def compose(self) -> ComposeResult:
with VerticalScroll(id="my-container"):
for i in range(10):
yield Vertical(Input(valid_empty=False, id=f"test-{i}", value="x"))
if __name__ == '__main__':
MyApp().run()
The initial display is sane.
Pressing the <down> key, however, does not redraw correctly.
A mouse click in the window at that stage triggers a correct redraw. Also please note that with a Vertical height of 3, the window paints correctly every 4 keypresses. Adding one to the height means 5 keypresses are required, and so on.
textual diagnose
<!-- This is valid Markdown, please paste the following directly in to a GitHub issue -->
# Textual Diagnostics
## Versions
| Name | Value |
|---------|--------|
| Textual | 3.5.0 |
| Rich | 14.0.0 |
## Python
| Name | Value |
|----------------|---------------------------------------------------------------|
| Version | 3.12.8 |
| Implementation | CPython |
| Compiler | Clang 19.1.6 |
| Executable | /Users/sholden/Projects/Python/textual-forms/.venv/bin/python |
## Operating System
| Name | Value |
|---------|--------------------------------------------------------------------------------------------------------|
| System | Darwin |
| Release | 24.5.0 |
| Version | Darwin Kernel Version 24.5.0: Tue Apr 22 19:48:46 PDT 2025; root:xnu-11417.121.6~2/RELEASE_ARM64_T8103 |
## Terminal
| Name | Value |
|----------------------|--------------------|
| Terminal Application | iTerm.app (3.5.14) |
| TERM | rxvt |
| COLORTERM | truecolor |
| FORCE_COLOR | *Not set* |
| NO_COLOR | *Not set* |
## Rich Console options
| Name | Value |
|----------------|---------------------|
| size | width=80, height=20 |
| legacy_windows | False |
| min_width | 1 |
| max_width | 80 |
| is_terminal | False |
| encoding | utf-8 |
| max_height | 20 |
| justify | None |
| overflow | None |
| no_wrap | False |
| highlight | None |
| markup | None |
| height | None |
We found the following entries in the FAQ which you may find helpful:
- How do I center a widget in a screen?
- Why doesn't Textual look good on macOS?
- Does Textual support images?
Feel free to close this issue if you found an answer in the FAQ. Otherwise, please give us a little time to review.
This project is developed and maintained by Will McGugan. Consider sponsoring Will's work on this project (and others).
This is an automated reply, generated by FAQtory
Can confirm I get the same issue in Windows Terminal. Here is a video. Also it does this if you just grab the scroll bar with the mouse and drag it, or use mouse wheel. Scrolling in general seems to cause it.
https://github.com/user-attachments/assets/04ce153d-a6da-4696-886f-61f138ba0735
Anecdotal evidence from Discord suggests this issue is probably within textual.
I think the problem is that scrolling won't cause any refresh/repaint of the container? After the initial render, the keylines won't be updated unless the container changes size (which does cause a repaint).
I haven't dived into this yet, so in case it helps someone else fix this, here's where the keylines are rendered:
https://github.com/Textualize/textual/blob/03979fc629579919d29c10a8ed6e10974fc05c4c/src/textual/widget.py#L4185-L4187
Definitely a bug. Generally contains don't need to be re-rendered since they have plain backgrounds. Keylines are the exception.
In the meantime, putting the keylines in a nested containers is a workaround:
from textual.app import App, ComposeResult
from textual.widgets import Input
from textual.containers import Horizontal, Vertical, VerticalScroll, VerticalGroup
class MyApp(App):
DEFAULT_CSS = """\
#my-container {
Vertical {
margin: 1;
width: 1fr;
height: 3;
}
.with-keylines {
keyline: heavy blue;
}
}
"""
def compose(self) -> ComposeResult:
with VerticalScroll(id="my-container"):
with VerticalGroup(classes="with-keylines"):
for i in range(10):
yield Vertical(Input(valid_empty=False, id=f"test-{i}", value="x"))
if __name__ == "__main__":
MyApp().run()