Repository: Textualize/textual

Issue:
Tidy the return type of `App.render`
Currently the return type of `App.render` is `RenderableType`; in our docs and in most places we talk about `App.render` returning a `RenderResult`. `RenderResult` is an alias for `RenderableType` and is a top-level value in `textual.app`.

This can cause a lint/type warning in dev's apps if they let their IDE complete the signature when writing their own `render` method, as it will often complete to something like this:

```python
    def render(self) -> RenderableType:
        return super().render()
```

which in turn will auto-import `RenderableType` from `textual.app`. This can then result in a warning like this:

![Screenshot 2024-01-23 at 14 16 46](https://github.com/Textualize/textual/assets/28237/df847da3-0171-4181-93b9-17ea7393338c)

We should change `App.render` so that it's typed as returning `RenderResult` rather than `RenderableType`, then such completion will use the type that *is* exported from `textual.app`.

Also note that [the code in the compose/render HOWTO seems to have fallen foul of this](https://textual.textualize.io/how-to/render-and-compose/) and has that type warning.

Discussion:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5b3d4909d..882082bab 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,7 +12,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
 - Mapping of ANSI colors to hex codes configurable via `App.ansi_theme_dark` and `App.ansi_theme_light` https://github.com/Textualize/textual/pull/4192
 - `Pilot.resize_terminal` to resize the terminal in testing https://github.com/Textualize/textual/issues/4212
 - Added `sort_children` method https://github.com/Textualize/textual/pull/4244
-- Support for pseudo-classes in nested TCSS https://github.com/Textualize/textual/issues/4039
 
 ### Fixed
 
@@ -32,7 +31,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
 ### Changed
 
 - Clicking a non focusable widget focus ancestors https://github.com/Textualize/textual/pull/4236
-- BREAKING: widget class names must start with a capital letter or an underscore `_` https://github.com/Textualize/textual/pull/4252
 
 ## [0.52.1] - 2024-02-20
 
diff --git a/docs/examples/how-to/render_compose.py b/docs/examples/how-to/render_compose.py
index b1413d3d0..06ce6a7fa 100644
--- a/docs/examples/how-to/render_compose.py
+++ b/docs/examples/how-to/render_compose.py
@@ -1,6 +1,6 @@
 from time import time
 
-from textual.app import App, ComposeResult, RenderableType
+from textual.app import App, ComposeResult, RenderResult
 from textual.containers import Container
 from textual.renderables.gradient import LinearGradient
 from textual.widgets import Static
@@ -41,7 +41,7 @@ class Splash(Container):
     def compose(self) -> ComposeResult:
         yield Static("Making a splash with Textual!")  # (2)!
 
-    def render(self) -> RenderableType:
+    def render(self) -> RenderResult:
         return LinearGradient(time() * 90, STOPS)  # (3)!
 
 
diff --git a/src/textual/app.py b/src/textual/app.py
index 124f2d25c..01b3634a9 100644
--- a/src/textual/app.py
+++ b/src/textual/app.py
@@ -1593,7 +1593,7 @@ class App(Generic[ReturnType], DOMNode):
                 for screen in self.screen_stack:
                     self.stylesheet.update(screen)
 
-    def render(self) -> RenderableType:
+    def render(self) -> RenderResult:
         return Blank(self.styles.background)
 
     ExpectType = TypeVar("ExpectType", bound=Widget)
diff --git a/src/textual/css/tokenize.py b/src/textual/css/tokenize.py
index 4193cc578..b029fad55 100644
--- a/src/textual/css/tokenize.py
+++ b/src/textual/css/tokenize.py
@@ -30,19 +30,6 @@ STRING = r"\".*?\""
 VARIABLE_REF = r"\$[a-zA-Z0-9_\-]+"
 
 IDENTIFIER = r"[a-zA-Z_\-][a-zA-Z0-9_\-]*"
-SELECTOR_TYPE_NAME = r"[A-Z_][a-zA-Z0-9_]*"
-"""Selectors representing Widget type names should start with upper case or '_'.
-
-The fact that a selector starts with an upper case letter or '_' is relevant in the
-context of nested CSS to help determine whether xxx:yyy is a declaration + value or a
-selector + pseudo-class."""
-DECLARATION_NAME = r"[a-z][a-zA-Z0-9_\-]*"
-"""Declaration of TCSS rules start with lowercase.
-
-The fact that a declaration starts with a lower case letter is relevant in the context
-of nested CSS to help determine whether xxx:yyy is a declaration + value or a selector
-+ pseudo-class.
-"""
 
 # Values permitted in variable and rule declarations.
 DECLARATION_VALUES = {
@@ -67,7 +54,7 @@ expect_root_scope = Expect(
     selector_start_id=r"\#" + IDENTIFIER,
     selector_start_class=r"\." + IDENTIFIER,
     selector_start_universal=r"\*",
-    selector_start=SELECTOR_TYPE_NAME,
+    selector_start=IDENTIFIER,
     variable_name=rf"{VARIABLE_REF}:",
     declaration_set_end=r"\}",
 ).expect_eof(True)
@@ -77,11 +64,11 @@ expect_root_nested = Expect(
     whitespace=r"\s+",
     comment_start=COMMENT_START,
     comment_line=COMMENT_LINE,
-    declaration_name=DECLARATION_NAME + r"\:",
+    declaration_name=r"[a-zA-Z_\-]+\:",
     selector_start_id=r"\#" + IDENTIFIER,
     selector_start_class=r"\." + IDENTIFIER,
     selector_start_universal=r"\*",
-    selector_start=SELECTOR_TYPE_NAME,
+    selector_start=IDENTIFIER,
     variable_name=rf"{VARIABLE_REF}:",
     declaration_set_end=r"\}",
     nested=r"\&",
@@ -111,10 +98,10 @@ expect_selector_continue = Expect(
     comment_start=COMMENT_START,
     comment_line=COMMENT_LINE,
     pseudo_class=r"\:[a-zA-Z_-]+",
-    selector_id=r"\#" + IDENTIFIER,
-    selector_class=r"\." + IDENTIFIER,
+    selector_id=r"\#[a-zA-Z_\-][a-zA-Z0-9_\-]*",
+    selector_class=r"\.[a-zA-Z_\-][a-zA-Z0-9_\-]*",
     selector_universal=r"\*",
-    selector=SELECTOR_TYPE_NAME,
+    selector=IDENTIFIER,
     combinator_child=">",
     new_selector=r",",
     declaration_set_start=r"\{",
@@ -130,13 +117,13 @@ expect_declaration = Expect(
     whitespace=r"\s+",
     comment_start=COMMENT_START,
     comment_line=COMMENT_LINE,
-    declaration_name=DECLARATION_NAME + r"\:",
+    declaration_name=r"[a-zA-Z_\-]+\:",
     declaration_set_end=r"\}",
     #
     selector_start_id=r"\#" + IDENTIFIER,
     selector_start_class=r"\." + IDENTIFIER,
     selector_start_universal=r"\*",
-    selector_start=SELECTOR_TYPE_NAME,
+    selector_start=IDENTIFIER,
 )
 
 expect_declaration_solo = Expect(
@@ -144,7 +131,7 @@ expect_declaration_solo = Expect(
     whitespace=r"\s+",
     comment_start=COMMENT_START,
     comment_line=COMMENT_LINE,
-    declaration_name=DECLARATION_NAME + r"\:",
+    declaration_name=r"[a-zA-Z_\-]+\:",
     declaration_set_end=r"\}",
 ).expect_eof(True)
 
diff --git a/src/textual/message_pump.py b/src/textual/message_pump.py
index 7c9ef51b0..b0dc922f5 100644
--- a/src/textual/message_pump.py
+++ b/src/textual/message_pump.py
@@ -16,17 +16,7 @@ import threading
 from asyncio import CancelledError, Queue, QueueEmpty, Task, create_task
 from contextlib import contextmanager
 from functools import partial
-from typing import (
-    TYPE_CHECKING,
-    Any,
-    Awaitable,
-    Callable,
-    Generator,
-    Iterable,
-    Type,
-    TypeVar,
-    cast,
-)
+from typing import TYPE_CHECKING, Any, Awaitable, Callable, Generator, Iterable, cast
 from weakref import WeakSet
 
 from . import Logger, events, log, messages
@@ -62,21 +52,18 @@ class MessagePumpClosed(Exception):
     pass
 
 
-_MessagePumpMetaSub = TypeVar("_MessagePumpMetaSub", bound="_MessagePumpMeta")
-
-
 class _MessagePumpMeta(type):
     """Metaclass for message pump. This exists to populate a Message inner class of a Widget with the
     parent classes' name.
     """
 
     def __new__(
-        cls: Type[_MessagePumpMetaSub],
+        cls,
         name: str,
         bases: tuple[type, ...],
         class_dict: dict[str, Any],
-        **kwargs: Any,
-    ) -> _MessagePumpMetaSub:
+        **kwargs,
+    ):
         namespace = camel_to_snake(name)
         isclass = inspect.isclass
         handlers: dict[
diff --git a/src/textual/widget.py b/src/textual/widget.py
index 29d522453..17a772375 100644
--- a/src/textual/widget.py
+++ b/src/textual/widget.py
@@ -32,15 +32,18 @@ from rich.console import (
     ConsoleRenderable,
     JustifyMethod,
     RenderableType,
-    RenderResult,
-    RichCast,
 )
+from rich.console import RenderResult as RichRenderResult
+from rich.console import RichCast
 from rich.measure import Measurement
 from rich.segment import Segment
 from rich.style import Style
 from rich.text import Text
 from typing_extensions import Self
 
+if TYPE_CHECKING:
+    from .app import RenderResult
+
 from . import constants, errors, events, messages
 from ._animator import DEFAULT_EASING, Animatable, BoundAnimator, EasingFunction
 from ._arrange import DockArrangeResult, arrange
@@ -152,7 +155,7 @@ class _Styled:
 
     def __rich_console__(
         self, console: "Console", options: "ConsoleOptions"
-    ) -> "RenderResult":
+    ) -> "RichRenderResult":
         style = console.get_style(self.style)
         result_segments = console.render(self.renderable, options)
 
@@ -243,10 +246,6 @@ class _BorderTitle:
         return title.markup
 
 
-class BadWidgetName(Exception):
-    """Raised when widget class names do not satisfy the required restrictions."""
-
-
 @rich.repr.auto
 class Widget(DOMNode):
     """
@@ -2892,17 +2891,11 @@ class Widget(DOMNode):
         inherit_css: bool = True,
         inherit_bindings: bool = True,
     ) -> None:
-        name = cls.__name__
-        if not name[0].isupper() and not name.startswith("_"):
-            raise BadWidgetName(
-                f"Widget subclass {name!r} should be capitalised or start with '_'."
-            )
-
+        base = cls.__mro__[0]
         super().__init_subclass__(
             inherit_css=inherit_css,
             inherit_bindings=inherit_bindings,
         )
-        base = cls.__mro__[0]
         if issubclass(base, Widget):
             cls.can_focus = base.can_focus if can_focus is None else can_focus
             cls.can_focus_children = (
@@ -3385,7 +3378,7 @@ class Widget(DOMNode):
             with self.app.batch_update():
                 yield
 
-    def render(self) -> RenderableType:
+    def render(self) -> RenderResult:
         """Get text or Rich renderable for this widget.
 
         Implement this for custom widgets.
diff --git a/src/textual/widgets/_button.py b/src/textual/widgets/_button.py
index 83a3237b2..110646e10 100644
--- a/src/textual/widgets/_button.py
+++ b/src/textual/widgets/_button.py
@@ -1,7 +1,7 @@
 from __future__ import annotations
 
 from functools import partial
-from typing import cast
+from typing import TYPE_CHECKING, cast
 
 import rich.repr
 from rich.console import ConsoleRenderable, RenderableType
@@ -9,6 +9,10 @@ from rich.text import Text, TextType
 from typing_extensions import Literal, Self
 
 from .. import events
+
+if TYPE_CHECKING:
+    from ..app import RenderResult
+
 from ..binding import Binding
 from ..css._error_tools import friendly_list
 from ..message import Message
@@ -61,16 +65,16 @@ class Button(Widget, can_focus=True):
             tint: $background 30%;
         }
 
-        &.-primary {            
+        &.-primary {
             background: $primary;
             color: $text;
             border-top: tall $primary-lighten-3;
-            border-bottom: tall $primary-darken-3;      
-                  
+            border-bottom: tall $primary-darken-3;
+
             &:hover {
                 background: $primary-darken-2;
                 color: $text;
-                border-top: tall $primary;                
+                border-top: tall $primary;
             }
 
             &.-active {
@@ -220,7 +224,7 @@ class Button(Widget, can_focus=True):
             return Text.from_markup(label)
         return label
 
-    def render(self) -> RenderableType:
+    def render(self) -> RenderResult:
         assert isinstance(self.label, Text)
         label = self.label.copy()
         label.stylize(self.rich_style)
diff --git a/src/textual/widgets/_digits.py b/src/textual/widgets/_digits.py
index d298f4e3e..3bd04fcc2 100644
--- a/src/textual/widgets/_digits.py
+++ b/src/textual/widgets/_digits.py
@@ -1,10 +1,11 @@
 from __future__ import annotations
 
-from typing import cast
+from typing import TYPE_CHECKING, cast
 
 from rich.align import Align, AlignMethod
-from rich.console import RenderableType
 
+if TYPE_CHECKING:
+    from ..app import RenderResult
 from ..geometry import Size
 from ..renderables.digits import Digits as DigitsRenderable
 from ..widget import Widget
@@ -68,7 +69,7 @@ class Digits(Widget):
         self._value = value
         self.refresh(layout=layout_required)
 
-    def render(self) -> RenderableType:
+    def render(self) -> RenderResult:
         """Render digits."""
         rich_style = self.rich_style
         digits = DigitsRenderable(self._value, rich_style)
diff --git a/src/textual/widgets/_footer.py b/src/textual/widgets/_footer.py
index b5e772ab6..5f3c60d92 100644
--- a/src/textual/widgets/_footer.py
+++ b/src/textual/widgets/_footer.py
@@ -1,13 +1,16 @@
 from __future__ import annotations
 
 from collections import defaultdict
-from typing import ClassVar, Optional
+from typing import TYPE_CHECKING, ClassVar, Optional
 
 import rich.repr
-from rich.console import RenderableType
 from rich.text import Text
 
 from .. import events
+
+if TYPE_CHECKING:
+    from ..app import RenderResult
+
 from ..reactive import reactive
 from ..widget import Widget
 
@@ -138,7 +141,7 @@ class Footer(Widget):
     def post_render(self, renderable):
         return renderable
 
-    def render(self) -> RenderableType:
+    def render(self) -> RenderResult:
         if self._key_text is None:
             self._key_text = self._make_key_text()
         return self._key_text
diff --git a/src/textual/widgets/_input.py b/src/textual/widgets/_input.py
index 828f2ff4a..0d3149f2d 100644
--- a/src/textual/widgets/_input.py
+++ b/src/textual/widgets/_input.py
@@ -2,10 +2,11 @@ from __future__ import annotations
 
 import re
 from dataclasses import dataclass
-from typing import ClassVar, Iterable
+from typing import TYPE_CHECKING, ClassVar, Iterable
 
 from rich.cells import cell_len, get_character_cell_size
-from rich.console import Console, ConsoleOptions, RenderableType, RenderResult
+from rich.console import Console, ConsoleOptions
+from rich.console import RenderResult as RichRenderResult
 from rich.highlighter import Highlighter
 from rich.segment import Segment
 from rich.text import Text
@@ -13,6 +14,10 @@ from typing_extensions import Literal
 
 from .. import events
 from .._segment_tools import line_crop
+
+if TYPE_CHECKING:
+    from ..app import RenderResult
+
 from ..binding import Binding, BindingType
 from ..css._error_tools import friendly_list
 from ..events import Blur, Focus, Mount
@@ -47,7 +52,7 @@ class _InputRenderable:
 
     def __rich_console__(
         self, console: "Console", options: "ConsoleOptions"
-    ) -> "RenderResult":
+    ) -> RichRenderResult:
         input = self.input
         result = input._value
         width = input.content_size.width
@@ -460,7 +465,7 @@ class Input(Widget, can_focus=True):
             return cell_len(self.placeholder)
         return self._position_to_cell(len(self.value)) + 1
 
-    def render(self) -> RenderableType:
+    def render(self) -> RenderResult:
         self.view_position = self.view_position
         if not self.value:
             placeholder = Text(self.placeholder, justify="left")
diff --git a/src/textual/widgets/_loading_indicator.py b/src/textual/widgets/_loading_indicator.py
index 24bc3cf2d..c715bbb99 100644
--- a/src/textual/widgets/_loading_indicator.py
+++ b/src/textual/widgets/_loading_indicator.py
@@ -1,11 +1,13 @@
 from __future__ import annotations
 
 from time import time
+from typing import TYPE_CHECKING
 
-from rich.console import RenderableType
 from rich.style import Style
 from rich.text import Text
 
+if TYPE_CHECKING:
+    from ..app import RenderResult
 from ..color import Gradient
 from ..events import Mount
 from ..widget import Widget
@@ -53,7 +55,7 @@ class LoadingIndicator(Widget):
         self._start_time = time()
         self.auto_refresh = 1 / 16
 
-    def render(self) -> RenderableType:
+    def render(self) -> RenderResult:
         if self.app.animation_level == "none":
             return Text("Loading...")
 
diff --git a/src/textual/widgets/_placeholder.py b/src/textual/widgets/_placeholder.py
index 9c9adf725..bc905001b 100644
--- a/src/textual/widgets/_placeholder.py
+++ b/src/textual/widgets/_placeholder.py
@@ -6,10 +6,13 @@ from itertools import cycle
 from typing import TYPE_CHECKING, Iterator
 from weakref import WeakKeyDictionary
 
-from rich.console import RenderableType
 from typing_extensions import Literal, Self
 
 from .. import events
+
+if TYPE_CHECKING:
+    from ..app import RenderResult
+
 from ..css._error_tools import friendly_list
 from ..reactive import Reactive, reactive
 from ..widget import Widget
@@ -128,7 +131,7 @@ class Placeholder(Widget):
         )
         self.styles.background = f"{next(colors)} 50%"
 
-    def render(self) -> RenderableType:
+    def render(self) -> RenderResult:
         """Render the placeholder.
 
         Returns:
diff --git a/src/textual/widgets/_static.py b/src/textual/widgets/_static.py
index 3ccd43c3d..81215fd30 100644
--- a/src/textual/widgets/_static.py
+++ b/src/textual/widgets/_static.py
@@ -1,9 +1,14 @@
 from __future__ import annotations
 
+from typing import TYPE_CHECKING
+
 from rich.console import RenderableType
 from rich.protocol import is_renderable
 from rich.text import Text
 
+if TYPE_CHECKING:
+    from ..app import RenderResult
+
 from ..errors import RenderError
 from ..widget import Widget
 
@@ -80,7 +85,7 @@ class Static(Widget, inherit_bindings=False):
             self._renderable = renderable
         self.clear_cached_dimensions()
 
-    def render(self) -> RenderableType:
+    def render(self) -> RenderResult:
         """Get a rich renderable for the widget's content.
 
         Returns:
diff --git a/src/textual/widgets/_switch.py b/src/textual/widgets/_switch.py
index 96cce3eeb..3060163bf 100644
--- a/src/textual/widgets/_switch.py
+++ b/src/textual/widgets/_switch.py
@@ -2,8 +2,8 @@ from __future__ import annotations
 
 from typing import TYPE_CHECKING, ClassVar
 
-from rich.console import RenderableType
-
+if TYPE_CHECKING:
+    from ..app import RenderResult
 from ..binding import Binding, BindingType
 from ..events import Click
 from ..geometry import Size
@@ -143,7 +143,7 @@ class Switch(Widget, can_focus=True):
     def watch_slider_pos(self, slider_pos: float) -> None:
         self.set_class(slider_pos == 1, "-on")
 
-    def render(self) -> RenderableType:
+    def render(self) -> RenderResult:
         style = self.get_component_rich_style("switch--slider")
         return ScrollBarRender(
             virtual_size=100,
diff --git a/src/textual/widgets/_toast.py b/src/textual/widgets/_toast.py
index e55eaa013..a8198f4b5 100644
--- a/src/textual/widgets/_toast.py
+++ b/src/textual/widgets/_toast.py
@@ -2,12 +2,15 @@
 
 from __future__ import annotations
 
-from typing import ClassVar
+from typing import TYPE_CHECKING, ClassVar
 
-from rich.console import RenderableType
 from rich.text import Text
 
 from .. import on
+
+if TYPE_CHECKING:
+    from ..app import RenderResult
+
 from ..containers import Container
 from ..css.query import NoMatches
 from ..events import Click, Mount
@@ -105,7 +108,7 @@ class Toast(Static, inherit_css=False):
         self._notification = notification
         self._timeout = notification.time_left
 
-    def render(self) -> RenderableType:
+    def render(self) -> RenderResult:
         """Render the toast's content.
 
         Returns:


Test patch:
diff --git a/tests/css/test_nested_css.py b/tests/css/test_nested_css.py
index e73d67e68..32086f2e4 100644
--- a/tests/css/test_nested_css.py
+++ b/tests/css/test_nested_css.py
@@ -7,7 +7,7 @@ from textual.color import Color
 from textual.containers import Vertical
 from textual.css.parse import parse
 from textual.css.tokenizer import EOFError, TokenError
-from textual.widgets import Button, Label
+from textual.widgets import Label
 
 
 class NestedApp(App):
@@ -110,76 +110,3 @@ def test_parse_errors(css: str, exception: type[Exception]) -> None:
     """Check some CSS which should fail."""
     with pytest.raises(exception):
         list(parse("", css, ("foo", "")))
-
-
-class PseudoClassesInNestedApp(App[None]):
-    CSS = """
-    Vertical {
-        Button:light, Button:dark {
-            background: red;
-        }
-
-        min-height: 3;  # inconsequential rule to add entropy.
-
-        #two, *:focus {
-            background: green !important;
-        }
-
-        height: auto;  # inconsequential rule to add entropy.
-
-        Label {
-            background: yellow;
-
-            &:light, &:dark {
-                background: red;
-            }
-
-            &:hover {
-                background: green !important;
-            }
-        }
-    }
-    """
-
-    AUTO_FOCUS = "Button"
-
-    def compose(self) -> ComposeResult:
-        with Vertical():
-            yield Button(id="one", classes="first_half")
-            yield Button(id="two", classes="first_half")
-        with Vertical():
-            yield Label("Hello, world!", id="three", classes="first_half")
-            yield Label("Hello, world!", id="four", classes="first_half")
-        with Vertical():
-            yield Button(id="five", classes="second_half")
-            yield Button(id="six", classes="second_half")
-            yield Label("Hello, world!", id="seven", classes="second_half")
-            yield Label("Hello, world!", id="eight", classes="second_half")
-
-
-async def test_pseudo_classes_work_in_nested_css() -> None:
-    """Makes sure pseudo-classes are correctly understood in nested TCSS.
-
-    Regression test for https://github.com/Textualize/textual/issues/4039.
-    """
-
-    app = PseudoClassesInNestedApp()
-    green = Color.parse("green")
-    red = Color.parse("red")
-    async with app.run_test() as pilot:
-        assert app.query_one("#one").styles.background == green
-        assert app.query_one("#two").styles.background == green
-        assert app.query_one("#five").styles.background == red
-        assert app.query_one("#six").styles.background == red
-
-        assert app.query_one("#three").styles.background == red
-        assert app.query_one("#four").styles.background == red
-        assert app.query_one("#seven").styles.background == red
-        assert app.query_one("#eight").styles.background == red
-
-        await pilot.hover("#eight")
-
-        assert app.query_one("#three").styles.background == red
-        assert app.query_one("#four").styles.background == red
-        assert app.query_one("#seven").styles.background == red
-        assert app.query_one("#eight").styles.background == green
diff --git a/tests/css/test_stylesheet.py b/tests/css/test_stylesheet.py
index 96f0591da..258190d83 100644
--- a/tests/css/test_stylesheet.py
+++ b/tests/css/test_stylesheet.py
@@ -207,55 +207,6 @@ def test_did_you_mean_for_css_property_names(
     assert help_text.summary == expected_summary
 
 
-@pytest.mark.parametrize(
-    "css_property_name,expected_property_name_suggestion",
-    [
-        ["backgroundu", "background"],
-        ["bckgroundu", "background"],
-        ["ofset-x", "offset-x"],
-        ["ofst_y", "offset-y"],
-        ["colr", "color"],
-        ["colour", "color"],
-        ["wdth", "width"],
-        ["wth", "width"],
-        ["wh", None],
-        ["xkcd", None],
-    ],
-)
-def test_did_you_mean_for_property_names_in_nested_css(
-    css_property_name: str, expected_property_name_suggestion: "str | None"
-) -> None:
-    """Test that we get nice errors with mistyped declaractions in nested CSS.
-
-    When implementing pseudo-class support in nested TCSS
-    (https://github.com/Textualize/textual/issues/4039), the first iterations didn't
-    preserve this so we add these tests to make sure we don't take this feature away
-    unintentionally.
-    """
-    stylesheet = Stylesheet()
-    css = """
-    Screen {
-        * {
-            border: blue;
-            ${PROPERTY}: red;
-        }
-    }
-    """.replace(
-        "${PROPERTY}", css_property_name
-    )
-
-    stylesheet.add_source(css)
-    with pytest.raises(StylesheetParseError) as err:
-        stylesheet.parse()
-
-    _, help_text = err.value.errors.rules[1].errors[0]
-    displayed_css_property_name = css_property_name.replace("_", "-")
-    expected_summary = f"Invalid CSS property {displayed_css_property_name!r}"
-    if expected_property_name_suggestion:
-        expected_summary += f". Did you mean '{expected_property_name_suggestion}'?"
-    assert help_text.summary == expected_summary
-
-
 @pytest.mark.parametrize(
     "css_property_name,css_property_value,expected_color_suggestion",
     [
diff --git a/tests/css/test_tokenize.py b/tests/css/test_tokenize.py
index 7b36e542a..d4dfba888 100644
--- a/tests/css/test_tokenize.py
+++ b/tests/css/test_tokenize.py
@@ -898,83 +898,3 @@ def test_allow_new_lines():
         ),
     ]
     assert list(tokenize(css, ("", ""))) == expected
-
-
-@pytest.mark.parametrize(
-    ["pseudo_class", "expected"],
-    [
-        ("blue", "blur"),
-        ("br", "blur"),
-        ("canfocus", "can-focus"),
-        ("can_focus", "can-focus"),
-        ("can-foc", "can-focus"),
-        ("drk", "dark"),
-        ("ark", "dark"),
-        ("disssabled", "disabled"),
-        ("enalbed", "enabled"),
-        ("focoswithin", "focus-within"),
-        ("focus_whitin", "focus-within"),
-        ("fcus", "focus"),
-        ("huver", "hover"),
-        ("LIght", "light"),
-    ],
-)
-def test_did_you_mean_pseudo_classes(pseudo_class: str, expected: str) -> None:
-    """Make sure we get the correct suggestion for pseudo-classes with typos."""
-
-    css = f"""
-    Button:{pseudo_class} {{
-        background: red;
-    }}
-    """
-
-    with pytest.raises(TokenError) as err:
-        list(tokenize(css, ("", "")))
-
-    assert f"unknown pseudo-class {pseudo_class!r}" in str(err.value)
-    assert f"did you mean {expected!r}" in str(err.value)
-
-
-@pytest.mark.parametrize(
-    ["pseudo_class", "expected"],
-    [
-        ("blue", "blur"),
-        ("br", "blur"),
-        ("canfocus", "can-focus"),
-        ("can_focus", "can-focus"),
-        ("can-foc", "can-focus"),
-        ("drk", "dark"),
-        ("ark", "dark"),
-        ("disssabled", "disabled"),
-        ("enalbed", "enabled"),
-        ("focoswithin", "focus-within"),
-        ("focus_whitin", "focus-within"),
-        ("fcus", "focus"),
-        ("huver", "hover"),
-        ("LIght", "light"),
-    ],
-)
-def test_did_you_mean_pseudo_classes_in_nested_css(
-    pseudo_class: str, expected: str
-) -> None:
-    """Test that we get nice errors for pseudo-classes with typos in nested TCSS.
-
-    When implementing pseudo-class support in nested TCSS
-    (https://github.com/Textualize/textual/issues/4039), the first iterations didn't
-    preserve this so we add these tests to make sure we don't take this feature away
-    unintentionally.
-    """
-
-    css = f"""
-    Screen {{
-        Button:{pseudo_class} {{
-            background: red;
-        }}
-    }}
-    """
-
-    with pytest.raises(TokenError) as err:
-        list(tokenize(css, ("", "")))
-
-    assert f"unknown pseudo-class {pseudo_class!r}" in str(err.value)
-    assert f"did you mean {expected!r}" in str(err.value)
diff --git a/tests/test_focus.py b/tests/test_focus.py
index ee955e23e..0942753a6 100644
--- a/tests/test_focus.py
+++ b/tests/test_focus.py
@@ -149,12 +149,12 @@ def test_no_focus_empty_selector(screen: Screen):
 
     screen.set_focus(screen.query_one("#foo"))
     assert screen.focused is not None
-    assert screen.focus_next("#bananas") is None
+    assert screen.focus_next("bananas") is None
     assert screen.focused is None
 
     screen.set_focus(screen.query_one("#foo"))
     assert screen.focused is not None
-    assert screen.focus_previous("#bananas") is None
+    assert screen.focus_previous("bananas") is None
     assert screen.focused is None
 
 
diff --git a/tests/test_query.py b/tests/test_query.py
index 19aac12db..6060a7ce7 100644
--- a/tests/test_query.py
+++ b/tests/test_query.py
@@ -244,7 +244,7 @@ async def test_query_set_styles_invalid_css_raises_error():
     app = App()
     async with app.run_test():
         with pytest.raises(DeclarationError):
-            app.query(Widget).set_styles(css="random-rule: 1fr;")
+            app.query(Widget).set_styles(css="random_rule: 1fr;")
 
 
 async def test_query_set_styles_kwds():
diff --git a/tests/test_widget.py b/tests/test_widget.py
index 182de27b7..7fcb264a3 100644
--- a/tests/test_widget.py
+++ b/tests/test_widget.py
@@ -11,7 +11,7 @@ from textual.css.errors import StyleValueError
 from textual.css.query import NoMatches
 from textual.geometry import Offset, Size
 from textual.message import Message
-from textual.widget import BadWidgetName, MountError, PseudoClasses, Widget
+from textual.widget import MountError, PseudoClasses, Widget
 from textual.widgets import Label, LoadingIndicator
 
 
@@ -513,12 +513,3 @@ async def test_sort_children_no_key() -> None:
             "l1",
             "l3",
         ]
-
-
-def test_bad_widget_name_raised() -> None:
-    """Ensure error is raised when bad class names are used for widgets."""
-
-    with pytest.raises(BadWidgetName):
-
-        class lowercaseWidget(Widget):
-            pass