elements describing form elements in PatternFly are
// supposed to have a `control-label` class.
// These precede control elements.
> .control-label {
text-align: right;
padding: 0;
margin: 0;
}
> :not(hr):not(p) {
line-height: 2.25rem;
}
> p {
margin: 0;
}
// Put all control elements to the right of the labels,
// stretching to the rightmost column
> :not(.control-label):not(hr):not(.ct-form-split):not(.ct-form-full) {
grid-column: ~"2 / -1";
}
// Auto-stretch elements to the grid (except when relaxed)
> :not(.ct-form-relax):not(.spinner) {
width: auto;
}
// Horizontal rules directly under a form-layout container serve to
// add some vertical space in forms. This is useful for visually
// grouping similar elements with whitespace.
//
// It's not the same as actually grouping elements (which can be done
// in the usual ways as well as adding a role="group".
> hr {
border: none;
// LESS needs this to be escaped with ~"". You'll see it below too.
// CSS wants the string to be 1 / -1 without escaping.
grid-column: ~"1 / -1";
height: 0.5rem;
// Reset padding to ensure all browsers treat this the same
margin: 0;
padding: 0;
}
// Auto-relax inputs with size
> input[size],
> .ct-validation-wrapper > input[size] {
justify-self: start;
}
> .ct-validation-wrapper {
display: flex;
flex-direction: column;
}
// Hack to allow number inputs to be sized on WebKit-based browsers
input[type=number] {
-webkit-appearance: textarea;
}
// Special considerations for widgets (and widget-like elements)
// This is a LESS mixin that will not be in the compiled CSS.
.widget-rules() {
> input,
> textarea,
> select,
> .bootstrap-select,
> .ct-select,
> .dropdown,
> .combobox-container,
> fieldset,
> [role=group],
> [data-field],
> .form-group,
> .btn-group,
> label.checkbox,
> label.radio,
> .checkbox-inline,
> .radio-inline {
line-height: var(--pf-global--LineHeight--md);
}
}
&, > .ct-validation-wrapper {
.widget-rules();
}
// Some elements need special width considerations
// as PatternFly normally fixes the width
> :not(.ct-form-relax):not(.spinner) {
width: auto !important;
}
// Elements with role="group" are used to group elements —
// fieldset was going to be used, but Chrome doesn't allow
// grid or flex placement for fieldsets (yet).
//
// Adding a group role is the same thing accessibilty-wise
// and lets us target all browsers properly.
//
// You can use this like:
//
//
// And non-div elements are also supported.
> [role=group],
> .ct-validation-wrapper > [role=group] {
align-self: start;
align-content: center;
display: grid;
grid-gap: @padding-small-vertical;
min-height: 2.25rem;
justify-content: start;
// Only support 3 splits for now (can change to 3 later, if needed)
grid-template-columns: repeat(3, auto);
&.ct-form-vertical {
> :not(.ct-form-split) {
// Stretch across the grid (unless it's a split)
grid-column: ~"1 / -1";
}
}
> .checkbox,
> .radio {
// Spacing is handled by grid, not margin
margin: 0;
&:first-child {
margin-top: 0.5rem;
}
}
}
> [role=group],
> .ct-validation-wrapper > [role=group],
> .ct-validation-wrapper > [data-field] {
// Allow dropdowns to expand as needed
&:not(.ct-form-relax) {
> .dropdown {
width: auto !important;
}
// s need to be coaxed to be 100%
> .ct-select {
width: 100%;
}
}
}
// Vertically align checkboxes and radios properly using flex
label.checkbox,
label.radio,
.checkbox > label,
.radio > label,
.checkbox-inline,
.radio-inline {
display: inline-flex;
padding-left: 0;
padding-right: @padding-small-horizontal;
align-items: center;
> input[type="checkbox"],
> input[type="radio"] {
margin: 0 0.5em 0 0;
position: static;
}
}
// Remove vertical spacing for fieldsets,
// as this is handled by the grid gap
fieldset {
> .checkbox,
> .radio {
&:first-child {
margin-top: 0;
}
&:last-child {
margin-bottom: 0;
}
}
}
// List groups override the grid gap, so we're adding it manually
.list-group {
margin-bottom: @padding-small-vertical;
}
// Relax split elements to only take up one column
> .ct-form-split {
grid-column: ~"auto / auto";
}
// Stretch to full width
> .ct-form-full {
grid-column: ~"1 / -1";
}
// Move warnings, errors, info, etc. up a bit to associate with previous field
.bump-up() {
position: relative;
margin-top: -0.5rem;
}
> .has-success,
> .has-warning,
> .has-error {
&:not(.form-group):not(fieldset):not([role=group]) {
//.bump-up();
}
}
> .help-block {
.bump-up();
}
.help-block {
--help-line-height: calc(var(--pf-global--LineHeight--md) * 1rem);
line-height: var(--help-line-height);
&:empty {
display: none;
}
.spinner {
position: relative;
// (baseline - height - border) / 2
top: calc((var(--help-line-height) - 16px - 2px) / 2);
}
}
.ct-form-box {
background: var(--color-gray-1);
border-width: 1px;
border-style: solid;
border-color: var(--color-gray-5);
padding: 0.5rem;
padding-top: 1rem;
width: 100%;
}
}
// Force a form element to stretch. Add as a class to `form-control`.
.ct-form-stretch {
justify-content: stretch !important;
}
// Instruct a `form-control` to not stretch.
.ct-form-relax {
justify-self: start;
}
// Reset .ct-form-split for small dialogs, as they don't have
// much width. This allows for using the same HTML layout in both
// narrow and normal dialogs.
.modal-dialog.modal-sm .ct-form > .ct-form-split {
grid-column: ~"2 / -1";
}
@media (max-width: 640px) {
// When inside of lists or modals & the page isn't wide enough,
// collapse (label + control) columns down to 1, to force splits on
// their own lines
.listing-ct-body,
.modal {
--ct-form-columns: 1;
}
}
// Alternate layout, for a split, used at ct-form grid-level:
// First form widget is as small as possible;
// Second takes up the rest of the space
.ct-form-minmax {
grid-template-columns: max-content min-content max-content 1fr;
}
// Alternate layout, for a split, used at ct-form grid-level:
// First form widget takes up as much space as it can;
// Second form widget is as small as possible
.ct-form-maxmin {
grid-template-columns: max-content 1fr max-content min-content;
}
@media (max-width: @screen-xs) {
// When inside of lists or modals & the page is *very* narrow,
// collapse the grid further, so labels are above controls
//
// Note: Padding variables below are outside the local scope of the
// .ct-form block, so they default to the global PatternFly
// values.
.listing-ct-body,
.modal {
.ct-form {
// Completely deconstruct the grid layout
grid-template-columns: initial;
> * {
// Don't restrict grid placement
grid-column: auto;
max-width: 100%;
}
// As control labels fill the row, left align and remove padding
> .control-label {
padding: 0;
text-align: left;
// Everything but the first label should have space to breathe
&:not(:first-child) {
margin: @padding-large-vertical 0 0;
}
}
// Reduce vertical height spacing between groups of elements
> hr {
height: @padding-large-vertical * 2;
}
}
}
}