diff --git a/django_cotton/templatetags/_component.py b/django_cotton/templatetags/_component.py
index a24810a..3dcf130 100644
--- a/django_cotton/templatetags/_component.py
+++ b/django_cotton/templatetags/_component.py
@@ -40,6 +40,10 @@ def render(self, context):
value = self._strip_quotes_safely(value)
if value is True: # Boolean attribute
component_data["attrs"][key] = True
+ elif key.startswith("::"): # Escaping 1 colon e.g for shorthand alpine
+ key = key[1:]
+ # component_data["slots"][key] = value
+ component_data["attrs"][key] = value
elif key.startswith(":"):
key = key[1:]
try:
diff --git a/django_cotton/tests/test_attributes.py b/django_cotton/tests/test_attributes.py
index e8a9f75..1f7aca3 100644
--- a/django_cotton/tests/test_attributes.py
+++ b/django_cotton/tests/test_attributes.py
@@ -483,3 +483,31 @@ def test_attributes_remain_unordered(self):
self.assertTrue(
"in default slot: " in compiled
)
+
+ def test_colon_escaping(self):
+ self.create_template(
+ "cotton/colon_escaping.html",
+ """
+ attrs: '{{ attrs }}'
+ """,
+ )
+
+ self.create_template(
+ "colon_escaping_view.html",
+ """
+
Let's tackle together a common UI requirement - tabs.
Now we have the design right, let's chop it up into components.
-The following key features allow you to build re-usable components with alpine.js:
+ +x-data
is accessible as {% verbatim %}{{ x_data }}{% endverbatim %}
inside the component as cotton makes available snake_case versions of all kebab-cased attributes. (If you use {% verbatim %}{{ attrs }}{% endverbatim %}
then the output will already be in the correct case).
+ :example
). Because single :
attribute prefixing is reserved for cotton's dynamic attributes,
+ we can escape the first colon using ::
. This will ensure the attribute maintains a single :
inside {% verbatim %}{{ attrs }}{% endverbatim %}
+ {% verbatim %}{{ slot }}{% endverbatim %}
- Default content injection.