Add decorative artist pattern background to desktop advertisements

Integrates an `ArtistPatternBg` component to wrap ad containers (`ArticleCardAd`, home page wide-picked ad section) with a decorative artist name pattern, enhancing visual appeal on desktop while leaving mobile unchanged.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 517dfa7b-26ac-463d-a6e1-a58c6df97188
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 7e26d45f-76ee-4e0c-8b18-8bffeb9e004b
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/f209e72a-0939-48fa-84fc-57854de71967/517dfa7b-26ac-463d-a6e1-a58c6df97188/jdAEdU5
Replit-Helium-Checkpoint-Created: true
This commit is contained in:
sebastjanartic 2026-03-04 13:58:11 +00:00
parent 43bce5f4e9
commit f2eb995d7d
5 changed files with 613 additions and 535 deletions

View File

@ -1,4 +1,5 @@
import { useEffect, useRef, useState } from "react";
import { useEffect, useMemo, useRef, useState } from "react";
import ArtistPatternBg from "./artist-pattern-bg";
type AdFormat = "auto" | "fluid" | "rectangle" | "horizontal" | "vertical" | "autorelaxed";
@ -77,16 +78,19 @@ export default function AdSense({
);
}
let adSeedCounter = 1;
export function ArticleCardAd() {
const seed = useMemo(() => adSeedCounter++, []);
return (
<div className="bg-card rounded-md border border-card-border overflow-hidden h-full">
<ArtistPatternBg className="rounded-md border border-card-border h-full bg-card" seed={seed}>
<AdSense
slot="3854634730"
format="fluid"
layoutKey="-6r+cy-10+8a-3"
style={{ display: "block" }}
/>
</div>
</ArtistPatternBg>
);
}

View File

@ -0,0 +1,73 @@
import { useMemo } from "react";
const ARTISTS = [
"Kastelruther Spatzen", "Melanie Payer", "Linda Feller", "Die Edlseer",
"Hansi Berger", "Sašo Avsenik", "Die Grubertaler", "Pagger Buam",
"John Prisco", "Igor und seine Oberkrainer", "Die Kaiser", "Marlena Martinelli",
"Spitzbua Markus", "Julia Raich", "Meissnitzer Band", "Tauern Echo",
"Brennholz", "Charly Kaiser", "Franz Nolf", "Franz Steiner",
"Die 3 Z'widern", "Mark Ed", "Natascha", "Meli Stein",
"SUSAL", "Pfundskerle", "Da Wadltreiber", "Sanny",
"Alex Reichinger", "Sigrid & Marina", "Folx Stadl", "Gipfelstammtisch",
];
function seededRandom(seed: number) {
let s = seed;
return () => {
s = (s * 16807 + 0) % 2147483647;
return s / 2147483647;
};
}
interface Props {
children: React.ReactNode;
className?: string;
seed?: number;
}
export default function ArtistPatternBg({ children, className = "", seed = 42 }: Props) {
const items = useMemo(() => {
const rng = seededRandom(seed);
const result: { name: string; x: number; y: number; size: number; opacity: number; rotation: number; italic: boolean }[] = [];
const count = 40;
for (let i = 0; i < count; i++) {
result.push({
name: ARTISTS[Math.floor(rng() * ARTISTS.length)],
x: rng() * 100,
y: (i / count) * 100 + (rng() - 0.5) * 8,
size: 9 + rng() * 9,
opacity: 0.03 + rng() * 0.05,
rotation: -12 + rng() * 24,
italic: rng() > 0.6,
});
}
return result;
}, [seed]);
return (
<div className={`relative overflow-hidden ${className}`}>
<div className="absolute inset-0 pointer-events-none select-none hidden lg:block" aria-hidden="true">
{items.map((item, i) => (
<span
key={i}
className="absolute whitespace-nowrap text-primary"
style={{
left: `${item.x}%`,
top: `${item.y}%`,
fontSize: `${item.size}px`,
opacity: item.opacity,
transform: `rotate(${item.rotation}deg)`,
fontStyle: item.italic ? "italic" : "normal",
fontWeight: item.italic ? 300 : 400,
}}
>
{item.name}
</span>
))}
</div>
<div className="relative z-[1]">
{children}
</div>
</div>
);
}

View File

@ -8,6 +8,7 @@ import { Skeleton } from "@/components/ui/skeleton";
import Header from "@/components/header";
import Footer from "@/components/footer";
import AdSense, { ArticleCardAd, MultiplexAd, SidebarAd } from "@/components/adsense";
import ArtistPatternBg from "@/components/artist-pattern-bg";
import { PhotoGalleryWidget } from "@/components/photo-gallery";
import { HoroscopeWidget } from "@/components/horoscope-widget";
import { RecipeWidget } from "@/components/recipe-widget";
@ -631,7 +632,7 @@ export default function Home() {
{widePickedArticles.length > 0 && (
<div className="grid grid-cols-1 lg:grid-cols-4 gap-4">
<div className="hidden lg:block rounded-lg overflow-hidden bg-card border border-card-border">
<ArtistPatternBg className="hidden lg:block rounded-lg overflow-hidden bg-card border border-card-border" seed={99}>
<AdSense
slot="3854634730"
format="fluid"
@ -639,7 +640,7 @@ export default function Home() {
style={{ display: "block" }}
className="w-full h-full min-h-[250px]"
/>
</div>
</ArtistPatternBg>
<div className="lg:col-span-3">
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
{widePickedArticles.map((a) => (

View File

@ -1,7 +1,7 @@
{
"access_token": "sl.u.AGVuqbSOwM_IaEQkqETN8XBMNt5yaozQHrwswboVTqkE2GeJpVRaGEjJYk_AEO1_PoylTdNl0pMwGO4_SS-REKTIm1Fls8sFKlJMN77GiJQJuGbB1qdrpwR22LpZDznp3rmkmoEhxiiDQiRUl5SBxBZTy19DWpx5nyV3Lc3eXvaOU9mKvXrOzvRqtDhFUxHlEKNCLO4s7buoYak9A9eqK27comnMEJMCKJ1Kqiho-ZKk9WNFkMSbdyQIAbM0Uwy0AUD4W69aSqmj41aVOT1Y_M_EepY1WnZjGX5t71iXFjTKqtbMIG_sCV9mkBITNXYfcYQwW6ZGuFW5mMBnQ1WJdjqPS_iVuL8tKfLTe2TBNq4-A3CPZwtOgIrara4-3pP6ifphxmkgeNLTD2Tw5q2Cl0KRF_ZBhoTO0PxgRaiBYhEnFWDdfp0Wsn0jk8BGk5OY4nsSDgJactpKmWT4itE1CE-R5QM1Dsd98BXkXoROf5CSc9d2UVcknM5fKp5RRNHhq_bdDl5-U2wt7SE_G_xf2pMVAZPO3m8YZWpeI21IyTJGrRe9NLSa7tkZpOTdEp3Bg_lHnh0IP2PP79-hlSsTtBQ4oclQ6iomGv-tRhcwt_hHdy71lhx2LUj4QwzyptYK_YSYCtgsMofpo7f5slJfWSP4-p0s_hU2hP2SC_Z9SLuPKn91bvaQLOu2PINxF0yNDo10Y1wUveZboN2S0KeK3tIyfNplQ2g6KEmBDGQgXlMabrkBZfSFqofhCYCX3sdqzQCJKK4IjXGmrFYozv4wjWHD0CwetaCBURmAqwdw6o7Be89HS2AbB5-iWxRDctSQALNS1VkE24lH9nelT31b_grTEZLkLZJqnbd4TjeY1b_AlPQ4p7d2dud0MekbzsDvFC7KAUdOTRDlsF_0YKYx2AohOu79Jz7jAWTaBFBQLsrr1IoemfAHDkLNhpxgQjWZZ4Wj3CMj-01IVSeRrX6BW_Gt7V_E5FgSnUTNZANYqMmNN7ggzmCwZltAI3uNLK_qi_XBN66uUDBMHoJD8MuHxXvWFa4r-rOEQh04YM6FpSltHq8I6fL0OYQe4zrrBOYMsu5Xg5NDg8e2n9iCq4s7CSYrbJrkvzJTPAjgy74hblV5mV6kryZ4YMtizfsMmY3PEH6thCby1tADA4gQSGouXwZVGm11Dc3SikM4wLPKnWwI2gU985QoB89vJmDLnv5FRvR_ryARI5_1TLuaWswfAYcZajGNvjS-7emRNTnVsuMl1E5uIcIKCXHrUIIkPGPTfe74NGrhMRi7fdGTtwMZgA0TEqiYFjEgabbqgazkz8F3tg",
"access_token": "sl.u.AGUQq1dMOSrEC_xUCcjdbFyPUzqtq04u2Ya543b_-ykVOkXeSHLtcM_d24Sslmr0M6mrqYCQ7oYeh9l4XQEZWTGYWR_rDE5R_qn48gdf5DXpKM2_pzyeQaVteA0d2It52ETJRbVN4kGsJZixcwfLXGw4LK0r6FQ7Q4FYtQaYvyZKK1hO1p3SC8jL9mC_8ZUk-M-yrNtYfKnYbm6ZrMXX1WJEtqfM3fe1xVl2r0wlpq4PkSwXn_oU_EbGy6ITeJHx5PNI7_XME_tYdNDCdm9ESI-kV15r7SDtfBQfUoVx1zGGa_wsQq_chjd-v0nCoRhIWvBtxEo_cf9FyHzyqQ9iLKid1hzPZnam5rdW87B4DQQocWk8JjcAwqU4MGQWrViT-LXb6lGiyIQo_LCJlexgdlHMl8iN05-Za5rexP_K2g7GiU7bC1wf8TMFoI8xYS0Kq7pHz-Kx2AEUzip39x0dmAbb4Y07NvGWI3Cfi0fwe_7GQeKJ9KxLBMZIbAXnIfIzqK8H49npJddDPWlXJSMH6i2f4nLH05nnlf9HmqsO05TOESzxMwTAOsEStFDW0eBXrUB-UeBT8S5sfeuvubNYUNqAVw4IXisjZ4nnpfaIytuuBgPZOzQdaQkdgpxZaJFQz6uXU0ZzPMz4OnDhStSGoj15NRJDJWbNynaNWxTWv0Gncg09AP6IEuH_KdQdckUMVdmCBrTd4vsVGd__MZfsqt_SWsgEPA-xnUXSIRvhbmXCDTJsI0VHFywVfqHXtupw6ljkauw_BMiJv0p8KZhj9NB3aWJPamsPLV4UYRn_Cy2wYY6leqiAiyUI4lmcFkGtrVzeeLDG3Ga3pfm_7WlMU6ZJNuwVprxZpjf9yKR6U6u4Bc5_WoRvr7PTTu5oNOjGD2KCdSOBPDVuSL0lyMPazz1WpJ4SNAcC47c61Bp9ywO2TtDMGkcmKr6ElWxiPn21B3Cu2nh6_sCgIDLocMGPslME1VJb-msrDu8PLGX_fEnsOJd_2pHhr_cp3E3Kt8CB4mQfEtaPDhYkEQ9FvhvmtZ2sj9gYFYs0-6wE0bBaLLQBLgkBMgV5VZycNZ_A0tiB-uymKuFHdDZXVoejIrxBuRB1EiB30ba68BcM_erBItG3riwX5PfSNqJNhx0SEMNCiKjBKUuGRz6UQj64aZXOh6yvImw0SxXw922ktqTkqOk_hpZtkeGx21JC_NXohbqZnFlAMHfLFbsZUW8iaSbIzeydUvB3Llz-92Vx0hQAj3mOnMCteHivD2ra2izz_UwzfOhQ_6nLVJeWlWp6LpwycPya32VDQCb118S3zkfyrJZHNg",
"refresh_token": "8-fiK0Io8Z8AAAAAAAAAAXn1QIGTwFTVWKF47COXY2bjqYlWyd4aRnFtQJ7usZ0y",
"expires_at": 1772628298578,
"expires_at": 1772647027085,
"app_key": "sjwprgka82p8tpv",
"app_secret": "g3vuczqo0rx3crz"
}

File diff suppressed because it is too large Load Diff