When composing the overall layout of a feature. How much white space to use, how to size and group regions and form fields, and how to separate them without clutter.
Generous, deliberate spacing and restrained width are what make a layout feel calm and scannable. The goal is never to fill the available space, it's to give content room to breathe so users can parse it at a glance. Most of the work is restraint: fewer arbitrary values, narrower containers, and more air than you think you need. The sections below follow that arc. For example, how much space to use, how wide to let things grow, how to group and separate them, and how much to reveal at once.
Begin with more space than feels right and remove it until the layout feels snug. Don't add space reactively after everything is already crammed together. Removing space is a deliberate, visible edit; under-spacing is easy to miss. Dense, packed UIs have their place for dashboards and data-heavy views, but that density should be a decision, not a default.
Take every gap, margin, and padding from the --app-scale-0x through --app-scale-12x tokens rather than hand-picking pixel values. q2-grid's gap prop (compact, normal, comfortable) is already wired to the scale, so you often won't reach for a raw token at all.
--app-scale-* tokens are a linear 5px scale, so the perceptual jump between steps shrinks at the top end (5px to 10px is +100%; 55px to 60px is only +9%). Prefer the lower steps for spacing within an element and reserve the higher steps for spacing between major regions.<!-- Pull gaps from the scale instead of arbitrary px -->
<div style="display: grid; gap: var(--app-scale-4x);">
<!-- stacked content -->
</div>A region doesn't have to be full-width just because the shell is. Cap content at a comfortable reading width and center it, design mobile-first, and let columns appear only as space allows. Stretching content edge-to-edge creates long, tiring scan distances for no real benefit.
Not every element should be fluid. Pair a fixed-width supporting region (e.g., a sidebar or a summary panel) with a flexible main region, give elements a sensible max-width, and only let them shrink once the screen is actually narrower than that, rather than scaling everything by the same percentage.
The same caution applies to proportion: don't tie one element's size to another with em units. Large elements should shrink faster than small ones on small screens, so a ratio that looks right on desktop rarely holds up. Tune a component's internal spacing independently of its font size so each can respond on its own terms.
Grouping is mostly a spacing problem: always leave more space around a group than within it. When the gap between a label and its input matches the gap between two separate fields, the form reads as one undifferentiated block and users can't tell what belongs together. When you do need to separate two regions, reach for spacing, a subtle elevation (q2-card elevation, which maps to --app-shadow-1 through --app-shadow-5), or a different background (--t-base vs --t-base-gray-1 / --t-base-gray-2) before you reach for a border. Borders add visual noise; the alternatives usually do the job more cleanly.
Every horizontal position where content starts is another edge the eye has to track. Line elements up on as few edges as you can (e.g., labels, values, and controls that share a left edge scan as one clean column), while content that steps in and out at slightly different positions feels noisy even when nothing is technically misaligned. Lean on q2-grid to keep regions on shared edges instead of nudging individual elements into place.
Don't put everything on screen at once just because it all belongs to the feature. Show the most important content and the most likely next step first, then let the rest unfold on demand (e.g., a collapsible q2-section for secondary detail, a q2-stepper to break a long task into stages, or tabs for parallel views). Progressive disclosure keeps the main content scannable and gives the page a clear path through it instead of a wall of fields.
The account overview below brings the principles together in one view. The whole page is capped and centered, so it never stretches edge to edge. A narrow summary sits in a supporting column next to a wider main column, sized to their content rather than split evenly, and the two regions collapse to a single column on small screens. Every gap comes from the grid rather than hand-picked pixels, the regions are separated by elevation instead of borders, and the secondary "Recent activity" content is tucked into a collapsible section so the accounts lead.
<q2-grid columns="1" md-columns="3" gap="comfortable">
<!-- Supporting region: a summary sized to its content, not the full row -->
<q2-grid-area md-column-span="1">
<q2-card elevation="1" type="non-clickable">
<q2-detail label="Total balance" stacked size="small">
<q2-currency amount="35814.62"></q2-currency>
</q2-detail>
<q2-detail label="Open accounts" description="3" stacked size="small"></q2-detail>
<q2-action-group style="margin-bottom: 0;">
<q2-btn intent="workflow-primary">Transfer</q2-btn>
</q2-action-group>
</q2-card>
</q2-grid-area>
<!-- Main region: the primary content takes the wider share of the row -->
<q2-grid-area md-column-span="2">
<q2-grid columns="1" gap="comfortable">
<q2-grid-area>
<q2-card elevation="1" type="non-clickable">
<q2-list label="Accounts">
<q2-item>
<q2-avatar slot="decorator" icon="people-group"></q2-avatar>
<div slot="header">Family Checking</div>
<q2-detail slot="body" label="Account number" description="xxxx-xxx-9538"></q2-detail>
<div slot="footer">
<q2-detail label="Balance" stacked size="small">
<q2-currency amount="4820.55"></q2-currency>
</q2-detail>
</div>
</q2-item>
<q2-item>
<q2-avatar slot="decorator" icon="piggy-bank"></q2-avatar>
<div slot="header">Emergency Savings</div>
<q2-detail slot="body" label="Account number" description="xxxx-xxx-0745"></q2-detail>
<div slot="footer">
<q2-detail label="Balance" stacked size="small">
<q2-currency amount="12953.25"></q2-currency>
</q2-detail>
</div>
</q2-item>
<q2-item>
<q2-avatar slot="decorator" icon="linechart-trend"></q2-avatar>
<div slot="header">Money Market</div>
<q2-detail slot="body" label="Account number" description="xxxx-xxx-9424"></q2-detail>
<div slot="footer">
<q2-detail label="Balance" stacked size="small">
<q2-currency amount="18040.82"></q2-currency>
</q2-detail>
</div>
</q2-item>
</q2-list>
</q2-card>
</q2-grid-area>
<q2-grid-area>
<q2-card type="non-clickable">
<q2-section label="Recent activity" collapsible>
<q2-list>
<q2-item>
<div slot="header">Payroll Deposit</div>
<q2-detail slot="body" label="Posted">
<q2-relative-time date="2025-03-05" unit="day"></q2-relative-time>
</q2-detail>
<q2-currency amount="3500.00" slot="footer"></q2-currency>
</q2-item>
<q2-item>
<div slot="header">Rent Payment</div>
<q2-detail slot="body" label="Posted">
<q2-relative-time date="2025-03-08" unit="day"></q2-relative-time>
</q2-detail>
<q2-currency amount="-1200.00" slot="footer"></q2-currency>
</q2-item>
</q2-list>
</q2-section>
</q2-card>
</q2-grid-area>
</q2-grid>
</q2-grid-area>
</q2-grid>
gap, separate regions with elevation, and hide secondary content in a collapsible section.