1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391
|
/* Form layout */
@import "./variables.scss";
:root {
// CSS variable to define the number of (label + control) columns.
// It dynamically changes on narrow pages (see media query below).
--ct-form-columns: 2;
}
// Cockpit Form Layout: Automatically have Cockpit display your form in
// an optimal layout.
//
// By default, all labels are aligned and sized properly and form elements
// stretch to take up the remaining space.
//
//
// There are additional classes and attributes you can add to each
// control directly under `ct-form`:
//
// `ct-form-relax`: Form elements normally stretch to take up the
// full space. You can relax their width by adding this class to the
// control. Inputs with a size attribute are auto-relaxed and do not
// need this class.
//
// `ct-form-stretch`: If a control has a width specified
// elsewhere, you can force it to stretch. This is mainly useful when
// using <div role="group"> to group elements.
//
// `ct-form-full`: Force a widget to be the full width of the form,
// invading the label space.
//
// role="group": When there are two related elements, such as a text
// input and a dropdown, you can group them together using this HTML
// attribute. It's similar in purpose to a <fieldset>, but works for
// layouts in Chrome (unlike fieldset). This can be attached to any
// container element, but will most likely be used with <div>. The role
// adds semantic meaning to the element for screen readers, and we key
// the CSS off of the role.
//
// `ct-form-box`: Visual styling for encapsulating a block of sub-options.
// Creates a gray box around elements.
//
// <hr>: While this is an element, it has a special meaning and is used
// to add some vertical spacing to a form.
//
//
// Alternate grid sizing:
// You can override division of space for controls by adding a class
// at grid level (.ct-form) to adjust size for "split" widgets:
// `ct-form-maxmin: First widget is wide; second is small.
// `ct-form-minmax`: First widget is small; second is wide.
//
//
// Most of the time, you can simply ignore all the optional classes (and
// attribute and hr element) and simply wrap your labels & controls in
// a <form class="ct-form"> and layout magic happens.
.ct-form {
// Locally redefine padding to Bootstrap values for this SASS block
--padding-y: var(--pf-global--spacer--md);
--padding-x: var(--pf-global--spacer--lg);
// Bootstrap & PatternFly use a 1px border around widgets
--border-width: 1px;
--widget-height: 2.25rem; // (36px for PF4 widgets)
align-self: start; // Don't vertically fill content by default
display: grid;
grid-gap: var(--padding-y) var(--padding-x);
// Repeat a label that is a minimum of 4em and its control that
// fills the remaining space by a CSS variable (default: 2)
grid-template-columns: repeat(var(--ct-form-columns), max-content 1fr);
justify-items: stretch;
align-content: baseline;
// All <label> elements describing form elements in PatternFly are
// supposed to have a `control-label` class (PF3) or `pf-c-form__label` (PF4).
// These precede control elements.
> .control-label,
> .pf-c-form__label {
padding: 0;
margin: 0;
text-align: left;
font-weight: var(--pf-global--FontWeight--bold);
font-size: var(--pf-global--FontSize--sm);
}
> :not(hr):not(p):not(.pf-c-form-control) {
line-height: var(--widget-height);
}
> p {
margin: 0;
}
// Put all control elements to the right of the labels,
// stretching to the rightmost column
> :not(.control-label):not(.pf-c-form__label):not(hr):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;
grid-column: 1 / -1;
height: 0;
// 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 SASS mixin that will not be in the compiled CSS.
@mixin widget-rules() {
> input,
> textarea,
> select,
> .bootstrap-select,
> .ct-select,
> .dropdown,
> .combobox-container,
> fieldset,
> [role=group],
> [data-field],
> .form-group,
> .btn-group,
> .pf-c-form__group-control,
> label.checkbox,
> label.radio,
> label.pf-c-radio,
> label.pf-c-check,
> .checkbox-inline,
> .radio-inline {
line-height: var(--pf-global--LineHeight--md);
}
}
&, > .ct-validation-wrapper {
@include 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:
// <div role="group">
//
// And non-div elements are also supported.
> [role=group],
> .ct-validation-wrapper > [role=group] {
align-self: start;
align-content: center;
display: grid;
grid-gap: var(--padding-y);
min-height: var(--widget-height);
justify-content: start;
// Only support 2 splits for now (can change to 3 later, if needed)
grid-template-columns: repeat(2, auto);
> .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;
}
// <select>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: var(--padding-x);
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: var(--padding-y);
}
// Relax split elements to only take up one column
// Stretch to full width
> .ct-form-full {
grid-column: 1 / -1;
}
// Move warnings, errors, info, etc. up a bit to associate with previous field
> .help-block {
position: relative;
margin-top: -0.5rem;
}
.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(--pf-global--palette--black-200);
border-width: 1px;
border-style: solid;
border-color: var(--pf-global--palette--black-400);
padding: 0.5rem 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;
}
@mixin deconstruct() {
// Only use one column
--ct-form-columns: 1;
// Don't set the line height for widgets;
// setting this to an invalid CSS rule invalidates it, so it falls back
--widget-height: not-needed;
// 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,
> .pf-c-form__label {
margin: 0 0 -.5rem;
padding: 1rem 0 0;
text-align: left;
}
}
@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 {
@include deconstruct();
}
}
}
// 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;
}
// Alternate to reduce spacing, used at ct-form grid-level
.ct-form-compact {
--padding-y: var(--pf-global--spacer--xs);
}
// Compact form used for info tables
.ct-form-info {
@extend .ct-form-compact;
// Invalidate widget height, to fall back to normal line height
--widget-height: invalidate-this;
> .control-label,
> .pf-c-form__label {
font-weight: normal;
}
}
@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 {
@include deconstruct();
}
}
}
|