Files
stack/apps/web/src/components/ui/MosaicLogo.tsx
Jason Woltje a5ed260fbd
All checks were successful
ci/woodpecker/push/web Pipeline was successful
feat(web): MS15 Phase 1 — Design System & App Shell (#451)
Co-authored-by: Jason Woltje <jason@diversecanvas.com>
Co-committed-by: Jason Woltje <jason@diversecanvas.com>
2026-02-22 20:57:06 +00:00

101 lines
2.8 KiB
TypeScript

"use client";
import type { CSSProperties } from "react";
export interface MosaicLogoProps {
/** Width and height in pixels (default: 36) */
size?: number;
/** Whether to animate rotation (default: false) */
spinning?: boolean;
/** Seconds for one full rotation (default: 20) */
spinDuration?: number;
/** Additional CSS classes for the root element */
className?: string;
}
/**
* MosaicLogo renders the 5-element Mosaic logo icon:
* - 4 corner squares (blue, purple, teal, amber)
* - 1 center circle (pink)
*
* Colors use CSS custom properties so they respond to theme changes.
* When `spinning` is true the logo rotates continuously, making it
* suitable for use as a loading indicator.
*/
export function MosaicLogo({
size = 36,
spinning = false,
spinDuration = 20,
className = "",
}: MosaicLogoProps): React.JSX.Element {
// Scale factor relative to the 36px reference design
const scale = size / 36;
// Derived dimensions
const squareSize = Math.round(14 * scale);
const circleSize = Math.round(11 * scale);
const borderRadius = Math.round(3 * scale);
const animationValue = spinning
? `mosaicLogoSpin ${String(spinDuration)}s linear infinite`
: undefined;
const containerStyle: CSSProperties = {
width: size,
height: size,
position: "relative",
flexShrink: 0,
animation: animationValue,
transformOrigin: "center",
};
const baseSquareStyle: CSSProperties = {
position: "absolute",
width: squareSize,
height: squareSize,
borderRadius,
};
const circleStyle: CSSProperties = {
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
width: circleSize,
height: circleSize,
borderRadius: "50%",
background: "var(--ms-pink-500)",
};
return (
<>
{spinning && (
<style>{`
@keyframes mosaicLogoSpin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
`}</style>
)}
<div style={containerStyle} className={className} role="img" aria-label="Mosaic logo">
{/* Top-left: blue */}
<div style={{ ...baseSquareStyle, top: 0, left: 0, background: "var(--ms-blue-500)" }} />
{/* Top-right: purple */}
<div style={{ ...baseSquareStyle, top: 0, right: 0, background: "var(--ms-purple-500)" }} />
{/* Bottom-right: teal */}
<div
style={{ ...baseSquareStyle, bottom: 0, right: 0, background: "var(--ms-teal-500)" }}
/>
{/* Bottom-left: amber */}
<div
style={{ ...baseSquareStyle, bottom: 0, left: 0, background: "var(--ms-amber-500)" }}
/>
{/* Center: pink circle */}
<div style={circleStyle} />
</div>
</>
);
}
export default MosaicLogo;