Skip to content

Aspect Ratio

Experimental Alpha

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

Vue
Lit
React
Live Preview

Constrained max-width

4:3 box

Square (1:1)

1:1 box

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:

Mountain landscape

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>
  );
}
Open in StackBlitz

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:

bash
npx ag init --framework FRAMEWORK # react, vue, lit, svelte, etc.
npx ag add AspectRatio

The 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
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
tsx
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)
html
<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

PropTypeDefaultDescription
widthnumber1Aspect ratio width (e.g., 16)
heightnumber1Aspect ratio height (e.g., 9)
max-widthnumberundefinedOptional maximum width in pixels

CSS Parts

  • ::part(ag-aspect-ratio) - The ratio enforcement wrapper