luminomorphism
Version:
A UI design system built around light, blur, ambient motion and perceptual feedback.
41 lines (40 loc) • 3.79 kB
JavaScript
class LMosaicGrid extends HTMLElement{constructor(){super(),this.shadow=this.attachShadow({mode:"open"}),this.cells=[],this.rows=4,this.cols=4,this.palette=["#00ffff","#ff00ff","#00ff80"],this.delay=80}static get observedAttributes(){return["rows","cols","palette","delay"]}attributeChangedCallback(e,s,i){if(e==="rows"){const t=parseInt(i);!isNaN(t)&&t>0&&(this.rows=t)}if(e==="cols"){const t=parseInt(i);!isNaN(t)&&t>0&&(this.cols=t)}if(e==="palette"&&(this.palette=i.split(",").map(t=>t.trim()).filter(Boolean)),e==="delay"){const t=parseInt(i);!isNaN(t)&&t>=0&&(this.delay=t)}this._buildGrid()}connectedCallback(){if(this.hasAttribute("rows")){const s=parseInt(this.getAttribute("rows"));!isNaN(s)&&s>0&&(this.rows=s)}if(this.hasAttribute("cols")){const s=parseInt(this.getAttribute("cols"));!isNaN(s)&&s>0&&(this.cols=s)}if(this.hasAttribute("palette")&&(this.palette=this.getAttribute("palette").split(",").map(s=>s.trim()).filter(Boolean)),this.hasAttribute("delay")){const s=parseInt(this.getAttribute("delay"));!isNaN(s)&&s>=0&&(this.delay=s)}const e=document.createElement("style");e.textContent=`
:host {
display: block;
width: 100%;
}
.grid {
display: grid;
grid-template-rows: repeat(var(--rows), 1fr);
grid-template-columns: repeat(var(--cols), 1fr);
gap: 8px;
width: 100%;
}
.cell {
position: relative;
width: 100%;
padding-top: 100%; /* Square cells */
overflow: hidden;
border-radius: 12px;
cursor: pointer;
background: rgba(255,255,255,0.05);
box-shadow:
0 4px 10px rgba(0,0,0,0.3),
inset 0 0 20px rgba(255,255,255,0.05);
transition: box-shadow 0.3s ease, filter 0.3s ease;
}
.cell-content {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
justify-content: center;
align-items: center;
color: white;
font-size: 1.2rem;
font-weight: bold;
pointer-events: none;
}
`,this.shadow.appendChild(e),this.gridEl=document.createElement("div"),this.gridEl.className="grid",this.shadow.appendChild(this.gridEl),this._buildGrid()}_buildGrid(){if(this.gridEl){this.gridEl.style.setProperty("--rows",this.rows),this.gridEl.style.setProperty("--cols",this.cols),this.gridEl.innerHTML="",this.cells=[];for(let e=0;e<this.rows;e++){const s=[];for(let i=0;i<this.cols;i++){const t=document.createElement("div");t.className="cell",t.dataset.row=e,t.dataset.col=i;const a=this.palette[Math.floor(Math.random()*this.palette.length)];t.style.background=this._makeBaseGradient(a);const r=document.createElement("div");r.className="cell-content",r.textContent="",t.appendChild(r),t.addEventListener("mouseenter",()=>{this._triggerRipple(parseInt(t.dataset.row),parseInt(t.dataset.col))}),this.gridEl.appendChild(t),s.push(t)}this.cells.push(s)}}}_makeBaseGradient(e){return`radial-gradient(circle at 30% 30%, ${e}33, ${e}05)`}_highlightCell(e){const s=this._extractColor(e.style.background);e.style.boxShadow="0 8px 20px rgba(0,0,0,0.6), 0 0 24px 10px "+s+"99",e.style.filter="brightness(1.8) saturate(1.4)"}_resetCell(e){e.style.boxShadow="0 4px 10px rgba(0,0,0,0.3), inset 0 0 20px rgba(255,255,255,0.05)",e.style.filter="brightness(1) saturate(1)"}_extractColor(e){const s=/#([0-9a-fA-F]{6})/i.exec(e);return s?"#"+s[1]:"#00ffff"}_triggerRipple(e,s){const i=new Set;for(let t=0;t<this.rows;t++)for(let a=0;a<this.cols;a++){const r=this.cells[t][a],l=(Math.abs(t-e)+Math.abs(a-s))*this.delay;i.has(r)||(i.add(r),setTimeout(()=>{this._highlightCell(r),setTimeout(()=>this._resetCell(r),this.delay*(this.rows+this.cols))},l))}}}customElements.define("l-mosaic-grid",LMosaicGrid);