Skip to content
ts
import Layout from "@ui/Layout.vue"

Layout

CSS provides four methods to arrange items in a container: Flow, Columns, Flex and Grid. To make typical alignment tasks in the Funkwhale UI easier, we have created a few useful presets.

By default, the items have a 32px gap. You can change it with the gap-x prop.

Apply presets

The following containers are responsive. Change your window's size or select a device preset from your browser's dev tools to see how layouts are affected by available space.

flex
grid
stack
columns

Add semantics

Add one of these props to your Layout component to turn them into semantic containers (without affecting their presentation):

Headings: "h1" | "h2" | "h3" | "h4" | "h5"

Sectioning: "nav" | "aside" | "header" | "footer" | "main"

Forms: "label" | "form"

Common props

gap-*: Set the gap to one of the defaults

ts
`gap-${'4' | '8' | '12' | '16' | '24' | '32' | '48' | '64' | 'auto'}`

no-gap: Remove the gap between items

vue
<script setup>
const noGap = ref(true);
</script>

<template>
  <Toggle v-model="noGap" label="no-gap" />

  <Layout flex :no-gap="noGap || undefined">
    <Card title="A" small />
    <Card title="B" small />
    <Card title="C" small />
    <Card title="D" small />
  </Layout>
</template>

A
B
C
D

Add fixed or flexible Spacers

Only available on:

  • stack
  • flex

If you add a spacer with attribute grow, it will push the other item until the Layout fills the available space. This only works if the parent element itself grows beyond its minimal contents.

vue
<script setup>
const isGrowing = ref(true);
</script>

<template>
  <Toggle v-model="isGrowing" label="Grow spacers" />

  <Layout stack style="height:25em;">
    <Alert red />
    <Alert purple />
    <Spacer :grow="isGrowing || undefined" />
    <Alert blue />
  </Layout>
</template>

Multiple spacers will distribute their growth evenly.

Note that you can set the minimum space occupied by the Spacer with its size prop (docs). Negative values can offset the gap of the Layout (but, due to a limitation of flexbox, not eat into the space occupied by adjacent items):

vue
<template>
  <Toggle v-model="isGrowing" label="Grow spacers" />

  <Layout stack style="height:35em;">
    <Alert blue />
    <Spacer :size="-32" :grow="isGrowing || undefined" />
    <Alert green />
    <Alert yellow />
    <Spacer :size="-32" :grow="isGrowing || undefined" />
    <Alert red />
  </Layout>
</template>

Using this component

A11y Checklist

Accessible layout

template
<Layout flex>
  <Button>1</Button>
  <Button>2</Button>
  <Button>3</Button>
  <Button>4</Button>
  <Button>5</Button>
</Layout>
<Layout grid>
  <Button>1</Button>
  <Button>2</Button>
  <Button>3</Button>
  <Button>4</Button>
  <Button>5</Button>
</Layout>
<Layout
  columns
  column-width="200"
>
  <Button>1</Button>
  <Button>2</Button>
  <Button>3</Button>
  <Button>4</Button>
  <Button>5</Button>
  <Button>6</Button>
</Layout>

Test this component in isolation against WCAG2 criteria