Size Sequencing Game
body {
font-family: ‘Inter’, Arial, sans-serif;
background-color: #f1f5f9; /* slate-100 */
margin: 0;
padding: 16px;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
box-sizing: border-box;
overscroll-behavior: none;
}
::selection {
background-color: #7dd3fc; /* sky-300 */
}
#game-container {
background-color: #ffffff; /* white */
padding: 24px; /* roughly p-6 */
border-radius: 12px; /* rounded-xl */
box-shadow: 0 20px 25px -5px rgba(0,0,0,0.1), 0 10px 10px -5px rgba(0,0,0,0.04); /* shadow-2xl */
width: 100%;
max-width: 672px; /* max-w-2xl */
box-sizing: border-box;
}
header {
margin-bottom: 24px; /* mb-6 */
text-align: center;
}
#activity-title {
font-size: 24px; /* roughly text-2xl */
font-weight: bold;
color: #334155; /* slate-700 */
}
#instruction {
color: #64748b; /* slate-500 */
margin-top: 4px; /* mt-1 */
font-size: 14px; /* roughly text-sm */
}
#target-zones-container {
margin-bottom: 32px; /* mb-8 */
padding: 8px; /* p-2 */
background-color: #f8fafc; /* slate-50 */
border-radius: 8px; /* rounded-lg */
}
#target-zones {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16px; /* gap-4 */
}
.drop-zone {
border: 2px dashed #cbd5e1; /* cool-gray-300 */
min-height: 100px; /* Adjusted from 120px to fit better */
aspect-ratio: 1 / 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
transition: background-color 0.2s ease;
position: relative;
padding-bottom: 20px;
border-radius: 6px; /* rounded-md */
box-sizing: border-box;
}
.drop-zone-label {
position: absolute;
bottom: 4px;
font-size: 0.75rem;
color: #64748b; /* slate-500 */
user-select: none;
}
.drop-zone-hover {
background-color: #e2e8f0; /* cool-gray-200 */
}
.drop-zone.occupied {
border-style: solid;
border-color: #94a3b8; /* cool-gray-400 */
}
.drop-zone .draggable-item {
flex-shrink: 0;
margin-top: auto;
margin-bottom: auto;
}
#source-objects-container {
padding: 12px; /* p-3 */
background-color: #e2e8f0; /* slate-200 */
border-radius: 8px; /* rounded-lg */
min-height: 120px; /* Adjusted */
display: flex;
align-items: center;
justify-content: center;
}
#source-placeholder {
color: #64748b; /* slate-500 */
font-style: italic;
}
#source-objects {
display: flex;
flex-wrap: wrap;
gap: 12px; /* roughly gap-3/gap-4 */
align-items: center;
justify-content: center;
}
.draggable-item {
touch-action: none;
cursor: grab;
display: flex;
align-items: center;
justify-content: center;
border: 2px solid transparent;
transition: transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
box-shadow: 0 1px 3px 0 rgba(0,0,0,0.1), 0 1px 2px 0 rgba(0,0,0,0.06); /* shadow */
}
.draggable-item:active {
cursor: grabbing;
transform: scale(1.1);
box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1), 0 2px 4px -1px rgba(0,0,0,0.06); /* shadow-md */
z-index: 1000;
}
.dragging {
opacity: 0.5;
}
/* Shape specific styles */
.shape-circle { border-radius: 50%; }
.shape-oval { border-radius: 50%; }
.shape-square { border-radius: 0.375rem; } /* 6px */
.shape-rectangle { border-radius: 0.375rem; } /* 6px */
.shape-star {
background: transparent !important;
border: none !important;
box-shadow: none !important;
color: #f59e0b; /* amber-500 for star color */
}
/* Item background colors (approximations) */
.bg-blue-500 { background-color: #3b82f6; }
.bg-yellow-400 { background-color: #facc15; }
.bg-red-500 { background-color: #ef4444; }
.bg-green-500 { background-color: #22c55e; }
.bg-purple-500 { background-color: #a855f7; }
.bg-orange-500 { background-color: #f97316; }
.bg-teal-500 { background-color: #14b8a6; }
.bg-pink-500 { background-color: #ec4899; }
.bg-indigo-500 { background-color: #6366f1; }
.bg-cyan-500 { background-color: #06b6d4; }
.bg-lime-500 { background-color: #84cc16; }
.bg-rose-500 { background-color: #f43f5e; }
/* Star text colors are handled by .shape-star, font sizes by specific text classes if needed */
.text-xl { font-size: 1.25rem; }
.text-3xl { font-size: 1.875rem; }
.text-5xl { font-size: 3rem; }
footer {
margin-top: 32px; /* mt-8 */
text-align: center;
}
#feedback-message {
min-height: 2em; /* Ensure space for message */
font-size: 1.125rem; /* text-lg */
font-weight: 500; /* font-medium */
margin-bottom: 16px; /* mb-4 */
}
.text-green-600 { color: #16a34a; }
.text-red-600 { color: #dc2626; }
button {
color: white;
font-weight: 600; /* font-semibold */
padding: 10px 20px; /* py-2/2.5 px-4/5 */
border-radius: 8px; /* rounded-lg */
box-shadow: 0 1px 3px 0 rgba(0,0,0,0.1), 0 1px 2px 0 rgba(0,0,0,0.06); /* shadow */
transition: background-color 0.15s ease;
cursor: pointer;
border: none;
}
button:disabled {
opacity: 0.7;
cursor: not-allowed;
}
#reset-activity-button {
background-color: #f59e0b; /* amber-500 */
margin-right: 8px; /* mr-2 */
}
#reset-activity-button:hover {
background-color: #d97706; /* amber-600 */
}
#next-activity-button {
background-color: #0ea5e9; /* sky-500 */
}
#next-activity-button:hover {
background-color: #0284c7; /* sky-600 */
}
.hidden { display: none !important; } /* !important to override other display styles */
#all-activities-complete {
font-size: 1.5rem; /* text-2xl */
font-weight: bold;
color: #16a34a; /* green-600 */
margin-top: 16px; /* mt-4 */
}
/* Modal styles */
.modal {
display: none;
position: fixed;
z-index: 1001;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0,0,0,0.5);
justify-content: center;
align-items: center;
}
.modal-content {
background-color: #ffffff;
margin: auto;
padding: 32px; /* p-8 */
border-radius: 0.5rem;
text-align: center;
box-shadow: 0 10px 15px -3px rgba(0,0,0,0.1), 0 4px 6px -2px rgba(0,0,0,0.05);
width: 90%;
max-width: 320px;
}
#success-emoji-display {
font-size: 4rem;
line-height: 1;
display: block;
animation: emoji-pop 0.6s cubic-bezier(0.68, -0.55, 0.27, 1.55);
}
@keyframes emoji-pop {
0% { transform: scale(0.5) rotate(-15deg); opacity: 0; }
60% { transform: scale(1.2) rotate(10deg); }
100% { transform: scale(1) rotate(0deg); opacity: 1; }
}
#success-message-modal {
font-size: 1.25rem; /* text-xl */
font-weight: 600; /* font-semibold */
color: #334155; /* slate-700 */
margin-top: 16px; /* mt-4 */
}
#modal-close-button {
margin-top: 24px; /* mt-6 */
background-color: #0ea5e9; /* sky-500 */
}
#modal-close-button:hover {
background-color: #0284c7; /* sky-600 */
}
@media (min-width: 640px) { /* sm breakpoint */
#activity-title { font-size: 30px; } /* sm:text-3xl */
#instruction { font-size: 1rem; } /* sm:text-base */
button { padding: 12px 24px; } /* sm:py-3 sm:px-6 */
#target-zones { gap: 16px; } /* sm:gap-4 */
#source-objects-container { min-height: 140px; }
}
Objects will appear here.
const activities = [
{
name: “Activity 1: Order the Circles”,
items: [
{ id: ‘a1_s’, sizeLabel: ‘small’, width: ’40px’, height: ’40px’, color: ‘bg-blue-500’, shape: ‘circle’, originalIndex: 0 },
{ id: ‘a1_m’, sizeLabel: ‘medium’, width: ’65px’, height: ’65px’, color: ‘bg-yellow-400’, shape: ‘circle’, originalIndex: 1 },
{ id: ‘a1_b’, sizeLabel: ‘big’, width: ’90px’, height: ’90px’, color: ‘bg-red-500’, shape: ‘circle’, originalIndex: 2 }
],
correctSequence: [‘small’, ‘medium’, ‘big’]
},
{
name: “Activity 2: Sort the Squares”,
items: [
{ id: ‘a2_s’, sizeLabel: ‘small’, width: ’35px’, height: ’35px’, color: ‘bg-green-500’, shape: ‘square’, originalIndex: 0 },
{ id: ‘a2_m’, sizeLabel: ‘medium’, width: ’60px’, height: ’60px’, color: ‘bg-purple-500’, shape: ‘square’, originalIndex: 1 },
{ id: ‘a2_b’, sizeLabel: ‘big’, width: ’85px’, height: ’85px’, color: ‘bg-orange-500’, shape: ‘square’, originalIndex: 2 }
],
correctSequence: [‘small’, ‘medium’, ‘big’]
},
{
name: “Activity 3: Arrange the Rectangles”,
items: [
{ id: ‘a3_s’, sizeLabel: ‘small’, width: ’30px’, height: ’50px’, color: ‘bg-teal-500’, shape: ‘rectangle’, originalIndex: 0 },
{ id: ‘a3_m’, sizeLabel: ‘medium’, width: ’40px’, height: ’75px’, color: ‘bg-pink-500’, shape: ‘rectangle’, originalIndex: 1 },
{ id: ‘a3_b’, sizeLabel: ‘big’, width: ’50px’, height: ‘100px’, color: ‘bg-indigo-500’, shape: ‘rectangle’, originalIndex: 2 }
],
correctSequence: [‘small’, ‘medium’, ‘big’]
},
{
name: “Activity 4: Order the Ovals”,
items: [
{ id: ‘a4_s’, sizeLabel: ‘small’, width: ’50px’, height: ’30px’, color: ‘bg-cyan-500’, shape: ‘oval’, originalIndex: 0 },
{ id: ‘a4_m’, sizeLabel: ‘medium’, width: ’75px’, height: ’45px’, color: ‘bg-lime-500’, shape: ‘oval’, originalIndex: 1 },
{ id: ‘a4_b’, sizeLabel: ‘big’, width: ‘100px’, height: ’60px’, color: ‘bg-rose-500’, shape: ‘oval’, originalIndex: 2 }
],
correctSequence: [‘small’, ‘medium’, ‘big’]
},
{
name: “Activity 5: Size the Stars”,
items: [
{ id: ‘a5_s’, sizeLabel: ‘small’, width: ’40px’, height: ’40px’, shape: ‘star’, content: ‘β’, fontSize: ‘text-xl’, originalIndex: 0 },
{ id: ‘a5_m’, sizeLabel: ‘medium’, width: ’60px’, height: ’60px’, shape: ‘star’, content: ‘β’, fontSize: ‘text-3xl’, originalIndex: 1 },
{ id: ‘a5_b’, sizeLabel: ‘big’, width: ’80px’, height: ’80px’, shape: ‘star’, content: ‘β’, fontSize: ‘text-5xl’, originalIndex: 2 }
],
correctSequence: [‘small’, ‘medium’, ‘big’]
}
];
let currentActivityIndex = 0;
let draggedItem = null;
const successEmojis = [‘π’, ‘π₯³’, ‘π€©’, ‘π’, ‘β¨’, ‘π’, ‘π―’, ‘π’, ‘π’];
const activityTitleEl = document.getElementById(‘activity-title’);
const sourceObjectsEl = document.getElementById(‘source-objects’);
const sourcePlaceholderEl = document.getElementById(‘source-placeholder’);
const targetZones = [
document.getElementById(‘target-1’),
document.getElementById(‘target-2’),
document.getElementById(‘target-3’)
];
const feedbackMessageEl = document.getElementById(‘feedback-message’);
const resetActivityButton = document.getElementById(‘reset-activity-button’);
const nextActivityButton = document.getElementById(‘next-activity-button’);
const allActivitiesCompleteEl = document.getElementById(‘all-activities-complete’);
const successModal = document.getElementById(‘success-modal’);
const successEmojiDisplay = document.getElementById(‘success-emoji-display’);
const successMessageModalEl = document.getElementById(‘success-message-modal’);
const modalCloseButton = document.getElementById(‘modal-close-button’);
function shuffleArray(array) {
for (let i = array.length – 1; i > 0; i–) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
}
function createDraggableItem(itemData) {
const itemEl = document.createElement(‘div’);
itemEl.id = itemData.id;
itemEl.classList.add(‘draggable-item’);
if (itemData.shape === ‘star’) {
itemEl.classList.add(‘shape-star’);
itemEl.textContent = itemData.content;
if (itemData.fontSize === ‘text-xl’) itemEl.style.fontSize = ‘1.25rem’;
if (itemData.fontSize === ‘text-3xl’) itemEl.style.fontSize = ‘1.875rem’;
if (itemData.fontSize === ‘text-5xl’) itemEl.style.fontSize = ‘3rem’;
} else {
itemEl.classList.add(itemData.color, `shape-${itemData.shape}`);
}
itemEl.style.width = itemData.width;
itemEl.style.height = itemData.height;
itemEl.draggable = true;
itemEl.dataset.sizeLabel = itemData.sizeLabel;
itemEl.addEventListener(‘dragstart’, (e) => {
draggedItem = itemEl;
e.dataTransfer.setData(‘text/plain’, itemEl.id);
e.dataTransfer.effectAllowed = ‘move’;
// Using a timeout for class addition can sometimes be tricky with CSPs if not careful,
// but arrow functions are generally fine. Let’s ensure this is not an issue.
// The ‘dragging’ class is for visual feedback and not critical if it causes CSP issues.
// For now, assume it’s okay as it’s not string evaluation.
setTimeout(() => itemEl.classList.add(‘dragging’), 0);
if (itemEl.parentElement.classList.contains(‘drop-zone’)) {
itemEl.parentElement.dataset.droppedSize = ”;
itemEl.parentElement.dataset.itemId = ”;
itemEl.parentElement.classList.remove(‘occupied’);
}
});
itemEl.addEventListener(‘dragend’, () => {
if (draggedItem) {
draggedItem.classList.remove(‘dragging’);
}
draggedItem = null;
});
return itemEl;
}
function getDraggableItemFromZone(zone) {
for (let child of zone.children) {
if (child.classList.contains(‘draggable-item’)) {
return child;
}
}
return null;
}
function loadActivity(index) {
if (index >= activities.length) {
allActivitiesCompleteEl.textContent = “π Congratulations! You’ve completed all activities! π”;
allActivitiesCompleteEl.classList.remove(‘hidden’);
activityTitleEl.textContent = “All Done!”;
sourceObjectsEl.innerHTML = ”;
sourcePlaceholderEl.classList.remove(‘hidden’);
sourcePlaceholderEl.textContent = “No more items!”;
targetZones.forEach(zone => {
const item = getDraggableItemFromZone(zone);
if (item) item.remove();
zone.classList.remove(‘occupied’);
zone.dataset.droppedSize = ”;
zone.dataset.itemId = ”;
});
feedbackMessageEl.innerHTML = ‘ ‘;
resetActivityButton.classList.add(‘hidden’);
nextActivityButton.classList.add(‘hidden’);
document.getElementById(‘instruction’).classList.add(‘hidden’);
return;
}
const activity = activities[index];
activityTitleEl.textContent = activity.name;
feedbackMessageEl.innerHTML = ‘ ‘;
nextActivityButton.classList.add(‘hidden’);
resetActivityButton.classList.remove(‘hidden’);
resetActivityButton.disabled = false;
allActivitiesCompleteEl.classList.add(‘hidden’);
document.getElementById(‘instruction’).classList.remove(‘hidden’);
sourceObjectsEl.innerHTML = ”;
targetZones.forEach(zone => {
const item = getDraggableItemFromZone(zone);
if (item) item.remove();
zone.classList.remove(‘occupied’);
zone.dataset.droppedSize = ”;
zone.dataset.itemId = ”;
});
const itemsToDisplay = […activity.items];
shuffleArray(itemsToDisplay);
if (itemsToDisplay.length > 0) {
sourcePlaceholderEl.classList.add(‘hidden’);
} else {
sourcePlaceholderEl.classList.remove(‘hidden’);
}
itemsToDisplay.forEach(itemData => {
const itemEl = createDraggableItem(itemData);
sourceObjectsEl.appendChild(itemEl);
});
setItemsDraggable(true);
}
function setItemsDraggable(isDraggable) {
document.querySelectorAll(‘.draggable-item’).forEach(item => {
item.draggable = isDraggable;
item.style.cursor = isDraggable ? ‘grab’ : ‘default’;
});
}
targetZones.forEach(targetZoneElement => {
targetZoneElement.addEventListener(‘dragover’, (e) => {
e.preventDefault();
const currentItemInZone = getDraggableItemFromZone(targetZoneElement);
if (!currentItemInZone || (currentItemInZone && currentItemInZone !== draggedItem)) {
targetZoneElement.classList.add(‘drop-zone-hover’);
}
e.dataTransfer.dropEffect = ‘move’;
});
targetZoneElement.addEventListener(‘dragleave’, () => {
targetZoneElement.classList.remove(‘drop-zone-hover’);
});
targetZoneElement.addEventListener(‘drop’, (e) => {
e.preventDefault();
// console.log(‘Drop event fired on target:’, targetZoneElement.id, ‘Dragged item ID:’, draggedItem ? draggedItem.id : ‘null’);
targetZoneElement.classList.remove(‘drop-zone-hover’);
if (!draggedItem) {
// console.error(‘draggedItem is null in drop event.’);
return;
}
const itemToDrop = draggedItem;
const targetZone = targetZoneElement;
const currentItemInTargetZone = getDraggableItemFromZone(targetZone);
if (currentItemInTargetZone && currentItemInTargetZone !== itemToDrop) {
sourceObjectsEl.appendChild(currentItemInTargetZone);
}
targetZone.appendChild(itemToDrop);
targetZone.dataset.droppedSize = itemToDrop.dataset.sizeLabel;
targetZone.dataset.itemId = itemToDrop.id;
targetZone.classList.add(‘occupied’);
checkSequence();
});
});
function checkSequence() {
const droppedItemsCount = targetZones.reduce((count, zone) => count + (getDraggableItemFromZone(zone) ? 1 : 0), 0);
if (droppedItemsCount === 3) {
const currentSequence = targetZones.map(zone => zone.dataset.droppedSize);
const correctSequence = activities[currentActivityIndex].correctSequence;
let isCorrect = true;
for (let i = 0; i < correctSequence.length; i++) {
if (currentSequence[i] !== correctSequence[i]) {
isCorrect = false;
break;
}
}
if (isCorrect) {
feedbackMessageEl.textContent = 'Excellent! That\'s the right order!';
feedbackMessageEl.classList.remove('text-red-600');
feedbackMessageEl.classList.add('text-green-600');
const randomEmoji = successEmojis[Math.floor(Math.random() * successEmojis.length)];
successEmojiDisplay.textContent = randomEmoji;
successMessageModalEl.textContent = "Great Job!";
successModal.style.display = 'flex';
setItemsDraggable(false);
resetActivityButton.disabled = true; // Disable reset when correct
if (currentActivityIndex {
if (resetActivityButton.disabled) return;
targetZones.forEach(zone => {
const item = getDraggableItemFromZone(zone);
if (item) {
sourceObjectsEl.appendChild(item);
zone.dataset.droppedSize = ”;
zone.dataset.itemId = ”;
zone.classList.remove(‘occupied’);
}
});
loadActivity(currentActivityIndex);
feedbackMessageEl.innerHTML = ‘ ‘;
setItemsDraggable(true);
});
modalCloseButton.addEventListener(‘click’, () => {
successModal.style.display = ‘none’;
});
nextActivityButton.addEventListener(‘click’, () => {
currentActivityIndex++;
loadActivity(currentActivityIndex);
nextActivityButton.classList.add(‘hidden’);
resetActivityButton.classList.remove(‘hidden’);
resetActivityButton.disabled = false;
});
// Initial load
loadActivity(currentActivityIndex);