Skip to content

Instantly share code, notes, and snippets.

@j0hnm4r5
Last active May 28, 2025 03:57
Show Gist options
  • Save j0hnm4r5/42381b84e626be6345cf31b914171a4e to your computer and use it in GitHub Desktop.
Save j0hnm4r5/42381b84e626be6345cf31b914171a4e to your computer and use it in GitHub Desktop.
Letterboxer
import React, { useEffect, useState } from "react";
type LetterboxerProps = {
/** The target width for the letterbox in pixels */
targetWidth: number;
/** The target height for the letterbox in pixels */
targetHeight: number;
/** Whether the content can scale larger than the target dimensions */
canScaleLargerThanTarget?: boolean;
/** The background color of the letterbox */
backgroundColor?: React.CSSProperties["backgroundColor"];
/** The children to render inside the letterbox */
children: React.ReactNode;
};
/**
* Letterbox component that maintains a fixed aspect ratio for its children.
* It scales the content to fit within the viewport while preserving the target aspect ratio.
* It can optionally allow scaling larger than the target dimensions.
*
* This is useful for applications that need to maintain a specific layout or aspect ratio, even when the viewport size changes dynamically or when viewed on different devices.
*/
export const Letterboxer: React.FC<LetterboxerProps> = ({
targetWidth,
targetHeight,
canScaleLargerThanTarget = false,
backgroundColor = "black",
children,
}) => {
const [scale, setScale] = useState(1);
useEffect(() => {
const updateScale = () => {
const { innerWidth, innerHeight } = window;
const widthRatio = innerWidth / targetWidth;
const heightRatio = innerHeight / targetHeight;
const newScale = Math.min(
widthRatio,
heightRatio,
canScaleLargerThanTarget ? Infinity : 1,
);
setScale(newScale);
};
updateScale();
window.addEventListener("resize", updateScale);
return () => window.removeEventListener("resize", updateScale);
}, [targetWidth, targetHeight, canScaleLargerThanTarget]);
return (
<div
style={{
position: "fixed",
inset: 0,
backgroundColor,
}}
>
<div
style={{
overflow: "hidden",
width: `${targetWidth}px`,
height: `${targetHeight}px`,
transform: `scale(${scale})`,
transformOrigin: "top left",
marginTop: `calc(50vh - ${(targetHeight * scale) / 2}px)`,
marginLeft: `calc(50vw - ${(targetWidth * scale) / 2}px)`,
}}
>
{children}
</div>
</div>
);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment