FILO = LIFO Stack Visualizer is a free, copy-paste ready interactive data structure demo
built with pure HTML, CSS, and vanilla JavaScript. It visually proves that
FILO (First-In Last-Out) and LIFO (Last-In First-Out) are identical — two names
for the same stack behavior. The #filo-stack container uses
flex-direction: column-reverse with justify-content: flex-end,
so new items visually appear at the top while being stack.lastChild in the DOM —
enabling correct LIFO pop with a single stack.removeChild(stack.lastChild).
Push animation: each 120×120px .box receives
@keyframes floatIn: from translateY(-400px) rotate(360deg)
to translateY(0) over 0.8s with
cubic-bezier(0.22, 1, 0.36, 1) — an ease-out-back curve that overshoots
the target position by ~10% before settling, creating a satisfying bounce.
Box color uses hsl((count * 45) % 360, 75%, 58%):
a 45° hue step cycles through the full color wheel every 8 pushes.
Pop animation: @keyframes floatOut flies the element to
translateY(-500px) scale(0.3) rotate(-360deg) over 0.8s ease-in,
with forwards fill keeping it invisible. A 800ms setTimeout
then removes the DOM node. Confetti fires at exactly 10 items:
confetti({particleCount:250, spread:90}).
The key DOM insight: stack.appendChild(box) adds items to the
end of the child list. stack.lastChild is always the most
recently added item — exactly what a LIFO pop needs to remove.
But visually, we want the newest item to appear at the top of the stack
tower. flex-direction: column-reverse reverses the visual order while leaving
the DOM order unchanged. So the first item pushed is at the visual bottom (last displayed),
and the last item pushed is at the visual top (first displayed) — perfectly representing
a real stack of plates. No array reversal or index math required.
FILO (First-In Last-Out) and LIFO (Last-In First-Out) describe identical behavior from two perspectives. FILO focuses on the first element pushed — it exits last. LIFO focuses on the most recently pushed element — it exits first. Both describe a stack data structure. FILO = LIFO = LILO = Stack. Real-world examples: a stack of plates (first plate placed is last used), browser back button (last page visited is first to return to), function call stack (last function called returns first), Ctrl+Z undo history.
FILO = LIFO Stack Visualizer is ideal for: CS students learning data structures, coding interview preparation, classroom demonstrations of stack behavior, JavaScript animation tutorials (CSS keyframes + setTimeout removal pattern, cubic-bezier bounce, HSL rainbow), glassmorphism UI reference, and any project needing an engaging interactive explanation of FILO/LIFO/stack concepts.
Released under Creative Commons Attribution 4.0 International (CC BY 4.0). Free for personal and commercial use with attribution to Coodeverse.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>FILO = LIFO = Stack - First-In Last-Out</title>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;900&display=swap" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/confetti.browser.min.js"></script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;900&display=swap');
:root {
--bg: #f0f9ff;
--card: rgba(255,255,255,0.35);
--text: #0c4a6e;
--accent: #0ea5e9;
--glow: #7dd3fc;
}
.dark {
--bg: #0c1e2e;
--card: rgba(12,74,110,0.5);
--text: #e0f2fe;
--accent: #38bdf8;
--glow: #7dd3fc;
}
body {
font-family: 'Inter', sans-serif;
background: var(--bg);
color: var(--text);
margin: 0;
padding: 20px;
min-height: 100vh;
transition: 0.5s;
}
.container {
max-width: 1000px;
margin: 0 auto;
text-align: center;
}
h1 {
font-size: 4.2rem;
font-weight: 900;
background: linear-gradient(90deg, #0ea5e9, #8b5cf6, #ec4899);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
margin: 2rem 0 0.5rem;
}
.subtitle {
font-size: 1.8rem;
opacity: 0.9;
margin-bottom: 3rem;
}
.card {
background: var(--card);
backdrop-filter: blur(20px);
border-radius: 36px;
padding: 3rem;
box-shadow: 0 30px 60px rgba(14,165,233,0.25);
border: 1px solid rgba(255,255,255,0.4);
margin: 2rem auto;
max-width: 900px;
}
h2 {
font-size: 3.2rem;
font-weight: 900;
background: linear-gradient(135deg, #0ea5e9, #38bdf8);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
#filo-stack {
display: flex;
flex-direction: column-reverse;
align-items: center;
min-height: 520px;
justify-content: flex-end;
gap: 18px;
padding: 2rem 0;
}
.box {
width: 120px;
height: 120px;
border-radius: 28px;
display: flex;
align-items: center;
justify-content: center;
font-size: 2.5rem;
font-weight: 900;
color: white;
box-shadow: 0 20px 40px rgba(0,0,0,0.3);
position: relative;
overflow: hidden;
}
.box::before {
content: '';
position: absolute;
inset: 0;
background: linear-gradient(135deg, rgba(255,255,255,0.4), transparent);
border-radius: 28px;
}
input {
padding: 18px 30px;
width: 300px;
border: none;
border-radius: 24px;
font-size: 1.4rem;
margin: 1.5rem;
text-align: center;
background: rgba(255,255,255,0.3);
backdrop-filter: blur(12px);
color: var(--text);
}
button {
padding: 18px 40px;
margin: 12px;
border: none;
border-radius: 24px;
font-size: 1.4rem;
font-weight: 700;
cursor: pointer;
background: linear-gradient(135deg, #0ea5e9, #38bdf8);
color: white;
box-shadow: 0 12px 25px rgba(14,165,233,0.4);
transition: all 0.3s;
}
button:hover {
transform: translateY(-6px) scale(1.08);
}
.status {
font-size: 1.9rem;
font-weight: bold;
color: #10b981;
min-height: 3rem;
margin: 1.5rem 0;
}
.info {
font-size: 1.5rem;
margin: 1rem 0;
}
.truth {
font-size: 3.5rem;
font-weight: 900;
background: linear-gradient(90deg, #0ea5e9, #ec4899, #f59e0b);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
margin: 4rem 0 2rem;
}
.toggle {
position: fixed;
top: 20px;
right: 20px;
padding: 14px 28px;
background: rgba(0,0,0,0.3);
border: none;
border-radius: 50px;
backdrop-filter: blur(10px);
color: white;
cursor: pointer;
z-index: 10;
}
@keyframes floatIn {
from { transform: translateY(-400px) rotate(360deg); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
@keyframes floatOut {
to { transform: translateY(-500px) scale(0.3) rotate(-360deg); opacity: 0; }
}
</style>
</head>
<body>
<button class="toggle" onclick="document.body.classList.toggle('dark')">Dark Mode</button>
<div class="container">
<h1>FILO</h1>
<p class="subtitle">First-In - Last-Out</p>
<div class="card">
<h2>FILO - First-In Last-Out</h2>
<div id="filo-stack"></div>
<input type="text" id="input" placeholder="Enter anything (e.g. Book, Cat, 42)" value="1">
<div>
<button onclick="add()">Add (Push)</button>
<button onclick="remove()">Remove (Pop)</button>
</div>
<div class="info">Size: <strong id="size">0</strong></div>
<div id="status" class="status"></div>
</div>
<div class="truth">FILO = LIFO = LILO = Stack</div>
<p style="font-size:1.6rem; opacity:0.85; max-width:800px; margin:0 auto 3rem;">
The first item you add will be the <strong>last</strong> one removed.<br>
This is <strong>exactly the same behavior</strong> as LIFO (Last-In First-Out).
</p>
<button onclick="reset()" style="padding:18px 50px; font-size:1.4rem; background:#64748b; border-radius:24px;">
Reset FILO
</button>
</div>
<script>
let count = 0;
function add() {
const val = document.getElementById('input').value.trim() || `Item ${++count}`;
const stack = document.getElementById('filo-stack');
const box = document.createElement('div');
box.className = 'box';
box.textContent = val;
box.style.background = `hsl(${(count * 45) % 360}, 75%, 58%)`;
box.style.animation = 'floatIn 0.8s cubic-bezier(0.22, 1, 0.36, 1)';
stack.appendChild(box);
document.getElementById('status').textContent = `Added "${val}" - First-In`;
updateSize();
if (stack.children.length === 10) confetti({particleCount: 250, spread: 90});
}
function remove() {
const stack = document.getElementById('filo-stack');
if (stack.children.length === 0) {
document.getElementById('status').textContent = 'Empty! Nothing to remove.';
return;
}
const removed = stack.lastChild.textContent;
stack.lastChild.style.animation = 'floatOut 0.8s ease-in forwards';
setTimeout(() => {
stack.removeChild(stack.lastChild);
updateSize();
}, 800);
document.getElementById('status').textContent = `Removed "${removed}" - Last-Out`;
}
function updateSize() {
document.getElementById('size').textContent = document.getElementById('filo-stack').children.length;
}
function reset() {
document.getElementById('filo-stack').innerHTML = '';
document.getElementById('status').textContent = 'All clear!';
updateSize();
}
window.onload = () => {
['Red', 'Green', 'Blue', 'Gold'].forEach((v, i) => {
setTimeout(() => {
document.getElementById('input').value = v;
add();
}, i * 1000);
});
};
</script>
</body>
</html>
Common questions about the free FILO = LIFO Stack Data Structure Visualizer project.
Yes — 100% free under Creative Commons Attribution 4.0. No sign-up, no payment. Click Download Project and use it for DSA study, classroom teaching, interview prep, or portfolio projects with attribution to Coodeverse.
FILO (First-In Last-Out) and LIFO (Last-In First-Out) describe identical behavior — they are two names for the same stack principle. FILO emphasizes the first item's fate (it exits last). LIFO emphasizes the most recent item's fate (it exits first). Both mean: the first item pushed is the last item popped. Real-world examples: a stack of plates, browser back button, function call stack, Ctrl+Z undo history. FILO = LIFO = LILO = Stack.
Define: @keyframes floatIn { from { transform: translateY(-400px) rotate(360deg); opacity: 0; } to { transform: translateY(0); opacity: 1; } }. Apply immediately after creating the element: box.style.animation = 'floatIn 0.8s cubic-bezier(0.22, 1, 0.36, 1)'. The cubic-bezier(0.22, 1, 0.36, 1) is an ease-out-back curve that overshoots the landing by ~10%, creating a satisfying bounce. Add the element to the DOM after setting the animation so the browser starts from frame 0.
Apply the exit animation first: stack.lastChild.style.animation = 'floatOut 0.8s ease-in forwards'. The keyframe: @keyframes floatOut { to { transform: translateY(-500px) scale(0.3) rotate(-360deg); opacity: 0; } }. The forwards fill keeps the element invisible after the animation. Then remove the DOM node after the animation finishes: setTimeout(() => { stack.removeChild(stack.lastChild); }, 800). The 800ms matches the animation duration.
Use: background: rgba(255,255,255,0.35) for translucent fill; backdrop-filter: blur(20px) for frosted glass blur; border: 1px solid rgba(255,255,255,0.4) for edge highlight; border-radius: 36px for curves; box-shadow: 0 30px 60px rgba(14,165,233,0.25) for depth. The parent element must have visible background content for backdrop-filter to create the blur. Add -webkit-backdrop-filter: blur(20px) for Safari.
Use: box.style.background = `hsl(${(count * 45) % 360}, 75%, 58%)`. A 45° hue step cycles through 8 colors before repeating: red (0°), yellow-orange (45°), green (90°), cyan-green (135°), cyan (180°), blue (225°), purple (270°), pink (315°). Saturation 75% keeps colors vivid; lightness 58% ensures good contrast with white text. No external color library needed.
Browse coodeverse.com/projects — Binary Search Visualizer (step-by-step O(log n) animation), Animated 3D Pie Chart, Pro Chronometer, Word Builder Puzzle, VORTEX Canvas, and more. All 100% free with complete HTML CSS JavaScript source code.