Commit db30234
committed
feature #40449 [TwigBridge] add tailwindcss form layout (kbond)
This PR was squashed before being merged into the 5.3-dev branch.
Discussion
----------
[TwigBridge] add tailwindcss form layout
| Q | A
| ------------- | ---
| Branch? | 5.x
| Bug fix? | no
| New feature? | yes
| Deprecations? | no
| Tickets | n/a
| License | MIT
| Doc PR | todo
Per @weaverryan's [call to action on twitter](https://twitter.com/weaverryan/status/1369654724107067392) and slack discussion.
It's been tricky to create a generic form layout for tailwind as it's a css *utility framework* and has an unlimited number of ways to style forms. With tailwindcss 2.0, the tailwind team released an [official form plugin](https://github.com/tailwindlabs/tailwindcss-forms) that provides form element reset/normalization that looks decent out of the box. This PR is an attempt to piggy-back on this plugin to provide a minimal Symfony form layout for tailwind. The goal is to have your forms look good in a tailwind/Symfony app with no customization (but of course allow customization as desired).
This layout **requires** tailwindcss 2 and the form plugin.
I followed the ["unstyled" demo](https://tailwindcss-forms.vercel.app/) for the form plugin as a style guide. Here is a screenshot of this layout used in [a demo Symfony app](https://github.com/kbond/symfony-tailwind) with several common form types (I'll try to keep this updated as I update the PR):

Some notes about the layout:
1. I tried to use as few tailwind classes as possible and avoid color (primary exception being the error color).
2. I decided on a mobile-first approach so out of the box, it will look decent on any device and drastically reduces the number of css classes/assumptions.
3. While other layouts merge classes passed by the user, I opted to replace. This ensures the user doesn't have to _undo_ the class decisions made by this layout. I also discovered "undoing" doesn't work as I expected anyway: `class="mt-1 mt-0"`, `mt-1` "wins" as `mt-1` comes later in the compiled stylesheet.
4. For the _low level_ blocks, I extracted the classes into their own "variables" (`row_class`, `widget_class`, `label_class`, `help_class`, `error_item_class`) to make it easier to extend and customize the layout. Note the `widget_disabled_class`/`widget_errors_class` variables: these are added even if you've overridden the `widget_class` variable.
### Customization
Customizing is especially important for this layout. Here are the two ways:
1. Twig form functions:
```twig
{{ form_row(form.title, {
row_class: 'my row classes',
label_class: 'my label classes',
error_item_class: 'my error item classes',
widget_class: 'my widget classes',
widget_disabled_class: 'my disabled widget classes',
widget_errors_class: 'my widget with error classes',
}) }}
```
2. Project specific form layout:
```twig
{% use 'tailwind_2_layout.html.twig' %}
{%- block form_row -%}
{%- set row_class = row_class|default('my row classes') -%}
{{- parent() -}}
{%- endblock form_row -%}
{%- block widget_attributes -%}
{%- set widget_class = widget_class|default('my widget classes') -%}
{%- set widget_disabled_class = widget_disabled_class|default('my disabled widget classes') -%}
{%- set widget_errors_class = widget_errors_class|default('my widget with error classes') -%}
{{- parent() -}}
{%- endblock widget_attributes -%}
{%- block form_label -%}
{%- set label_class = label_class|default('my label classes') -%}
{{- parent() -}}
{%- endblock form_label -%}
{%- block form_help -%}
{%- set help_class = help_class|default('my label classes') -%}
{{- parent() -}}
{%- endblock form_help -%}
{%- block form_errors -%}
{%- set error_item_class = error_item_class|default('my error item classes') -%}
{{- parent() -}}
{%- endblock form_errors -%}
```
#### Customization POC/Demo
With this custom form theme:
```twig
{%- block form_label -%}
{%- set label_class = label_class|default('block text-gray-500 uppercase tracking-wider text-sm font-bold') -%}
{{- parent() -}}
{%- endblock -%}
{%- block widget_attributes -%}
{%- set widget_class = widget_class|default('mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50') -%}
{{- parent() -}}
{%- endblock -%}
{%- block checkbox_widget -%}
{%- set widget_class = widget_class|default('mr-2 rounded border-gray-300 text-indigo-600 shadow-sm focus:border-indigo-300 focus:ring focus:ring-offset-0 focus:ring-indigo-200 focus:ring-opacity-50') -%}
{{- parent() -}}
{%- endblock -%}
{%- block checkbox_label -%}
{%- set label_class = label_class|default('block text-gray-800') -%}
{{- block('form_label') -}}
{%- endblock -%}
```
The above example looks like this:

Commits
-------
3719a409b6 [TwigBridge] add tailwindcss form layout1 file changed
+71
-0
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
0 commit comments