diff options
| author | antoliny0919 <antoliny0919@gmail.com> | 2025-08-07 22:17:50 +0900 |
|---|---|---|
| committer | Jacob Walls <jacobtylerwalls@gmail.com> | 2026-02-27 07:43:45 -0500 |
| commit | 187a789f99ecbc708de517c6b54d480b68ba59fe (patch) | |
| tree | a9ad3ba592bf6c65cb14b39110adbfbaa2825a51 /django/contrib | |
| parent | d4ab33af061c13e290b6996756b2c72578891285 (diff) | |
Fixed #34643 -- Moved inputs beneath labels and errors in admin forms.
Thanks Sarah Boyce and Jacob Walls for reviews.
Co-authored-by: Hrushikesh Vaidya <hrushikeshrv@gmail.com>
Diffstat (limited to 'django/contrib')
13 files changed, 96 insertions, 274 deletions
diff --git a/django/contrib/admin/static/admin/css/base.css b/django/contrib/admin/static/admin/css/base.css index ec2ad6ab93..ea7707eb89 100644 --- a/django/contrib/admin/static/admin/css/base.css +++ b/django/contrib/admin/static/admin/css/base.css @@ -498,9 +498,6 @@ input, textarea, select, .form-row p, form .button { font-weight: normal; font-size: 0.8125rem; } -.form-row div.help { - padding: 2px 3px; -} textarea { vertical-align: top; @@ -700,7 +697,7 @@ ul.messagelist li.error { } ul.errorlist { - margin: 0 0 4px; + margin: 0; padding: 0; color: var(--error-fg); background: var(--body-bg); @@ -709,7 +706,6 @@ ul.errorlist { ul.errorlist li { font-size: 0.8125rem; display: block; - margin-bottom: 4px; overflow-wrap: break-word; } @@ -731,17 +727,6 @@ td ul.errorlist li { margin: 0; } -.form-row.errors { - margin: 0; - border: none; - border-bottom: 1px solid var(--hairline-color); - background: none; -} - -.form-row.errors ul.errorlist li { - padding-left: 0; -} - .errors input, .errors select, .errors textarea, td ul.errorlist + input, td ul.errorlist + select, td ul.errorlist + textarea { border: 1px solid var(--error-fg); diff --git a/django/contrib/admin/static/admin/css/forms.css b/django/contrib/admin/static/admin/css/forms.css index 5d2c1d2018..c474368184 100644 --- a/django/contrib/admin/static/admin/css/forms.css +++ b/django/contrib/admin/static/admin/css/forms.css @@ -24,15 +24,18 @@ form .form-row p { .flex-container { display: flex; + gap: 10px; + flex-direction: column; + align-items: flex-start; } -.form-multiline { - flex-wrap: wrap; +.flex-container div.checkbox { + display: flex; } -.form-multiline > div, -.form-multiline > fieldset { - padding-bottom: 10px; +.form-multiline { + flex-wrap: wrap; + flex-direction: row; } /* FORM LABELS */ @@ -41,6 +44,7 @@ legend, label { font-weight: normal; color: var(--body-quiet-color); font-size: 0.8125rem; + padding: 0; } .required legend, legend.required, @@ -59,7 +63,8 @@ form div.radiolist.inline div { } form div.radiolist label { - width: auto; + display: inline-block; + padding: 4px 10px 0 0; } form div.radiolist input[type="radio"] { @@ -94,7 +99,6 @@ fieldset .inline-heading, /* ALIGNED FIELDSETS */ .aligned fieldset { - flex-grow: 1; border-top: none; } @@ -104,21 +108,7 @@ fieldset .inline-heading, .aligned legend { float: inline-start; -} - -.aligned legend, -.aligned label { - display: block; - padding: 4px 10px 0 0; - min-width: 160px; - width: 160px; - word-wrap: break-word; -} - -.aligned label:not(.vCheckboxLabel):after { - content: ''; - display: inline-block; - vertical-align: middle; + padding: 0.5rem 0; } .aligned label + p, .aligned .checkbox-row + div.help, .aligned label + div.readonly { @@ -143,30 +133,14 @@ fieldset .inline-heading, width: 350px; } -form .aligned ul { - margin-left: 160px; - padding-left: 10px; -} - form .aligned div.radiolist { display: block; margin: 0; padding: 0; } -form .aligned p.help, -form .aligned div.help { - margin-top: 0; - margin-left: 160px; - padding-left: 10px; -} - -form .aligned p.date div.help.timezonewarning, -form .aligned p.datetime div.help.timezonewarning, -form .aligned p.time div.help.timezonewarning { +form .aligned fieldset div.help { margin-left: 0; - padding-left: 0; - font-weight: normal; } form .aligned p.help:last-child, @@ -175,16 +149,6 @@ form .aligned div.help:last-child { padding-bottom: 0; } -form .aligned input + p.help, -form .aligned textarea + p.help, -form .aligned select + p.help, -form .aligned input + div.help, -form .aligned textarea + div.help, -form .aligned select + div.help { - margin-left: 160px; - padding-left: 10px; -} - form .aligned select option:checked { background-color: var(--selected-row); color: var(--body-fg); @@ -194,6 +158,11 @@ form .aligned ul li { list-style: none; } +form .aligned div.help ul { + padding-left: 0; + margin-left: 0; +} + form .aligned table p { margin-left: 0; padding-left: 0; @@ -212,32 +181,6 @@ form .aligned table p { width: 610px; } -fieldset .fieldBox { - margin-right: 20px; -} - -/* WIDE FIELDSETS */ - -.wide label, -.wide legend { - width: 200px; -} - -form .wide p.help, -form .wide ul.errorlist, -form .wide div.help { - padding-left: 50px; -} - -form div.help ul { - padding-left: 0; - margin-left: 0; -} - -.colM fieldset.wide .vLargeTextField, .colM fieldset.wide .vXMLLargeTextField { - width: 450px; -} - /* COLLAPSIBLE FIELDSETS */ .collapse summary .fieldset-heading, diff --git a/django/contrib/admin/static/admin/css/responsive.css b/django/contrib/admin/static/admin/css/responsive.css index 93abf79953..c8af03ba90 100644 --- a/django/contrib/admin/static/admin/css/responsive.css +++ b/django/contrib/admin/static/admin/css/responsive.css @@ -205,12 +205,6 @@ input[type="submit"], button { min-height: 0; } - fieldset .fieldBox + .fieldBox { - margin-top: 10px; - padding-top: 10px; - border-top: 1px solid var(--hairline-color); - } - textarea { max-width: 100%; max-height: 120px; @@ -224,7 +218,7 @@ input[type="submit"], button { .aligned .datetimeshortcuts, .aligned .related-lookup + strong { align-self: center; - margin-left: 15px; + margin-left: 0.5rem; } form .aligned div.radiolist { @@ -465,11 +459,7 @@ input[type="submit"], button { } .flex-container { - flex-flow: column; - } - - .flex-container.checkbox-row { - flex-flow: row; + align-items: stretch; } textarea { @@ -480,22 +470,6 @@ input[type="submit"], button { width: auto; } - fieldset .fieldBox + .fieldBox { - margin-top: 15px; - padding-top: 15px; - } - - .aligned legend, - .aligned label { - width: 100%; - min-width: auto; - padding: 0 0 10px; - } - - .aligned label:after { - max-height: 0; - } - .aligned .form-row input, .aligned .form-row select, .aligned .form-row textarea { @@ -513,13 +487,6 @@ input[type="submit"], button { padding: 1px 0 0 5px; } - .aligned label + p, - .aligned label + div.help, - .aligned label + div.readonly { - padding: 0; - margin-left: 0; - } - .aligned p.file-upload { font-size: 0.8125rem; } @@ -533,37 +500,10 @@ input[type="submit"], button { padding-bottom: 0; } - .aligned .timezonewarning { - flex: 1 0 100%; - margin-top: 5px; - } - - form .aligned .form-row div.help { - width: 100%; - margin: 5px 0 0; - padding: 0; - } - - form .aligned ul, - form .aligned ul.errorlist { - margin-left: 0; - padding-left: 0; - } - - form .aligned div.radiolist { - margin-top: 5px; - margin-right: 15px; - margin-bottom: -3px; - } - form .aligned div.radiolist:not(.inline) div + div { margin-top: 5px; } - form .aligned fieldset div.flex-container { - display: unset; - } - /* Related widget */ .related-widget-wrapper { diff --git a/django/contrib/admin/static/admin/css/responsive_rtl.css b/django/contrib/admin/static/admin/css/responsive_rtl.css index b336bbfbe9..cd32867688 100644 --- a/django/contrib/admin/static/admin/css/responsive_rtl.css +++ b/django/contrib/admin/static/admin/css/responsive_rtl.css @@ -48,17 +48,6 @@ /* MOBILE */ @media (max-width: 767px) { - [dir="rtl"] .aligned .related-lookup, - [dir="rtl"] .aligned .datetimeshortcuts { - margin-left: 0; - margin-right: 15px; - } - - [dir="rtl"] .aligned ul, - [dir="rtl"] form .aligned ul.errorlist { - margin-right: 0; - } - [dir="rtl"] #changelist-filter { margin-left: 0; margin-right: 0; diff --git a/django/contrib/admin/static/admin/css/rtl.css b/django/contrib/admin/static/admin/css/rtl.css index ba4d0bf549..aa7c4e8636 100644 --- a/django/contrib/admin/static/admin/css/rtl.css +++ b/django/contrib/admin/static/admin/css/rtl.css @@ -124,29 +124,8 @@ thead th.sorted .text { /* FORMS */ -.aligned label, -.aligned legend { - padding: 0 0 3px 1em; -} - -.submit-row a.deletelink { - margin-left: 0; - margin-right: auto; -} - -.vDateField, .vTimeField { - margin-left: 2px; -} - -.aligned .form-row input { - margin-left: 5px; -} - form .aligned ul { - margin-right: 163px; - padding-right: 10px; - margin-left: 0; - padding-left: 0; + margin: 0; } form ul.inline li { @@ -155,48 +134,10 @@ form ul.inline li { padding-left: 7px; } -form .aligned p.help, -form .aligned div.help { - margin-left: 0; - margin-right: 160px; - padding-right: 10px; -} - -form div.help ul, -form .aligned .checkbox-row + .help, -form .aligned p.date div.help.timezonewarning, -form .aligned p.datetime div.help.timezonewarning, -form .aligned p.time div.help.timezonewarning { - margin-right: 0; - padding-right: 0; -} - -form .wide p.help, -form .wide ul.errorlist, -form .wide div.help { - padding-left: 0; - padding-right: 50px; -} - .submit-row { text-align: right; } -fieldset .fieldBox { - margin-left: 20px; - margin-right: 0; -} - -.errorlist li { - background-position: 100% 12px; - padding: 0; -} - -.errornote { - background-position: 100% 12px; - padding: 10px 12px; -} - /* WIDGETS */ .calendarnav-previous { diff --git a/django/contrib/admin/static/admin/css/widgets.css b/django/contrib/admin/static/admin/css/widgets.css index c0de045c68..c2a3836b90 100644 --- a/django/contrib/admin/static/admin/css/widgets.css +++ b/django/contrib/admin/static/admin/css/widgets.css @@ -302,7 +302,8 @@ p.datetime { } p.datetime label { - display: inline; + display: block; + padding: 0.5rem 0; } .datetime span { @@ -313,7 +314,6 @@ p.datetime label { } .datetime input, .form-row .datetime input.vDateField, .form-row .datetime input.vTimeField { - margin-left: 5px; margin-bottom: 4px; } diff --git a/django/contrib/admin/static/admin/js/admin/DateTimeShortcuts.js b/django/contrib/admin/static/admin/js/admin/DateTimeShortcuts.js index 6251614863..b30a668a2e 100644 --- a/django/contrib/admin/static/admin/js/admin/DateTimeShortcuts.js +++ b/django/contrib/admin/static/admin/js/admin/DateTimeShortcuts.js @@ -68,7 +68,7 @@ } // Check if warning is already there. - if (inp.parentNode.querySelectorAll('.' + warningClass).length) { + if (inp.parentNode.parentNode.querySelectorAll('.' + warningClass).length) { return; } @@ -96,7 +96,12 @@ warning.classList.add('help', warningClass); warning.id = `${field_id}_timezone_warning_helptext`; warning.textContent = message; - inp.parentNode.appendChild(warning); + const errorList = inp.parentNode.parentNode.querySelector('ul.errorlist'); + if (errorList) { + errorList.before(warning); + } else { + inp.parentNode.before(warning); + } }, // Add clock widget to a given field addClock: function(inp) { diff --git a/django/contrib/admin/templates/admin/auth/user/change_password.html b/django/contrib/admin/templates/admin/auth/user/change_password.html index e8d3da82bd..70b68f6de5 100644 --- a/django/contrib/admin/templates/admin/auth/user/change_password.html +++ b/django/contrib/admin/templates/admin/auth/user/change_password.html @@ -39,29 +39,37 @@ <fieldset class="module aligned"> <div class="form-row"> - {{ form.usable_password.errors }} - <fieldset>{{ form.usable_password.legend_tag }} {{ form.usable_password }}</fieldset> - {% if form.usable_password.help_text %} - <div class="help"{% if form.usable_password.id_for_label %} id="{{ form.usable_password.id_for_label }}_helptext"{% endif %}> - <p>{{ form.usable_password.help_text|safe }}</p> - </div> - {% endif %} + <fieldset class="flex-container{% if form.usable_password.errors %} errors{% endif %}">{{ form.usable_password.legend_tag }} + {% if form.usable_password.help_text %} + <div class="help"{% if form.usable_password.id_for_label %} id="{{ form.usable_password.id_for_label }}_helptext"{% endif %}> + <p>{{ form.usable_password.help_text|safe }}</p> + </div> + {% endif %} + {{ form.usable_password.errors }} + {{ form.usable_password }} + </fieldset> </div> <div class="form-row field-password1"> - {{ form.password1.errors }} - <div class="flex-container">{{ form.password1.label_tag }} {{ form.password1 }}</div> + <div class="flex-container{% if form.password1.errors %} errors{% endif %}"> + {{ form.password1.label_tag }} {% if form.password1.help_text %} <div class="help"{% if form.password1.id_for_label %} id="{{ form.password1.id_for_label }}_helptext"{% endif %}>{{ form.password1.help_text|safe }}</div> {% endif %} + {{ form.password1.errors }} + {{ form.password1 }} + </div> </div> <div class="form-row field-password2"> - {{ form.password2.errors }} - <div class="flex-container">{{ form.password2.label_tag }} {{ form.password2 }}</div> + <div class="flex-container{% if form.password2.errors %} errors{% endif %}"> + {{ form.password2.label_tag }} {% if form.password2.help_text %} <div class="help"{% if form.password2.id_for_label %} id="{{ form.password2.id_for_label }}_helptext"{% endif %}>{{ form.password2.help_text|safe }}</div> {% endif %} + {{ form.password2.errors }} + {{ form.password2 }} + </div> </div> </fieldset> diff --git a/django/contrib/admin/templates/admin/includes/fieldset.html b/django/contrib/admin/templates/admin/includes/fieldset.html index 1fd303ea82..70c68655c5 100644 --- a/django/contrib/admin/templates/admin/includes/fieldset.html +++ b/django/contrib/admin/templates/admin/includes/fieldset.html @@ -8,33 +8,35 @@ <div class="description">{{ fieldset.description|safe }}</div> {% endif %} {% for line in fieldset %} - <div class="form-row{% if line.fields|length == 1 and line.errors %} errors{% endif %}{% if not line.has_visible_field %} hidden{% endif %}{% for field in line %}{% if field.field.name %} field-{{ field.field.name }}{% endif %}{% endfor %}"> - {% if line.fields|length == 1 %}{{ line.errors }}{% else %}<div class="flex-container form-multiline">{% endif %} + <div class="form-row{% if not line.fields|length == 1 %} flex-container form-multiline{% endif %}{% if not line.has_visible_field %} hidden{% endif %}{% for field in line %}{% if field.field.name %} field-{{ field.field.name }}{% endif %}{% endfor %}"> {% for field in line %} {% if field.is_fieldset %}<fieldset{% if field.field.help_text %} aria-describedby="{{ field.field.id_for_label }}_helptext"{% endif %}>{{ field.label_tag }}{% endif %} - <div> - {% if not line.fields|length == 1 and not field.is_readonly %}{{ field.errors }}{% endif %} - <div class="flex-container{% if not line.fields|length == 1 %} fieldBox{% if field.field.name %} field-{{ field.field.name }}{% endif %}{% if not field.is_readonly and field.errors %} errors{% endif %}{% if field.field.is_hidden %} hidden{% endif %}{% endif %}{% if field.is_checkbox %} checkbox-row{% endif %}"> - {% if field.is_checkbox %} - {{ field.field }}{% if not field.is_fieldset %}{{ field.label_tag }}{% endif %} - {% else %} - {% if not field.is_fieldset %}{{ field.label_tag }}{% endif %} - {% if field.is_readonly %} - <div class="readonly">{{ field.contents }}</div> - {% else %} - {{ field.field }} - {% endif %} - {% endif %} + <div class="flex-container{% if not field.is_readonly and field.errors or line.fields|length == 1 and line.errors %} errors{% endif %}{% if not line.fields|length == 1 %} fieldBox{% if field.field.name %} field-{{ field.field.name }}{% endif %}{% if field.field.is_hidden %} hidden{% endif %}{% endif %}{% if field.is_checkbox %} checkbox-row{% endif %}"> + {% if field.is_checkbox %} + <div class="checkbox"> + {{ field.field }} + {% if not field.is_fieldset %}{{ field.label_tag }}{% endif %} </div> + {% else %} + {% if not field.is_fieldset %}{{ field.label_tag }}{% endif %} + {% endif %} {% if field.field.help_text %} <div class="help{% if field.field.is_hidden %} hidden{% endif %}"{% if field.field.id_for_label %} id="{{ field.field.id_for_label }}_helptext"{% endif %}> <div>{{ field.field.help_text|safe }}</div> </div> {% endif %} + {% if line.fields|length == 1 %}{{ line.errors }}{% endif %} + {% if not line.fields|length == 1 and not field.is_readonly %}{{ field.errors }}{% endif %} + {% if not field.is_checkbox %} + {% if field.is_readonly %} + <div class="readonly">{{ field.contents }}</div> + {% else %} + {{ field.field }} + {% endif %} + {% endif %} </div> {% if field.is_fieldset %}</fieldset>{% endif %} {% endfor %} - {% if not line.fields|length == 1 %}</div>{% endif %} </div> {% endfor %} {% if fieldset.name and fieldset.is_collapsible %}</details>{% endif %} diff --git a/django/contrib/admin/templates/registration/password_change_form.html b/django/contrib/admin/templates/registration/password_change_form.html index d377c201ce..91c99c7fd1 100644 --- a/django/contrib/admin/templates/registration/password_change_form.html +++ b/django/contrib/admin/templates/registration/password_change_form.html @@ -31,27 +31,36 @@ <p>{% translate 'Please enter your old password, for security’s sake, and then enter your new password twice so we can verify you typed it in correctly.' %}</p> -<fieldset class="module aligned wide"> +<fieldset class="module aligned"> <div class="form-row"> - {{ form.old_password.errors }} - <div class="flex-container">{{ form.old_password.label_tag }} {{ form.old_password }}</div> + <div class="flex-container{% if form.old_password.errors %} errors{% endif %}"> + {{ form.old_password.label_tag }} + {{ form.old_password.errors }} + {{ form.old_password }} + </div> </div> <div class="form-row"> - {{ form.new_password1.errors }} - <div class="flex-container">{{ form.new_password1.label_tag }} {{ form.new_password1 }}</div> - {% if form.new_password1.help_text %} - <div class="help"{% if form.new_password1.id_for_label %} id="{{ form.new_password1.id_for_label }}_helptext"{% endif %}>{{ form.new_password1.help_text|safe }}</div> - {% endif %} + <div class="flex-container{% if form.new_password1.errors %} errors{% endif %}"> + {{ form.new_password1.label_tag }} + {% if form.new_password1.help_text %} + <div class="help"{% if form.new_password1.id_for_label %} id="{{ form.new_password1.id_for_label }}_helptext"{% endif %}>{{ form.new_password1.help_text|safe }}</div> + {% endif %} + {{ form.new_password1.errors }} + {{ form.new_password1 }} + </div> </div> <div class="form-row"> - {{ form.new_password2.errors }} - <div class="flex-container">{{ form.new_password2.label_tag }} {{ form.new_password2 }}</div> - {% if form.new_password2.help_text %} - <div class="help"{% if form.new_password2.id_for_label %} id="{{ form.new_password2.id_for_label }}_helptext"{% endif %}>{{ form.new_password2.help_text|safe }}</div> - {% endif %} + <div class="flex-container{% if form.new_password2.errors %} errors{% endif %}"> + {{ form.new_password2.label_tag }} + {% if form.new_password2.help_text %} + <div class="help"{% if form.new_password2.id_for_label %} id="{{ form.new_password2.id_for_label }}_helptext"{% endif %}>{{ form.new_password2.help_text|safe }}</div> + {% endif %} + {{ form.new_password2.errors }} + {{ form.new_password2 }} + </div> </div> </fieldset> diff --git a/django/contrib/admin/templates/registration/password_reset_confirm.html b/django/contrib/admin/templates/registration/password_reset_confirm.html index 2ad675da24..ffe51b59f8 100644 --- a/django/contrib/admin/templates/registration/password_reset_confirm.html +++ b/django/contrib/admin/templates/registration/password_reset_confirm.html @@ -20,16 +20,16 @@ <fieldset class="module aligned"> <input class="hidden" autocomplete="username" value="{{ form.user.get_username }}"> <div class="form-row field-password1"> - {{ form.new_password1.errors }} - <div class="flex-container"> + <div class="flex-container{% if form.new_password1.errors %} errors{% endif %}"> <label for="id_new_password1">{% translate 'New password:' %}</label> + {{ form.new_password1.errors }} {{ form.new_password1 }} </div> </div> <div class="form-row field-password2"> - {{ form.new_password2.errors }} - <div class="flex-container"> + <div class="flex-container{% if form.new_password2.errors %} errors{% endif %}"> <label for="id_new_password2">{% translate 'Confirm password:' %}</label> + {{ form.new_password2.errors }} {{ form.new_password2 }} </div> </div> diff --git a/django/contrib/admin/templates/registration/password_reset_form.html b/django/contrib/admin/templates/registration/password_reset_form.html index 3737414d81..31c84fdcc7 100644 --- a/django/contrib/admin/templates/registration/password_reset_form.html +++ b/django/contrib/admin/templates/registration/password_reset_form.html @@ -17,9 +17,9 @@ <form method="post">{% csrf_token %} <fieldset class="module aligned"> <div class="form-row field-email"> - {{ form.email.errors }} <div class="flex-container"> <label for="id_email">{% translate 'Email address:' %}</label> + {{ form.email.errors }} {{ form.email }} </div> </div> diff --git a/django/contrib/auth/forms.py b/django/contrib/auth/forms.py index aff0cca342..254a135ec1 100644 --- a/django/contrib/auth/forms.py +++ b/django/contrib/auth/forms.py @@ -158,7 +158,7 @@ class SetUnusablePasswordMixin: required=False, initial="true", choices={"true": _("Enabled"), "false": _("Disabled")}, - widget=forms.RadioSelect(attrs={"class": "radiolist inline"}), + widget=forms.RadioSelect(attrs={"class": "radiolist"}), help_text=help_text, ) |
