We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Placeholder cycle per app
The placeholder stores the current color in a classvar.

This means that a second app will start where the previous app left off. And may return a different color from the first iteration.

Not much of an issue in practice, but in the docs the colors change every screenshot. I think we need a separate cycle per app. Suggest we have an independent sequence for each app instance.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `docs/examples/styles/width_comparison.py`
Content:
```
1 from textual.app import App
2 from textual.containers import Horizontal
3 from textual.widgets import Placeholder, Label, Static
4 
5 
6 class Ruler(Static):
7     def compose(self):
8         ruler_text = "····•" * 100
9         yield Label(ruler_text)
10 
11 
12 class HeightComparisonApp(App):
13     def compose(self):
14         yield Horizontal(
15             Placeholder(id="cells"),  # (1)!
16             Placeholder(id="percent"),
17             Placeholder(id="w"),
18             Placeholder(id="h"),
19             Placeholder(id="vw"),
20             Placeholder(id="vh"),
21             Placeholder(id="auto"),
22             Placeholder(id="fr1"),
23             Placeholder(id="fr3"),
24         )
25         yield Ruler()
26 
27 
28 app = HeightComparisonApp(css_path="width_comparison.css")
29
```

Path: `src/textual/widgets/_placeholder.py`
Content:
```
1 """Provides a Textual placeholder widget; useful when designing an app's layout."""
2 
3 from __future__ import annotations
4 
5 from itertools import cycle
6 
7 from rich.console import RenderableType
8 from typing_extensions import Literal, Self
9 
10 from .. import events
11 from ..css._error_tools import friendly_list
12 from ..reactive import Reactive, reactive
13 from ..widget import Widget
14 
15 PlaceholderVariant = Literal["default", "size", "text"]
16 """The different variants of placeholder."""
17 
18 _VALID_PLACEHOLDER_VARIANTS_ORDERED: list[PlaceholderVariant] = [
19     "default",
20     "size",
21     "text",
22 ]
23 _VALID_PLACEHOLDER_VARIANTS: set[PlaceholderVariant] = set(
24     _VALID_PLACEHOLDER_VARIANTS_ORDERED
25 )
26 _PLACEHOLDER_BACKGROUND_COLORS = [
27     "#881177",
28     "#aa3355",
29     "#cc6666",
30     "#ee9944",
31     "#eedd00",
32     "#99dd55",
33     "#44dd88",
34     "#22ccbb",
35     "#00bbcc",
36     "#0099cc",
37     "#3366bb",
38     "#663399",
39 ]
40 _LOREM_IPSUM_PLACEHOLDER_TEXT = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam feugiat ac elit sit amet accumsan. Suspendisse bibendum nec libero quis gravida. Phasellus id eleifend ligula. Nullam imperdiet sem tellus, sed vehicula nisl faucibus sit amet. Praesent iaculis tempor ultricies. Sed lacinia, tellus id rutrum lacinia, sapien sapien congue mauris, sit amet pellentesque quam quam vel nisl. Curabitur vulputate erat pellentesque mauris posuere, non dictum risus mattis."
41 
42 
43 class InvalidPlaceholderVariant(Exception):
44     """Raised when an invalid Placeholder variant is set."""
45 
46 
47 class Placeholder(Widget):
48     """A simple placeholder widget to use before you build your custom widgets.
49 
50     This placeholder has a couple of variants that show different data.
51     Clicking the placeholder cycles through the available variants, but a placeholder
52     can also be initialised in a specific variant.
53 
54     The variants available are:
55 
56     | Variant | Placeholder shows                              |
57     |---------|------------------------------------------------|
58     | default | Identifier label or the ID of the placeholder. |
59     | size    | Size of the placeholder.                       |
60     | text    | Lorem Ipsum text.                              |
61     """
62 
63     DEFAULT_CSS = """
64     Placeholder {
65         content-align: center middle;
66         overflow: hidden;
67         color: $text;
68     }
69     Placeholder.-text {
70         padding: 1;
71     }
72     """
73 
74     # Consecutive placeholders get assigned consecutive colors.
75     _COLORS = cycle(_PLACEHOLDER_BACKGROUND_COLORS)
76     _SIZE_RENDER_TEMPLATE = "[b]{} x {}[/b]"
77 
78     variant: Reactive[PlaceholderVariant] = reactive[PlaceholderVariant]("default")
79 
80     _renderables: dict[PlaceholderVariant, str]
81 
82     @classmethod
83     def reset_color_cycle(cls) -> None:
84         """Reset the placeholder background color cycle."""
85         cls._COLORS = cycle(_PLACEHOLDER_BACKGROUND_COLORS)
86 
87     def __init__(
88         self,
89         label: str | None = None,
90         variant: PlaceholderVariant = "default",
91         *,
92         name: str | None = None,
93         id: str | None = None,
94         classes: str | None = None,
95     ) -> None:
96         """Create a Placeholder widget.
97 
98         Args:
99             label: The label to identify the placeholder.
100                 If no label is present, uses the placeholder ID instead.
101             variant: The variant of the placeholder.
102             name: The name of the placeholder.
103             id: The ID of the placeholder in the DOM.
104             classes: A space separated string with the CSS classes
105                 of the placeholder, if any.
106         """
107         # Create and cache renderables for all the variants.
108         self._renderables = {
109             "default": label if label else f"#{id}" if id else "Placeholder",
110             "size": "",
111             "text": "\n\n".join(_LOREM_IPSUM_PLACEHOLDER_TEXT for _ in range(5)),
112         }
113 
114         super().__init__(name=name, id=id, classes=classes)
115 
116         self.styles.background = f"{next(Placeholder._COLORS)} 50%"
117 
118         self.variant = self.validate_variant(variant)
119         """The current variant of the placeholder."""
120 
121         # Set a cycle through the variants with the correct starting point.
122         self._variants_cycle = cycle(_VALID_PLACEHOLDER_VARIANTS_ORDERED)
123         while next(self._variants_cycle) != self.variant:
124             pass
125 
126     def render(self) -> RenderableType:
127         """Render the placeholder.
128 
129         Returns:
130             The value to render.
131         """
132         return self._renderables[self.variant]
133 
134     def cycle_variant(self) -> Self:
135         """Get the next variant in the cycle.
136 
137         Returns:
138             The `Placeholder` instance.
139         """
140         self.variant = next(self._variants_cycle)
141         return self
142 
143     def watch_variant(
144         self, old_variant: PlaceholderVariant, variant: PlaceholderVariant
145     ) -> None:
146         self.remove_class(f"-{old_variant}")
147         self.add_class(f"-{variant}")
148 
149     def validate_variant(self, variant: PlaceholderVariant) -> PlaceholderVariant:
150         """Validate the variant to which the placeholder was set."""
151         if variant not in _VALID_PLACEHOLDER_VARIANTS:
152             raise InvalidPlaceholderVariant(
153                 "Valid placeholder variants are "
154                 + f"{friendly_list(_VALID_PLACEHOLDER_VARIANTS)}"
155             )
156         return variant
157 
158     async def _on_click(self, _: events.Click) -> None:
159         """Click handler to cycle through the placeholder variants."""
160         self.cycle_variant()
161 
162     def _on_resize(self, event: events.Resize) -> None:
163         """Update the placeholder "size" variant with the new placeholder size."""
164         self._renderables["size"] = self._SIZE_RENDER_TEMPLATE.format(*event.size)
165         if self.variant == "size":
166             self.refresh()
167
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/docs/examples/styles/width_comparison.py b/docs/examples/styles/width_comparison.py
--- a/docs/examples/styles/width_comparison.py
+++ b/docs/examples/styles/width_comparison.py
@@ -1,6 +1,6 @@
 from textual.app import App
 from textual.containers import Horizontal
-from textual.widgets import Placeholder, Label, Static
+from textual.widgets import Label, Placeholder, Static
 
 
 class Ruler(Static):
@@ -9,7 +9,7 @@
         yield Label(ruler_text)
 
 
-class HeightComparisonApp(App):
+class WidthComparisonApp(App):
     def compose(self):
         yield Horizontal(
             Placeholder(id="cells"),  # (1)!
@@ -25,4 +25,6 @@
         yield Ruler()
 
 
-app = HeightComparisonApp(css_path="width_comparison.css")
+app = WidthComparisonApp(css_path="width_comparison.css")
+if __name__ == "__main__":
+    app.run()
diff --git a/src/textual/widgets/_placeholder.py b/src/textual/widgets/_placeholder.py
--- a/src/textual/widgets/_placeholder.py
+++ b/src/textual/widgets/_placeholder.py
@@ -3,10 +3,14 @@
 from __future__ import annotations
 
 from itertools import cycle
+from typing import Iterator
+from weakref import WeakKeyDictionary
 
 from rich.console import RenderableType
 from typing_extensions import Literal, Self
 
+from textual.app import App
+
 from .. import events
 from ..css._error_tools import friendly_list
 from ..reactive import Reactive, reactive
@@ -72,18 +76,13 @@
     """
 
     # Consecutive placeholders get assigned consecutive colors.
-    _COLORS = cycle(_PLACEHOLDER_BACKGROUND_COLORS)
+    _COLORS: WeakKeyDictionary[App, Iterator[str]] = WeakKeyDictionary()
     _SIZE_RENDER_TEMPLATE = "[b]{} x {}[/b]"
 
     variant: Reactive[PlaceholderVariant] = reactive[PlaceholderVariant]("default")
 
     _renderables: dict[PlaceholderVariant, str]
 
-    @classmethod
-    def reset_color_cycle(cls) -> None:
-        """Reset the placeholder background color cycle."""
-        cls._COLORS = cycle(_PLACEHOLDER_BACKGROUND_COLORS)
-
     def __init__(
         self,
         label: str | None = None,
@@ -113,8 +112,6 @@
 
         super().__init__(name=name, id=id, classes=classes)
 
-        self.styles.background = f"{next(Placeholder._COLORS)} 50%"
-
         self.variant = self.validate_variant(variant)
         """The current variant of the placeholder."""
 
@@ -123,6 +120,13 @@
         while next(self._variants_cycle) != self.variant:
             pass
 
+    def on_mount(self) -> None:
+        """Set the color for this placeholder."""
+        colors = Placeholder._COLORS.setdefault(
+            self.app, cycle(_PLACEHOLDER_BACKGROUND_COLORS)
+        )
+        self.styles.background = f"{next(colors)} 50%"
+
     def render(self) -> RenderableType:
         """Render the placeholder.

{"golden_diff": "diff --git a/docs/examples/styles/width_comparison.py b/docs/examples/styles/width_comparison.py\n--- a/docs/examples/styles/width_comparison.py\n+++ b/docs/examples/styles/width_comparison.py\n@@ -1,6 +1,6 @@\n from textual.app import App\n from textual.containers import Horizontal\n-from textual.widgets import Placeholder, Label, Static\n+from textual.widgets import Label, Placeholder, Static\n \n \n class Ruler(Static):\n@@ -9,7 +9,7 @@\n         yield Label(ruler_text)\n \n \n-class HeightComparisonApp(App):\n+class WidthComparisonApp(App):\n     def compose(self):\n         yield Horizontal(\n             Placeholder(id=\"cells\"),  # (1)!\n@@ -25,4 +25,6 @@\n         yield Ruler()\n \n \n-app = HeightComparisonApp(css_path=\"width_comparison.css\")\n+app = WidthComparisonApp(css_path=\"width_comparison.css\")\n+if __name__ == \"__main__\":\n+    app.run()\ndiff --git a/src/textual/widgets/_placeholder.py b/src/textual/widgets/_placeholder.py\n--- a/src/textual/widgets/_placeholder.py\n+++ b/src/textual/widgets/_placeholder.py\n@@ -3,10 +3,14 @@\n from __future__ import annotations\n \n from itertools import cycle\n+from typing import Iterator\n+from weakref import WeakKeyDictionary\n \n from rich.console import RenderableType\n from typing_extensions import Literal, Self\n \n+from textual.app import App\n+\n from .. import events\n from ..css._error_tools import friendly_list\n from ..reactive import Reactive, reactive\n@@ -72,18 +76,13 @@\n     \"\"\"\n \n     # Consecutive placeholders get assigned consecutive colors.\n-    _COLORS = cycle(_PLACEHOLDER_BACKGROUND_COLORS)\n+    _COLORS: WeakKeyDictionary[App, Iterator[str]] = WeakKeyDictionary()\n     _SIZE_RENDER_TEMPLATE = \"[b]{} x {}[/b]\"\n \n     variant: Reactive[PlaceholderVariant] = reactive[PlaceholderVariant](\"default\")\n \n     _renderables: dict[PlaceholderVariant, str]\n \n-    @classmethod\n-    def reset_color_cycle(cls) -> None:\n-        \"\"\"Reset the placeholder background color cycle.\"\"\"\n-        cls._COLORS = cycle(_PLACEHOLDER_BACKGROUND_COLORS)\n-\n     def __init__(\n         self,\n         label: str | None = None,\n@@ -113,8 +112,6 @@\n \n         super().__init__(name=name, id=id, classes=classes)\n \n-        self.styles.background = f\"{next(Placeholder._COLORS)} 50%\"\n-\n         self.variant = self.validate_variant(variant)\n         \"\"\"The current variant of the placeholder.\"\"\"\n \n@@ -123,6 +120,13 @@\n         while next(self._variants_cycle) != self.variant:\n             pass\n \n+    def on_mount(self) -> None:\n+        \"\"\"Set the color for this placeholder.\"\"\"\n+        colors = Placeholder._COLORS.setdefault(\n+            self.app, cycle(_PLACEHOLDER_BACKGROUND_COLORS)\n+        )\n+        self.styles.background = f\"{next(colors)} 50%\"\n+\n     def render(self) -> RenderableType:\n         \"\"\"Render the placeholder.\n", "issue": "Placeholder cycle per app\nThe placeholder stores the current color in a classvar.\n\nThis means that a second app will start where the previous app left off. And may return a different color from the first iteration.\n\nNot much of an issue in practice, but in the docs the colors change every screenshot. I think we need a separate cycle per app. Suggest we have an independent sequence for each app instance.\n", "before_files": [{"content": "from textual.app import App\nfrom textual.containers import Horizontal\nfrom textual.widgets import Placeholder, Label, Static\n\n\nclass Ruler(Static):\n    def compose(self):\n        ruler_text = \"\u00b7\u00b7\u00b7\u00b7\u2022\" * 100\n        yield Label(ruler_text)\n\n\nclass HeightComparisonApp(App):\n    def compose(self):\n        yield Horizontal(\n            Placeholder(id=\"cells\"),  # (1)!\n            Placeholder(id=\"percent\"),\n            Placeholder(id=\"w\"),\n            Placeholder(id=\"h\"),\n            Placeholder(id=\"vw\"),\n            Placeholder(id=\"vh\"),\n            Placeholder(id=\"auto\"),\n            Placeholder(id=\"fr1\"),\n            Placeholder(id=\"fr3\"),\n        )\n        yield Ruler()\n\n\napp = HeightComparisonApp(css_path=\"width_comparison.css\")\n", "path": "docs/examples/styles/width_comparison.py"}, {"content": "\"\"\"Provides a Textual placeholder widget; useful when designing an app's layout.\"\"\"\n\nfrom __future__ import annotations\n\nfrom itertools import cycle\n\nfrom rich.console import RenderableType\nfrom typing_extensions import Literal, Self\n\nfrom .. import events\nfrom ..css._error_tools import friendly_list\nfrom ..reactive import Reactive, reactive\nfrom ..widget import Widget\n\nPlaceholderVariant = Literal[\"default\", \"size\", \"text\"]\n\"\"\"The different variants of placeholder.\"\"\"\n\n_VALID_PLACEHOLDER_VARIANTS_ORDERED: list[PlaceholderVariant] = [\n    \"default\",\n    \"size\",\n    \"text\",\n]\n_VALID_PLACEHOLDER_VARIANTS: set[PlaceholderVariant] = set(\n    _VALID_PLACEHOLDER_VARIANTS_ORDERED\n)\n_PLACEHOLDER_BACKGROUND_COLORS = [\n    \"#881177\",\n    \"#aa3355\",\n    \"#cc6666\",\n    \"#ee9944\",\n    \"#eedd00\",\n    \"#99dd55\",\n    \"#44dd88\",\n    \"#22ccbb\",\n    \"#00bbcc\",\n    \"#0099cc\",\n    \"#3366bb\",\n    \"#663399\",\n]\n_LOREM_IPSUM_PLACEHOLDER_TEXT = \"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam feugiat ac elit sit amet accumsan. Suspendisse bibendum nec libero quis gravida. Phasellus id eleifend ligula. Nullam imperdiet sem tellus, sed vehicula nisl faucibus sit amet. Praesent iaculis tempor ultricies. Sed lacinia, tellus id rutrum lacinia, sapien sapien congue mauris, sit amet pellentesque quam quam vel nisl. Curabitur vulputate erat pellentesque mauris posuere, non dictum risus mattis.\"\n\n\nclass InvalidPlaceholderVariant(Exception):\n    \"\"\"Raised when an invalid Placeholder variant is set.\"\"\"\n\n\nclass Placeholder(Widget):\n    \"\"\"A simple placeholder widget to use before you build your custom widgets.\n\n    This placeholder has a couple of variants that show different data.\n    Clicking the placeholder cycles through the available variants, but a placeholder\n    can also be initialised in a specific variant.\n\n    The variants available are:\n\n    | Variant | Placeholder shows                              |\n    |---------|------------------------------------------------|\n    | default | Identifier label or the ID of the placeholder. |\n    | size    | Size of the placeholder.                       |\n    | text    | Lorem Ipsum text.                              |\n    \"\"\"\n\n    DEFAULT_CSS = \"\"\"\n    Placeholder {\n        content-align: center middle;\n        overflow: hidden;\n        color: $text;\n    }\n    Placeholder.-text {\n        padding: 1;\n    }\n    \"\"\"\n\n    # Consecutive placeholders get assigned consecutive colors.\n    _COLORS = cycle(_PLACEHOLDER_BACKGROUND_COLORS)\n    _SIZE_RENDER_TEMPLATE = \"[b]{} x {}[/b]\"\n\n    variant: Reactive[PlaceholderVariant] = reactive[PlaceholderVariant](\"default\")\n\n    _renderables: dict[PlaceholderVariant, str]\n\n    @classmethod\n    def reset_color_cycle(cls) -> None:\n        \"\"\"Reset the placeholder background color cycle.\"\"\"\n        cls._COLORS = cycle(_PLACEHOLDER_BACKGROUND_COLORS)\n\n    def __init__(\n        self,\n        label: str | None = None,\n        variant: PlaceholderVariant = \"default\",\n        *,\n        name: str | None = None,\n        id: str | None = None,\n        classes: str | None = None,\n    ) -> None:\n        \"\"\"Create a Placeholder widget.\n\n        Args:\n            label: The label to identify the placeholder.\n                If no label is present, uses the placeholder ID instead.\n            variant: The variant of the placeholder.\n            name: The name of the placeholder.\n            id: The ID of the placeholder in the DOM.\n            classes: A space separated string with the CSS classes\n                of the placeholder, if any.\n        \"\"\"\n        # Create and cache renderables for all the variants.\n        self._renderables = {\n            \"default\": label if label else f\"#{id}\" if id else \"Placeholder\",\n            \"size\": \"\",\n            \"text\": \"\\n\\n\".join(_LOREM_IPSUM_PLACEHOLDER_TEXT for _ in range(5)),\n        }\n\n        super().__init__(name=name, id=id, classes=classes)\n\n        self.styles.background = f\"{next(Placeholder._COLORS)} 50%\"\n\n        self.variant = self.validate_variant(variant)\n        \"\"\"The current variant of the placeholder.\"\"\"\n\n        # Set a cycle through the variants with the correct starting point.\n        self._variants_cycle = cycle(_VALID_PLACEHOLDER_VARIANTS_ORDERED)\n        while next(self._variants_cycle) != self.variant:\n            pass\n\n    def render(self) -> RenderableType:\n        \"\"\"Render the placeholder.\n\n        Returns:\n            The value to render.\n        \"\"\"\n        return self._renderables[self.variant]\n\n    def cycle_variant(self) -> Self:\n        \"\"\"Get the next variant in the cycle.\n\n        Returns:\n            The `Placeholder` instance.\n        \"\"\"\n        self.variant = next(self._variants_cycle)\n        return self\n\n    def watch_variant(\n        self, old_variant: PlaceholderVariant, variant: PlaceholderVariant\n    ) -> None:\n        self.remove_class(f\"-{old_variant}\")\n        self.add_class(f\"-{variant}\")\n\n    def validate_variant(self, variant: PlaceholderVariant) -> PlaceholderVariant:\n        \"\"\"Validate the variant to which the placeholder was set.\"\"\"\n        if variant not in _VALID_PLACEHOLDER_VARIANTS:\n            raise InvalidPlaceholderVariant(\n                \"Valid placeholder variants are \"\n                + f\"{friendly_list(_VALID_PLACEHOLDER_VARIANTS)}\"\n            )\n        return variant\n\n    async def _on_click(self, _: events.Click) -> None:\n        \"\"\"Click handler to cycle through the placeholder variants.\"\"\"\n        self.cycle_variant()\n\n    def _on_resize(self, event: events.Resize) -> None:\n        \"\"\"Update the placeholder \"size\" variant with the new placeholder size.\"\"\"\n        self._renderables[\"size\"] = self._SIZE_RENDER_TEMPLATE.format(*event.size)\n        if self.variant == \"size\":\n            self.refresh()\n", "path": "src/textual/widgets/_placeholder.py"}], "after_files": [{"content": "from textual.app import App\nfrom textual.containers import Horizontal\nfrom textual.widgets import Label, Placeholder, Static\n\n\nclass Ruler(Static):\n    def compose(self):\n        ruler_text = \"\u00b7\u00b7\u00b7\u00b7\u2022\" * 100\n        yield Label(ruler_text)\n\n\nclass WidthComparisonApp(App):\n    def compose(self):\n        yield Horizontal(\n            Placeholder(id=\"cells\"),  # (1)!\n            Placeholder(id=\"percent\"),\n            Placeholder(id=\"w\"),\n            Placeholder(id=\"h\"),\n            Placeholder(id=\"vw\"),\n            Placeholder(id=\"vh\"),\n            Placeholder(id=\"auto\"),\n            Placeholder(id=\"fr1\"),\n            Placeholder(id=\"fr3\"),\n        )\n        yield Ruler()\n\n\napp = WidthComparisonApp(css_path=\"width_comparison.css\")\nif __name__ == \"__main__\":\n    app.run()\n", "path": "docs/examples/styles/width_comparison.py"}, {"content": "\"\"\"Provides a Textual placeholder widget; useful when designing an app's layout.\"\"\"\n\nfrom __future__ import annotations\n\nfrom itertools import cycle\nfrom typing import Iterator\nfrom weakref import WeakKeyDictionary\n\nfrom rich.console import RenderableType\nfrom typing_extensions import Literal, Self\n\nfrom textual.app import App\n\nfrom .. import events\nfrom ..css._error_tools import friendly_list\nfrom ..reactive import Reactive, reactive\nfrom ..widget import Widget\n\nPlaceholderVariant = Literal[\"default\", \"size\", \"text\"]\n\"\"\"The different variants of placeholder.\"\"\"\n\n_VALID_PLACEHOLDER_VARIANTS_ORDERED: list[PlaceholderVariant] = [\n    \"default\",\n    \"size\",\n    \"text\",\n]\n_VALID_PLACEHOLDER_VARIANTS: set[PlaceholderVariant] = set(\n    _VALID_PLACEHOLDER_VARIANTS_ORDERED\n)\n_PLACEHOLDER_BACKGROUND_COLORS = [\n    \"#881177\",\n    \"#aa3355\",\n    \"#cc6666\",\n    \"#ee9944\",\n    \"#eedd00\",\n    \"#99dd55\",\n    \"#44dd88\",\n    \"#22ccbb\",\n    \"#00bbcc\",\n    \"#0099cc\",\n    \"#3366bb\",\n    \"#663399\",\n]\n_LOREM_IPSUM_PLACEHOLDER_TEXT = \"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam feugiat ac elit sit amet accumsan. Suspendisse bibendum nec libero quis gravida. Phasellus id eleifend ligula. Nullam imperdiet sem tellus, sed vehicula nisl faucibus sit amet. Praesent iaculis tempor ultricies. Sed lacinia, tellus id rutrum lacinia, sapien sapien congue mauris, sit amet pellentesque quam quam vel nisl. Curabitur vulputate erat pellentesque mauris posuere, non dictum risus mattis.\"\n\n\nclass InvalidPlaceholderVariant(Exception):\n    \"\"\"Raised when an invalid Placeholder variant is set.\"\"\"\n\n\nclass Placeholder(Widget):\n    \"\"\"A simple placeholder widget to use before you build your custom widgets.\n\n    This placeholder has a couple of variants that show different data.\n    Clicking the placeholder cycles through the available variants, but a placeholder\n    can also be initialised in a specific variant.\n\n    The variants available are:\n\n    | Variant | Placeholder shows                              |\n    |---------|------------------------------------------------|\n    | default | Identifier label or the ID of the placeholder. |\n    | size    | Size of the placeholder.                       |\n    | text    | Lorem Ipsum text.                              |\n    \"\"\"\n\n    DEFAULT_CSS = \"\"\"\n    Placeholder {\n        content-align: center middle;\n        overflow: hidden;\n        color: $text;\n    }\n    Placeholder.-text {\n        padding: 1;\n    }\n    \"\"\"\n\n    # Consecutive placeholders get assigned consecutive colors.\n    _COLORS: WeakKeyDictionary[App, Iterator[str]] = WeakKeyDictionary()\n    _SIZE_RENDER_TEMPLATE = \"[b]{} x {}[/b]\"\n\n    variant: Reactive[PlaceholderVariant] = reactive[PlaceholderVariant](\"default\")\n\n    _renderables: dict[PlaceholderVariant, str]\n\n    def __init__(\n        self,\n        label: str | None = None,\n        variant: PlaceholderVariant = \"default\",\n        *,\n        name: str | None = None,\n        id: str | None = None,\n        classes: str | None = None,\n    ) -> None:\n        \"\"\"Create a Placeholder widget.\n\n        Args:\n            label: The label to identify the placeholder.\n                If no label is present, uses the placeholder ID instead.\n            variant: The variant of the placeholder.\n            name: The name of the placeholder.\n            id: The ID of the placeholder in the DOM.\n            classes: A space separated string with the CSS classes\n                of the placeholder, if any.\n        \"\"\"\n        # Create and cache renderables for all the variants.\n        self._renderables = {\n            \"default\": label if label else f\"#{id}\" if id else \"Placeholder\",\n            \"size\": \"\",\n            \"text\": \"\\n\\n\".join(_LOREM_IPSUM_PLACEHOLDER_TEXT for _ in range(5)),\n        }\n\n        super().__init__(name=name, id=id, classes=classes)\n\n        self.variant = self.validate_variant(variant)\n        \"\"\"The current variant of the placeholder.\"\"\"\n\n        # Set a cycle through the variants with the correct starting point.\n        self._variants_cycle = cycle(_VALID_PLACEHOLDER_VARIANTS_ORDERED)\n        while next(self._variants_cycle) != self.variant:\n            pass\n\n    def on_mount(self) -> None:\n        \"\"\"Set the color for this placeholder.\"\"\"\n        colors = Placeholder._COLORS.setdefault(\n            self.app, cycle(_PLACEHOLDER_BACKGROUND_COLORS)\n        )\n        self.styles.background = f\"{next(colors)} 50%\"\n\n    def render(self) -> RenderableType:\n        \"\"\"Render the placeholder.\n\n        Returns:\n            The value to render.\n        \"\"\"\n        return self._renderables[self.variant]\n\n    def cycle_variant(self) -> Self:\n        \"\"\"Get the next variant in the cycle.\n\n        Returns:\n            The `Placeholder` instance.\n        \"\"\"\n        self.variant = next(self._variants_cycle)\n        return self\n\n    def watch_variant(\n        self, old_variant: PlaceholderVariant, variant: PlaceholderVariant\n    ) -> None:\n        self.remove_class(f\"-{old_variant}\")\n        self.add_class(f\"-{variant}\")\n\n    def validate_variant(self, variant: PlaceholderVariant) -> PlaceholderVariant:\n        \"\"\"Validate the variant to which the placeholder was set.\"\"\"\n        if variant not in _VALID_PLACEHOLDER_VARIANTS:\n            raise InvalidPlaceholderVariant(\n                \"Valid placeholder variants are \"\n                + f\"{friendly_list(_VALID_PLACEHOLDER_VARIANTS)}\"\n            )\n        return variant\n\n    async def _on_click(self, _: events.Click) -> None:\n        \"\"\"Click handler to cycle through the placeholder variants.\"\"\"\n        self.cycle_variant()\n\n    def _on_resize(self, event: events.Resize) -> None:\n        \"\"\"Update the placeholder \"size\" variant with the new placeholder size.\"\"\"\n        self._renderables[\"size\"] = self._SIZE_RENDER_TEMPLATE.format(*event.size)\n        if self.variant == \"size\":\n            self.refresh()\n", "path": "src/textual/widgets/_placeholder.py"}]}