cotton
for

Building a re-usable form input field

Componentizing form fields enable you to easily maintain a consistent style across your whole app. This approach is particularly favourable when using a utility first CSS design system like Tailwind where every aspect of the style is detailed and maintained in isolated component files.

For this example, we're using Tailwind for styling. Let's start with a standard input:

form_view.html
<input type="text" name="shoe_size" placeholder="Shoe Size" class="border rounded shadow px-3 py-1.5">
preview

Having to repeat this code more than once may get tedious and difficult to maintain. Let's componentize.

cotton/input.html
<input type="text" name="{{ name }}" placeholder="{{ placeholder }}" class="border rounded shadow px-3 py-1.5">
form_view.html
<c-input name="shoe_size" placeholder="Show Size" />
<c-input name="country" placeholder="Country" />
<c-input name="age" placeholder="Age" />
preview

Change the input type

You will probably need more than just a text input in your project. So let's declare an attribute `text` in <c-vars />. Adding it as a var will allow us to set "text" as the default. Additionally, it will be excluded from {{ attrs }}:

cotton/input.html
<c-vars type="text" />

<input type="{{ type }}" {{ attrs }} class="border rounded shadow px-3 py-1.5">
form_view.html
<c-input name="some_text" placeholder="Just a text field" />
<c-input type="email" name="email" placeholder="Email" />
<c-input type="password" name="password" placeholder="Password" />
<c-input type="number" name="Count" placeholder="Password" />
preview

Adding validation state

Let's also handle displaying errors when we need to.

cotton/input.html
<c-vars type="text" errors />

<input type="{{ type }}" {{ attrs }} class="border rounded shadow px-3 py-1.5 {% if errors %}border-red-500{% endif %}">

{% if errors %}
    <div class="text-red-500 text-sm mt-1">{{ errors.as_text }}</div>
{% endif %}
form_view.html
<c-input name="surname" placeholder="Surname" :errors="form.surname.errors" />
preview
Your surname is required.

Bonus - Adding icons

To take customization a step further and to demonstrate the flexibility of cotton, let's now give our input component the ability to display icons.

cotton/input.html
<c-vars type="text" leading_icon trailing_icon />

<div class="border rounded shadow flex items-center">
    {% if leading_icon %}
        <div class="pl-3">{{ leading_icon }}</div>
    {% endif %}

    <input type="{{ type }}" {{ attrs }} class="px-3 py-1.5 w-full">
</div>
form_view.html
<c-input name="text" name="username" placeholder="Username">
    <c-slot name="leading_icon">
        <svg>...</svg>
    </c-slot>
</c-input>

<c-input type="password" name="password" name="password" placeholder="Password">
    <c-slot name="leading_icon">
        <svg>...</svg>
    </c-slot>
</c-input>
preview