diff --git a/src/index.html b/src/index.html
index 3b93c21..4c5ff30 100644
--- a/src/index.html
+++ b/src/index.html
@@ -2,6 +2,7 @@
+
CODE CRISPIES - Learn CSS Interactively
diff --git a/src/js/app.js b/src/js/app.js
index 98b01a8..ee89c2c 100644
--- a/src/js/app.js
+++ b/src/js/app.js
@@ -32,6 +32,7 @@ const elements = {
moduleSelectorBtn: document.getElementById('module-selector-btn'),
resetBtn: document.getElementById('reset-btn'),
helpBtn: document.getElementById('help-btn'),
+ lessonContainer: document.querySelector('.lesson-container'),
};
// Initialize the lesson engine
@@ -63,12 +64,59 @@ async function initializeModules() {
} else if (state.modules.length > 0) {
selectModule(state.modules[0].id);
}
+
+ // Update progress indicator on module selector button
+ updateModuleSelectorButtonProgress();
} catch (error) {
console.error('Failed to load modules:', error);
elements.lessonDescription.textContent = 'Failed to load modules. Please refresh the page.';
}
}
+// Update progress indicator on module selector button
+function updateModuleSelectorButtonProgress() {
+ if (!state.modules.length) return;
+
+ // Calculate overall progress across all modules
+ let totalLessons = 0;
+ let totalCompleted = 0;
+
+ state.modules.forEach(module => {
+ totalLessons += module.lessons.length;
+ const progress = state.userProgress[module.id];
+ if (progress && progress.completed) {
+ totalCompleted += progress.completed.length;
+ }
+ });
+
+ const percentComplete = totalLessons > 0 ? Math.round((totalCompleted / totalLessons) * 100) : 0;
+
+ // Create progress indicator
+ const progressBar = document.createElement('div');
+ progressBar.className = 'progress-indicator';
+ progressBar.style.cssText = `
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ height: 3px;
+ width: ${percentComplete}%;
+ background-color: var(--success-color);
+ border-radius: 0 3px 3px 0;
+ `;
+
+ // Add progress percentage text
+ elements.moduleSelectorBtn.innerHTML = `Progress ${percentComplete}%`;
+ elements.moduleSelectorBtn.style.position = 'relative';
+
+ // Remove any existing progress bar before adding new one
+ const existingBar = elements.moduleSelectorBtn.querySelector('.progress-indicator');
+ if (existingBar) {
+ existingBar.remove();
+ }
+
+ elements.moduleSelectorBtn.appendChild(progressBar);
+}
+
// Select a module
function selectModule(moduleId) {
const selectedModule = state.modules.find(module => module.id === moduleId);
@@ -95,6 +143,17 @@ function selectModule(moduleId) {
// Save the last selected module
localStorage.setItem('lastModuleId', moduleId);
+
+ // Reset any success indicators
+ resetSuccessIndicators();
+}
+
+// Reset success indicators
+function resetSuccessIndicators() {
+ elements.lessonContainer.classList.remove('success-highlight');
+ elements.lessonTitle.classList.remove('success-text');
+ const headings = elements.lessonContainer.querySelectorAll('h2, h3, h4');
+ headings.forEach(heading => heading.classList.remove('success-text'));
}
// Load the current lesson
@@ -113,6 +172,9 @@ function loadCurrentLesson() {
const lesson = state.currentModule.lessons[state.currentLessonIndex];
lessonEngine.setLesson(lesson);
+ // Reset any success indicators
+ resetSuccessIndicators();
+
// Update UI
renderLesson(
elements.lessonTitle,
@@ -138,6 +200,9 @@ function loadCurrentLesson() {
// Save current progress
state.userProgress[state.currentModule.id].current = state.currentLessonIndex;
saveUserProgress();
+
+ // Update progress indicator on module selector button
+ updateModuleSelectorButtonProgress();
}
// Update navigation buttons state
@@ -191,11 +256,18 @@ function runCode() {
if (!moduleProgress.completed.includes(state.currentLessonIndex)) {
moduleProgress.completed.push(state.currentLessonIndex);
saveUserProgress();
+ updateModuleSelectorButtonProgress();
}
- // Show success feedback
+ // Show success feedback with visual indicators
showFeedback(true, validationResult.message || 'Great job! Your code works correctly.');
+ // Add success visual indicators
+ elements.lessonContainer.classList.add('success-highlight');
+ elements.lessonTitle.classList.add('success-text');
+ const headings = elements.lessonContainer.querySelectorAll('h3, h4');
+ headings.forEach(heading => heading.classList.add('success-text'));
+
// Apply the code to see the result
lessonEngine.applyUserCode(userCode);
@@ -205,8 +277,11 @@ function runCode() {
elements.nextBtn.classList.remove('btn-disabled');
}
} else {
- // Show error feedback
- showFeedback(false, validationResult.message || 'Your code doesn\'t seem to be correct. Try again!');
+ // Reset any success indicators
+ resetSuccessIndicators();
+
+ // Show error feedback (with friendly message)
+ showFeedback(false, validationResult.message || 'Not quite there yet! Let\'s try again.');
}
}
@@ -285,7 +360,7 @@ function showHelp() {
- Run - Test your CSS code
- Previous/Next - Navigate between lessons
- - Modules - Select a different learning module
+ - Progress - Select a different learning module
- Reset Progress - Clear all your saved progress
@@ -294,6 +369,8 @@ function showHelp() {
Use the preview area to see how your CSS affects the elements
Your progress is automatically saved in your browser storage
You can revisit completed lessons at any time
+ Press Tab in the code editor to indent with two spaces
+ Use Ctrl+Enter to quickly run your code
`;
@@ -326,6 +403,9 @@ function resetProgress() {
} else if (state.modules.length > 0) {
selectModule(state.modules[0].id);
}
+
+ // Update progress indicator
+ updateModuleSelectorButtonProgress();
});
elements.modalContainer.classList.remove('hidden');
@@ -336,6 +416,22 @@ function closeModal() {
elements.modalContainer.classList.add('hidden');
}
+// Handle tab key in the code editor
+function handleTabKey(e) {
+ if (e.key === 'Tab') {
+ e.preventDefault();
+
+ const start = e.target.selectionStart;
+ const end = e.target.selectionEnd;
+
+ // Add two spaces at cursor position
+ e.target.value = e.target.value.substring(0, start) + ' ' + e.target.value.substring(end);
+
+ // Move cursor position after the inserted spaces
+ e.target.selectionStart = e.target.selectionEnd = start + 2;
+ }
+}
+
// Initialize the application
function init() {
loadUserProgress();
@@ -350,6 +446,9 @@ function init() {
elements.resetBtn.addEventListener('click', resetProgress);
elements.helpBtn.addEventListener('click', showHelp);
+ // Add tab key handler for the code input
+ elements.codeInput.addEventListener('keydown', handleTabKey);
+
// Handle keyboard shortcuts
document.addEventListener('keydown', (e) => {
// Ctrl+Enter to run code
diff --git a/src/js/renderer.js b/src/js/renderer.js
index be8655e..60e7896 100644
--- a/src/js/renderer.js
+++ b/src/js/renderer.js
@@ -9,7 +9,7 @@ let feedbackElement = null;
* Render the module list in the sidebar
* @param {HTMLElement} container - The container element for the module list
* @param {Array} modules - The list of modules
- * @param {Function} onSelectModule - Callback when a module is selected
+ * @param { Function} onSelectModule - Callback when a module is selected
*/
export function renderModuleList(container, modules, onSelectModule) {
// Clear the container
diff --git a/src/lessons/configs/tailwindcss.json b/src/lessons/configs/tailwindcss.json
new file mode 100644
index 0000000..e69de29
diff --git a/src/styles/main.css b/src/styles/main.css
index 9338fef..9638c91 100644
--- a/src/styles/main.css
+++ b/src/styles/main.css
@@ -107,10 +107,16 @@ body {
/* Lesson Container */
.lesson-container {
+ display: flex;
+ flex-flow: column;
+ align-items: stretch;
+ justify-content: flex-start;
+ gap: 2rem;
background-color: var(--panel-bg);
border-radius: 8px;
box-shadow: var(--shadow);
padding: 2rem;
+ height: 100%;
margin-bottom: 2rem;
}
@@ -128,6 +134,7 @@ body {
/* Challenge Container */
.challenge-container {
display: flex;
+ flex: 1;
flex-direction: column;
gap: 1.5rem;
margin-bottom: 2rem;
@@ -216,6 +223,11 @@ code {
/* Controls */
.controls {
+ /*justify-self: end;*/
+ /*position: absolute;*/
+ /*left: 2rem;*/
+ /*right: 2rem;*/
+ /*bottom: 2rem;*/
display: flex;
justify-content: space-between;
align-items: center;