Skip to content

useAvailableHeight ​

Measures how much room a pane actually has, for the panels that fill the screen and scroll inside themselves — Chat, Email.

Import ​

ts
import { useAvailableHeight } from '@shared/composables/useAvailableHeight'

Signature ​

ts
function useAvailableHeight(target: Ref<HTMLElement | null>): {
  height: Ref<string | null>
  measure: () => void
}

Returns ​

MemberTypeDescription
heightRef<string | null>A CSS length such as '742px'. null until the first measurement.
measure() => voidRe-measures on the next animation frame.

The floor is 420px. Below that the pane stops shrinking and the page is allowed to scroll instead.

Example ​

vue
<script setup lang="ts">
import { useAvailableHeight } from '@shared/composables/useAvailableHeight'
import { ref } from 'vue'

const pane = ref<HTMLElement | null>(null)
const { height } = useAvailableHeight(pane)
</script>

<template>
  <div ref="pane" class="overflow-y-auto" :style="{ height: height ?? undefined }">
    <slot />
  </div>
</template>

Notes ​

  • h-full cannot do this. main is flex-1 with no min-h-0, so a percentage height resolves against a content-driven height and CSS falls back to the content's own. The number has to be measured.
  • The measurement is the scrolling column's client height, minus the chrome above and below main, minus the padding and borders of every ancestor between the target and main.
  • Those insets are walked, not read from a rect. Reading only main's padding misses the card that ModernLayout adds, and a bounding rect includes the flex slack that is itself a function of the height being computed.
  • With no main or no scrolling ancestor it falls back to 100dvh minus the element's top.
  • Resize is coalesced into one animation frame. Resize events fire in bursts, and measuring mid-burst reads a layout that is about to change again.
  • It measures twice on mount — once as soon as the element exists, once after the layout settles — because fonts loading and the sidebar transition both shift the chrome height.
  • It re-measures when target changes, so a v-if on the pane is handled.
  • Listeners and the pending frame are cleaned up before unmount.