Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
805d8c5
docs: apply labels aside to fields placed directly in form layout
vursen Sep 21, 2026
a45a4d9
wip
vursen Sep 22, 2026
5dd1599
revise
vursen Sep 22, 2026
d7d785b
add css example
vursen Sep 22, 2026
564df44
remove register button from examples
vursen Sep 22, 2026
2750887
docs: document the side label alignment property
vursen Sep 23, 2026
a3f5e6c
docs: drop form item wrappers from the side label styling example
vursen Sep 23, 2026
18148fd
docs: rework the custom CSS example for labels-aside mode
vursen Sep 24, 2026
5a6d757
docs: recommend auto-responsive mode and add column span to its section
vursen Sep 25, 2026
d10cf17
docs: keep the original wording for the Vaadin 26 default mode note
vursen Sep 25, 2026
2d76cd0
docs: describe form item as a label and column placement wrapper
vursen Sep 25, 2026
8747833
docs: drop the label position link from the form item usage section
vursen Sep 25, 2026
94c7a27
docs: name a non-field component in the form item usage example
vursen Sep 25, 2026
f495d36
docs: describe form item usage in terms of wrapping
vursen Sep 25, 2026
e589122
docs: restore the "can also be used" phrasing in form item usage
vursen Sep 25, 2026
89eb4c4
docs: lead the form item usage section with wrapping
vursen Sep 25, 2026
01b73a5
docs: simplify the form item usage opening sentence
vursen Sep 25, 2026
0de7625
docs: describe form item as a wrapper that provides a label
vursen Sep 25, 2026
efaf7d5
docs: add label and button examples to the form item usage section
vursen Sep 25, 2026
895655f
docs: move the aria and custom field notes before the button example
vursen Sep 25, 2026
faecf78
docs: align the form rows screenshot to the left
vursen Sep 25, 2026
9c96e43
docs: describe form item as providing a field-like layout with a label
vursen Sep 25, 2026
2128333
docs: link labels-aside mode to the label position section
vursen Sep 25, 2026
11a1566
docs: link labels-aside mode to the label position section
vursen Sep 25, 2026
605af30
rewording
vursen Sep 25, 2026
09ce167
docs: mention field features as a reason to use custom field
vursen Sep 25, 2026
d8616bd
docs: lead the custom field sentence with the full-featured field case
vursen Sep 25, 2026
6ef6657
docs: reword the form item usage section
vursen Sep 25, 2026
8e7ec12
docs: render the form rows screenshot as an inline image to keep it l…
vursen Sep 25, 2026
5a3c927
Update articles/components/form-layout/index.adoc
vursen Sep 28, 2026
9a0a263
Merge branch 'main' into form-layout-labels-aside-fields
vursen Sep 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added articles/components/form-layout/form-rows.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
194 changes: 173 additions & 21 deletions articles/components/form-layout/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,18 @@
----
--

Form Layout automatically adjusts how fields are laid out in columns and rows to utilize the available space and avoid overflow. There are two discrete layouting modes that determine how this works:
Form Layout automatically adjusts how fields are laid out in columns and rows to utilize the available space and avoid overflow. There are two discrete layouting modes that determine how this works and what APIs are available:

Check failure on line 37 in articles/components/form-layout/index.adoc

View workflow job for this annotation

GitHub Actions / lint

[vale] reported by reviewdog 🐶 [Vale.Spelling] Did you really mean 'layouting'? Raw Output: {"message":"[Vale.Spelling] Did you really mean 'layouting'?","location":{"path":"articles/components/form-layout/index.adoc","range":{"start":{"line":37,"column":153},"end":{"line":37,"column":162}}},"severity":"ERROR","code":{"value":"Vale.Spelling"}}

Check warning on line 37 in articles/components/form-layout/index.adoc

View workflow job for this annotation

GitHub Actions / lint

[vale] reported by reviewdog 🐶 [Vaadin.ThereIs] Don't start a sentence with 'There are'. Raw Output: {"message":"[Vaadin.ThereIs] Don't start a sentence with 'There are'.","location":{"path":"articles/components/form-layout/index.adoc","range":{"start":{"line":37,"column":130},"end":{"line":37,"column":139}}},"severity":"WARNING","code":{"value":"Vaadin.ThereIs"}}

- <<#auto-responsive-mode,Auto-responsive mode>>, based on column width
- <<#responsive-steps-mode,Responsive steps mode>>, based on defined breakpoints

.Default mode depends on feature flag
.Auto-Responsive Mode Is Recommended
[NOTE]
The default mode depends on the <<{articles}/flow/configuration/feature-flags#,feature flag>> `defaultAutoResponsiveFormLayout`. When enabled, auto-responsive mode is the default; otherwise, responsive steps mode is the default.
====
Auto-responsive mode is recommended for new projects, as it provides a simpler configuration API and features not available in responsive steps mode.

In Vaadin 26, auto-responsive mode will be enabled by default, and responsive steps mode will be a deprecated legacy feature. You can prepare for this change already today by enabling the <<{articles}/flow/configuration/feature-flags#,feature flag>> `defaultAutoResponsiveFormLayout`.

Check warning on line 47 in articles/components/form-layout/index.adoc

View workflow job for this annotation

GitHub Actions / lint

[vale] reported by reviewdog 🐶 [Vaadin.Will] Avoid using 'will'. Raw Output: {"message":"[Vaadin.Will] Avoid using 'will'.","location":{"path":"articles/components/form-layout/index.adoc","range":{"start":{"line":47,"column":90},"end":{"line":47,"column":94}}},"severity":"WARNING","code":{"value":"Vaadin.Will","url":"https://vaadin.com/docs/contributing/docs/styleguide#present-tense"}}

Check warning on line 47 in articles/components/form-layout/index.adoc

View workflow job for this annotation

GitHub Actions / lint

[vale] reported by reviewdog 🐶 [Vaadin.Will] Avoid using 'will'. Raw Output: {"message":"[Vaadin.Will] Avoid using 'will'.","location":{"path":"articles/components/form-layout/index.adoc","range":{"start":{"line":47,"column":36},"end":{"line":47,"column":40}}},"severity":"WARNING","code":{"value":"Vaadin.Will","url":"https://vaadin.com/docs/contributing/docs/styleguide#present-tense"}}

Check warning on line 47 in articles/components/form-layout/index.adoc

View workflow job for this annotation

GitHub Actions / lint

[vale] reported by reviewdog 🐶 [Vaadin.Versions] Don't refer to a specific Vaadin version. Raw Output: {"message":"[Vaadin.Versions] Don't refer to a specific Vaadin version.","location":{"path":"articles/components/form-layout/index.adoc","range":{"start":{"line":47,"column":4},"end":{"line":47,"column":13}}},"severity":"WARNING","code":{"value":"Vaadin.Versions","url":"https://vaadin.com/docs/contributing/docs/styleguide#vaadin-versions"}}
====

== Auto-Responsive Mode

Expand All @@ -68,16 +72,16 @@
----
--

.Feature Flag Controls Default Mode
[NOTE]
Auto-responsive mode can be set as default by setting the <<{articles}/flow/configuration/feature-flags#,feature flag>> `defaultAutoResponsiveFormLayout` to true.

The sections below describe features available in auto-responsive mode.
The sections below describe features and APIs available exclusively in auto-responsive mode.

=== Form Rows

In auto-responsive mode, all fields are laid out in a single column by default. To render multiple fields side-by-side, i.e. in multiple columns, they can be grouped into Form Rows.

image:form-rows.png["A form where the first name and last name fields share one Form Row, the email address field stands alone, and the password and confirm password fields share another Form Row", width=440]

In the image above, Form Rows are highlighted in blue. Single-field rows do not need Form Row wrappers.

[.example,themes="lumo,aura"]
--
[source,java]
Expand All @@ -104,7 +108,7 @@

=== Label Position

By default, labels are rendered above the field. Labels can be rendered next to the field by wrapping fields into Form Items and switching to labels-aside mode.
By default, labels are rendered above the field. Labels can be rendered next to the field by switching the layout to labels-aside mode.

[.example,themes="lumo,aura"]
--
Expand All @@ -124,15 +128,77 @@
----
--

The field label must be applied on the Form Item rather than the field itself.

When the Form Layout's width isn't sufficient to render labels next to fields, it automatically reverts back to rendering them above fields.
If the Form Layout isn't wide enough to render labels next to fields, it falls back to rendering them above.

.UX Tip: Labels Aside Work Best In Single Column Forms
[NOTE]
Forms with labels next to the fields can be confusing if fields are rendered in multiple columns. Although Form Layout supports combining side-labels with multiple columns, this combination is not recommended.

The width of side labels can be configured with the `--vaadin-form-layout-label-width` CSS property. The Flow API also has a dedicated `setLabelWidth` API for this.
The width of side labels can be configured with the `--vaadin-form-layout-label-width` CSS property. The Flow API also has a dedicated `setLabelWidth` method for this.

In labels-aside mode, elements other than Vaadin input field components start in the label column by default. To align them with the fields instead, wrap them in Form Items (with or without labels):

[.example]
--
[source,java]
----
<source-info group="Flow"></source-info>
var nameInput = new Input();
formLayout.addFormItem(nameInput, "Name");

var saveButton = new Button("Save");
formLayout.addFormItem(saveButton);
----

[source,tsx]
----
<source-info group="React"></source-info>
<FormItem>
<label slot="label">Name</label>
<input type="text" />
</FormItem>

<FormItem>
<Button>Save</Button>
</FormItem>
----

[source,html]
----
<source-info group="Lit"></source-info>
<vaadin-form-item>
<label slot="label">Name</label>
<input type="text" />
</vaadin-form-item>

<vaadin-form-item>
<vaadin-button>Save</vaadin-button>
</vaadin-form-item>
----
--

Alternatively, you can write custom CSS. When labels are rendered on the side, Form Layout sets the `has-labels-aside` attribute on its host element and the `data-form-layout-has-labels-aside` attribute on children. You can target these attributes to adapt your elements to labels-aside mode, for example:

[source,css]
----
/* To define a two-column layout with labels aside: */

custom-element[data-form-layout-has-labels-aside] {
display: grid;
grid-template-columns: var(--vaadin-form-layout-label-width) 1fr;
column-gap: var(--vaadin-form-layout-label-spacing);
}

/* or, instead, to push the whole element to the input column: */

custom-element[data-form-layout-has-labels-aside] {
margin-inline-start: calc(
var(--vaadin-form-layout-label-width) +
var(--vaadin-form-layout-label-spacing)
);
}
----


=== Column Width

Expand Down Expand Up @@ -234,15 +300,43 @@
----
--

=== Column Span

Fields can span multiple columns by providing a column span number larger than 1.

[.example,themes="lumo,aura"]
--
[source,java]
----
include::{root}/src/main/java/com/vaadin/demo/component/formlayout/FormLayoutColspan.java[render,tags=snippet,indent=0,group=Flow]
----

[source,tsx]
----
include::{root}/frontend/demo/component/formlayout/react/form-layout-colspan.tsx[render,tags=snippet,indent=0,group=React]
----

[source,typescript]
----
include::{root}/frontend/demo/component/formlayout/form-layout-colspan.ts[render,tags=snippet,indent=0,group=Lit]
----
--

Column span is capped to the number of columns currently in the layout to prevent overflow.

== Responsive Steps Mode

In responsive steps mode, columns are defined explicitly for a number of breakpoints, or _steps_, based on the layout's width, and fields are automatically laid out in the available columns, wrapping to the next row as needed.

The default breakpoint configuration is one column below a layout width of `40em`, and two columns above that.

.Feature Flag Controls Default Mode
.Responsive Steps Mode Not Recommended
[NOTE]
The <<{articles}/flow/configuration/feature-flags#,feature flag>> `defaultAutoResponsiveFormLayout` makes auto-responsive mode the default. With the flag enabled, individual Form Layouts can be switched to responsive steps mode by defining the responsive steps manually.
====
Auto-responsive mode is recommended for new projects, as it provides a simpler configuration API and features not available in responsive steps mode.

In Vaadin 26, auto-responsive mode will be enabled by default, and responsive steps mode will be a deprecated legacy feature. You can prepare for this change already today by enabling the <<{articles}/flow/configuration/feature-flags#,feature flag>> `defaultAutoResponsiveFormLayout`.

Check warning on line 336 in articles/components/form-layout/index.adoc

View workflow job for this annotation

GitHub Actions / lint

[vale] reported by reviewdog 🐶 [Vaadin.Will] Avoid using 'will'. Raw Output: {"message":"[Vaadin.Will] Avoid using 'will'.","location":{"path":"articles/components/form-layout/index.adoc","range":{"start":{"line":336,"column":90},"end":{"line":336,"column":94}}},"severity":"WARNING","code":{"value":"Vaadin.Will","url":"https://vaadin.com/docs/contributing/docs/styleguide#present-tense"}}

Check warning on line 336 in articles/components/form-layout/index.adoc

View workflow job for this annotation

GitHub Actions / lint

[vale] reported by reviewdog 🐶 [Vaadin.Will] Avoid using 'will'. Raw Output: {"message":"[Vaadin.Will] Avoid using 'will'.","location":{"path":"articles/components/form-layout/index.adoc","range":{"start":{"line":336,"column":36},"end":{"line":336,"column":40}}},"severity":"WARNING","code":{"value":"Vaadin.Will","url":"https://vaadin.com/docs/contributing/docs/styleguide#present-tense"}}

Check warning on line 336 in articles/components/form-layout/index.adoc

View workflow job for this annotation

GitHub Actions / lint

[vale] reported by reviewdog 🐶 [Vaadin.Versions] Don't refer to a specific Vaadin version. Raw Output: {"message":"[Vaadin.Versions] Don't refer to a specific Vaadin version.","location":{"path":"articles/components/form-layout/index.adoc","range":{"start":{"line":336,"column":4},"end":{"line":336,"column":13}}},"severity":"WARNING","code":{"value":"Vaadin.Versions","url":"https://vaadin.com/docs/contributing/docs/styleguide#vaadin-versions"}}
====

The default breakpoint configuration is one column below a layout width of `40em`, and two columns above that.

=== Setting the Breakpoints

Expand Down Expand Up @@ -356,7 +450,7 @@

To prevent a field from stretching to fill the full width of the column, wrap it in a Form Item. Note that the field may overflow the Form Item and break the layout if its width is larger than the width of the column. To avoid this, set the field's maximum width to 100%.

== Column Span
=== Column Span

Fields can span multiple columns. Note that fields wrapped into Form Items must have their columns span set on the Form Item instead of the field itself.

Expand All @@ -380,7 +474,7 @@

Column span is capped to the number of columns currently in the layout to prevent overflow.

== AI Form Filler
== AI-Powered Form Filler

Use the commercial [classname]`FormAIController` to let users fill a Form Layout from natural-language input or attached files. See <<ai-powered#, AI Form Filler [badge-flow]#Flow#>>.

Expand All @@ -389,9 +483,67 @@

=== Form Item Usage

Form Item is only intended for wrapping individual input field components or native html `<input>` elements. It automatically applies an https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-labelledby[`aria-labelledby`] attribute to the element for screen reader support, which only works correctly with input fields and Web Components with an `ariaTarget` property that returns an appropriate element to which the `aria-labelledby` attribute can be applied.
Form Item is a wrapper that gives an element a field-like layout with a label. Use it to provide a label for native HTML `<input>` elements or custom web components that have no label of their own:

[.example]
--
[source,java]
----
<source-info group="Flow"></source-info>
var nameInput = new Input();
formLayout.addFormItem(nameInput, "Name");
----

[source,tsx]
----
<source-info group="React"></source-info>
<FormItem>
<label slot="label">Name</label>
<input type="text" />
</FormItem>
----

[source,html]
----
<source-info group="Lit"></source-info>
<vaadin-form-item>
<label slot="label">Name</label>
<input type="text" />
</vaadin-form-item>
----
--

Form Item connects the label to the wrapped element with the https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-labelledby[`aria-labelledby`] attribute to make it available to screen readers. HTML input elements support this attribute out of the box. Custom web components, on the other hand, need to define an `ariaTarget` property that returns an appropriate element inside the component that should receive the `aria-labelledby` attribute.

Form Item only adds a label and wraps a single element. For a full-featured field with error messages and helper text, or to combine multiple inputs in one form field, use <<../custom-field#,Custom Field>> instead.

To combine multiple inputs in the same form field, or to provide a label to other elements that lack labels, use <<../custom-field#,Custom Field>> instead. A Custom Field can be further wrapped into a Form Item for side-label support.
In <<#label-position,labels-aside mode>>, Form Item can also be used without a label to align non-field content, such as buttons, with the input column:

[.example]
--
[source,java]
----
<source-info group="Flow"></source-info>
var saveButton = new Button("Save");
formLayout.addFormItem(saveButton);
----

[source,tsx]
----
<source-info group="React"></source-info>
<FormItem>
<Button>Save</Button>
</FormItem>
----

[source,html]
----
<source-info group="Lit"></source-info>
<vaadin-form-item>
<vaadin-button>Save</vaadin-button>
</vaadin-form-item>
----
--

=== Scrolling

Expand Down
6 changes: 5 additions & 1 deletion articles/components/form-layout/styling.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ include::../_styling-section-theming-props.adoc[tag=style-properties]

=== Side Labels

The following style properties and Flow methods can be used to customize the width of labels and the spacing between them and the fields in label-aside mode:
The following style properties and Flow methods can be used to customize the width of labels, the spacing between them and the fields, and [since:com.vaadin:vaadin@V25.4]#their alignment# in label-aside mode:

[cols="2,3,2"]
|===
Expand All @@ -37,6 +37,10 @@ The following style properties and Flow methods can be used to customize the wid
|Spacing between side label and field
|`--vaadin-form-layout-label-spacing`
|`setLabelSpacing`

|Side label alignment
|`--vaadin-form-layout-label-text-align`
|`setLabelTextAlign`
|===

[.example,themes="lumo,aura"]
Expand Down
17 changes: 4 additions & 13 deletions frontend/demo/component/formlayout/form-layout-label-styling.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import 'Frontend/demo/init'; // hidden-source-line
import '@vaadin/form-layout';
import '@vaadin/form-layout/vaadin-form-item.js';
import '@vaadin/text-field';
import '@vaadin/email-field';
import { html, LitElement } from 'lit';
Expand All @@ -24,20 +23,12 @@ export class Example extends LitElement {
style="
--vaadin-form-layout-label-width: 10em;
--vaadin-form-layout-label-spacing: 2em;
--vaadin-form-layout-label-text-align: end;
"
>
<vaadin-form-item>
<label slot="label">First name</label>
<vaadin-text-field></vaadin-text-field>
</vaadin-form-item>
<vaadin-form-item>
<label slot="label">Last name</label>
<vaadin-text-field></vaadin-text-field>
</vaadin-form-item>
<vaadin-form-item>
<label slot="label">Email address</label>
<vaadin-email-field></vaadin-email-field>
</vaadin-form-item>
<vaadin-text-field label="First name"></vaadin-text-field>
<vaadin-text-field label="Last name"></vaadin-text-field>
<vaadin-email-field label="Email address"></vaadin-email-field>
</vaadin-form-layout>
`;
// end::snippet[]
Expand Down
19 changes: 5 additions & 14 deletions frontend/demo/component/formlayout/form-layout-labels-aside.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import 'Frontend/demo/init'; // hidden-source-line
import '@vaadin/form-layout';
import '@vaadin/form-layout/vaadin-form-item.js';
import '@vaadin/text-field';
import '@vaadin/checkbox';
import '@vaadin/email-field';
import '@vaadin/password-field';
import '@vaadin/split-layout';
import { html, LitElement } from 'lit';
import { customElement } from 'lit/decorators.js';
Expand All @@ -29,18 +29,9 @@ export class Example extends LitElement {
// tag::snippet[]
return html`
<vaadin-form-layout style="width: 100%" auto-responsive labels-aside>
<vaadin-form-item>
<label slot="label">First name</label>
<vaadin-text-field></vaadin-text-field>
</vaadin-form-item>
<vaadin-form-item>
<label slot="label">Last name</label>
<vaadin-text-field></vaadin-text-field>
</vaadin-form-item>
<vaadin-form-item>
<label slot="label">Email address</label>
<vaadin-email-field></vaadin-email-field>
</vaadin-form-item>
<vaadin-email-field label="Email"></vaadin-email-field>
<vaadin-password-field label="Password"></vaadin-password-field>
<vaadin-checkbox label="Subscribe"></vaadin-checkbox>
</vaadin-form-layout>
`;
// end::snippet[]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { reactExample } from 'Frontend/demo/react-example'; // hidden-source-line
import React from 'react';
import { EmailField, FormItem, FormLayout, TextField } from '@vaadin/react-components';
import { EmailField, FormLayout, TextField } from '@vaadin/react-components';

function Example() {
// tag::snippet[]
Expand All @@ -11,20 +11,12 @@ function Example() {
style={{
'--vaadin-form-layout-label-width': '10em',
'--vaadin-form-layout-label-spacing': '2em',
'--vaadin-form-layout-label-text-align': 'end',
}}
>
<FormItem>
<label slot="label">First name</label>
<TextField />
</FormItem>
<FormItem>
<label slot="label">Last name</label>
<TextField />
</FormItem>
<FormItem>
<label slot="label">Email address</label>
<EmailField />
</FormItem>
<TextField label="First name" />
<TextField label="Last name" />
<EmailField label="Email address" />
</FormLayout>
);
// end::snippet[]
Expand Down
Loading
Loading