Summary
Since 5.3.0, the FormSelect enhancement wraps every select.form-select in a
.custom-select-wrapper and renders a .custom-select-display combobox in its place. The
accompanying CSS handles several layout contexts explicitly, but not Bootstrap's
.input-group — so any input group containing a select renders broken: the group grows 1rem
taller than its control, sibling addons and buttons stretch out of alignment, and the select no
longer fills the group.
Still present in 5.6.0.
Environment
- LeptonX 5.3.0, 5.4.0, 5.4.1, 5.5.0, 5.6.0 (checked each stable release — identical)
- ABP 10.3 → 10.6, Blazor Server; the same CSS ships in the MVC theme
- All menu variants (
side-menu,top-menu,hybrid-menu,public-website) and all flavors (bootstrap-dim.css,bootstrap-dark.css,bootstrap-light.css+.rtlvariants)
Reproduction
<div class="input-group input-group-sm" style="width: 220px">
<select class="form-select form-select-sm">
<option>Option A</option>
<option>Option B</option>
</select>
<span class="input-group-text">i</span>
</div>
Expected: control and addon the same height and vertically aligned; control fills the remaining width.
Actual: the group is 1rem taller than the control, the addon stretches to the full group height while the control sits at the top, and the control does not fill the group.
Measured on identical markup before and after the upgrade:
| | LeptonX 4.x | LeptonX 5.3+ |
|---|---|---|
| .input-group height | 24px | 40px |
| rendered control | select 90×24, flex: 1 1 auto | .custom-select-display 61×24 inside a display: block wrapper |
| .input-group-text height | 24px | 40px (stretched) |
| enclosing table row height | 38px | 42px |
Cause
Two rules, from the bootstrap-*.css files in
Volo.Abp.AspNetCore.Components.Web.LeptonXTheme (and the equivalents in
Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonX):
.custom-select-wrapper { position: relative; margin-bottom: 1rem; }
.custom-select-wrapper > select.form-select { display: none; }
.input-groupis a flex row, so the wrapper'smargin-bottom: 1remis added to the row. The group grows by 16px and the sibling.input-group-text/.btnchildren stretch to the new height while the wrapper does not..custom-select-wrapperis now the flex child, so Bootstrap's.input-group > .form-select { flex: 1 1 auto; width: 1% }no longer matches anything and the control does not fill the group.
There is precedent for handling this per context — the CSS already special-cases DataTables, validation state and floating labels:
div.dt-container div.dt-length .custom-select-wrapper {
display: inline-block; margin-bottom: 0; width: auto; vertical-align: middle;
}
.custom-select-wrapper.is-invalid > .custom-select-display { border-color: #c00d49 !important; }
.form-floating .custom-select-display { height: calc(3.5rem + 2px); ... }
but there is no equivalent for .input-group.
Secondary problem: sizing set on the native select is lost
Because the native <select> is hidden, anything applied to it no longer affects the rendered
control:
form-select-sm/form-select-lgare not propagated to.custom-select-display, so a small select renders at default size.- Inline styles on the select (e.g.
style="max-width: 92px") are lost entirely.
This is most visible with component libraries that render the <select> themselves — with
Blazorise, <Select Size="Size.Small" Style="max-width: 92px" /> loses both.
There also appears to be no way to opt out of the enhancement, per element or globally.
Suggested fix
Add .input-group handling alongside the existing special cases:
.input-group > .custom-select-wrapper {
flex: 1 1 auto;
width: 1%;
min-width: 0;
margin-bottom: 0;
}
Ideally also propagate the native select's size class to .custom-select-display, and honor its
inline width constraints. An opt-out hook (a class or data- attribute that FormSelect.init
skips) would help applications that render selects through a component library.
Workaround
For anyone else hitting this — app CSS that restores the native select:
.custom-select-wrapper { display: contents; }
.custom-select-wrapper > select.form-select { display: block !important; }
body .custom-select-wrapper > .custom-select-display,
body .custom-select-wrapper > .custom-options-container { display: none !important; }
/* `display: contents` makes the native select a flex item of the input group again, but
Bootstrap's `.input-group > .form-select` selector still cannot match it. */
.input-group > .custom-select-wrapper > select.form-select {
flex: 1 1 auto;
width: 1%;
min-width: 0;
}
The body prefix on the hide rule is necessary: the theme declares
.custom-select-wrapper > .custom-select-display { display: flex !important }, so at equal
specificity the theme's stylesheet wins if it loads later and the combobox renders alongside the
restored native select.
4 Answer(s)
-
0
- Add a small LeptonX override in your app stylesheet to special-case Bootstrap
.input-group(this fixes both the extra 1rem height and the lost flex sizing caused by.custom-select-wrapperbecoming the flex item):
/* LeptonX 5.3+ FormSelect inside Bootstrap input-group fix */ .input-group > .custom-select-wrapper { flex: 1 1 auto; width: 1%; min-width: 0; /* prevent the wrapper's default spacing from increasing the flex row height */ margin-bottom: 0; } /* optional: make the display fill the wrapper */ .input-group > .custom-select-wrapper > .custom-select-display { width: 100%; }- If you also need sm/lg sizing to match
form-select-sm/form-select-lg, add explicit sizing rules for the generated display (because LeptonX hides the native<select>and the size classes don’t affect the visible element):
/* propagate Bootstrap select sizing to LeptonX display */ .custom-select-wrapper > select.form-select-sm + .custom-select-display { padding-top: .25rem; padding-bottom: .25rem; font-size: .875rem; } .custom-select-wrapper > select.form-select-lg + .custom-select-display { padding-top: .5rem; padding-bottom: .5rem; font-size: 1.25rem; }- If you need to honor inline width constraints like
style="max-width: 92px"applied on the native<select>, CSS alone can’t reliably “copy” inline styles to the generated.custom-select-display. In that case the only deterministic workaround is what you already discovered: restore the native select (or write a tiny JS hook that copies computed width/max-width from the<select>to.custom-select-displayafter LeptonX initializes).
No verified source available for this specific
.input-group+FormSelectwrapper issue/fix (it’s a theme CSS gap), but the override approach is consistent with ABP’s recommended UI overriding mechanism.- https://abp.io/docs/10.3/framework/architecture/modularity/extending/overriding-user-interface
This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.
Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post) - Add a small LeptonX override in your app stylesheet to special-case Bootstrap
-
0
Let me know if this is the right place to submit these types of bugs.
Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post) -
0
Thanks — the workaround is understood; we already landed the equivalent on our side, and I agree that restoring the native select is the only deterministic option once inline widths on the
<select>are involved.To be clear about the ask, though: this is a report of a gap in the shipped theme CSS, not a request for an app-side workaround. Can it be tracked as a theme bug?
.custom-select-wrapper { margin-bottom: 1rem }with no.input-groupcounterpart means every Bootstrap input group containing a select renders misaligned out of the box, in an unmodified template. The CSS already special-casesdiv.dt-container div.dt-length,.is-invalid/.is-validand.form-floating, so.input-grouplooks like an omission rather than a deliberate limitation. It is unchanged across 5.3.0, 5.4.0, 5.4.1, 5.5.0 and 5.6.0.Two things would close it properly:
- Ship the
.input-group > .custom-select-wrapperrule in the theme, rather than having every application add it. - An opt-out hook — a class or
data-attribute thatFormSelect.initskips — for applications that render selects through a component library (Blazorise, etc.) and need the native element's own classes and inline styles to apply.
Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post) - Ship the
-
0
Hi,
Yes, this is the right place for these reports — thanks for the detailed analysis.
You're right, this is a gap in the theme CSS: there is no rule integrating
.custom-select-wrapperinto.input-group, unlike the existing DataTables / validation /.form-floatingspecial cases. We'll fix the custom select's.input-groupintegration in the theme (both the Blazor and MVC packages) in the next patch release, and we'll evaluate the opt-out hook forFormSelect.inittogether with it, since component-library scenarios like Blazorise need the native element.Until the patch is out, this app-side CSS keeps the custom select and restores the group layout:
.input-group > .custom-select-wrapper { flex: 1 1 auto; width: 1%; min-width: 0; margin-bottom: 0; } /* match Bootstrap's corner flattening for grouped controls */ .input-group > .custom-select-wrapper:not(:first-child) > .custom-select-display { border-top-left-radius: 0; border-bottom-left-radius: 0; } .input-group > .custom-select-wrapper:not(:last-child) > .custom-select-display { border-top-right-radius: 0; border-bottom-right-radius: 0; }Your native-select restore is still the right call where inline widths on the
<select>matter, so feel free to keep that instead.One correction on the sizing point: the
.input-groupissue is still present in 5.6.0, but the separateform-select-sm/form-select-lgissue was fixed there — the generated display is now styled based on those classes. Inline styles on the native select are still not transferred; an opt-out hook would let those selects stay native and keep their own styles.Your ticket has been refunded.
Thanks
Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)