Skip to content

@vibe-labs/design-vue-responsive

Vue 3 responsive layout components for the Vibe Design System. Container-query-driven grid, stack, and container wrapper with a composable for imperative breakpoint access.

Installation

ts
import { VibeResponsiveContainer, VibeResponsiveGrid, VibeResponsiveStack, useContainerBreakpoint } from "@vibe-labs/design-vue-responsive";

Requires CSS from @vibe-labs/design-components-responsive (loaded via @vibe-labs/design-components).


Components

VibeResponsiveContainer

Establishes a container query context. Wrap any area that needs responsive children.

Usage

vue
<VibeResponsiveContainer>
  <!-- children can use container queries -->
</VibeResponsiveContainer>

<VibeResponsiveContainer name="sidebar" type="size">
  <!-- named container with both-axis sizing -->
</VibeResponsiveContainer>

Props

PropTypeDefaultDescription
tagstring"div"HTML element to render
typeContainerType"inline"inline · size · normal
nameContainerNamemain · sidebar · card · panel

Slots

SlotDescription
defaultContent

VibeResponsiveGrid

12-column grid with per-breakpoint column counts. Must be inside a VibeResponsiveContainer.

Usage

vue
<VibeResponsiveContainer>
  <VibeResponsiveGrid :cols="1" :cols-sm="2" :cols-lg="3" :cols-xl="4">
    <div>Card</div>
    <div>Card</div>
    <div>Card</div>
    <div>Card</div>
  </VibeResponsiveGrid>
</VibeResponsiveContainer>

Props

PropTypeDefaultDescription
tagstring"div"HTML element to render
colsGridColumn1Default column count
colsXsGridColumnColumns at xs (480px)
colsSmGridColumnColumns at sm (640px)
colsMdGridColumnColumns at md (768px)
colsLgGridColumnColumns at lg (1024px)
colsXlGridColumnColumns at xl (1280px)
cols2xlGridColumnColumns at 2xl (1536px)

Gap defaults to var(--responsive-grid-gap). Override with spacing utility classes or inline style.

Slots

SlotDescription
defaultGrid items

VibeResponsiveStack

Flex column that switches to row at a breakpoint. Must be inside a VibeResponsiveContainer.

Usage

vue
<VibeResponsiveContainer>
  <VibeResponsiveStack breakpoint="md">
    <aside>Sidebar</aside>
    <main>Content</main>
  </VibeResponsiveStack>
</VibeResponsiveContainer>

Props

PropTypeDefaultDescription
tagstring"div"HTML element to render
breakpointBreakpoint"md"Breakpoint to switch to row

Gap defaults to var(--responsive-stack-gap). Override with spacing utility classes or inline style.

Slots

SlotDescription
defaultStack children

Composables

useContainerBreakpoint(target)

Observes a container element's inline size via ResizeObserver and exposes reactive breakpoint state. Use for logic that CSS container queries can't handle (swapping components, changing props, conditional rendering).

vue
<script setup>
import { ref } from "vue";
import { VibeResponsiveContainer, useContainerBreakpoint } from "@vibe-labs/design-vue-responsive";

const containerRef = ref<HTMLElement>();
const { current, above, below, width } = useContainerBreakpoint(containerRef);
</script>

<template>
  <VibeResponsiveContainer ref="containerRef">
    <p>Width: {{ width }}px — Breakpoint: {{ current ?? "below xs" }}</p>
    <DetailedTable v-if="above('lg')" />
    <CompactList v-else />
  </VibeResponsiveContainer>
</template>

Parameters

ParamTypeDescription
targetMaybeRefOrGetter<HTMLElement | null>Container element to observe

Returns

PropertyTypeDescription
currentRef<Breakpoint | null>Largest matching breakpoint, or null if below xs
above(bp: Breakpoint) => booleanTrue when container width >= breakpoint
below(bp: Breakpoint) => booleanTrue when container width < breakpoint
widthRef<number>Current container width in pixels

Cleans up automatically via onScopeDispose.


Dependencies

PackagePurpose
@vibe-labs/design-components-responsiveCSS + TypeScript types
@vibe-labs/coreShared utilities
vue (peer)Vue 3.5+

Build

bash
npm run build

Usage Guide

Setup

ts
import {
  VibeResponsiveContainer,
  VibeResponsiveGrid,
  VibeResponsiveStack,
  useContainerBreakpoint,
} from "@vibe-labs/design-vue-responsive";
import "@vibe-labs/design-components-responsive";

VibeResponsiveContainer — Practical Examples

Responsive Card Grid

vue
<script setup lang="ts">
import { VibeResponsiveContainer, VibeResponsiveGrid } from "@vibe-labs/design-vue-responsive";

interface Product { id: number; name: string; price: number }
defineProps<{ products: Product[] }>();
</script>

<template>
  <VibeResponsiveContainer>
    <VibeResponsiveGrid :cols="1" :cols-sm="2" :cols-lg="3" :cols-xl="4">
      <div v-for="product in products" :key="product.id" class="card">
        <h3>{{ product.name }}</h3>
        <p>{{ product.price }}</p>
      </div>
    </VibeResponsiveGrid>
  </VibeResponsiveContainer>
</template>

Sidebar + Main Content Layout

vue
<script setup lang="ts">
import { VibeResponsiveContainer, VibeResponsiveStack } from "@vibe-labs/design-vue-responsive";
</script>

<template>
  <VibeResponsiveContainer name="main">
    <VibeResponsiveStack breakpoint="lg">
      <aside style="width: 280px; flex-shrink: 0">
        <slot name="sidebar" />
      </aside>
      <main style="flex: 1; min-width: 0">
        <slot />
      </main>
    </VibeResponsiveStack>
  </VibeResponsiveContainer>
</template>

Named Container for Panel Queries

vue
<template>
  <VibeResponsiveContainer name="card" type="inline">
    <VibeResponsiveGrid :cols="1" :cols-md="2">
      <slot />
    </VibeResponsiveGrid>
  </VibeResponsiveContainer>
</template>

useContainerBreakpoint — Usage Example

vue
<script setup lang="ts">
import { ref } from "vue";
import {
  VibeResponsiveContainer,
  useContainerBreakpoint,
} from "@vibe-labs/design-vue-responsive";

const containerRef = ref<HTMLElement>();
const { current, above, below, width } = useContainerBreakpoint(containerRef);
</script>

<template>
  <VibeResponsiveContainer ref="containerRef">
    <!-- Swap between rich table and compact card list -->
    <DataTable v-if="above('lg')" :columns="fullColumns" :items="items" />
    <CardList v-else :items="items" />

    <!-- Debug info -->
    <p class="debug">{{ width }}px / {{ current ?? 'xs' }}</p>
  </VibeResponsiveContainer>
</template>

Common Patterns

Adaptive Component (Component Swap at Breakpoint)

vue
<script setup lang="ts">
import { ref } from "vue";
import { useContainerBreakpoint } from "@vibe-labs/design-vue-responsive";

const containerRef = ref<HTMLElement>();
const { above } = useContainerBreakpoint(containerRef);
</script>

<template>
  <div ref="containerRef">
    <FullNav v-if="above('md')" />
    <HamburgerMenu v-else />
  </div>
</template>

2-Up on Small, 4-Up on Large

vue
<template>
  <VibeResponsiveContainer>
    <VibeResponsiveGrid :cols="2" :cols-lg="4">
      <FeatureCard v-for="feature in features" :key="feature.id" :feature="feature" />
    </VibeResponsiveGrid>
  </VibeResponsiveContainer>
</template>

Stack That Reverses Direction at Breakpoint

vue
<template>
  <!-- Stack: column → row at md. Children order: hero first on mobile, sidebar first on desktop via CSS order -->
  <VibeResponsiveContainer>
    <VibeResponsiveStack breakpoint="md">
      <div style="flex: 1"><HeroContent /></div>
      <div style="width: 320px"><SidebarWidget /></div>
    </VibeResponsiveStack>
  </VibeResponsiveContainer>
</template>

Vibe