Skip to content

Commit f6248a4

Browse files
committed
fix: rewrite field alignment with CSS Grid subgrid
The flexbox margin-top:auto approach fundamentally cannot sync positions across separate flex containers. Replace with CSS Grid + subgrid which shares three row tracks (label | input | help text) across all columns in a .fields-aligned-row. Labels, inputs, and help text now align perfectly regardless of label wrapping or help text presence. Bump version to 0.63.6.
1 parent e8ca893 commit f6248a4

3 files changed

Lines changed: 57 additions & 37 deletions

File tree

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [0.63.6] - 2026-04-02
11+
12+
### Fixed
13+
- **Side-by-side field alignment rewritten with CSS Grid + subgrid**
14+
replaced the flexbox `margin-top: auto` approach (which could never sync
15+
positions across separate flex containers) with a CSS Grid layout that
16+
uses `subgrid` to share three row tracks (label | input | help text)
17+
across all columns. Labels, inputs, and help text now align perfectly
18+
regardless of label wrapping or the presence/absence of help text in
19+
any column.
20+
1021
## [0.63.5] - 2026-04-02
1122

1223
### Fixed

django_forms_workflows/static/django_forms_workflows/css/forms.css

Lines changed: 45 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2,45 +2,54 @@
22
forms.css — Alignment, spacing & responsive polish for django-forms-workflows
33
═══════════════════════════════════════════════════════════════════════════ */
44

5-
/* ── Side-by-side field alignment ────────────────────────────────────────
6-
When two or more fields sit in a .row.fields-aligned-row, every column
7-
becomes a flex column so .mb-3 can stretch to fill it. The input/select
8-
uses margin-top:auto to push itself to the bottom of the label area,
9-
keeping inputs horizontally aligned regardless of label wrapping. */
10-
11-
/* 1. Make each column a flex column so .mb-3 can stretch */
12-
.row.fields-aligned-row > [class*="col-"],
13-
.row.fields-aligned-row > .field-wrapper {
14-
display: flex;
15-
flex-direction: column;
16-
}
17-
18-
/* 2. .mb-3 fills the column and is itself a flex column */
19-
.row.fields-aligned-row > [class*="col-"] > .mb-3,
20-
.row.fields-aligned-row > .field-wrapper > .mb-3 {
21-
display: flex;
22-
flex-direction: column;
23-
flex: 1 1 auto;
24-
}
5+
/* ── Side-by-side field alignment (CSS Grid + subgrid) ─────────────────
6+
When two or more fields sit in a .row.fields-aligned-row we switch from
7+
Bootstrap's flexbox row to a CSS Grid so that labels, inputs and help
8+
text each share a common row track across columns. This guarantees
9+
perfect horizontal alignment regardless of label wrapping or the
10+
presence / absence of help text in any column.
11+
12+
Grid row tracks: 1 = label | 2 = input | 3 = help text / errors
13+
──────────────────────────────────────────────────────────────────────── */
14+
15+
/* 1. The row becomes a CSS Grid; columns flow automatically */
16+
@media (min-width: 768px) {
17+
.row.fields-aligned-row {
18+
display: grid !important;
19+
grid-auto-flow: column;
20+
grid-auto-columns: 1fr;
21+
grid-template-rows: auto auto auto;
22+
column-gap: var(--bs-gutter-x, 1.5rem);
23+
margin-left: 0;
24+
margin-right: 0;
25+
}
2526

26-
/* 3. Labels stay at their natural size */
27-
.row.fields-aligned-row > [class*="col-"] > .mb-3 > label,
28-
.row.fields-aligned-row > .field-wrapper > .mb-3 > label {
29-
flex-shrink: 0;
30-
}
27+
/* 2. Each field-wrapper spans all 3 row tracks and inherits them */
28+
.row.fields-aligned-row > .field-wrapper {
29+
display: grid;
30+
grid-template-rows: subgrid;
31+
grid-row: span 3;
32+
/* Reset Bootstrap column flex/width — grid handles sizing */
33+
width: auto !important;
34+
max-width: none !important;
35+
flex: none !important;
36+
padding-left: 0;
37+
padding-right: 0;
38+
}
3139

32-
/* 4. Push the input/select to the bottom of the label area */
33-
.row.fields-aligned-row > [class*="col-"] > .mb-3 > .form-control,
34-
.row.fields-aligned-row > [class*="col-"] > .mb-3 > .form-select,
35-
.row.fields-aligned-row > .field-wrapper > .mb-3 > .form-control,
36-
.row.fields-aligned-row > .field-wrapper > .mb-3 > .form-select {
37-
margin-top: auto;
38-
}
40+
/* 3. .mb-3 also inherits the 3 row tracks via subgrid */
41+
.row.fields-aligned-row > .field-wrapper > .mb-3 {
42+
display: grid;
43+
grid-template-rows: subgrid;
44+
grid-row: span 3;
45+
margin-bottom: 0 !important; /* grid gap handles spacing */
46+
}
3947

40-
/* 5. Help text sits after the input at its natural size */
41-
.row.fields-aligned-row > [class*="col-"] > .mb-3 > .form-text,
42-
.row.fields-aligned-row > .field-wrapper > .mb-3 > .form-text {
43-
flex-shrink: 0;
48+
/* 4. Labels sit at the bottom of their row-track so short labels
49+
align with the bottom of taller (wrapping) labels. */
50+
.row.fields-aligned-row > .field-wrapper > .mb-3 > label {
51+
align-self: end;
52+
}
4453
}
4554

4655
/* ── Consistent field spacing ────────────────────────────────────────── */

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "django-forms-workflows"
3-
version = "0.63.5"
3+
version = "0.63.6"
44
description = "Enterprise-grade, database-driven form builder with approval workflows and external data integration"
55
license = "LGPL-3.0-only"
66
readme = "README.md"

0 commit comments

Comments
 (0)