Appearance
@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
| Prop | Type | Default | Description |
|---|---|---|---|
tag | string | "div" | HTML element to render |
type | ContainerType | "inline" | inline · size · normal |
name | ContainerName | — | main · sidebar · card · panel |
Slots
| Slot | Description |
|---|---|
default | Content |
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
| Prop | Type | Default | Description |
|---|---|---|---|
tag | string | "div" | HTML element to render |
cols | GridColumn | 1 | Default column count |
colsXs | GridColumn | — | Columns at xs (480px) |
colsSm | GridColumn | — | Columns at sm (640px) |
colsMd | GridColumn | — | Columns at md (768px) |
colsLg | GridColumn | — | Columns at lg (1024px) |
colsXl | GridColumn | — | Columns at xl (1280px) |
cols2xl | GridColumn | — | Columns at 2xl (1536px) |
Gap defaults to var(--responsive-grid-gap). Override with spacing utility classes or inline style.
Slots
| Slot | Description |
|---|---|
default | Grid 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
| Prop | Type | Default | Description |
|---|---|---|---|
tag | string | "div" | HTML element to render |
breakpoint | Breakpoint | "md" | Breakpoint to switch to row |
Gap defaults to var(--responsive-stack-gap). Override with spacing utility classes or inline style.
Slots
| Slot | Description |
|---|---|
default | Stack 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
| Param | Type | Description |
|---|---|---|
target | MaybeRefOrGetter<HTMLElement | null> | Container element to observe |
Returns
| Property | Type | Description |
|---|---|---|
current | Ref<Breakpoint | null> | Largest matching breakpoint, or null if below xs |
above | (bp: Breakpoint) => boolean | True when container width >= breakpoint |
below | (bp: Breakpoint) => boolean | True when container width < breakpoint |
width | Ref<number> | Current container width in pixels |
Cleans up automatically via onScopeDispose.
Dependencies
| Package | Purpose |
|---|---|
@vibe-labs/design-components-responsive | CSS + TypeScript types |
@vibe-labs/core | Shared utilities |
vue (peer) | Vue 3.5+ |
Build
bash
npm run buildUsage 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>