Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 912d3e5

Browse files
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): ![New-Post](https://user-images.githubusercontent.com/127811/112684961-3d2cc380-8e4a-11eb-8e43-0c08d2eecd7a.png) 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: ![New-Post (3)](https://user-images.githubusercontent.com/127811/112705040-657ce800-8e73-11eb-965f-de289e9b978a.png) Commits ------- 3719a40 [TwigBridge] add tailwindcss form layout
2 parents 9abeb24 + 3719a40 commit 912d3e5

File tree

1 file changed

+71
-0
lines changed

1 file changed

+71
-0
lines changed
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
{# @experimental in 5.2 #}
2+
3+
{% use 'form_div_layout.html.twig' %}
4+
5+
{%- block form_row -%}
6+
{%- set row_attr = row_attr|merge({ class: row_attr.class|default(row_class|default('mb-6')) }) -%}
7+
{{- parent() -}}
8+
{%- endblock form_row -%}
9+
10+
{%- block widget_attributes -%}
11+
{%- set attr = attr|merge({ class: attr.class|default(widget_class|default('mt-1 w-full')) ~ (disabled ? ' ' ~ widget_disabled_class|default('border-gray-300 text-gray-500')) ~ (errors|length ? ' ' ~ widget_errors_class|default('border-red-700')) }) -%}
12+
{{- parent() -}}
13+
{%- endblock widget_attributes -%}
14+
15+
{%- block form_label -%}
16+
{%- set label_attr = label_attr|merge({ class: label_attr.class|default(label_class|default('block text-gray-800')) }) -%}
17+
{{- parent() -}}
18+
{%- endblock form_label -%}
19+
20+
{%- block form_help -%}
21+
{%- set help_attr = help_attr|merge({ class: help_attr.class|default(help_class|default('mt-1 text-gray-600')) }) -%}
22+
{{- parent() -}}
23+
{%- endblock form_help -%}
24+
25+
{%- block form_errors -%}
26+
{%- if errors|length > 0 -%}
27+
<ul>
28+
{%- for error in errors -%}
29+
<li class="{{ error_item_class|default('text-red-700') }}">{{ error.message }}</li>
30+
{%- endfor -%}
31+
</ul>
32+
{%- endif -%}
33+
{%- endblock form_errors -%}
34+
35+
{%- block choice_widget_expanded -%}
36+
{%- set attr = attr|merge({ class: attr.class|default('mt-2') }) -%}
37+
<div {{ block('widget_container_attributes') }}>
38+
{%- for child in form %}
39+
<div class="flex items-center">
40+
{{- form_widget(child) -}}
41+
{{- form_label(child, null, { translation_domain: choice_translation_domain }) -}}
42+
</div>
43+
{% endfor -%}
44+
</div>
45+
{%- endblock choice_widget_expanded -%}
46+
47+
{%- block checkbox_row -%}
48+
{%- set row_attr = row_attr|merge({ class: row_attr.class|default(row_class|default('mb-6')) }) -%}
49+
{%- set widget_attr = {} -%}
50+
{%- if help is not empty -%}
51+
{%- set widget_attr = {attr: {'aria-describedby': id ~"_help"}} -%}
52+
{%- endif -%}
53+
<div{% with {attr: row_attr} %}{{ block('attributes') }}{% endwith %}>
54+
{{- form_errors(form) -}}
55+
<div class="inline-flex items-center">
56+
{{- form_widget(form, widget_attr) -}}
57+
{{- form_label(form) -}}
58+
</div>
59+
{{- form_help(form) -}}
60+
</div>
61+
{%- endblock checkbox_row -%}
62+
63+
{%- block checkbox_widget -%}
64+
{%- set widget_class = widget_class|default('mr-2') -%}
65+
{{- parent() -}}
66+
{%- endblock checkbox_widget -%}
67+
68+
{%- block radio_widget -%}
69+
{%- set widget_class = widget_class|default('mr-2') -%}
70+
{{- parent() -}}
71+
{%- endblock radio_widget -%}

0 commit comments

Comments
 (0)