Aspect-ratio tab and grouping primitives for video surfaces, shared between admin editors and the public `VideoBitesStrip` component. ## Key Components ### Types - **`VizardAspectRatio`** — Union type for Vizard clip extraction ratios: `'9:16' | '16:9' | '1:1'` - **`RatioCategory`** — Grid/tab grouping category: `'portrait' | 'square' | 'landscape'` - **`VideoTeaserWithRatio`** — Extends `VideoTeaser` with an optional `aspect_ratio` field; canonical type for display/aggregation code ### Constants - **`RATIO_GRID_CLASS`** — Tailwind grid classes per `RatioCategory` (admin editors) - **`RATIO_TO_CSS_ASPECT`** — CSS `aspect-ratio` values per category, consumed by strip cards and placeholders ### Components - **`RatioTabs`** — Tab wrapper rendering only populated ratio groups; uses `forceMount` + CSS hiding to prevent scroll-jump on tab switch. Collapses to a flat render when only one ratio has content. ### Utilities - **`detectAspectRatio(ratioString?, width?, height?)`** — Resolves a `VizardAspectRatio` from a Vizard ratio string, falling back to dimension inference. Defaults to `'9:16'`. - **`ratioToCategory(ratio)`** — Maps a `VizardAspectRatio` to its `RatioCategory` - **`groupByAspectRatio(items, getAspectRatio)`** — Buckets an array into portrait/square/landscape groups; includes `hasMultiple` flag for downstream tab-vs-grid decisions ## Usage Example ```typescript import { detectAspectRatio, groupByAspectRatio, RatioTabs, RATIO_TO_CSS_ASPECT, } from './video-ratio-tabs'; // Detect ratio from Vizard metadata, with dimension fallback const ratio = detectAspectRatio(bite.aspect_ratio, bite.width, bite.height); // → '9:16' // Group bites into buckets const { portrait, square, landscape, hasMultiple } = groupByAspectRatio( bites, (b) => detectAspectRatio(b.aspect_ratio), ); // Render tabbed grid (admin editors only) }, square: { count: square.length, render: () => }, landscape:{ count: landscape.length, render: () => }, }} defaultTab="portrait" /> // Apply CSS aspect-ratio in strip cards const aspectStyle = { aspectRatio: RATIO_TO_CSS_ASPECT['portrait'] }; // '9 / 16' ``` > **Scope note:** `RatioTabs` and `RATIO_GRID_CLASS` are admin-only (`VideoBitesEditor`, `VideoLibraryGrid`). The public `VideoBitesStrip` consumes only `detectAspectRatio` and `RATIO_TO_CSS_ASPECT`. **Source:** [`lib/components/video-ratio-tabs.tsx`](https://github.com/flamingo-stack/openframe-oss-lib/blob/main/lib/components/video-ratio-tabs.tsx)