// parts.jsx — reusable components & visuals for ALIAD site // Exports to window: Logo, LogoMark, Icon, InstaTile, DotsField, PhotoSlot, Silhouette // ─── ALIAD logo mark (the circular dots) ────────────────────────────────── function LogoMark({ size = 56, blue = '#1F5673', gold = '#E5B73B' }) { const dots = []; const outer = [[200,150],[205,195],[185,235],[150,268],[110,285],[70,285],[40,270],[22,240]]; outer.forEach(([x,y],i) => dots.push({x,y,r:22-i*1.2,c:blue})); const mid = [[205,110],[185,80],[155,60],[120,50],[85,55],[55,75],[38,105]]; mid.forEach(([x,y],i) => dots.push({x,y,r:14-i*0.6,c:i<3?gold:blue})); const inner = [[165,95],[140,78],[115,75],[92,88],[78,110],[72,138],[80,165],[98,188],[122,200],[148,200],[170,188],[183,168],[188,145],[182,125]]; inner.forEach(([x,y],i) => dots.push({x,y,r:Math.max(2.5,9-i*0.45),c:gold})); return ( {dots.map((d,i)=>())} ); } function Logo({ size = 40, color = '#C25A30', subColor = '#C25A30', tagline = true }) { return (
Association ALIAD
{tagline && (
Au service de tous les âges
)}
); } // ─── Service & UI icons ────────────────────────────────────────────────── function Icon({ name, size = 44, color = 'currentColor' }) { const s = { width:size, height:size, fill:'none', stroke:color, strokeWidth:1.6, strokeLinecap:'round', strokeLinejoin:'round' }; if (name === 'home') return (); if (name === 'family') return (); if (name === 'meal') return (); if (name === 'mediation') return (); if (name === 'wellbeing') return (); if (name === 'youth') return (); if (name === 'arrow') return (); if (name === 'phone') return (); if (name === 'mail') return (); if (name === 'pin') return (); if (name === 'clock') return (); if (name === 'play') return (); if (name === 'insta') return (); if (name === 'fb') return (); return null; } // ─── Decorative dot pattern ────────────────────────────────────────────── function DotsField({ color = '#1F5673', opacity = 0.18, w = 260, h = 260 }) { const dots = []; const cols = 8, rows = 8; for (let r = 0; r < rows; r++) for (let c = 0; c < cols; c++) { const x = 14 + c*(w-28)/(cols-1); const y = 14 + r*(h-28)/(rows-1); const d = Math.hypot(c - cols+1, r - rows+1) / Math.hypot(cols-1, rows-1); const rad = 1 + (1-d) * 5; dots.push(); } return {dots}; } // ─── Silhouette illustrations (Afro-Caribbean figures, placeholders) ───── // Soft, respectful, abstract silhouettes — warm brown skin tone, natural // hair / head wraps, suggested smile. Stand in until real photos are dropped. const SKIN_LIGHT = '#A87B5C'; const SKIN_MID = '#7E5238'; const SKIN_DEEP = '#5C3621'; const WRAP_A = '#C25A30'; // terracotta const WRAP_B = '#E5B73B'; // gold function Silhouette({ kind = 'elder-woman', bg = '#F8F1E1' }) { // viewBox 400x400 (square). Components are positioned to look composed. const figures = { 'elder-woman': ( {/* shoulders */} {/* dress accent */} {/* neck */} {/* face */} {/* head wrap (madras) */} {/* knot */} {/* eyes — gentle closed-smile arcs */} {/* smile */} {/* earrings */} ), 'elder-man': ( {/* shoulders */} {/* shirt collar */} {/* neck */} {/* face */} {/* hair — short white/grey beard suggestion */} {/* grey hair specks */} {/* short beard */} {/* eyes */} {/* smile */} ), 'grandma-child': ( {/* grandma — left */} {/* child — right */} {/* twin afro puffs */} {/* face */} ), 'family': ( {/* back layer — father */} {/* mother — right */} {/* hair — natural afro */} {/* child — front center */} ), 'caregiver': ( {/* elder — seated, left */} {/* white hair */} {/* caregiver — standing behind, right */} {/* hair pulled back */} {/* caring hand on shoulder — abstract */} ), 'ateliers': ( {/* table / surface */} {/* three small figures around a table */} {[ {x:90, hair:'#1A0F08', skin:SKIN_DEEP, top:WRAP_A, hairType:'afro'}, {x:200, hair:WRAP_A, skin:SKIN_LIGHT, top:'#234862', hairType:'wrap'}, {x:310, hair:'#1A0F08', skin:SKIN_MID, top:WRAP_B, hairType:'short'}, ].map((f, i) => ( {f.hairType === 'afro' && } {f.hairType === 'wrap' && } {f.hairType === 'short' && } ))} ), }; return ( {/* soft sun / halo */} {figures[kind] || figures['elder-woman']} ); } // Convert a Silhouette to a data URL (for use as ) function silhouetteDataUrl(kind, bg) { const tmpDiv = document.createElement('div'); ReactDOM.createRoot(tmpDiv).render(); // We can't easily serialize React-rendered SVG synchronously; instead // render the SVG markup directly with a string builder. Use a manual // approach. return null; // not used — we render as the visible background instead } // ─── PhotoSlot — drop-zone with silhouette placeholder ────────────────── // Renders the Silhouette underneath; layers an on top with a // transparent placeholder so when the user drops a real photo it covers // the silhouette cleanly. function PhotoSlot({ slotId, kind = 'elder-woman', ratio = '4/3', label, tone = 'gold', style, radius = 18 }) { const tones = { gold: '#F8E9B8', blue: '#CFE0EA', terracotta: '#F0CFBE', paper: '#F8F1E1', }; const bg = tones[tone] || tones.gold; return (
{/* silhouette fallback */}
{/* image-slot overlay — fills entire tile */} {slotId && ( )} {label && (
{label}
)}
); } // ─── Instagram-style tile ──────────────────────────────────────────────── function InstaTile({ slotId, kind, caption, date, postType = 'photo', tag }) { return (
{tag || '@aliadassociation'}
{postType === 'video' && (
Reel
)}
{caption}
{date}
); } Object.assign(window, { Logo, LogoMark, Icon, InstaTile, DotsField, PhotoSlot, Silhouette });