Auth fertig erstellt. Aktuell läuft review durch KI

This commit is contained in:
2026-08-29 14:07:44 +02:00
commit 625d026f59
70 changed files with 17032 additions and 0 deletions
+131
View File
@@ -0,0 +1,131 @@
import { useEffect, useRef, useState, type ReactNode, type CSSProperties } from 'react';
interface CrosshairProps {
children: ReactNode;
/** Optionales Label, das vor den Dimensionen angezeigt wird, z. B. "Header" */
label?: string;
/** Beliebige CSS-Farbe für Rahmen/Kreuz, überschreibt --crosshair-color */
color?: string;
/** Schraffur-Hintergrund aktivieren (.hatch Utility) */
hatch?: boolean;
/** Größe der Schraffur in px, überschreibt --hatch-size */
hatchSize?: number;
/** Live-Breite/Höhe oben links einblenden (per ResizeObserver) */
showDimensions?: boolean;
className?: string;
}
export function Crosshair({
children,
label,
color,
hatch = false,
hatchSize,
showDimensions = true,
className = '',
}: CrosshairProps) {
const ref = useRef<HTMLDivElement>(null);
const [size, setSize] = useState({ w: 0, h: 0 });
useEffect(() => {
const el = ref.current;
if (!el || !showDimensions) return;
const ro = new ResizeObserver(([entry]) => {
const { width, height } = entry.contentRect;
setSize({ w: Math.round(width), h: Math.round(height) });
});
ro.observe(el);
return () => ro.disconnect();
}, [showDimensions]);
const style: CSSProperties = {
...(color ? { ['--crosshair-color' as string]: color } : {}),
...(hatchSize ? { ['--hatch-size' as string]: `${hatchSize}px` } : {}),
};
return (
<div
ref={ref}
className={`crosshair ${hatch ? 'hatch' : ''} ${className}`.trim()}
style={style}
>
{showDimensions && (
<span className="pointer-events-none absolute left-1 top-1 z-50 rounded bg-black/70 px-1.5 py-0.5 font-mono text-[10px] leading-none text-white">
{label ? `${label} · ` : ''}
{size.w}×{size.h}
</span>
)}
{children}
</div>
);
}
/** Zeigt den berechneten z-index eines Elements als Badge oben rechts an. */
export function ZIndexBadge({
children,
className = '',
}: {
children: ReactNode;
className?: string;
}) {
const ref = useRef<HTMLDivElement>(null);
const [z, setZ] = useState('auto');
useEffect(() => {
const el = ref.current;
if (!el) return;
setZ(getComputedStyle(el).zIndex);
}, []);
return (
<div ref={ref} className={`relative ${className}`.trim()}>
<span className="pointer-events-none absolute right-1 top-1 z-[9999] rounded bg-black/70 px-1.5 py-0.5 font-mono text-[10px] leading-none text-white">
z: {z}
</span>
{children}
</div>
);
}
/**
* Definiert die SVG-Filter für die colorblind-* Utilities aus dev-utilities.css.
* Einmal im App-Root rendern (z. B. direkt unter <body>/<App>) — erzeugt selbst
* kein sichtbares Element, nur die <filter>-Definitionen im DOM.
*/
export function ColorblindFilters() {
return (
<svg aria-hidden="true" className="absolute h-0 w-0 overflow-hidden">
<defs>
<filter id="protanopia">
<feColorMatrix
type="matrix"
values="0.567 0.433 0 0 0
0.558 0.442 0 0 0
0 0.242 0.758 0 0
0 0 0 1 0"
/>
</filter>
<filter id="deuteranopia">
<feColorMatrix
type="matrix"
values="0.625 0.375 0 0 0
0.7 0.3 0 0 0
0 0.3 0.7 0 0
0 0 0 1 0"
/>
</filter>
<filter id="tritanopia">
<feColorMatrix
type="matrix"
values="0.95 0.05 0 0 0
0 0.433 0.567 0 0
0 0.475 0.525 0 0
0 0 0 1 0"
/>
</filter>
</defs>
</svg>
);
}