Repository: pretix/pretix

Issue:
Visually indicate optional and required fields
Currently, when I see users walking through e.g. the event creation process, one of the main sources of confusion is that it is unclear which fields are optional and which are not. We should therefore establish a pattern to make this clearer. However, I'm unsure which pattern it should be. I've come up with the following options so far:

## Option 1: The asterisk
![optional0](https://user-images.githubusercontent.com/64280/31308099-9b119cf2-ab70-11e7-9299-6cf203ea3adf.png)
This option (asterisk in the primary color for required fields) is already used in the frontend.

**Pro:** Well-known
**Con:** In most backend forms, more fields are required than optional. Therefore, most fields would have an asterisk and it would look really cluttered, I don't like it much.

## Option 2: "Optional" in help text
![optional1](https://user-images.githubusercontent.com/64280/31308096-9b0f99ac-ab70-11e7-93f7-abf358a94f61.png)
This option is already used in some places, but we could use it consistently in all places.

**Pro:** It is unobtrusive and does not look cluttered.
**Con:** It comes after the field in the reading flow. When watching users, I commonly see that users first think about what to fill in there, then fill the field, then read that it was optional in the first place → Ineffective.

## Option 3: "Optional" in placeholder
![optional2](https://user-images.githubusercontent.com/64280/31308097-9b10b076-ab70-11e7-8909-9758d8b16dcc.png)

**Pro:** It is unobtrusive and you see it at the right point in time
**Con:** It is easy to oversee and it makes using placeholders as example values harder.

## Option 4: "Optional" at the label
![optional3](https://user-images.githubusercontent.com/64280/31308100-9b11f738-ab70-11e7-89b3-a2dd34e46a07.png)

**Pro:** It is seen at the right point in time
**Con:** I fear it looks more cluttered than options 2 or 3 when used throughout a page.

## Option 5: "Optional" as input field add-on
![optional4](https://user-images.githubusercontent.com/64280/31308098-9b119e5a-ab70-11e7-8810-d8e1b34f4e82.png)
This could also vanish as soon as the field gets filled in.

**Pro:** I think it looks great and sits in the right place
**Con:** I have no idea how to use this concept for other field types like multi-line textboxes or checkbox groups.


I'd love to get some feedback!


Discussion:
Just for the record:

## Option 6: Bold and normal labels

Bold form field labels would indicate mandatory inputs, normal labels would be applied to optional inputs.

**Pro:** It's visually distinguishes the fields at first glance. 
**Con:** Maybe a cluttered look. May not be understandable.
See also here for feedback received: https://twitter.com/_rami_/status/916652986671263744
Related to option 1: You can invert the asterisk so fields mark with the asterisk is marked as optional and not required. Place the legend above all fields.
@chrko Thanks for the comment!
> Related to option 1: You can invert the asterisk so fields mark with the asterisk is marked as optional and not required. Place the legend above all fields.

Unsure. People do not tend to read legends and the asterisk is highly associated with required fields all over the internet (and in pretix' frontend itself).
I like Option 6 + Option 4 best. I.e., distinguish required (bold) vs. optional (normal, with “optional” label).
I would prefer option 4. Because:

- Option 1 is easily misunderstood as mandatory or a footnote.
- Option 2 is better but it requires the user to move their view if they want to check if a field is optional or not.
- Option 3: If there is some input you have no easy way to determine if the field was optional or not.
- Option 4: It is clear, in time and you see it directly if you browse over the different fields. Also it avoids the problems of option 1-3.
- Option 5: Nice candidate but it is a new concept, so people could get confused especially if you vanish the label after some input. Also, as you mention, it isn't usable for all types of input.
- Option 6: It breaks the look and feel of the design. Also it could be a problem for users with a screen reader. And it implies that the user understands the meaning. 
@stapelberg
> I like Option 6 + Option 4 best. I.e., distinguish required (bold) vs. optional (normal, with “optional” label).

I think the combination of 6+4 is worse then either of them alone for the reason that then "optional" seems like a part of the field label (there might be two-line labels) and makes parsing the information harder.
I never thought option 1 might have issues. But it's true: People don't tend to read legends, even if there would be one.

Obviously, option 5 would be the easiest to notice because of the color. I don't like the idea of introducing an additional place with information to the right of the field, though. And colors are already used for other information in some forms (password strength, non-matching passwords, wrong format, not allowed to leave empty, ...).

I have used option 2 before. But whether it's a good solution depends on the form and on how much text there already is in the help texts.

Option 4 would be my preferred solution. I've seen it before and thought it looked good. Also it's right after reading the field title that you want to know if you have to enter something.
Option 4 is my preferred solution as well. It has the advantage that users will have to look where the optional marker is situated. If they were not to read the label, they would not know what to fill in. So when you read the label you mentally mark it “optional” almost without effort.
I'd say Option 4, too! Easy ✌️
Option 4 is easily the most intuitive one, can I proceed with this?
Hi @SanketDG, thanks for the comment and the offer to implement it! I've already started doing it, though, and it is quite complicated to do it since there are multiple non-obvious ways in pretix a field could be required (e.g. multi-language fields are never *technically* required, as it is at most required to fill *at least one* language, never to fill all of them. So Django thinks they're optional, while they're not). I already handled most of this, I just need to continue adapting testing it for all forms, so I think I'll do a PR myself over the next days.

Resolving patch:
diff --git a/src/pretix/control/forms/renderers.py b/src/pretix/control/forms/renderers.py
new file mode 100644
--- /dev/null
+++ b/src/pretix/control/forms/renderers.py
@@ -0,0 +1,54 @@
+from bootstrap3.renderers import FieldRenderer
+from bootstrap3.text import text_value
+from django.forms import CheckboxInput
+from django.forms.utils import flatatt
+from django.utils.html import format_html
+from django.utils.safestring import mark_safe
+from django.utils.translation import pgettext
+from i18nfield.forms import I18nFormField
+
+
+def render_label(content, label_for=None, label_class=None, label_title='', optional=False):
+    """
+    Render a label with content
+    """
+    attrs = {}
+    if label_for:
+        attrs['for'] = label_for
+    if label_class:
+        attrs['class'] = label_class
+    if label_title:
+        attrs['title'] = label_title
+    builder = '<{tag}{attrs}>{content}{opt}</{tag}>'
+    return format_html(
+        builder,
+        tag='label',
+        attrs=mark_safe(flatatt(attrs)) if attrs else '',
+        opt=mark_safe('<br><span class="optional">{}</span>'.format(pgettext('form', 'Optional'))) if optional else '',
+        content=text_value(content),
+    )
+
+
+class ControlFieldRenderer(FieldRenderer):
+    def __init__(self, *args, **kwargs):
+        kwargs['layout'] = 'horizontal'
+        super().__init__(*args, **kwargs)
+
+    def add_label(self, html):
+        label = self.get_label()
+
+        if hasattr(self.field.field, '_required'):
+            # e.g. payment settings forms where a field is only required if the payment provider is active
+            required = self.field.field._required
+        elif isinstance(self.field.field, I18nFormField):
+            required = self.field.field.one_required
+        else:
+            required = self.field.field.required
+
+        html = render_label(
+            label,
+            label_for=self.field.id_for_label,
+            label_class=self.get_label_class(),
+            optional=not required and not isinstance(self.widget, CheckboxInput)
+        ) + html
+        return html
diff --git a/src/pretix/settings.py b/src/pretix/settings.py
--- a/src/pretix/settings.py
+++ b/src/pretix/settings.py
@@ -513,7 +513,12 @@
 CELERY_RESULT_SERIALIZER = 'json'
 
 BOOTSTRAP3 = {
-    'success_css_class': ''
+    'success_css_class': '',
+    'field_renderers': {
+        'default': 'bootstrap3.renderers.FieldRenderer',
+        'inline': 'bootstrap3.renderers.InlineFieldRenderer',
+        'control': 'pretix.control.forms.renderers.ControlFieldRenderer',
+    },
 }
 
 AUTH_PASSWORD_VALIDATORS = [


Test patch: