Bringing component based design to Django templates.
Project description
Django Cotton
Bringing component-based design to Django templates.
Cotton aims to overcome certain limitations that exist in the django template system that hold us back when we want to apply modern practises to compose UIs in a modular and reusable way.
Key Features
- Modern UI Composition: Efficiently compose and reuse UI components.
- Interoperable with Django: Cotton enhances django's existing template system.
- HTML-like Syntax: Better code editor support as component tags are similar to html tags.
- Minimal Overhead: Compiles to native Django components with dynamic caching.
- Ideal for Tailwind usage: Helps encapsulate content and style in one file.
- Compliments HTMX: Create smart components, reducing repetition and enhancing maintainability.
Walkthrough
<!-- button.cotton.html -->
<a href="/" class="...">I'm a static button</a>
<!-- template -->
<c-button />
<!-- output -->
<a href="/" class="...">I'm a static button</a>
An unlikely example but it shows we can use cotton like an include tag. Let's make this more useful:
<!-- button.cotton.html -->
<a href="/" class="...">{{ slot }}</a>
<!-- template -->
<c-button>Contact</c-button>
<!-- output -->
<a href="/" class="...">Contact</a>
Add attributes
<!-- button.cotton.html -->
<a href="{{ url }}" class="...">
{{ slot }}
</a>
<!-- template -->
<c-button url="/contact">Contact</c-button>
<!-- output -->
<a href="/contact" class="...">
Contact
</a>
Utilize named slots
Named slots are a powerful concept. It allows us to provide HTML to appear in one or more areas in the component. Here we allow the button to optionally display an icon:
<!-- button.cotton.html -->
<a href="{{ url }}" class="...">
{{ slot }}
{% if icon %}
{{ icon }}
{% endif %}
</a>
<!-- template -->
<c-button url="/contact">
Contact
<c-slot name="icon">
<svg>...</svg>
</c-slot>
</c-button>
Named slots can also contain any django native template logic:
<!-- template -->
<c-button url="/contact">
<c-slot name="icon">
{% if mode == 'edit' %}
<svg id="pencil">...</svg>
{% else %}
<svg id="disk">...</svg>
{% endif %}
</c-slot>
</c-button>
Pass template variable as an attribute
To pass a template variable you prepend the attribute name with a colon :
<!-- button.cotton.html -->
<a href="{{ url }}" class="...">
{{ slot }} <strong>{{ click_count }}</strong>
</a>
<!-- template -->
<c-button :click_count="click_count">Contact</c-button>
You are effectively passing the variable by reference. You could achieve a similar thing by using a named slot, which will be passing the value of the variable instead:
<!-- template -->
<c-button>
Contact
<c-slot name="click_count">
{{ click_count }}
</c-slot>
</c-button>
To demonstrate another example of passing a variable by reference, consider a bio card component:
<!-- template -->
<c-bio-card :user="user" />
That has a component definiton like:
<!-- bio_card.cotton.html -->
<div class="...">
<img src="{{ user.avatar }}" alt="...">
{{ user.username }} {{ user.country_code }}
</div>
Add boolean attribute
Boolean attributes reduce boilerplate when we just want to indicate a certain attribute should be True
or not.
<!-- template -->
<c-button url="/contact" external>Contact</c-button>
By passing just the attribute name without a value, it will automatically be provided to the component as True
<!-- button.cotton.html -->
<a href="{{ url }}" {% if external %} target="_blank" {% endif %} class="...">
{{ slot }}
</a>
Default attributes with <c-vars>
Django templates adhere quite strictly to the MVC model and does not permit much data control in the View. But what if we want to handle data for the purpose of UI state only? Having this in the back would surely convolute the backend code. For this, Cotton can set simple attribute values that help allow us to set default values for our component attributes.
<!-- button.cotton.html -->
<c-vars theme="bg-purple-500" />
<a href="..." class="{{ theme }}">
{{ slot }}
</a>
<!-- template -->
<c-button>I'm a purple button</c-button>
<!-- output -->
<a href="..." class="bg-purple-500">
I'm a purple button
</a>
Now we have a default theme for our button, but it is overridable:
<!-- template -->
<c-button theme="bg-green-500">But I'm green</c-button>
<!-- output -->
<a href="..." class="bg-green-500">
But I'm green
</a>
Create flexible, re-usable inputs with {{ attrs }}
{{ attrs }}
is a special variable that contains all the attributes passed to the component in an key="value" format. This is useful when you want to pass all attributes to a child element. For example, you have inputs that can have any number of attributes defined:
<!-- input.cotton.html -->
<input type="text" class="..." {{ attrs }} />
<!-- example usage -->
<c-input placeholder="Enter your name" />
<c-input name="country" id="country" value="Japan" />
<c-input class="highlighted" required />
If you combine this with the c-vars
tag, any property defined there will be excluded from {{ attrs }}
. For example:
<!-- input.cotton.html -->
<c-vars type="text" />
<input {{ attrs }} class="..." />
<!-- example usage -->
<c-input type="password" placeholder="Password" />
<!-- `type` will not be in {{ attrs }} -->
An example with HTMLX
Cotton helps carve out re-usable components, here we show how to make a re-usable form, reducing code repetition and improving maintainability:
<!-- form.cotton.html -->
<div id="result" class="..."></div>
<form hx-post="{{ url }}" hx-target="#result" hx-swap="outerHTML">
{{ slot }}
</form>
<!-- template -->
<c-form url="/contact">
<input type="text" name="name" placeholder="Enter your name" />
<button type="submit">Submit</button>
</c-form>
<c-form url="/buy">
<input type="text" name="type" />
<input type="text" name="quantity" />
<button type="submit">Submit</button>
</c-form>
Usage Basics
- File Extensions: Views templates that contain Cotton and Cotton components themselves should use the
.cotton.html
extension. - Component Placement: Components should be placed in the
templates/cotton
folder. - Naming Conventions:
- Component filenames use snake_case:
my_component.cotton.html
- Components are called using kebab-case:
<c-my-component />
- Component filenames use snake_case:
Changelog
v0.9.1 (2024-06-08) - Initial open source release
v0.9.2 (2024-06-08) - Readme update
v0.9.3 (2024-06-09) - Fixed loader docs + readme
v0.9.4 (2024-06-11) - Added boolean attributes
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for django_cotton-0.9.6-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 6c9e80722e9b7e5d3e5ee66787423bb54eb52b836a5fdca5d3fd35134f5948c6 |
|
MD5 | cce14807bb2e1d3c66cbd26a30443a52 |
|
BLAKE2b-256 | d6c23d21076b9d04439d1e9502210512747f233427e994732a0196e13f8edd2e |