diff --git a/src/components/BorderGlow.css b/src/components/BorderGlow.css index dbab05d..8845e27 100644 --- a/src/components/BorderGlow.css +++ b/src/components/BorderGlow.css @@ -6,6 +6,8 @@ --border-radius: 28px; --glow-padding: 40px; --cone-spread: 25; + /* Kartenfarbe nach Theme: --card-bg-dark / --card-bg-light kommen aus BorderGlow.tsx */ + --card-bg: var(--card-bg-dark, #000); position: relative; border-radius: var(--border-radius); isolation: isolate; @@ -25,6 +27,12 @@ rgba(0, 0, 0, 0.1) 0px 32px 64px; } +/* Heller Modus: helle Kartenfarbe und ein sichtbarer Rand (weiß mit 8 % Deckkraft wäre auf Weiß unsichtbar) */ +html:not(.dark) .border-glow-card { + --card-bg: var(--card-bg-light, var(--card-bg-dark, #000)); + border-color: hsl(var(--border)); +} + .border-glow-card::before, .border-glow-card::after, .border-glow-card > .edge-light { @@ -147,6 +155,20 @@ 0 0 50px 2px var(--glow-color-10, hsl(40deg 80% 80% / 10%)); } +/* Heller Modus: "plus-lighter" ginge auf einer hellen Karte im Weiß unter, und die blasse Glow-Farbe wäre kaum sichtbar. + Der Kantenschein wird deshalb normal gemischt und in einem kräftigeren Violett gezeichnet. */ +html:not(.dark) .border-glow-card > .edge-light { + mix-blend-mode: normal; +} + +html:not(.dark) .border-glow-card > .edge-light::before { + box-shadow: + inset 0 0 0 1px hsl(268deg 83% 62% / 70%), + inset 0 0 3px 0 hsl(268deg 83% 62% / 45%), + inset 0 0 12px 0 hsl(268deg 83% 62% / 30%), + inset 0 0 25px 2px hsl(268deg 83% 62% / 15%); +} + .border-glow-inner { display: flex; flex-direction: column; diff --git a/src/components/BorderGlow.tsx b/src/components/BorderGlow.tsx index aa90a62..1b4018b 100644 --- a/src/components/BorderGlow.tsx +++ b/src/components/BorderGlow.tsx @@ -56,7 +56,10 @@ interface BorderGlowProps { className?: string; edgeSensitivity?: number; glowColor?: string; + /** Kartenfarbe im dunklen Modus (und Standard, wenn lightBackgroundColor fehlt). */ backgroundColor?: string; + /** Kartenfarbe im hellen Modus; ohne Angabe gilt backgroundColor in beiden Themes. */ + lightBackgroundColor?: string; borderRadius?: number; glowRadius?: number; glowIntensity?: number; @@ -72,6 +75,7 @@ const BorderGlow = ({ edgeSensitivity = 30, glowColor = '40 80 80', backgroundColor = '#120F17', + lightBackgroundColor, borderRadius = 28, glowRadius = 40, glowIntensity = 1.0, @@ -183,7 +187,8 @@ const BorderGlow = ({ onPointerMove={animated ? undefined : handlePointerMove} className={`border-glow-card ${className}`} style={{ - '--card-bg': backgroundColor, + '--card-bg-dark': backgroundColor, + '--card-bg-light': lightBackgroundColor ?? backgroundColor, '--edge-sensitivity': edgeSensitivity, '--border-radius': `${borderRadius}px`, '--glow-padding': `${glowRadius}px`, diff --git a/src/components/Contact.tsx b/src/components/Contact.tsx index abb8f3a..e4c78bb 100644 --- a/src/components/Contact.tsx +++ b/src/components/Contact.tsx @@ -12,6 +12,7 @@ const Contact = () => { edgeSensitivity={30} glowColor="40 80 80" backgroundColor="#120F17" + lightBackgroundColor="hsl(0 0% 96%)" borderRadius={28} glowRadius={40} glowIntensity={1} diff --git a/src/components/Values.tsx b/src/components/Values.tsx index ef73f68..08af901 100644 --- a/src/components/Values.tsx +++ b/src/components/Values.tsx @@ -69,6 +69,7 @@ const Values = () => { edgeSensitivity={30} glowColor="40 80 80" backgroundColor="hsl(0 0% 6%)" + lightBackgroundColor="hsl(0 0% 96%)" borderRadius={8} glowRadius={30} glowIntensity={0.8} diff --git a/src/pages/About.tsx b/src/pages/About.tsx index 6025d32..1f83bba 100644 --- a/src/pages/About.tsx +++ b/src/pages/About.tsx @@ -105,6 +105,7 @@ const About = () => { edgeSensitivity={30} glowColor="40 80 80" backgroundColor="hsl(0 0% 4%)" + lightBackgroundColor="hsl(0 0% 96%)" borderRadius={8} glowRadius={30} glowIntensity={0.8} @@ -128,6 +129,7 @@ const About = () => { edgeSensitivity={30} glowColor="40 80 80" backgroundColor="hsl(0 0% 4%)" + lightBackgroundColor="hsl(0 0% 96%)" borderRadius={8} glowRadius={30} glowIntensity={0.8} @@ -151,6 +153,7 @@ const About = () => { edgeSensitivity={30} glowColor="40 80 80" backgroundColor="hsl(0 0% 4%)" + lightBackgroundColor="hsl(0 0% 96%)" borderRadius={8} glowRadius={30} glowIntensity={0.8} diff --git a/src/test/sofort-paket.test.tsx b/src/test/sofort-paket.test.tsx index 5c0b54d..ed99f48 100644 --- a/src/test/sofort-paket.test.tsx +++ b/src/test/sofort-paket.test.tsx @@ -600,3 +600,53 @@ describe("A4 Glow-Überschrift (Fix A)", () => { expect(weg.mock.calls.map(([typ]) => typ)).toEqual(expect.arrayContaining(["pointermove", "scroll"])); }); }); + +describe("A1 Karten (BorderGlow) im hellen Modus lesbar", () => { + // Dunkel bleibt exakt wie vorher; hell bekommt eine helle Kartenfarbe. + const KARTEN: { name: string; ui: React.ReactElement; anzahl: number; dunkel: string }[] = [ + { name: "Values (Was Sie bekommen)", ui: , anzahl: 6, dunkel: "hsl(0 0% 6%)" }, + { name: "Kontaktabschnitt (Der nächste Schritt)", ui: , anzahl: 1, dunkel: "#120F17" }, + { name: "Über uns (Der Unterschied)", ui: , anzahl: 3, dunkel: "hsl(0 0% 4%)" }, + ]; + + it.each(KARTEN)("$name: dunkle Kartenfarbe unverändert, helle Variante vorhanden", ({ ui, anzahl, dunkel }) => { + const { container } = zeige(ui); + const karten = [...container.querySelectorAll(".border-glow-card")]; + expect(karten).toHaveLength(anzahl); + for (const karte of karten) { + expect(karte.style.getPropertyValue("--card-bg-dark")).toBe(dunkel); + const hell = karte.style.getPropertyValue("--card-bg-light"); + expect(hell).not.toBe(""); + expect(hell).not.toBe(dunkel); + expect(leuchtdichte(farbe(hell))).toBeGreaterThan(0.85); + } + }); + + it.each(KARTEN)("$name: Text und Icons erreichen in beiden Themes mindestens 4,5:1", ({ ui }) => { + const { container } = zeige(ui); + const karte = container.querySelector(".border-glow-card") as HTMLElement; + const hintergrund = { hell: farbe(karte.style.getPropertyValue("--card-bg-light")), dunkel: farbe(karte.style.getPropertyValue("--card-bg-dark")) }; + for (const thema of THEMEN) { + // text-foreground (Titel, Hover-Zustand der Icons) und text-muted-foreground (Fließtext, Icons). + // Der blaue CTA-Button im Kontaktabschnitt hat eigene Farben (Weiß auf #0793CF, 3,45:1, in beiden Themes gleich) + // und gehört nicht zu den Theme-Farben; siehe Bericht. + for (const name of ["foreground", "muted-foreground"]) { + expect(kontrast(tokenFarbe(thema, name), hintergrund[thema === "hell" ? "hell" : "dunkel"])).toBeGreaterThanOrEqual(4.5); + } + } + }); + + it("BorderGlow wählt die Kartenfarbe per CSS nach Theme und zeigt im hellen Modus einen sichtbaren Rand", () => { + const css = lies("src/components/BorderGlow.css"); + expect(css).toMatch(/--card-bg:\s*var\(--card-bg-dark/); + expect(css).toMatch(/html:not\(\.dark\)\s+\.border-glow-card\s*\{[^}]*--card-bg:\s*var\(--card-bg-light/); + expect(css).toMatch(/html:not\(\.dark\)\s+\.border-glow-card\s*\{[^}]*border-color:/); + }); + + it("der Kantenschein der Karten wird im hellen Modus normal statt mit plus-lighter gemischt (sonst unsichtbar auf Weiß)", () => { + const css = lies("src/components/BorderGlow.css"); + expect(css).toMatch(/html:not\(\.dark\)\s+\.border-glow-card\s*>\s*\.edge-light\s*\{[^}]*mix-blend-mode:\s*normal/); + // dunkel bleibt wie vorher + expect(css).toMatch(/\.border-glow-card\s*>\s*\.edge-light\s*\{[^}]*mix-blend-mode:\s*plus-lighter/); + }); +});