Skip to content
ts
import Section from '@ui/Section.vue'

Layout section

Sections divide the page vertically

ts
const props = defineProps<{
  columnsPerItem?: 1 | 2 | 3 | 4
  alignLeft?: boolean
  action?: { text: string, id?: string } & (ComponentProps<typeof Link> | ComponentProps<typeof Button>)
  icon?: string
  badge?: 'loading' | number
} & {
  [H in `h${ '1' | '2' | '3' | '4' | '5' | '6' }`]? : string
} & {
  [S in 'page-heading' | 'section-heading' | 'large-section-heading' | 'subsection-heading' | 'caption' | 'title' | 'radio' | 'secondary' ]? : true
} & {
  [Operation in 'expand' | 'collapse']?: () => void
}>()

Choose an appropriate heading level for each section. You can use all props for Heading, including h1 to h6 and stylistic variants such as radio or page-heading.

template
<Section h1="My title" />

My title

template
<Section
  h2="My title"
  radio
/>

My title

Align the section

template
<Section h2="My title" alignLeft />

Make the section header align with the section contents

The section aligns its title and items to a grid, following the designs. To make sure the header of a section exactly aligns with its contents, set the item width (in number of columns). For example,

Mixed content
template
  :columns-per-item="1"
Normal cards
template
  :columns-per-item="3"
Large cards, Activities
template
  :columns-per-item="4"

For a complete overview of column widths for common funkwhale components, see the table in using-width

Move individual items within and across grid-cells

For child items, you can use all known CSS grid placement techniques:

Stretch over all columns
css
  grid-column: 1 / -1;

Fill the whole grid, no matter how wide the screen is

Span multiple rows/columns
css
  grid-row: span 3
Move within grid cell
css
  align-self: start;
  justify-self: center;

Place individual items to the edge of their current cell or cells

Provide an action

The link or button will be shown on the right side of the header. Use action.text to set the label (required). You can use all Link props or Button props inside the action prop! Note that the button or link label will be in line with the heading.

template
<Section
    h2="With a link"
    :action="{
      text: 'My library',
      to: '/',
      icon: 'bi-star'
    }"
/>
<Section
  h2="With a button"
  :action="{
      text: 'Say hello!',
      onClick: ()=>console.log('Hello'),
      primary: true,
      solid: true,
      icon: 'bi-save'
  }"
/>

Add icons and slots

template
<Section
  icon="bi-heart"
>
  <template #topleft>
    <Pill>#Audiology</Pill>
    <Spacer size-12 />
    <Pill>#Phonologics</Pill>
  </template>
</Section>

Set gaps between consecutive sections

Place consecutive sections into a Layout stack with a 64px gap (gap-64) to give them a regular vertical rhythm. Use different sizes for very small or very large headings.

Note the spacer above the layout. By default, sections begin at the baseline of the heading. This enables us to explicitly define the vertical rhythm, independently of the heading's line height.

Mix sections of different item widths

template
<Layout flex>
<Toggle v-model="alignLeft" label="Left-align the layout"/>
</Layout>

<Spacer />

<Layout stack gap-64>

  <Section
    :alignLeft="alignLeft"
    :columns-per-item="2"
    h2="Cards (2-wide items)"
    :action="{
      text:'Documentation on Cards',
      to:'../card'
    }"
  >
    <Card small default solid raised title="Relatively Long Album Name">
        Artist Name
    </Card>
    <Card small default solid raised title="Relatively Long Album Name">
        Artist Name
    </Card>
    <Card small default solid raised title="Relatively Long Album Name">
        Artist Name
    </Card>
  </Section>

  <Section
    :alignLeft="alignLeft"
    :columns-per-item="3"
    h2="Activities (3-wide items)"
    :action="{
      text:'Delete selected items',
      onClick:()=>console.log('Deleted :-)')
    }"
  >
    <Activity :track="track" :user="user" />
    <Activity :track="track" :user="user" />
    <Activity :track="track" :user="user" />
  </Section>

</Layout>

Collapse and expand the section

By adding either collapse or expand to the props, you add Accordion behavior to the section. The heading will become a clickable button.

ts
const sections = ref([false, false, false])
template
<Section
  v-for="(section, index) in sections"
  :key="`${index}${section}`"
  :h2="`Section ${index} (${section})`"
  align-left
  v-bind="
    section
      ? { collapse: () => { sections[index] = false } }
      : { expand: () => { sections[index] = true } }
  "
>
  Content {{ section }}
</Section>
Content
Content
Content

Add a badge

  • Add badge="loading" to show a spinner after the heading
  • Add `badge="1" to show an encircled number after the heading

Heading
1

Responsivity

  • Cards and Activities snap to the grid columns. They have intrinsic widths, expressed in the number of columns they span. For Card, it is 3 and for Activity, it is 4.
  • On a typical laptop screen, you may have 4 album cards or 3 activities side-by-side. On a typical mobile screen, you will have one medium card or two small ones in a row.
  • The remaining space is evenly distributed.
  • Title rows align with the content below them. The action on the right will always end with the last item in the grid. Resize the window to observe how the items move.

Using this component

A11y Checklist

Accessible section

Section heading (h3)

Link

1

Content

2

Content

3

Content
template

<Section
  align-left
  :columns-per-item="3"
  large-section-heading
  h3="Section heading (h3)"
  :action="{
    text:'Link',
    to:'https://funkwhale.audio'
  }"
>
  <Card
    small
    default
    solid
    blue
    category="h4"
    title="1"
    to="https://funkwhale.audio"
  >
    Content
  </Card>
  <Card
    small
    default
    solid
    red
    category="h4"
    title="2"
    to="https://funkwhale.audio"
  >
    Content
  </Card>
  <Card
    small
    default
    solid
    green
    category="h4"
    title="3"
    to="https://funkwhale.audio"
  >
    Content
  </Card>
</Section>

Test this component in isolation against WCAG2 criteria