Building & Designing an Interactive Multi-Festival Calendar in Oracle APEX with a Deep Dive into Diwali & Christmas
Introduction
Oracle APEX is widely used for building business applications, but it can also be leveraged to create visually engaging and interactive user experiences.
In this blog, we explore how to design and build a multi-festival calendar solution in Oracle APEX, along with detailed implementations for individual festivals. We start with a unified multi-festival calendar that combines multiple celebrations into a single dynamic interface, and then dive deeper into specific patterns through a highlight-based Diwali calendar and a countdown-style Christmas calendar.
Using simple technologies like HTML, CSS, JavaScript, and Dynamic Actions, we create rich, interactive UI components without relying on plugins or complex backend logic.
This approach demonstrates how Oracle APEX can go beyond traditional use cases and be used to build modern, user-centric, and visually compelling applications.
Step by Step Approaches
PART 1 - Implementing the Interactive Multi-Festival Calendar in Oracle APEX
In this part, we integrate Diwali, Christmas, Pongal, and Ramadan into one dynamic interface, allowing users to switch between festivals, view country-specific holidays, and interact with detailed information through tooltips and panels.
This section focuses on creating a scalable, reusable, and fully interactive calendar architecture using Dynamic Actions, JavaScript, and CSS in Oracle APEX.
🧱 Step 1: Create Page Items
🧱 Step 2: Add Page Inline CSS
/* Collapsible selector bar */
#festival_selector_reg{background-color:#1a0a2e!important;border:1.5px solid #ff6b1a!important;border-radius:10px}
#festival_selector_reg .t-Region-header{background-color:#1a0a2e!important}
#festival_selector_reg .t-Region-headerTitle,#festival_selector_reg .t-Icon{color:#ffd700!important;font-family:'Philosopher',serif}
/* Main container */
.fc-container{display:flex;min-height:580px;border-radius:14px;overflow:hidden;box-shadow:0 16px 60px rgba(0,0,0,0.5)}
/* Sidebar */
.fc-sidebar{width:32%;padding:44px 28px;position:relative;overflow:hidden;transition:background 0.5s}
.fc-sidebar::before{content:'';position:absolute;inset:0;background-image:radial-gradient(circle,rgba(255,215,0,0.08) 1px,transparent 1px);background-size:24px 24px;pointer-events:none}
/* 4 Festival Sidebar Themes */
.fc-sidebar.theme-christmas{background:linear-gradient(160deg,#0d2818,#1a3d2b,#0a1f12)}
.fc-sidebar.theme-diwali {background:linear-gradient(160deg,#0a0318,#1a0a2e,#0d0520)}
.fc-sidebar.theme-pongal {background:linear-gradient(160deg,#1a0f00,#3d2200,#1f1000)}
.fc-sidebar.theme-ramadan {background:linear-gradient(160deg,#050a18,#0a1530,#060e22)}
.fc-festival-icon{font-size:3rem;display:block;margin-bottom:10px;animation:iconGlow 2s ease-in-out infinite}
@keyframes iconGlow{0%,100%{filter:drop-shadow(0 0 16px rgba(255,165,0,0.8))}50%{filter:drop-shadow(0 0 30px rgba(255,215,0,1))}}
.fc-title{font-family:'Philosopher',serif;font-size:2.2rem;font-weight:700;letter-spacing:5px;color:#ffd700;text-shadow:0 2px 18px rgba(255,215,0,0.4)}
.fc-tagline{font-size:0.75rem;color:rgba(255,255,255,0.4);letter-spacing:3px;text-transform:uppercase;margin-top:4px}
.fc-quote-section{border-left:3px solid #ff6b1a;padding-left:14px;margin-top:24px}
.fc-quote{font-family:'Philosopher',serif;font-size:1rem;color:#ffd700;font-style:italic}
.fc-country-label{font-size:0.8rem;color:#ff9b6b;margin-top:12px;display:block;font-weight:600}
.fc-ornaments{font-size:1.2rem;margin-top:20px;letter-spacing:5px}
/* Calendar right panel */
.fc-calendar-main{width:68%;padding:24px 20px;background:#0f0a1a;overflow-y:auto}
.fc-month-hdr{font-family:'Philosopher',serif;font-size:1.05rem;color:#ff9b6b;margin:14px 0 8px;padding-bottom:5px;border-bottom:1px solid rgba(255,107,26,0.22)}
.fc-weekdays{display:grid;grid-template-columns:repeat(7,1fr);gap:3px;margin-bottom:3px}
.fc-wd{text-align:center;font-family:'JetBrains Mono',monospace;font-size:0.58rem;color:rgba(255,215,0,0.45);text-transform:uppercase;padding:3px 0}
.fc-grid{display:grid;grid-template-columns:repeat(7,1fr);gap:3px;margin-bottom:16px}
.fc-day{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:7px 2px;border-radius:7px;min-height:48px;background:rgba(255,255,255,0.03);border:1px solid rgba(255,255,255,0.05);cursor:default;position:relative;transition:transform 0.15s}
.fc-day.empty{background:transparent;border:none;pointer-events:none}
.fc-day.has-info{cursor:pointer}
.fc-day.has-info:hover{transform:scale(1.08);z-index:10}
.fc-day-num{font-size:0.92rem;font-weight:600;color:rgba(240,220,180,0.85);line-height:1}
.fc-day-lbl{font-size:0.48rem;color:rgba(255,255,255,0.3);margin-top:3px;text-align:center}
.fc-day.day-past{opacity:0.28}
.fc-day.day-today{background:rgba(46,204,113,0.18)!important;border-color:#2ecc71!important;box-shadow:0 0 10px rgba(46,204,113,0.3)}
.fc-day.day-today .fc-day-num{color:#2ecc71}
Section B - Festival Day Highlights (all 4 festivals)
/* Christmas */
.fc-day.xmas-main{background:linear-gradient(135deg,rgba(192,57,43,0.28),rgba(231,76,60,0.18))!important;border-color:#f1c40f!important;border-width:2px!important;animation:xmasGlow 2s ease-in-out infinite}
.fc-day.xmas-main .fc-day-num{color:#f1c40f}
@keyframes xmasGlow{0%,100%{box-shadow:0 0 20px rgba(241,196,15,0.55)}50%{box-shadow:0 0 38px rgba(241,196,15,0.9)}}
/* Diwali */
.fc-day.dw-main {background:linear-gradient(135deg,rgba(255,69,0,0.28),rgba(255,107,26,0.18))!important;border-color:#ff4500!important;border-width:2px!important;animation:diwaliGlow 1.8s ease-in-out infinite}
.fc-day.dw-main .fc-day-num{color:#ff6b1a}
@keyframes diwaliGlow{0%,100%{box-shadow:0 0 18px rgba(255,69,0,0.6)}50%{box-shadow:0 0 36px rgba(255,107,26,0.95)}}
.fc-day.dw-dhant{background:rgba(255,215,0,0.14)!important;border-color:#ffd700!important;box-shadow:0 0 12px rgba(255,215,0,0.4)}
.fc-day.dw-dhant .fc-day-num{color:#ffd700}
.fc-day.dw-narak{background:rgba(255,170,0,0.14)!important;border-color:#ffaa00!important;box-shadow:0 0 10px rgba(255,170,0,0.4)}
.fc-day.dw-narak .fc-day-num{color:#ffaa00}
.fc-day.dw-govar{background:rgba(255,107,26,0.14)!important;border-color:#ff6b1a!important;box-shadow:0 0 10px rgba(255,107,26,0.35)}
.fc-day.dw-govar .fc-day-num{color:#ff9b6b}
.fc-day.dw-bhai {background:rgba(155,77,255,0.14)!important;border-color:#9b4dff!important;box-shadow:0 0 10px rgba(155,77,255,0.35)}
.fc-day.dw-bhai .fc-day-num{color:#c084ff}
/* Pongal */
.fc-day.pg-main {background:linear-gradient(135deg,rgba(255,200,0,0.28),rgba(255,150,0,0.18))!important;border-color:#ffc800!important;border-width:2px!important;animation:pongalGlow 2s ease-in-out infinite}
.fc-day.pg-main .fc-day-num{color:#ffc800}
@keyframes pongalGlow{0%,100%{box-shadow:0 0 18px rgba(255,200,0,0.55)}50%{box-shadow:0 0 36px rgba(255,180,0,0.9)}}
.fc-day.pg-bhogi {background:rgba(255,120,0,0.18)!important;border-color:#ff7800!important;box-shadow:0 0 12px rgba(255,120,0,0.45)}
.fc-day.pg-bhogi .fc-day-num{color:#ff9840}
.fc-day.pg-mattu {background:rgba(80,160,40,0.18)!important;border-color:#50a028!important;box-shadow:0 0 10px rgba(80,160,40,0.4)}
.fc-day.pg-mattu .fc-day-num{color:#80d050}
.fc-day.pg-kaanum{background:rgba(0,180,160,0.16)!important;border-color:#00b4a0!important;box-shadow:0 0 10px rgba(0,180,160,0.35)}
.fc-day.pg-kaanum .fc-day-num{color:#40d4c0}
/* Ramadan */
.fc-day.rm-start{background:rgba(30,80,180,0.20)!important;border-color:#4a90d9!important;box-shadow:0 0 12px rgba(74,144,217,0.45)}
.fc-day.rm-start .fc-day-num{color:#7ab8f0}
.fc-day.rm-qadr {background:linear-gradient(135deg,rgba(255,215,0,0.22),rgba(200,160,0,0.14))!important;border-color:#ffd700!important;border-width:2px!important;animation:qadrGlow 2s ease-in-out infinite}
.fc-day.rm-qadr .fc-day-num{color:#ffd700}
@keyframes qadrGlow{0%,100%{box-shadow:0 0 18px rgba(255,215,0,0.5)}50%{box-shadow:0 0 36px rgba(255,215,0,0.9)}}
.fc-day.rm-eid {background:linear-gradient(135deg,rgba(0,180,80,0.25),rgba(0,140,60,0.15))!important;border-color:#00c860!important;border-width:2px!important;animation:eidGlow 2s ease-in-out infinite}
.fc-day.rm-eid .fc-day-num{color:#00e870}
@keyframes eidGlow{0%,100%{box-shadow:0 0 18px rgba(0,180,80,0.5)}50%{box-shadow:0 0 36px rgba(0,200,90,0.9)}}
🧱 Step 3: Add the Static HTML Region
Create a Static Content region. Set Template: Blank with Attributes. Paste this HTML into Source:
<!-- Floating hover tooltip (hidden by default) -->
<div id="fc-tooltip" class="fc-tooltip"></div>
<!-- Main calendar container -->
<div class="fc-container">
<!-- Sidebar -->
<div class="fc-sidebar theme-diwali" id="fc-sidebar">
<span class="fc-festival-icon" id="fc-icon">🪔</span>
<h1 class="fc-title" id="fc-title">DIWALI</h1>
<div class="fc-tagline" id="fc-tagline">Festival of Lights 2025</div>
<div class="fc-quote-section">
<p class="fc-quote" id="fc-quote">
दीपावली की शुभकामनाएं
</p>
<span class="fc-country-label" id="fc-country-label">
🌍 Select festival above
</span>
</div>
<div class="fc-ornaments" id="fc-ornaments">🪔 ✨ 🎆</div>
</div>
<!-- Calendar grids (JS fills these) -->
<div class="fc-calendar-main">
<div id="fc-months"></div>
</div>
</div>
<!-- Click Detail Panel (hidden until a festival day is clicked) -->
<div id="fc-detail-panel">
<div class="dp-header">
<div class="dp-title" id="dp-title">Country Leave Details</div>
<button class="dp-close"
onclick="document.getElementById('fc-detail-panel').className=''">✕</button>
</div>
<div class="dp-body" id="dp-body"></div>
</div>
🧱 Step 4: Create the 4 Dynamic Actions
All 4 DAs go on P4_FESTIVAL → Change with Fire on Initialization: YES and Action type Execute JavaScript Code.
DA1 - Festival Data | Sequence 10
Stores all country arrays as window globals so the other DAs can access them:
window.XMAS_DATA = [
["🇮🇳","India","Christmas Day","Restricted","1 day"],
["🇺🇸","USA","Christmas Day","Public","1 day"],
["🇬🇧","UK","Christmas Day + Boxing","Public","2 days"],
["🇸🇬","Singapore","Christmas Day","Public","1 day"],
["🇦🇺","Australia","Christmas Day + Boxing","Public","2 days"],
["🇨🇦","Canada","Christmas Day + Boxing","Public","2 days"],
["🇩🇪","Germany","1st & 2nd Christmas Day","Public","2 days"],
["🇵🇭","Philippines","Christmas Day","Public","1 day"]
];
window.DIWALI_DATA = [
["🇮🇳","India","Diwali / Deepawali","Public","1 day"],
["🇸🇬","Singapore","Deepavali","Public","1 day"],
["🇬🇧","UK","Diwali","Optional","1 day"],
["🇺🇸","USA","Diwali (recognized)","Optional","1 day"],
["🇨🇦","Canada","Diwali","Optional","1 day"],
["🇦🇺","Australia","Diwali","Optional","1 day"]
];
window.PONGAL_DATA = [
["🇮🇳","India (Tamil Nadu)","Thai Pongal","Public","4 days"],
["🇱🇰","Sri Lanka","Thai Pongal","Public","1 day"],
["🇸🇬","Singapore","Pongal","Optional","1 day"],
["🇲🇾","Malaysia","Thai Pongal","Public","1 day"],
["🇬🇧","UK","Pongal","Optional","1 day"],
["🇺🇸","USA","Pongal","Optional","1 day"]
];
window.RAMADAN_DATA = [
["🇮🇳","India","Ramadan / Ramzan","Public","1 day"],
["🇸🇦","Saudi Arabia","Ramadan","Public","30 days"],
["🇦🇪","UAE","Ramadan","Public","30 days"],
["🇵🇰","Pakistan","Ramadan","Public","1 day"],
["🇬🇧","UK","Ramadan","Optional","1 day"],
["🇺🇸","USA","Ramadan","Optional","1 day"],
["🇸🇬","Singapore","Ramadan / Hari Raya","Public","1 day"],
["🇲🇾","Malaysia","Ramadan / Hari Raya","Public","1 day"]
];
DA2 - Festival Config | Sequence 20
Builds window.FC config object based on selected festival. Updates sidebar text:
var fv=apex.item("P4_FESTIVAL").getValue();
var mEl=document.getElementById("fc-months");
if(!mEl||!fv){return;}
mEl.innerHTML="";
var XD=window.XMAS_DATA||[];
var DD=window.DIWALI_DATA||[];
var PD=window.PONGAL_DATA||[];
var RD=window.RAMADAN_DATA||[];
var isX=(fv==="CHRISTMAS");
var isDW=(fv==="DIWALI");
var isPG=(fv==="PONGAL");
var isR25=(fv==="RAMADAN2025");
var isR26=(fv==="RAMADAN2026");
if(isX){
window.FC={
icon:"🎄",title:"CHRISTMAS",tagline:"Advent Calendar 2025",
quote:"Peace on earth, goodwill to all.",
ornaments:"🎁 🔔 ⭐ 🦌 🎅",
sideClass:"fc-sidebar theme-christmas",
countries:XD,months:[[2025,11]],
festDates:{"2025-12-25":{cls:"xmas-main",lbl:"Christmas",data:XD}}
};
} else if(isDW){
window.FC={
icon:"🪔",title:"DIWALI",tagline:"Festival of Lights 2025",
quote:"Happy Diwali!",
ornaments:"🪔 ✨ 🎆 🪷 🎇",
sideClass:"fc-sidebar theme-diwali",
countries:DD,months:[[2025,9],[2025,10]],
festDates:{
"2025-10-18":{cls:"dw-dhant",lbl:"Dhanteras",data:DD},
"2025-10-19":{cls:"dw-narak",lbl:"Narak Chat.",data:DD},
"2025-10-20":{cls:"dw-main",lbl:"Diwali",data:DD},
"2025-10-21":{cls:"dw-govar",lbl:"Govardhan",data:DD},
"2025-10-22":{cls:"dw-bhai",lbl:"Bhai Dooj",data:DD}
}
};
} else if(isPG){
window.FC={
icon:"🌾",title:"PONGAL",tagline:"Harvest Festival 2026",
quote:"Pongalo Pongal!",
ornaments:"🌾 🐄 ☀️ 🪔 🌺",
sideClass:"fc-sidebar theme-pongal",
countries:PD,months:[[2026,0]],
festDates:{
"2026-01-13":{cls:"pg-bhogi",lbl:"Bhogi",data:PD},
"2026-01-14":{cls:"pg-main",lbl:"Thai Pongal",data:PD},
"2026-01-15":{cls:"pg-mattu",lbl:"Mattu Pongal",data:PD},
"2026-01-16":{cls:"pg-kaanum",lbl:"Kaanum Pongal",data:PD}
}
};
} else if(isR25){
window.FC={
icon:"☪️",title:"RAMADAN",tagline:"Ramadan 1446H - 2025",
quote:"Ramadan Mubarak!",
ornaments:"☪️ 🌙 ✨ 🕌 🌟",
sideClass:"fc-sidebar theme-ramadan",
countries:RD,months:[[2025,1],[2025,2]],
festDates:{
"2025-02-28":{cls:"rm-start",lbl:"Ramadan Start",data:RD},
"2025-03-27":{cls:"rm-qadr",lbl:"Lailat al-Qadr",data:RD},
"2025-03-30":{cls:"rm-eid",lbl:"Eid al-Fitr",data:RD}
}
};
} else {
window.FC={
icon:"☪️",title:"RAMADAN",tagline:"Ramadan 1447H - 2026",
quote:"Ramadan Kareem!",
ornaments:"☪️ 🌙 ✨ 🕌 🌟",
sideClass:"fc-sidebar theme-ramadan",
countries:RD,months:[[2026,1],[2026,2]],
festDates:{
"2026-02-17":{cls:"rm-start",lbl:"Ramadan Start",data:RD},
"2026-03-15":{cls:"rm-qadr",lbl:"Lailat al-Qadr",data:RD},
"2026-03-19":{cls:"rm-eid",lbl:"Eid al-Fitr",data:RD}
}
};
}
var sb=document.getElementById("fc-sidebar");
if(sb){sb.className=window.FC.sideClass;}
function sT(id,v){var e=document.getElementById(id);if(e){e.textContent=v;}}
sT("fc-icon",window.FC.icon);
sT("fc-title",window.FC.title);
sT("fc-tagline",window.FC.tagline);
sT("fc-quote",window.FC.quote);
sT("fc-ornaments",window.FC.ornaments);
var t=new Date();t.setHours(0,0,0,0);
window.FC_TODAY=t;
window.toKey=function(d){
return d.getFullYear()+"-"+
String(d.getMonth()+1).padStart(2,"0")+"-"+
String(d.getDate()).padStart(2,"0");
};
window.FC_TODAYKEY=window.toKey(t);
var cSel=document.getElementById("P4_COUNTRY");
var cVal=apex.item("P4_COUNTRY").getValue();
var cTxt="";
if(cSel&&cVal&&cVal!=="ALL"){
cTxt=cSel.options[cSel.selectedIndex].text;
}else{
cTxt="🌍 "+window.FC.countries.length+" Countries";
}
sT("fc-country-label",cTxt);
window.FC_COUNTRY=cVal;
DA3 - Festival Helpers | Sequence 30
Defines tooltip and detail panel functions on window — shared by DA4:
window.makeBadge=function(t){
var b=document.createElement("span");
b.className="badge";
if(t==="Public"){b.className+=" badge-public";b.textContent="Public";}
else if(t==="Restricted"){b.className+=" badge-restricted";b.textContent="Restricted";}
else{b.className+=" badge-optional";b.textContent="Optional";}
return b;
};
window.showDP=function(lbl,data){
var p=document.getElementById("fc-detail-panel");
var b=document.getElementById("dp-body");
var ti=document.getElementById("dp-title");
if(!p||!b){return;}
ti.textContent="Countries: "+lbl;
b.innerHTML="";
var h=document.createElement("div");
h.className="dp-row-hdr";
["Country","Holiday","Leave","Days"].forEach(function(x){
var s=document.createElement("span");s.textContent=x;h.appendChild(s);
});
b.appendChild(h);
data.forEach(function(c){
var r=document.createElement("div");r.className="dp-row";
var nd=document.createElement("div");nd.className="dp-country-name";
var fl=document.createElement("span");fl.textContent=c[0];
var nm=document.createElement("span");nm.textContent=c[1];
nd.appendChild(fl);nd.appendChild(nm);
var hn=document.createElement("div");hn.className="dp-holiday-name";hn.textContent=c[2];
var lt=document.createElement("div");lt.appendChild(window.makeBadge(c[3]));
var ld=document.createElement("div");ld.className="dp-days";ld.textContent=c[4];
r.appendChild(nd);r.appendChild(hn);r.appendChild(lt);r.appendChild(ld);
b.appendChild(r);
});
p.className="visible";
p.scrollIntoView({behavior:"smooth",block:"nearest"});
};
var tp=document.getElementById("fc-tooltip");
window.posTip=function(e){
if(!tp){return;}
var x=e.clientX+14,y=e.clientY-10;
if(x+310>window.innerWidth){x=e.clientX-320;}
if(y+200>window.innerHeight){y=e.clientY-210;}
tp.style.left=x+"px";tp.style.top=y+"px";
};
window.hideTip=function(){if(tp){tp.className="fc-tooltip";}};
window.showTip=function(e,lbl,data){
if(!tp){return;}
tp.innerHTML="";
var td=document.createElement("div");td.className="fc-tooltip-title";
td.textContent=lbl;tp.appendChild(td);
data.forEach(function(c){
var r=document.createElement("div");r.className="fc-tooltip-row";
var lf=document.createElement("div");lf.className="fc-tooltip-country";
var fl=document.createElement("span");fl.textContent=c[0];
var nm=document.createElement("span");nm.textContent=c[1];
lf.appendChild(fl);lf.appendChild(nm);
r.appendChild(lf);r.appendChild(window.makeBadge(c[3]));
tp.appendChild(r);
});
tp.className="fc-tooltip visible";
window.posTip(e);
};
DA4 - Festival Build Calendar | Sequence 40
Reads window.FC and builds the real 7-column calendar grid with all event listeners:
var CF=window.FC;
var mEl=document.getElementById("fc-months");
if(!CF||!mEl){return;}
var MN=["January","February","March","April","May","June","July","August","September","October","November","December"];
var WD=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"];
var tk=window.toKey;
var tdy=window.FC_TODAY;
var tdk=window.FC_TODAYKEY;
function buildMonth(container,year,month){
var hdr=document.createElement("div");hdr.className="fc-month-hdr";
hdr.textContent=MN[month]+" "+year;container.appendChild(hdr);
var wdRow=document.createElement("div");wdRow.className="fc-weekdays";
WD.forEach(function(w){
var d=document.createElement("div");d.className="fc-wd";d.textContent=w;wdRow.appendChild(d);
});
container.appendChild(wdRow);
var grid=document.createElement("div");grid.className="fc-grid";
var first=new Date(year,month,1);
var last=new Date(year,month+1,0);
var dow=first.getDay();
for(var e=0;e<dow;e++){
var em=document.createElement("div");em.className="fc-day empty";grid.appendChild(em);
}
for(var d=1;d<=last.getDate();d++){
var cd=new Date(year,month,d);
var ck=tk(cd);
var fi=CF.festDates[ck];
var css="fc-day";
var lb="";
if(fi){css+=" "+fi.cls+" has-info";lb=fi.lbl;}
else if(ck===tdk){css+=" day-today";}
else if(cd<tdy){css+=" day-past";}
var dd=document.createElement("div");dd.className=css;
var ne=document.createElement("div");ne.className="fc-day-num";ne.textContent=String(d);dd.appendChild(ne);
if(lb){var le=document.createElement("div");le.className="fc-day-lbl";le.textContent=lb;dd.appendChild(le);}
if(fi){
(function(info){
dd.addEventListener("mouseenter",function(ev){window.showTip(ev,info.lbl,info.data);});
dd.addEventListener("mousemove",function(ev){window.posTip(ev);});
dd.addEventListener("mouseleave",window.hideTip);
dd.addEventListener("click",function(){window.hideTip();window.showDP(info.lbl,info.data);});
})(fi);
}
grid.appendChild(dd);
}
container.appendChild(grid);
}
CF.months.forEach(function(m){buildMonth(mEl,m[0],m[1]);});
var pnl=document.getElementById("fc-detail-panel");
if(pnl){pnl.className="";}
🧱 Step 5: Create the Show/Hide Ramadan Year Item Dynamic Actions
Create 2 separate Dynamic Actions on P4_FESTIVAL Change to show/hide P4_RAMADAN_YEAR:
DA Name:
Show Ramadan Year
Client-side Condition (JS Expression):
DA Name:
Hide Ramadan Year
Client-side Condition (JS Expression):
🪔 PART 2 - Diwali Calendar (MULTI-DAY HIGHLIGHT)
In this blog, we explore how to design interactive festival calendars in Oracle APEX by focusing on individual celebrations first.
We begin with Diwali, one of the most vibrant multi-day festivals, followed by Christmas, a globally celebrated event with simpler calendar logic. Each part focuses on a specific festival to demonstrate different design patterns, UI behaviors, and user interactions.
Let’s start with designing the Diwali festival calendar.
🧱 Step 1: Create Page
-
Blank Page:
Diwali Calendar
📍 Step 2: Country Selector
- In Page Designer, create a Select List page item as P3_COUNTRY and select type as Select List
- Under List of Values → Type choose Static Values
Static Values:
🇮🇳 India
🇬🇧 UK
🇺🇸 USA🇸🇬 Singapore🇨🇦 Canada🇦🇺 Australiaand give return values as 2025-10-20 for all the countriesStep 3: Create the Collapsible Selector Region
- Create the Collapsible Selector Region
- Right-click Regions → Create Region
- Set Template: Collapsible
- Set Title: Select Your Country
- Set Static ID: diwali_selector_reg
- Place P3_COUNTRY select list inside this region
Step 4: Add the Inline CSS
Go to Page → CSS → Inline and paste the full below given CSS code.Deep purple #1a0a2e background, flame orange #ff6b1a accents, and gold #ffd700 highlights.Each festival day gets its own glow colour via a dedicated CSS class.CSS Code :/* === Purple & Gold Collapsible Bar === */ #diwali_selector_reg { background-color: #1a0a2e !important; border: 1.5px solid #ff6b1a !important; border-radius: 10px; } #diwali_selector_reg .t-Region-header { background-color: #1a0a2e !important; } #diwali_selector_reg .t-Region-headerTitle, #diwali_selector_reg .t-Icon { color: #ffd700 !important; font-family: 'Philosopher', serif; font-size: 1.1rem !important; } /* === Main Container === */ .dw-container { display: flex; min-height: 640px; border-radius: 14px; overflow: hidden; box-shadow: 0 16px 60px rgba(255,107,26,0.2); } /* === Left Sidebar (35%) === */ .dw-sidebar { width: 35%; background: linear-gradient(160deg, #0a0318 0%, #1a0a2e 50%, #0d0520 100%); color: #fff; padding: 48px 32px; position: relative; overflow: hidden; } /* Star pattern on sidebar */ .dw-sidebar::before { content: ''; position: absolute; inset: 0; background-image: radial-gradient(circle, rgba(255,215,0,0.12) 1px, transparent 1px), radial-gradient(circle, rgba(255,107,26,0.08) 1.5px, transparent 1.5px); background-size: 28px 28px, 50px 50px; pointer-events: none; } .dw-diya-icon { font-size: 3.5rem; display: block; margin-bottom: 12px; animation: diyaFlicker 1.8s ease-in-out infinite; filter: drop-shadow(0 0 18px rgba(255,107,26,0.9)); } @keyframes diyaFlicker { 0%,100% { filter: drop-shadow(0 0 18px rgba(255,107,26,0.9)); } 50% { filter: drop-shadow(0 0 35px rgba(255,165,0,1)) drop-shadow(0 0 10px rgba(255,69,0,0.8)); } } .dw-title { font-family: 'Philosopher', serif; font-size: 2.8rem; font-weight: 700; color: #ffd700; letter-spacing: 6px; line-height: 1.1; text-shadow: 0 2px 20px rgba(255,215,0,0.5); } .dw-subtitle { font-family: 'Poppins', sans-serif; font-size: 0.78rem; color: rgba(255,255,255,0.4); letter-spacing: 3px; text-transform: uppercase; margin-top: 4px; } .dw-greeting-section { border-left: 3px solid #ff6b1a; padding-left: 16px; margin-top: 28px; } .dw-hindi { font-family: 'Philosopher', serif; font-size: 1.5rem; color: #ffd700; font-style: italic; line-height: 1.6; } .dw-english { font-size: 0.83rem; color: rgba(255,255,255,0.55); margin-top: 8px; font-style: italic; } .dw-country-label { font-size: 0.83rem; color: #ff9b6b; margin-top: 14px; display: block; font-weight: 600; } .dw-ornaments { font-size: 1.3rem; margin-top: 24px; letter-spacing: 6px; } /* === Right Calendar Area (65%) === */ .dw-calendar-main { width: 65%; padding: 28px 24px; background: #0f0a1a; overflow-y: auto; } .dw-month-header { font-family: 'Philosopher', serif; font-size: 1.15rem; color: #ff9b6b; margin: 16px 0 10px; padding-bottom: 6px; border-bottom: 1px solid rgba(255,107,26,0.25); letter-spacing: 2px; } .dw-month-header:first-child { margin-top: 0; } /* Weekday labels row */ .dw-weekdays { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; margin-bottom: 4px; } .dw-wd { text-align: center; font-family: 'JetBrains Mono', monospace; font-size: 0.6rem; letter-spacing: 1px; color: rgba(255,215,0,0.5); text-transform: uppercase; padding: 4px 0; } /* === 7-column calendar grid === */ .dw-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; margin-bottom: 20px; } .dw-day { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 8px 3px; border-radius: 8px; background: rgba(255,255,255,0.03); border: 1px solid rgba(255,255,255,0.06); min-height: 52px; transition: transform 0.15s; cursor: default; } .dw-day:hover { transform: scale(1.06); } .dw-day.empty { background: transparent; border: none; pointer-events: none; } .dw-day-num { font-size: 1rem; font-weight: 600; color: rgba(240,220,180,0.85); line-height: 1; } .dw-day-lbl { font-size: 0.52rem; color: rgba(255,255,255,0.35); margin-top: 3px; text-align: center; line-height: 1.2; } /* Past days */ .dw-day.day-past { opacity: 0.3; } /* Today */ .dw-day.day-today { background: rgba(46,204,113,0.18) !important; border-color: #2ecc71 !important; box-shadow: 0 0 12px rgba(46,204,113,0.35); } .dw-day.day-today .dw-day-num { color: #2ecc71; } /* Dhanteras — Gold */ .dw-day.day-dhanteras { background: rgba(255,215,0,0.14) !important; border-color: #ffd700 !important; box-shadow: 0 0 14px rgba(255,215,0,0.4); animation: goldPulse 2.5s ease-in-out infinite; } .dw-day.day-dhanteras .dw-day-num { color: #ffd700; } .dw-day.day-dhanteras .dw-day-lbl { color: rgba(255,215,0,0.8); } @keyframes goldPulse { 0%,100% { box-shadow: 0 0 14px rgba(255,215,0,0.4); } 50% { box-shadow: 0 0 28px rgba(255,215,0,0.75); } } /* Narak Chaturdashi — Amber */ .dw-day.day-narak { background: rgba(255,170,0,0.14) !important; border-color: #ffaa00 !important; box-shadow: 0 0 14px rgba(255,170,0,0.45); } .dw-day.day-narak .dw-day-num { color: #ffaa00; } .dw-day.day-narak .dw-day-lbl { color: rgba(255,170,0,0.8); } /* Diwali Main — Flame Red (the big one!) */ .dw-day.day-diwali { background: linear-gradient(135deg, rgba(255,69,0,0.25), rgba(255,107,26,0.18)) !important; border-color: #ff4500 !important; border-width: 2px !important; box-shadow: 0 0 22px rgba(255,69,0,0.6); animation: diwaliGlow 1.8s ease-in-out infinite; } .dw-day.day-diwali .dw-day-num { color: #ff6b1a; font-size: 1.1rem; } .dw-day.day-diwali .dw-day-lbl { color: rgba(255,107,26,0.9); } @keyframes diwaliGlow { 0%,100% { box-shadow: 0 0 22px rgba(255,69,0,0.6); } 50% { box-shadow: 0 0 40px rgba(255,107,26,0.95); } } /* Govardhan Puja — Orange */ .dw-day.day-govardhan { background: rgba(255,107,26,0.14) !important; border-color: #ff6b1a !important; box-shadow: 0 0 12px rgba(255,107,26,0.4); } .dw-day.day-govardhan .dw-day-num { color: #ff9b6b; } .dw-day.day-govardhan .dw-day-lbl { color: rgba(255,155,107,0.8); } /* Bhai Dooj — Purple */ .dw-day.day-bhai { background: rgba(155,77,255,0.14) !important; border-color: #9b4dff !important; box-shadow: 0 0 12px rgba(155,77,255,0.4); } .dw-day.day-bhai .dw-day-num { color: #c084ff; } .dw-day.day-bhai .dw-day-lbl { color: rgba(192,132,255,0.8); }Step 5: Add the Static HTML Region (Calendar Shell)
- Create a Static Content region.
- Set Template to Blank with Attributes.
- Paste this HTML into Source.
- The two grid divs (dw-grid-oct and dw-grid-nov) are intentionally empty - JavaScript fills them:
HTML Source Code :
<div class="dw-container"> <!-- LEFT: Dark sidebar --> <div class="dw-sidebar"> <span class="dw-diya-icon">🪔</span> <h1 class="dw-title">DIWALI</h1> <div class="dw-subtitle">Festival of Lights · 2025</div> <div class="dw-greeting-section"> <p class="dw-hindi"> दीपावली की<br/>शुभकामनाएं </p> <p class="dw-english"> "May the festival of lights bring joy, prosperity and happiness to your life." </p> <!-- Updated dynamically by JavaScript --> <span class="dw-country-label" id="dw-country-name"> 🌍 Select your country above </span> </div> <div class="dw-ornaments">🪔 ✨ 🎆 🪷 🎇</div> </div> <!-- RIGHT: Calendar (JavaScript fills these grids) --> <div class="dw-calendar-main"> <!-- October 2025 --> <div class="dw-month-header">🍂 October 2025</div> <div class="dw-weekdays"> <div class="dw-wd">Sun</div><div class="dw-wd">Mon</div> <div class="dw-wd">Tue</div><div class="dw-wd">Wed</div> <div class="dw-wd">Thu</div><div class="dw-wd">Fri</div> <div class="dw-wd">Sat</div> </div> <div id="dw-grid-oct" class="dw-grid"></div> <!-- November 2025 --> <div class="dw-month-header">🌸 November 2025</div> <div class="dw-weekdays"> <div class="dw-wd">Sun</div><div class="dw-wd">Mon</div> <div class="dw-wd">Tue</div><div class="dw-wd">Wed</div> <div class="dw-wd">Thu</div><div class="dw-wd">Fri</div> <div class="dw-wd">Sat</div> </div> <div id="dw-grid-nov" class="dw-grid"></div> </div> </div>Step 6: Create the Dynamic Action + JavaScript
6a - Dynamic Action Setup
Property ValueName Generate Diwali Calendar Event Change Selection Type Item Item P3_COUNTRY Fire on Initialization ✅ YES True Action Execute JavaScript Code6b - JavaScript (No Backticks -APEX Safe)⚠️ This JavaScript uses zero backtick template literals - only document.createElement and .textContent.This prevents the SyntaxError: Unexpected token ',' that APEX causes when backticks get corruptedon paste.JavaScript Code :var diwaliDateStr = apex.item("P3_COUNTRY").getValue(); var gridOct = document.getElementById("dw-grid-oct"); var gridNov = document.getElementById("dw-grid-nov"); var labelEl = document.getElementById("dw-country-name"); if (!gridOct || !gridNov) { return; } gridOct.innerHTML = ""; gridNov.innerHTML = ""; if (!diwaliDateStr) { return; } // ── Update sidebar country label ────────────────────────── var selectEl = document.getElementById("P3_COUNTRY"); if (selectEl && labelEl) { labelEl.textContent = "📍 " + selectEl.options[selectEl.selectedIndex].text; } // ── Festival dates relative to Diwali main day ──────────── var diwaliMain = new Date(diwaliDateStr); var dhanteras = new Date(diwaliMain); dhanteras.setDate(diwaliMain.getDate() - 2); var narakChat = new Date(diwaliMain); narakChat.setDate(diwaliMain.getDate() - 1); var govardhan = new Date(diwaliMain); govardhan.setDate(diwaliMain.getDate() + 1); var bhaiDooj = new Date(diwaliMain); bhaiDooj.setDate(diwaliMain.getDate() + 2); // Convert to date-only strings for comparison (YYYY-MM-DD) function toKey(d) { return d.getFullYear() + "-" + String(d.getMonth() + 1).padStart(2, "0") + "-" + String(d.getDate()).padStart(2, "0"); } var today = new Date(); today.setHours(0,0,0,0); var todayKey = toKey(today); var diwaliKey = toKey(diwaliMain); var dhanKey = toKey(dhanteras); var narakKey = toKey(narakChat); var govKey = toKey(govardhan); var bhaiKey = toKey(bhaiDooj); // ── Function to build one month's grid ─────────────────── function buildMonth(grid, year, month) { // month is 0-indexed (0=Jan, 9=Oct, 10=Nov) var firstDay = new Date(year, month, 1); var lastDay = new Date(year, month + 1, 0); var startDow = firstDay.getDay(); // 0=Sun // Add empty cells for days before month starts for (var e = 0; e < startDow; e++) { var emptyDiv = document.createElement("div"); emptyDiv.className = "dw-day empty"; grid.appendChild(emptyDiv); } // Add one cell per day for (var d = 1; d <= lastDay.getDate(); d++) { var cellDate = new Date(year, month, d); var cellKey = toKey(cellDate); // Determine state and label var cssClass = "dw-day"; var festLabel = ""; if (cellKey === diwaliKey) { cssClass += " day-diwali"; festLabel = "🪔 Diwali"; } else if (cellKey === dhanKey) { cssClass += " day-dhanteras"; festLabel = "💛 Dhanteras"; } else if (cellKey === narakKey) { cssClass += " day-narak"; festLabel = "🔥 Narak Chat";} else if (cellKey === govKey) { cssClass += " day-govardhan"; festLabel = "🌿 Govardhan"; } else if (cellKey === bhaiKey) { cssClass += " day-bhai"; festLabel = "💜 Bhai Dooj"; } else if (cellKey === todayKey) { cssClass += " day-today"; festLabel = "Today"; } else if (cellDate < today) { cssClass += " day-past"; } // Build cell using createElement (no backticks) var dayDiv = document.createElement("div"); dayDiv.className = cssClass; var numDiv = document.createElement("div"); numDiv.className = "dw-day-num"; numDiv.textContent = String(d); var lblDiv = document.createElement("div"); lblDiv.className = "dw-day-lbl"; lblDiv.textContent = festLabel; dayDiv.appendChild(numDiv); if (festLabel) { dayDiv.appendChild(lblDiv); } grid.appendChild(dayDiv); } } // ── Build October (month=9) and November (month=10) ─────── buildMonth(gridOct, 2025, 9); buildMonth(gridNov, 2025, 10);Step 7: Save, Run & Verify
🪔 Deep purple collapsible selector region renders with gold text 🪔 Dark sidebar shows DIWALI title in gold Philosopher font with flickering diya 🪔 Hindi greeting "दीपावली की शुभकामनाएं" visible in sidebar 🪔 October 2025 grid renders with correct weekday alignment (starts Wednesday) 🪔 November 2025 grid renders below October 🪔 Oct 18 - Dhanteras glows gold ✨ 🪔 Oct 19 - Narak Chaturdashi glows amber 🔥 🪔 Oct 20 - Diwali Main pulses flame red with animated glow 🪔 🪔 Oct 21 - Govardhan Puja glows orange 🌿 🪔 Oct 22 - Bhai Dooj glows purple 💜 🪔 Today's date auto-highlighted in green 🪔 Past days faded to 30% opacityFinal Output:
India:🧱 Step 1: Create Page
Blank Page:
Christmas Calender📍 Step 2: Country Selector
Before writing a single line of CSS, create the country selector that drives the whole calendar. The return value stores the Christmas Day date string for each country - JavaScript parses it directly, no AJAX needed.
- In Page Designer, create a Select List page item as P2_COUNTRY and select type as Select List
- Under List of Values → Type choose Static Values
Static Values:
🇮🇳 India🇺🇸 USA
🇬🇧 UK🇦🇺 Australia🇩🇪 Germany🇵🇭 Philippinesand give return values as 2025-12-25 for all the countriesStep 3: Create the Collapsible Selector Region
- Create the Collapsible Selector Region
- Right-click Regions → Create Region
- Set Template: Collapsible
- Set Title: Select Your Country
- Set Static ID: xmas_selector_reg
- Place P2_COUNTRY select list inside this region
Step 3: Load Google Fonts
Go to App Builder → Your App → Shared Components → User Interface Attributes → CSS tab → File URLs and add:
https://fonts.googleapis.com/css2?family=Mountains+of+Christmas:wght@400;700&family=Nunito:wght@300;400;600;700&display=swap
Font Used For Effect
Mountains of Christmas CHRISTMAS title, section headings Festive, rounded, holiday feel
Nunito Body text, day labels, dates Friendly, rounded, highly readableStep 5: Add the Inline CSS
Go to Page → CSS → Inline and paste the full block code below. The palette uses deep forest green #1a3d2b, festive red #c0392b, and warm gold #f1c40f. The light-string animation runs purely in CSS - no JavaScript required for it.CSS Code :* ============================================CHRISTMAS CALENDAR — Inline Page CSS============================================ *//* === Festive Red & Green Collapsible Bar === */#xmas_selector_reg {background-color: #1a3d2b !important;border: 1.5px solid #c0392b !important;border-radius: 10px;}#xmas_selector_reg .t-Region-header {background-color: #1a3d2b !important;}#xmas_selector_reg .t-Region-headerTitle,#xmas_selector_reg .t-Icon {color: #f1c40f !important;font-family: 'Mountains of Christmas', cursive;font-size: 1.1rem !important;}/* === Festive Light String === */.xmas-lights {display: flex;align-items: flex-end;gap: 0;padding: 4px 0 8px;position: relative;margin-bottom: 16px;}.xmas-lights::before {content: '';position: absolute;top: 6px; left: 0; right: 0;height: 2px;background: rgba(255,255,255,0.2);}.xmas-bulb {width: 12px; height: 16px;border-radius: 50% 50% 60% 60%;margin: 0 16px;animation: xmasBlink 2s ease-in-out infinite;}.xmas-bulb:nth-child(4n+1) { background: #e74c3c; box-shadow: 0 0 8px 3px #e74c3c; animation-delay: 0s; }.xmas-bulb:nth-child(4n+2) { background: #27ae60; box-shadow: 0 0 8px 3px #27ae60; animation-delay: 0.5s; }.xmas-bulb:nth-child(4n+3) { background: #f1c40f; box-shadow: 0 0 8px 3px #f1c40f; animation-delay: 1s; }.xmas-bulb:nth-child(4n+4) { background: #5dade2; box-shadow: 0 0 8px 3px #5dade2; animation-delay: 1.5s; }@keyframes xmasBlink {0%,100% { opacity: 1; }50% { opacity: 0.3; }}/* === Main Layout === */.xmas-container {display: flex;min-height: 620px;border-radius: 12px;overflow: hidden;box-shadow: 0 12px 48px rgba(0,0,0,0.4);}/* === Left Sidebar (38%) === */.xmas-sidebar {width: 38%;background: linear-gradient(160deg, #0d2818 0%, #1a3d2b 50%, #0a1f12 100%);color: #fff;padding: 50px 36px;position: relative;overflow: hidden;}/* Subtle snowflake pattern on sidebar */.xmas-sidebar::before {content: '';position: absolute; inset: 0;background-image: radial-gradient(circle, rgba(255,255,255,0.06) 1px, transparent 1px);background-size: 24px 24px;pointer-events: none;}.xmas-main-title {font-family: 'Mountains of Christmas', cursive;font-size: 3rem;font-weight: 700;color: #f1c40f;letter-spacing: 6px;line-height: 1.1;text-shadow: 0 2px 16px rgba(241,196,15,0.4);}.xmas-year {font-family: 'Nunito', sans-serif;font-size: 1rem;color: rgba(255,255,255,0.5);letter-spacing: 4px;margin-top: 4px;text-transform: uppercase;}.xmas-greeting-section {border-left: 3px solid #c0392b;padding-left: 18px;margin-top: 30px;}.xmas-quote {font-family: 'Nunito', sans-serif;font-size: 1.1rem;color: #a8e8c0;line-height: 1.8;font-style: italic;}.xmas-country-label {font-family: 'Nunito', sans-serif;font-size: 0.85rem;color: #f1c40f;margin-top: 12px;display: block;font-weight: 700;}.xmas-ornaments {font-size: 1.4rem;margin-top: 28px;letter-spacing: 4px;}/* === Right Calendar Area (62%) === */.xmas-calendar-main {width: 62%;padding: 36px 32px;background: #fafcff;}.xmas-calendar-header {font-family: 'Mountains of Christmas', cursive;font-size: 1.5rem;color: #1a3d2b;margin-bottom: 20px;text-align: center;}.xmas-calendar-header span { color: #c0392b; }/* === CSS Grid: 5 columns × 5 rows = 25 days === */.xmas-grid {display: grid;grid-template-columns: repeat(5, 1fr);gap: 12px;width: 100%;}.xmas-day-cell {display: flex;flex-direction: column;align-items: center;justify-content: center;padding: 12px 6px;border-radius: 10px;background: #f0f4ff;border: 1.5px solid #dce4f5;transition: transform 0.2s, box-shadow 0.2s;font-family: 'Nunito', sans-serif;cursor: default;}.xmas-day-cell:hover {transform: translateY(-3px);box-shadow: 0 6px 20px rgba(26,107,58,0.2);}.xmas-day-num {font-size: 1.4rem;font-weight: 700;color: #1a3d2b;line-height: 1;}.xmas-day-date {font-size: 0.65rem;color: #7a90b8;margin-top: 4px;text-align: center;font-weight: 600;}/* Christmas Day — the big one! */.xmas-day {background: linear-gradient(135deg, #c0392b, #e74c3c) !important;border-color: #f1c40f !important;border-width: 2.5px !important;box-shadow: 0 0 20px rgba(241,196,15,0.5);animation: xmasGlow 2s ease-in-out infinite;}.xmas-day .xmas-day-num { color: #f1c40f !important; font-size: 1.1rem; }.xmas-day .xmas-day-date { color: rgba(255,255,255,0.85) !important; }@keyframes xmasGlow {0%,100% { box-shadow: 0 0 20px rgba(241,196,15,0.5); }50% { box-shadow: 0 0 36px rgba(241,196,15,0.85); }}/* Past days — greyed out */.day-past {opacity: 0.45;background: #e8ecf5 !important;}/* Today's day — green highlight */.day-today {background: linear-gradient(135deg, #1a6b3a, #27ae60) !important;border-color: #2ecc71 !important;}.day-today .xmas-day-num { color: #fff !important; }.day-today .xmas-day-date { color: rgba(255,255,255,0.8) !important; }Step 6: Add the Static HTML Region (Calendar Shell)
- Create a Static Content region.
- Set Template to Blank with Attributes.
- In the Source, paste this HTML shell — the JavaScript in Step 6 fills the xmas-grid div dynamically:
HTML Source Code :
<!-- Outer flex container --><div class="xmas-container"><!-- LEFT: Forest-green sidebar --><div class="xmas-sidebar"><!-- Blinking light string --><div class="xmas-lights"><div class="xmas-bulb"></div><div class="xmas-bulb"></div><div class="xmas-bulb"></div><div class="xmas-bulb"></div><div class="xmas-bulb"></div><div class="xmas-bulb"></div></div><h1 class="xmas-main-title">🎄 CHRISTMAS</h1><div class="xmas-year">Advent Calendar 2025</div><div class="xmas-greeting-section"><p class="xmas-quote">"Peace on earth, goodwill to all —count the days to Christmaswith joy and celebration."</p><!-- JS will update this label --><span class="xmas-country-label"id="xmas-country-name">🌍 Select your country above</span></div><div class="xmas-ornaments">🎁 🔔 ❄️ ⭐ 🦌</div></div><!-- RIGHT: Dynamic Advent Grid (JavaScript fills this) --><div class="xmas-calendar-main"><div class="xmas-calendar-header">Advent: Dec 1 → <span>Dec 25 🎄</span></div><!-- Grid is empty — JavaScript builds it --><div id="xmas-grid" class="xmas-grid"></div></div></div> </div>Step 7: Create the Dynamic Action + JavaScript
6a - Dynamic Action Setup
Property ValueName Generate Christmas CalendarEvent ChangeSelection Type ItemItem P2_COUNTRYFire on Initialization ✅ YES⚠️ Fire on Initialization must be YES. Without this, the calendar grid will be completely empty when the page first loads. The user would have to change the select list manually before anything appears.6b - Add the Execute JavaScript Code ActionDelete the default "Show" action. Add Execute JavaScript Code and paste the full script below. This script builds all 25 Advent days, automatically highlights today, greys out past days, and makes Christmas Day glow:JavaScript Code:var countryVal = apex.item("P2_COUNTRY").getValue();var grid = document.getElementById("xmas-grid");if (!grid) { return; }grid.innerHTML = "";if (!countryVal) { return; }// Update sidebar labelvar selectEl = document.getElementById("P2_COUNTRY");var labelEl = document.getElementById("xmas-country-name");if (selectEl && labelEl) {labelEl.textContent = "📍 " + selectEl.options[selectEl.selectedIndex].text;}// Date setupvar today = new Date();today.setHours(0, 0, 0, 0);var adventStart = new Date("2025-12-01");// Build 25 Advent day cellsfor (var i = 0; i < 25; i++) {var cellDate = new Date(adventStart.getTime());cellDate.setDate(adventStart.getDate() + i);cellDate.setHours(0, 0, 0, 0);var dayNum = cellDate.getDate();var fmtDate = cellDate.toLocaleDateString("en-US", {weekday : "short",month : "short",day : "numeric"});var isChristmas = (dayNum === 25);var isToday = (cellDate.getTime() === today.getTime());var isPast = (cellDate < today && !isToday);var cssClass = "xmas-day-cell";if (isChristmas) { cssClass += " xmas-day"; }else if (isToday) { cssClass += " day-today"; }else if (isPast) { cssClass += " day-past"; }var icon;if (isChristmas) { icon = "🎄"; }else if (isToday) { icon = "⭐"; }else if (isPast) { icon = "✓"; }else { icon = String(dayNum); }var label;if (isChristmas) { label = "Christmas!"; }else { label = fmtDate; }var dayDiv = document.createElement("div");dayDiv.className = cssClass;var numSpan = document.createElement("div");numSpan.className = "xmas-day-num";numSpan.textContent = icon;var dateSpan = document.createElement("div");dateSpan.className = "xmas-day-date";dateSpan.textContent = label;dayDiv.appendChild(numSpan);dayDiv.appendChild(dateSpan);grid.appendChild(dayDiv);}Step 7: Save, Run & Verify
Hit Save and Run. Here's your complete verification checklist: ✅Festive red-&-green collapsible region appears at the top with the country selector ✅Split layout renders - dark forest-green sidebar on the left ✅"CHRISTMAS" title in Mountains of Christmas font glows in gold ✅Blinking multi-colour light string animates inside the sidebar ✅25 Advent day cells appear in a 5-column CSS Grid (Dec 1–25) ✅Past days are automatically greyed out based on today's real date ✅Today's cell glows in green with a ⭐ icon ✅December 25 cell glows red & gold with animated pulse and "Christmas!" label ✅Switching country updates the sidebar label instantly -no page reloadFinal Output:
India:Conclusion
Building engaging calendar experiences in Oracle APEX is not just about presenting dates-it’s about
designing dynamic interfaces that adapt to different use cases and user expectations.
In this blog, we explored a complete journey of calendar design and implementation. We started with a
multi-festival calendar, combining multiple celebrations into a single interactive interface, and then
deep-dived into two distinct implementations: a highlight-based Diwali calendar and a countdown-style
Christmas calendar. Each approach demonstrates a different design pattern-one focusing on multi-day
festival visibility and the other on progressive user engagement.
Despite their differences, all implementations are built using the same core technologies: HTML, CSS,
JavaScript, and Oracle APEX Dynamic Actions. This highlights how flexible and powerful Oracle APEX
can be when combined with thoughtful UI design and client-side logic.
The key takeaway is that by understanding and applying different design patterns, developers can create
scalable, reusable, and visually rich calendar solutions-from single-event experiences to fully dynamic
multi-festival systems-without relying on plugins or complex backend processing.
.png)
.png)
.png)
.png)
Comments
Post a Comment