Skip to main content
Light Dark System

Layout

A template is a list of widgets. Layout is how you say where they go.

With no layout at all

A template that says nothing puts its widgets in the page one after another and lets each one take whatever space its own CSS asks for:

<et2-textbox label="Name"></et2-textbox>
<et2-date label="Preferred day"></et2-date>
<et2-button label="Save"></et2-button>
<et2-button label="Cancel"></et2-button>

Every eTemplate widget is a block, so they come out one per line, in source order: the two inputs stretch across the whole width, the buttons take only what they need but still get a line each. Nothing sits beside anything else, and nothing lines up - each input starts wherever its own label happens to end.

They do reflow, but each one on its own. A widget drops its label above its input once the two stop fitting side by side, and where that happens depends on how long that widget’s label is: narrow the example above and “Preferred day” gives up well before “Name” does, so there is a whole range of widths at which the form is half wrapped and half not. Nothing reflows together, and no widget can be told to take the space left at the bottom.

That is workable for two or three widgets and for nothing else. Everything below is a way of saying more than that, in the order you should reach for them. They also combine: an et2-hbox holding a field and its unit is one child of a layout, and a box you have styled yourself can sit inside either.

Approach Reach for it when
The layout attribute A form or a dialog - labelled fields that should reflow as the popup or panel changes size. The default answer.
Boxes A few widgets that belong together as one unit: a field and its unit, a row of buttons, a group you show and hide together.
Other layout widgets Tabs, a folding section, a titled group, a split pane - an arrangement a widget already provides.
Your own CSS The arrangement is specific to this one screen and none of the above says it.
grid Rows repeated from content. Otherwise, a last resort.

The layout attribute

Put layout on the container and its children arrange themselves - and re-arrange themselves when the window, the popup or the panel they are in changes size. No wrapper, no counting columns.

<et2-template id="myapp.edit" layout="2-column" style="max-width: 60em">
    <et2-textbox id="name" label="Name"></et2-textbox>
    <et2-select id="status" label="Status"></et2-select>
    <et2-date id="due" label="Due"></et2-date>
    <et2-number id="amount" label="Amount"></et2-number>
</et2-template>

Drag the handle on the right edge of the box, and it goes through three states:

  1. Two columns, while there is room for two.
  2. One column, labels still beside their inputs - below about 850px, where a second column would go under --column-min-width.
  3. One column, every label above its input - below 600px. All four move at the same moment, not one field at a time as they did with no layout at all. That is the part a form cannot do for itself, and it comes with the layout: every field gets the same label width, until there is no room for a label beside an input at all.

The example is capped at 60em so that it stops at two columns instead of growing a third on a wide screen. (see Layout Controller)

The layouts are stack (one per line), 2-column, and edit (2-column with the conventions an edit dialog wants), and <et2-template> and <et2-customfields> are the widgets that accept them.

See Layout Controller for how the columns collapse, the span and grow attributes, lining up labels, and the CSS variables that control when it all happens.

Boxes: et2-hbox and et2-vbox

A box groups widgets so they can be arranged, shown, hidden or styled as one thing. et2-hbox puts its children in a row, et2-vbox in a column, and both leave a gap between them. et2-box is the same widget with no direction fixed and no gap.

<et2-hbox>
    <et2-number id="quantity" label="Quantity"></et2-number>
    <et2-textbox id="unit" label="Unit"></et2-textbox>
</et2-hbox>
<et2-vbox>
    <et2-number id="quantity-v" label="Quantity"></et2-number>
    <et2-textbox id="unit-v" label="Unit"></et2-textbox>
</et2-vbox>

The same two widgets, in an et2-hbox and then an et2-vbox. Either way they are one child of whatever holds the box, so the widget above can arrange, hide or style the pair as a unit.

Use align on the box to move all of its children to one end, or on a single child to push just that one over. A footer with Save on the left and Cancel on the right is one align="right":

<et2-hbox>
    <et2-button label="Save"></et2-button>
    <et2-button label="Apply"></et2-button>
    <et2-button align="right" label="Cancel"></et2-button>
</et2-hbox>

Other layout widgets

Boxes are the plain containers; several widgets exist to arrange things in a particular way, and each has its own page in the component reference under Layout:

Widget What it does
et2-tabbox Tabs - one panel visible at a time. Grows to fill a layout on its own.
et2-details A section the user can fold away.
et2-groupbox A titled, framed group of widgets.
et2-split Two panes with a divider the user can drag.
et2-app-box The application’s own frame - header, content and footer.
et2-visually-hidden Content for screen readers only, taking no space.

They are containers like the boxes above, so the same rules apply: they hold their children, and they are themselves one child of whatever holds them.

Your own CSS

When the arrangement is specific to one screen, give the container a class and write the CSS yourself.

The one thing to know: a box’s flex row lives in its shadow DOM, so rules aimed at the tag do nothing - et2-box.address { display: grid } sets a property on an element whose children are laid out one level further in. Target ::part(base) instead:

et2-box.address::part(base) {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: var(--sl-spacing-small);
}

That CSS belongs in the app’s own stylesheet (<app>/templates/default/app.css). For rules that only make sense for one template, <et2-styles> carries them in the template file itself. Either way, prefer em and the existing CSS variables over fixed pixels - see Styling.

grid, and when you still need it

<grid> is the original way to lay out a template: you declare the columns, you declare the rows, and what you get is what everybody gets at every window size.

It is still the only construct that repeats rows from content. A grid’s last row is repeated once per entry in the content array its widgets read from - ${row} in a child’s id is what says “whichever row this turns out to be” - and that is how nextmatch rows and every content-driven list are built:


<grid id="events">
	<columns>
		<column/>
		<column/>
	</columns>
	<rows>
		<row class="th">
			<et2-description value="Time"/>
			<et2-description value="Title"/>
		</row>
		<row>
			<et2-date-time id="${row}[time]" readonly="true"/>
			<et2-description id="${row}[title]"/>
		</row>
	</rows>
</grid>

(The header row is why a content row is row 2 - a grid’s rows are numbered from 1, including the ones that hold nothing but labels.)

For anything else - and especially for a form - it is the thing the layout attribute replaces. A dialog built as one table.et2_grid cannot reflow, cannot collapse to one column, and cannot hand its leftover height to the widget that should have it.