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/cqhunits - 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:
- Wrapper: Container query provider (
container-type: size) - Aspect Scaler: Preserves combined aspect ratio (SVG + label)
- 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
| Name | Type | Default | Required | Description |
|---|---|---|---|---|
viewBoxWidth | number | — | Yes | Width of the SVG viewBox (W) |
viewBoxHeight | number | — | Yes | Height of the SVG viewBox (H) |
displayMode | "scaleToFit" | "fill" | scaleToFit | No | How the component scales within its container |
labelMode | "visible" | "hidden" | "none" | visible | No | Label display mode |
labelHeightUnits | number | 15 | No | Label height in viewBox units (L) |
minWidth | string | — | No | CSS min-width for the wrapper |
minHeight | string | — | No | CSS min-height for the wrapper |
className | string | — | No | Additional CSS classes |
style | React.CSSProperties | — | No | Additional 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.
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.
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.
<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).
<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.
<AdaptiveBox viewBoxWidth={100} viewBoxHeight={100} labelMode="none">
<AdaptiveBox.Svg>{/* SVG */}</AdaptiveBox.Svg>
{/* No AdaptiveBox.Label needed */}
</AdaptiveBox>Label Positioning
Position (Above/Below)
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)
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= viewBoxWidthH= viewBoxHeightL= 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:
<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:
<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:
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
- Learn about Default Components Sizing for component sizing and base units
- Explore Audio Parameters for parameter-based controls
- Check out Control Primitives for building custom controls
- See View System and View Primitives for custom visualizations