Aspect Ratio
This library is a work-in-progress. We are releasing it early to gather feedback, but it is not ready for production.
The AgnosticUI AspectRatio component provides an easy, modern way to reserve space for embedded content (videos, iframes, third-party widgets) while preserving a given aspect ratio. It leverages the native CSS aspect-ratio property and exposes a simple API.
Examples
Live Preview
Constrained max-width
Square (1:1)
CSS Shadow Parts Customization
The aspect ratio component exposes the ::part(ag-aspect-ratio) selector, allowing you to style the internal container with custom CSS:
Responsive 16:9 iframe
View Vue Code
<template>
<div>
<h2>Constrained max-width</h2>
<ag-aspect-ratio
class="mbs4"
:width="4"
:height="3"
:max-width="400"
>
<div style="background:var(--ag-background-tertiary);color:var(--vp-c-text-1);;width:100%;height:100%;display:flex;align-items:center;justify-content:center">4:3 box</div>
</ag-aspect-ratio>
<h2>Square (1:1)</h2>
<div class="full-width">
<ag-aspect-ratio
class="mbs4"
:width="1"
:height="1"
style="width:200px; margin: 0 auto;"
>
<div style="background:var(--ag-background-tertiary);color:var(--vp-c-text-1);;width:100%;height:100%;display:flex;align-items:center;justify-content:center">1:1 box</div>
</ag-aspect-ratio>
</div>
<div class="mbe32"></div>
<div class="mbe4">
<h2>CSS Shadow Parts Customization</h2>
</div>
<p class="mbe16">The aspect ratio component exposes the <code>::part(ag-aspect-ratio)</code> selector, allowing you to style the internal container with custom CSS:</p>
<div class="custom-example">
<ag-aspect-ratio
class="mbs4"
:width="16"
:height="9"
style="max-width: 600px;"
aria-label="Styled aspect ratio"
>
<img
src="https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=800"
alt="Mountain landscape"
style="width: 100%; height: 100%; object-fit: cover;"
/>
</ag-aspect-ratio>
</div>
<h2>Responsive 16:9 iframe</h2>
<ag-aspect-ratio
class="mbs4"
:width="16"
:height="9"
style="max-width: 600px;"
aria-label="16:9 iframe"
>
<iframe
title="16:9 iframe"
aria-label="16:9 iframe"
style="width:100%;height:100%;border:0;"
srcdoc="<!doctype html><html><head><meta charset='utf-8'><style>html,body{height:100%;margin:0}</style></head><body><div style='width:100%;height:100%;display:flex;align-items:center;justify-content:center;font-family:sans-serif;'>16:9 iframe</div></body></html>"
frameborder="0"
></iframe>
</ag-aspect-ratio>
</div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
import { VueAspectRatio } from "agnosticui-core/aspect-ratio/vue";
export default defineComponent({
name: "VueAspectRatio",
components: {
VueAspectRatio,
},
});
</script>
<style>
.custom-example ag-aspect-ratio::part(ag-aspect-ratio) {
border: 3px solid var(--agnostic-primary);
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
overflow: hidden;
}
</style>
Live Preview
View Lit / Web Component Code
import { LitElement, html } from 'lit';
import 'agnosticui-core/aspect-ratio';
export class AspectRatioLitExamples extends LitElement {
// Render in light DOM to access global utility classes
createRenderRoot() {
return this;
}
render() {
return html`
<div>
<h2>Constrained max-width</h2>
<ag-aspect-ratio
class="mbs4"
.width=${4}
.height=${3}
.maxWidth=${400}
>
<div style="background:var(--ag-background-tertiary);color:var(--vp-c-text-1);width:100%;height:100%;display:flex;align-items:center;justify-content:center">
4:3 box
</div>
</ag-aspect-ratio>
<h2>Square (1:1)</h2>
<div class="full-width">
<ag-aspect-ratio
class="mbs4"
.width=${1}
.height=${1}
style="width:200px; margin: 0 auto;"
>
<div style="background:var(--ag-background-tertiary);color:var(--vp-c-text-1);width:100%;height:100%;display:flex;align-items:center;justify-content:center">
1:1 box
</div>
</ag-aspect-ratio>
</div>
<div class="mbe32"></div>
<div class="mbe4">
<h2>CSS Shadow Parts Customization</h2>
</div>
<p class="mbe16">
The aspect ratio component exposes the <code>::part(ag-aspect-ratio)</code> selector, allowing you to style the internal container with custom CSS:
</p>
<div class="custom-example">
<ag-aspect-ratio
class="mbs4"
.width=${16}
.height=${9}
style="max-width: 600px;"
aria-label="Styled aspect ratio"
>
<img
src="https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=800"
alt="Mountain landscape"
style="width: 100%; height: 100%; object-fit: cover;"
/>
</ag-aspect-ratio>
</div>
<h2>Responsive 16:9 iframe</h2>
<ag-aspect-ratio
class="mbs4"
.width=${16}
.height=${9}
style="max-width: 600px;"
aria-label="16:9 iframe"
>
<iframe
title="16:9 iframe"
aria-label="16:9 iframe"
style="width:100%;height:100%;border:0;"
srcdoc="<!doctype html><html><head><meta charset='utf-8'><style>html,body{height:100%;margin:0}</style></head><body><div style='width:100%;height:100%;display:flex;align-items:center;justify-content:center;font-family:sans-serif;'>16:9 iframe</div></body></html>"
frameborder="0"
></iframe>
</ag-aspect-ratio>
<style>
.custom-example ag-aspect-ratio::part(ag-aspect-ratio) {
border: 3px solid var(--agnostic-primary);
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
overflow: hidden;
}
</style>
</div>
`;
}
}
customElements.define('aspect-ratio-lit-examples', AspectRatioLitExamples);
Interactive Preview: Click the "Open in StackBlitz" button below to see this example running live in an interactive playground.
View React Code
import { ReactAspectRatio } from "agnosticui-core/aspect-ratio/react";
export default function AspectRatioReactExamples() {
return (
<div>
<h2>Constrained max-width</h2>
<ReactAspectRatio
className="mbs4"
width={4}
height={3}
maxWidth={400}
>
<div style={{ background: 'var(--ag-background-tertiary)', color: 'var(--vp-c-text-1)', width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
4:3 box
</div>
</ReactAspectRatio>
<h2>Square (1:1)</h2>
<div className="full-width">
<ReactAspectRatio
className="mbs4"
width={1}
height={1}
style={{ width: '200px', margin: '0 auto' }}
>
<div style={{ background: 'var(--ag-background-tertiary)', color: 'var(--vp-c-text-1)', width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
1:1 box
</div>
</ReactAspectRatio>
</div>
<div className="mbe32"></div>
<div className="mbe4">
<h2>CSS Shadow Parts Customization</h2>
</div>
<p className="mbe16">
The aspect ratio component exposes the <code>::part(ag-aspect-ratio)</code> selector, allowing you to style the internal container with custom CSS:
</p>
<div className="custom-example">
<ReactAspectRatio
className="mbs4"
width={16}
height={9}
style={{ maxWidth: '600px' }}
aria-label="Styled aspect ratio"
>
<img
src="https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=800"
alt="Mountain landscape"
style={{ width: '100%', height: '100%', objectFit: 'cover' }}
/>
</ReactAspectRatio>
</div>
<h2>Responsive 16:9 iframe</h2>
<ReactAspectRatio
className="mbs4"
width={16}
height={9}
style={{ maxWidth: '600px' }}
aria-label="16:9 iframe"
>
<iframe
title="16:9 iframe"
aria-label="16:9 iframe"
style={{ width: '100%', height: '100%', border: '0' }}
srcDoc="<!doctype html><html><head><meta charset='utf-8'><style>html,body{height:100%;margin:0}</style></head><body><div style='width:100%;height:100%;display:flex;align-items:center;justify-content:center;font-family:sans-serif;'>16:9 iframe</div></body></html>"
frameBorder="0"
/>
</ReactAspectRatio>
{/* CSS Parts customization styles */}
<style>{`
.custom-example ag-aspect-ratio::part(ag-aspect-ratio) {
border: 3px solid var(--agnostic-primary);
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
overflow: hidden;
}
`}</style>
</div>
);
}
Usage
TIP
The framework examples below import AgnosticUI as an npm package. Alternatively, you can use the CLI for complete control, AI/LLM visibility, and full code ownership:
npx ag init --framework FRAMEWORK # react, vue, lit, svelte, etc.
npx ag add AspectRatioThe CLI copies source code directly into your project, giving you full visibility and control. After running npx ag add, you'll receive exact import instructions.
Vue
<template>
<section>
<VueAspectRatio :width="16" :height="9">
<iframe src="https://www.youtube.com/embed/dQw4w9WgXcQ" frameborder="0"></iframe>
</VueAspectRatio>
<VueAspectRatio :width="16" :height="9" :max-width="600">
<div style="background: #ccc; width: 100%; height: 100%; display: flex; align-items: center; justify-content: center;">
16:9 with max-width
</div>
</VueAspectRatio>
<VueAspectRatio :width="1" :height="1">
<div style="background: #667eea; width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; color: white;">
1:1 Square
</div>
</VueAspectRatio>
</section>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { VueAspectRatio } from 'agnosticui-core/aspect-ratio/vue';
export default defineComponent({
components: { VueAspectRatio }
});
</script>React
import { ReactAspectRatio } from 'agnosticui-core/aspect-ratio/react';
export default function Example() {
return (
<section>
<ReactAspectRatio width={16} height={9}>
<iframe src="https://www.youtube.com/embed/dQw4w9WgXcQ" frameborder="0"></iframe>
</ReactAspectRatio>
<ReactAspectRatio width={16} height={9} maxWidth={600}>
<div style={{ background: '#ccc', width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
16:9 with max-width
</div>
</ReactAspectRatio>
<ReactAspectRatio width={1} height={1}>
<div style={{ background: '#667eea', width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', color: 'white' }}>
1:1 Square
</div>
</ReactAspectRatio>
</section>
);
}Lit (Web Components)
<script type="module">
import 'agnosticui-core/aspect-ratio';
</script>
<section>
<ag-aspect-ratio .width="16" .height="9">
<iframe src="https://www.youtube.com/embed/dQw4w9WgXcQ" frameborder="0"></iframe>
</ag-aspect-ratio>
<ag-aspect-ratio .width="16" .height="9" .max-width="600">
<div style="background: #ccc; width: 100%; height: 100%; display: flex; align-items: center; justify-content: center;">
16:9 with max-width
</div>
</ag-aspect-ratio>
<ag-aspect-ratio .width="1" .height="1">
<div style="background: #667eea; width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; color: white;">
1:1 Square
</div>
</ag-aspect-ratio>
</section>Why use aspect-ratio?
- Cleaner code than padding-top hacks
- Prevents layout shift by reserving correct space
- Native browser support across modern browsers
Props
| Prop | Type | Default | Description |
|---|---|---|---|
width | number | 1 | Aspect ratio width (e.g., 16) |
height | number | 1 | Aspect ratio height (e.g., 9) |
max-width | number | undefined | Optional maximum width in pixels |
CSS Parts
::part(ag-aspect-ratio)- The ratio enforcement wrapper