Skip to content

Responsive layout ​

Decide what each region does as the width drops, then wire the components. A region can stay and get narrower, move into a layer that opens on demand, or leave the page. When three regions are sharing a row and none of them has room, drop one. Do not shrink all of them by the same amount.

The shell breakpoint ​

mobileNav.breakpoint on AppShell is the width where the side navigation moves into the drawer. The default is md.

ValueWidth
sm640px
md768px
lg1024px
xl1280px
2xl1536px
nonestays inline

Below that width the side navigation leaves the page and the drawer takes over. A top navigation inside the same shell uses that drawer instead of opening a second one. Set mobileNav to false to turn this off. Inside the shell, useAppShellMobile() reports whether the viewport is narrow and whether the drawer is open. Import it from @astryx-vue/core.

Content ​

Layout splits the page into header, start, content, end, and footer slots. contentWidth caps the content column. Dividers still run the full width. A number is pixels. Body text is easiest to read at about 40 to 60 characters a line, so hold the column with this prop instead of wrapping every section in its own container.

A LayoutPanel in the end slot takes a fixed column with width. When the row gets too narrow for that column, do not keep it on the page next to the side navigation.

vue
<script setup lang="ts">
import { useMediaQuery } from '@astryx-vue/core'
import { AppShell } from '@astryx-vue/core/AppShell'
import { Layout, LayoutContent, LayoutPanel } from '@astryx-vue/core/Layout'
import { SideNav, SideNavItem } from '@astryx-vue/core/SideNav'

const isNarrow = useMediaQuery('(max-width: 1024px)')
</script>

<template>
  <AppShell :mobile-nav="{ breakpoint: 'md' }">
    <template #sideNav>
      <SideNav>
        <SideNavItem href="/orders" label="Orders" />
      </SideNav>
    </template>
    <Layout :content-width="720">
      <LayoutContent>
        <p>Orders</p>
      </LayoutContent>
      <template v-if="!isNarrow" #end>
        <LayoutPanel :width="380" has-divider>
          <p>Details</p>
        </LayoutPanel>
      </template>
    </Layout>
  </AppShell>
</template>

Above 1024px the page has three regions: the side navigation, the content, and a 380px side panel. At 1024px the side panel leaves the row. Open it in a Dialog or a BottomSheet when it is needed. At md (768px) the side navigation moves into the shell drawer. useMediaQuery reads the match during setup, then follows changes.

Columns that reflow ​

A numeric columns on Grid is that many columns at every width. It does not drop a column on its own. :columns="7" is still seven columns on a phone. To drop columns as the width drops, pass { minWidth, max }. minWidth is the minimum pixel width of a column, and max is the most columns the grid will create. When another column would fall short of minWidth, the grid uses fewer columns. The last remaining column can shrink to the container. gap takes space between columns and counts toward whether another column fits.

vue
<Grid :columns="{ minWidth: 240, max: 4 }" :gap="3">
  <!-- Four columns when there is room, fewer when there is not -->
</Grid>

<Grid :columns="7" :gap="1">
  <!-- Seven days, seven columns at every width -->
</Grid>

On that same columns object, repeat: 'fit' collapses empty tracks so the columns that are present stretch across the row. The default, fill, keeps the empty tracks so every column stays the same width.