AdaptiveBox

Complete reference for AdaptiveBox component, the layout system that powers all AudioUI controls.

AdaptiveBox is the layout component that powers all AudioUI controls. It provides a CSS-only layout system for SVG-based controls with optional labels, ensuring responsive behavior without JavaScript overhead.

Overview

AdaptiveBox solves the layout challenge of combining SVG graphics with text labels in a responsive, container-aware way. It uses CSS container queries and aspect-ratio calculations to create a scalable layout system.

Key Features

  • Pure CSS layout: No ResizeObserver or JavaScript calculations
  • Container queries: Responsive sizing using cqw/cqh units
  • Aspect-preserving scaling: Maintains component proportions
  • Label integration: Seamless label positioning and alignment
  • Performance optimized: Zero JavaScript overhead for layout

Architecture

AdaptiveBox uses a three-layer structure:

  1. Wrapper: Container query provider (container-type: size)
  2. Aspect Scaler: Preserves combined aspect ratio (SVG + label)
  3. Content: SVG and label in a two-row grid

DOM Structure

<AdaptiveBox viewBoxWidth={100} viewBoxHeight={100} labelHeightUnits={15}>
  <AdaptiveBox.Svg>
    {/* SVG content */}
  </AdaptiveBox.Svg>
  <AdaptiveBox.Label>Label Text</AdaptiveBox.Label>
</AdaptiveBox>

Props Reference

AdaptiveBox Props

NameTypeDefaultRequiredDescription
viewBoxWidthnumberYesWidth of the SVG viewBox (W)
viewBoxHeightnumberYesHeight of the SVG viewBox (H)
displayMode"scaleToFit" | "fill"scaleToFitNoHow the component scales within its container
labelMode"visible" | "hidden" | "none"visibleNoLabel display mode
labelHeightUnitsnumber15NoLabel height in viewBox units (L)
minWidthstringNoCSS min-width for the wrapper
minHeightstringNoCSS min-height for the wrapper
classNamestringNoAdditional CSS classes
styleReact.CSSPropertiesNoAdditional inline styles

AdaptiveBox.Svg Props

type AdaptiveBoxSvgProps = {
  viewBoxWidth?: number; // Override from AdaptiveBox (rarely needed)
  viewBoxHeight?: number; // Override from AdaptiveBox (rarely needed)
  hAlign?: "start" | "center" | "end"; // Horizontal alignment
  vAlign?: "start" | "center" | "end"; // Vertical alignment
  preserveAspectRatio?: string; // SVG preserveAspectRatio (auto-set in fill mode)
  onWheel?: (e: WheelEvent) => void; // Wheel event handler
  onMouseDown?: (e: MouseEvent) => void; // Mouse down handler
  // ... other SVG event handlers
};

AdaptiveBox.Label Props

type AdaptiveBoxLabelProps = {
  position?: "above" | "below"; // Default: "below"
  align?: "start" | "center" | "end"; // Default: "center"
  labelMode?: "visible" | "hidden" | "none"; // Override from AdaptiveBox
  children?: React.ReactNode; // Label content
};

Display Modes

scaleToFit (Default)

The component scales to fit within its container while preserving aspect ratio. Letterboxing occurs on the non-limiting axis.

ScaleToFit.tsx
import { AdaptiveBox } from "@cutoff/audio-ui-react";
 
export default function ScaleToFitExample() {
  return (
    <div style={{ width: "200px", height: "150px", border: "1px solid #ccc" }}>
      <AdaptiveBox
        viewBoxWidth={100}
        viewBoxHeight={100}
        labelHeightUnits={15}
        displayMode="scaleToFit"
      >
        <AdaptiveBox.Svg>
          <circle cx="50" cy="50" r="40" fill="currentColor" />
        </AdaptiveBox.Svg>
        <AdaptiveBox.Label>Scale to Fit</AdaptiveBox.Label>
      </AdaptiveBox>
    </div>
  );
}

fill

The component fills its container completely. The SVG may distort to fill the available space.

Fill.tsx
import { AdaptiveBox } from "@cutoff/audio-ui-react";
 
export default function FillExample() {
  return (
    <div style={{ width: "200px", height: "150px", border: "1px solid #ccc" }}>
      <AdaptiveBox
        viewBoxWidth={100}
        viewBoxHeight={100}
        labelHeightUnits={15}
        displayMode="fill"
      >
        <AdaptiveBox.Svg>
          <circle cx="50" cy="50" r="40" fill="currentColor" />
        </AdaptiveBox.Svg>
        <AdaptiveBox.Label>Fill Container</AdaptiveBox.Label>
      </AdaptiveBox>
    </div>
  );
}

Label Modes

visible (Default)

Label is rendered and visible. Space is reserved in the layout.

LabelVisible.tsx
<AdaptiveBox viewBoxWidth={100} viewBoxHeight={100} labelMode="visible">
  <AdaptiveBox.Svg>{/* SVG */}</AdaptiveBox.Svg>
  <AdaptiveBox.Label>Visible Label</AdaptiveBox.Label>
</AdaptiveBox>

hidden

Label space is reserved but the label is not rendered (useful for consistent sizing).

LabelHidden.tsx
<AdaptiveBox viewBoxWidth={100} viewBoxHeight={100} labelMode="hidden">
  <AdaptiveBox.Svg>{/* SVG */}</AdaptiveBox.Svg>
  <AdaptiveBox.Label>Hidden Label</AdaptiveBox.Label>
</AdaptiveBox>

none

No label space is reserved. Component uses SVG-only aspect ratio.

LabelNone.tsx
<AdaptiveBox viewBoxWidth={100} viewBoxHeight={100} labelMode="none">
  <AdaptiveBox.Svg>{/* SVG */}</AdaptiveBox.Svg>
  {/* No AdaptiveBox.Label needed */}
</AdaptiveBox>

Label Positioning

Position (Above/Below)

LabelPosition.tsx
import { AdaptiveBox } from "@cutoff/audio-ui-react";
 
export default function LabelPositionExample() {
  return (
    <div style={{ display: "flex", gap: "20px" }}>
      <AdaptiveBox viewBoxWidth={100} viewBoxHeight={100}>
        <AdaptiveBox.Svg>{/* SVG */}</AdaptiveBox.Svg>
        <AdaptiveBox.Label position="above">Above</AdaptiveBox.Label>
      </AdaptiveBox>
      <AdaptiveBox viewBoxWidth={100} viewBoxHeight={100}>
        <AdaptiveBox.Svg>{/* SVG */}</AdaptiveBox.Svg>
        <AdaptiveBox.Label position="below">Below</AdaptiveBox.Label>
      </AdaptiveBox>
    </div>
  );
}

Alignment (Start/Center/End)

LabelAlignment.tsx
import { AdaptiveBox } from "@cutoff/audio-ui-react";
 
export default function LabelAlignmentExample() {
  return (
    <div style={{ display: "flex", gap: "20px" }}>
      <AdaptiveBox viewBoxWidth={100} viewBoxHeight={100}>
        <AdaptiveBox.Svg>{/* SVG */}</AdaptiveBox.Svg>
        <AdaptiveBox.Label align="start">Start</AdaptiveBox.Label>
      </AdaptiveBox>
      <AdaptiveBox viewBoxWidth={100} viewBoxHeight={100}>
        <AdaptiveBox.Svg>{/* SVG */}</AdaptiveBox.Svg>
        <AdaptiveBox.Label align="center">Center</AdaptiveBox.Label>
      </AdaptiveBox>
      <AdaptiveBox viewBoxWidth={100} viewBoxHeight={100}>
        <AdaptiveBox.Svg>{/* SVG */}</AdaptiveBox.Svg>
        <AdaptiveBox.Label align="end">End</AdaptiveBox.Label>
      </AdaptiveBox>
    </div>
  );
}

Layout Algorithm

Step 1: Wrapper Setup

The wrapper fills its parent and provides container query units:

.wrapper {
  width: 100%;
  height: 100%;
  container-type: size; /* Enables cqw/cqh units */
}

Step 2: Aspect Calculation

The aspect scaler calculates the combined aspect ratio:

Aspect Ratio = W / (H + L)

Where:

  • W = viewBoxWidth
  • H = viewBoxHeight
  • L = labelHeightUnits

Step 3: Scaling

In scaleToFit mode:

.aspect-scaler {
  aspect-ratio: W / (H + L);
  width: min(100%, calc(100cqh * W / (H + L)));
  height: auto;
}

In fill mode:

.aspect-scaler {
  width: 100%;
  height: 100%;
}

Step 4: Grid Layout

The scaler uses a two-row grid:

.grid-template-rows: H_percent% L_percent%;

Label below (default): SVG in row 1, Label in row 2 Label above: Label in row 1, SVG in row 2

Alignment

Scaler Alignment

Control how the scaler aligns within the wrapper:

ScalerAlignment.tsx
<AdaptiveBox
  viewBoxWidth={100}
  viewBoxHeight={100}
  style={{
    justifySelf: "start", // or "center", "end"
    alignSelf: "start", // or "center", "end"
  }}
>
  <AdaptiveBox.Svg>{/* SVG */}</AdaptiveBox.Svg>
  <AdaptiveBox.Label>Aligned</AdaptiveBox.Label>
</AdaptiveBox>

Or use hAlign/vAlign on AdaptiveBox.Svg:

SvgAlignment.tsx
<AdaptiveBox viewBoxWidth={100} viewBoxHeight={100}>
  <AdaptiveBox.Svg hAlign="start" vAlign="center">
    {/* SVG */}
  </AdaptiveBox.Svg>
  <AdaptiveBox.Label>Aligned</AdaptiveBox.Label>
</AdaptiveBox>

Overlays

Add HTML content overlays above the SVG:

Overlay.tsx
import { AdaptiveBox } from "@cutoff/audio-ui-react";
 
export default function OverlayExample() {
  return (
    <AdaptiveBox viewBoxWidth={100} viewBoxHeight={100}>
      <AdaptiveBox.Svg>
        <circle cx="50" cy="50" r="40" fill="currentColor" />
      </AdaptiveBox.Svg>
      <div
        style={{
          position: "absolute",
          top: 0,
          left: 0,
          width: "100%",
          height: "100%",
          pointerEvents: "none",
          gridRow: "1 / 2", // Same row as SVG
          display: "flex",
          alignItems: "center",
          justifyContent: "center",
          zIndex: 10,
        }}
      >
        <span style={{ fontSize: "24px", fontWeight: "bold" }}>50%</span>
      </div>
      <AdaptiveBox.Label>With Overlay</AdaptiveBox.Label>
    </AdaptiveBox>
  );
}

Performance Considerations

  • Pure CSS: No JavaScript calculations for layout
  • Container queries: Browser-optimized responsive sizing
  • CSS variables: Computed once, cached by browser
  • No layout shifts: Dimensions available from first render
  • Hardware acceleration: CSS transforms and aspect-ratio are GPU-accelerated

Integration with Components

All AudioUI components use AdaptiveBox internally. You typically don't need to use AdaptiveBox directly unless building custom controls. However, understanding AdaptiveBox helps when:

  • Building custom controls with Control Primitives
  • Creating custom layouts
  • Understanding component sizing behavior
  • Debugging layout issues

Next Steps