Skip to content

Commit 0f4816f

Browse files
authored
experiment: add form-layout base styles and visual tests (#9743)
1 parent fabaed5 commit 0f4816f

File tree

51 files changed

+1065
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+1065
-0
lines changed

packages/form-layout/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
"type": "module",
2222
"files": [
2323
"src",
24+
"!src/styles/*-base-styles.d.ts",
25+
"!src/styles/*-base-styles.js",
2426
"theme",
2527
"vaadin-*.d.ts",
2628
"vaadin-*.js",
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/**
2+
* @license
3+
* Copyright (c) 2018 - 2025 Vaadin Ltd.
4+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5+
*/
6+
import type { CSSResult } from 'lit';
7+
8+
export const formItemStyles: CSSResult;
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/**
2+
* @license
3+
* Copyright (c) 2018 - 2025 Vaadin Ltd.
4+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5+
*/
6+
import '@vaadin/component-base/src/style-props.js';
7+
import { css } from 'lit';
8+
9+
export const formItemStyles = css`
10+
:host {
11+
/* By default, when auto-responsive mode is disabled, labels should be displayed beside the fields. */
12+
--_form-item-labels-above: ' '; /* false */
13+
--_form-item-labels-aside: initial; /* true */
14+
15+
align-items: var(--_form-item-labels-aside, baseline);
16+
display: inline-flex;
17+
flex-flow: var(--_form-item-labels-above, column) nowrap;
18+
justify-self: stretch;
19+
margin: calc(0.5 * var(--vaadin-form-item-row-spacing, var(--vaadin-form-layout-row-spacing, 1em))) 0;
20+
}
21+
22+
:host([label-position='top']) {
23+
--_form-item-labels-above: initial; /* true */
24+
--_form-item-labels-aside: ' '; /* false */
25+
}
26+
27+
:host([hidden]) {
28+
display: none !important;
29+
}
30+
31+
[part='label'] {
32+
color: var(--vaadin-form-item-label-color, var(--vaadin-color));
33+
flex: 0 0 auto;
34+
font-size: var(--vaadin-form-item-label-font-size, inherit);
35+
font-weight: var(--vaadin-form-item-label-font-weight, 500);
36+
line-height: var(--vaadin-form-item-label-line-height, inherit);
37+
width: var(
38+
--_form-item-labels-aside,
39+
var(--vaadin-form-item-label-width, var(--vaadin-form-layout-label-width, 8em))
40+
);
41+
word-break: break-word;
42+
}
43+
44+
#spacing {
45+
flex: 0 0 auto;
46+
width: var(--vaadin-form-item-label-spacing, var(--vaadin-form-layout-label-spacing, 1em));
47+
}
48+
49+
#content {
50+
flex: 1 1 auto;
51+
min-width: 0;
52+
}
53+
54+
#content ::slotted(.full-width) {
55+
box-sizing: border-box;
56+
min-width: 0;
57+
width: 100%;
58+
}
59+
`;
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/**
2+
* @license
3+
* Copyright (c) 2018 - 2025 Vaadin Ltd.
4+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5+
*/
6+
import type { CSSResult } from 'lit';
7+
8+
export const formLayoutStyles: CSSResult;
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
/**
2+
* @license
3+
* Copyright (c) 2018 - 2025 Vaadin Ltd.
4+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5+
*/
6+
import '@vaadin/component-base/src/style-props.js';
7+
import { css } from 'lit';
8+
9+
export const formLayoutStyles = css`
10+
:host {
11+
/* Default values */
12+
--vaadin-form-layout-label-spacing: var(--vaadin-gap-container-inline);
13+
--vaadin-form-layout-label-width: 8em;
14+
--vaadin-form-layout-column-spacing: calc(var(--vaadin-gap-container-inline) * 2);
15+
--vaadin-form-layout-row-spacing: calc(var(--vaadin-gap-container-block) * 2);
16+
17+
align-self: stretch;
18+
display: block;
19+
max-width: 100%;
20+
}
21+
22+
:host([hidden]) {
23+
display: none !important;
24+
}
25+
26+
:host(:not([auto-responsive])) #layout {
27+
align-items: baseline; /* default \`stretch\` is not appropriate */
28+
display: flex;
29+
flex-wrap: wrap; /* the items should wrap */
30+
}
31+
32+
:host(:not([auto-responsive])) #layout ::slotted(*) {
33+
/* Items should neither grow nor shrink. */
34+
flex-grow: 0;
35+
flex-shrink: 0;
36+
37+
/* Margins make spacing between the columns */
38+
margin-inline: calc(0.5 * var(--vaadin-form-layout-column-spacing));
39+
}
40+
41+
#layout ::slotted(br) {
42+
display: none;
43+
}
44+
45+
:host([auto-responsive]) {
46+
/* Column width */
47+
--_column-width: var(--vaadin-field-default-width, 12em);
48+
--_column-width-labels-above: var(--_column-width);
49+
--_column-width-labels-aside: calc(
50+
var(--_column-width) + var(--vaadin-form-layout-label-width) + var(--vaadin-form-layout-label-spacing)
51+
);
52+
53+
/* Column gap */
54+
--_min-total-gap: calc((var(--_min-columns) - 1) * var(--vaadin-form-layout-column-spacing));
55+
--_max-total-gap: calc((var(--_max-columns) - 1) * var(--vaadin-form-layout-column-spacing));
56+
57+
/* Minimum form layout width */
58+
--_min-width-labels-above: calc(var(--_min-columns) * var(--_column-width-labels-above) + var(--_min-total-gap));
59+
--_min-width-labels-aside: calc(var(--_min-columns) * var(--_column-width-labels-aside) + var(--_min-total-gap));
60+
--_min-width: var(--_min-width-labels-above);
61+
62+
/* Maximum form layout width */
63+
--_max-width-labels-above: calc(var(--_max-columns) * var(--_column-width-labels-above) + var(--_max-total-gap));
64+
--_max-width-labels-aside: calc(var(--_max-columns) * var(--_column-width-labels-aside) + var(--_max-total-gap));
65+
--_max-width: var(--_max-width-labels-above);
66+
67+
display: flex;
68+
min-width: var(--_min-width);
69+
}
70+
71+
:host([auto-responsive]) #layout {
72+
/* By default, labels should be displayed above the fields */
73+
--_form-item-labels-above: initial; /* true */
74+
--_form-item-labels-aside: ' '; /* false */
75+
76+
/* CSS grid related properties */
77+
--_grid-column-width: var(--_column-width-labels-above);
78+
--_grid-repeat: var(--_grid-column-width);
79+
80+
display: grid;
81+
gap: var(--vaadin-form-layout-row-spacing) var(--vaadin-form-layout-column-spacing);
82+
83+
/*
84+
Auto-columns can be created when an item's colspan exceeds the rendered column count.
85+
By setting auto-columns to 0, we exclude these columns from --_grid-rendered-column-count,
86+
which is then used to cap the colspan.
87+
*/
88+
grid-auto-columns: 0;
89+
90+
grid-template-columns: repeat(auto-fill, var(--_grid-repeat));
91+
justify-items: start;
92+
93+
/*
94+
Firefox requires min-width on both :host and #layout to allow the layout
95+
to shrink below the value specified in the CSS width property above.
96+
*/
97+
min-width: var(--_min-width);
98+
99+
/*
100+
To prevent the layout from exceeding the column limit defined by --_max-columns,
101+
its width needs to be constrained:
102+
103+
1. "width" is used instead of "max-width" because, together with the default "flex: 0 1 auto",
104+
it allows the layout to shrink to its minimum width inside <vaadin-horizontal-layout>, which
105+
wouldn't work otherwise.
106+
107+
2. "width" is used instead of "flex-basis" to make the layout expand to the maximum
108+
number of columns inside <vaadin-overlay>, which creates a new stacking context
109+
without a predefined width.
110+
*/
111+
width: var(--_max-width);
112+
}
113+
114+
:host([auto-responsive]) #layout::before {
115+
background-position-y: var(--_min-width-labels-aside);
116+
}
117+
118+
:host([auto-responsive]) #layout ::slotted(*) {
119+
/* Make form items inherit label position from the layout */
120+
--_form-item-labels-above: inherit;
121+
--_form-item-labels-aside: inherit;
122+
123+
/* By default, place each child on a new row */
124+
grid-column: 1 / span min(var(--_grid-colspan, 1), var(--_grid-rendered-column-count));
125+
126+
/* Form items do not need margins in auto-responsive mode */
127+
margin: 0;
128+
}
129+
130+
:host([auto-responsive][auto-rows]) #layout ::slotted(*) {
131+
grid-column-start: var(--_grid-colstart, auto);
132+
}
133+
134+
:host([auto-responsive][labels-aside]) {
135+
--_max-width: var(--_max-width-labels-aside);
136+
}
137+
138+
:host([auto-responsive][labels-aside]) #layout[fits-labels-aside] {
139+
--_form-item-labels-above: ' '; /* false */
140+
--_form-item-labels-aside: initial; /* true */
141+
--_grid-column-width: var(--_column-width-labels-aside);
142+
}
143+
144+
:host([auto-responsive][expand-columns]) #layout {
145+
/*
146+
The "min" value in minmax ensures that once "maxColumns" is reached, the grid stops adding
147+
new columns and instead expands the existing ones evenly to fill the available space.
148+
149+
The "max" value in minmax allows CSS grid columns to grow and evenly distribute any space
150+
that remains when there isn't room for an additional column and "maxColumns" hasn't been
151+
reached yet.
152+
*/
153+
--_grid-repeat: minmax(
154+
max(var(--_grid-column-width), calc((100% - var(--_max-total-gap)) / var(--_max-columns))),
155+
1fr
156+
);
157+
158+
/* Allow the layout to take up full available width of the parent element. */
159+
flex-grow: 1;
160+
}
161+
`;
162+
163+
export const formLayoutSlotStyles = css`
164+
/* Using :where to ensure user styles always take precedence */
165+
:where(
166+
vaadin-form-layout[auto-responsive] > *,
167+
vaadin-form-layout[auto-responsive] vaadin-form-row > *,
168+
vaadin-form-layout[auto-responsive] vaadin-form-item > *
169+
) {
170+
box-sizing: border-box;
171+
max-width: 100%;
172+
}
173+
174+
:where(
175+
vaadin-form-layout[auto-responsive][expand-fields] > *,
176+
vaadin-form-layout[auto-responsive][expand-fields] vaadin-form-row > *,
177+
vaadin-form-layout[auto-responsive][expand-fields] vaadin-form-item > *
178+
) {
179+
min-width: 100%;
180+
}
181+
`;
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/**
2+
* @license
3+
* Copyright (c) 2018 - 2025 Vaadin Ltd.
4+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5+
*/
6+
import type { CSSResult } from 'lit';
7+
8+
export const formRowStyles: CSSResult;
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/**
2+
* @license
3+
* Copyright (c) 2018 - 2025 Vaadin Ltd.
4+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5+
*/
6+
import { css } from 'lit';
7+
8+
export const formRowStyles = css`
9+
:host {
10+
display: contents;
11+
}
12+
13+
:host([hidden]) {
14+
display: none !important;
15+
}
16+
17+
::slotted(*) {
18+
/* Make form items inherit label position from the layout */
19+
--_form-item-labels-above: inherit;
20+
--_form-item-labels-aside: inherit;
21+
22+
grid-column: auto / span min(var(--_grid-colspan, 1), var(--_grid-rendered-column-count));
23+
}
24+
25+
::slotted(:first-child) {
26+
grid-column-start: 1;
27+
}
28+
`;

0 commit comments

Comments
 (0)