Repository: Textualize/textual

Issue:
Improve error message when value `none` is in a list of text styles
When you provide an illegal value for the CSS rule `link-style`, you get a helpful message displaying all legal values, saying that `text-style` expects a space-separated list of those.

If you set `text-style: bold italic reverse` it works fine.

If you set `text-style: none bold italic reverse` you are being silly, but there is no real reason for this to fail (I think). Yet, the error we get is 

```py
StyleSyntaxError: unable to parse 'none' as color; 'none' is not a valid color
```

To repro:

```py
from textual.app import App

class MyApp(App):
    DEFAULT_CSS = "* { text-style: bold italic none }"
```


Discussion:
We found the following entry in the [FAQ](https://github.com/textualize/textual/blob/main/FAQ.md) which you may find helpful:


- [Does Textual support images?](https://github.com/textualize/textual/blob/main/FAQ.md#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 is an automated reply, generated by [FAQtory](https://github.com/willmcgugan/faqtory)
This is because the parsing is handle by Rich with does expect a color. But you're right, we can do better. Text-style doesn't take a color so we shouldn't complain about it.

I think the solution is to validate that this attribute contains only the supported text styles.

Resolving patch:
diff --git a/src/textual/css/_help_text.py b/src/textual/css/_help_text.py
--- a/src/textual/css/_help_text.py
+++ b/src/textual/css/_help_text.py
@@ -729,6 +729,7 @@ def style_flags_property_help_text(
                 f"Style flag values such as [i]{property_name}[/] expect space-separated values"
             ),
             Bullet(f"Permitted values are {friendly_list(VALID_STYLE_FLAGS)}"),
+            Bullet("The value 'none' cannot be mixed with others"),
             *ContextSpecificBullets(
                 inline=[
                     Bullet(
diff --git a/src/textual/css/_style_properties.py b/src/textual/css/_style_properties.py
--- a/src/textual/css/_style_properties.py
+++ b/src/textual/css/_style_properties.py
@@ -12,6 +12,7 @@
 from operator import attrgetter
 from typing import TYPE_CHECKING, Generic, Iterable, NamedTuple, TypeVar, cast
 
+import rich.errors
 import rich.repr
 from rich.style import Style
 
@@ -909,7 +910,17 @@ def __set__(self, obj: StylesBase, style_flags: Style | str | None):
                             self.name, word, context="inline"
                         ),
                     )
-            style = Style.parse(style_flags)
+            try:
+                style = Style.parse(style_flags)
+            except rich.errors.StyleSyntaxError as error:
+                if "none" in words and len(words) > 1:
+                    raise StyleValueError(
+                        "cannot mix 'none' with other style flags",
+                        help_text=style_flags_property_help_text(
+                            self.name, " ".join(words), context="inline"
+                        ),
+                    ) from None
+                raise error from None
             if obj.set_rule(self.name, style):
                 obj.refresh()
 


Test patch:
diff --git a/tests/test_style_properties.py b/tests/test_style_properties.py
--- a/tests/test_style_properties.py
+++ b/tests/test_style_properties.py
@@ -1,4 +1,8 @@
+import pytest
+from rich.style import Style
+
 from textual.color import Color
+from textual.css.errors import StyleValueError
 from textual.css.styles import Styles
 
 
@@ -7,3 +11,22 @@ def test_box_normalization():
     styles = Styles()
     styles.border_left = ("none", "red")
     assert styles.border_left == ("", Color.parse("red"))
+
+
+@pytest.mark.parametrize("style_attr", ["text_style", "link_style"])
+def test_text_style_none_with_others(style_attr):
+    """Style "none" mixed with others should give custom Textual exception."""
+    styles = Styles()
+
+    with pytest.raises(StyleValueError):
+        setattr(styles, style_attr, "bold none underline italic")
+
+
+@pytest.mark.parametrize("style_attr", ["text_style", "link_style"])
+def test_text_style_set_to_none(style_attr):
+    """Setting text style to "none" should clear the styles."""
+    styles = Styles()
+    setattr(styles, style_attr, "bold underline italic")
+    assert getattr(styles, style_attr) != Style.null()
+    setattr(styles, style_attr, "none")
+    assert getattr(styles, style_attr) == Style.null()