diff --git a/src/app.js b/src/app.js index 015f4ac..d8fc76a 100644 --- a/src/app.js +++ b/src/app.js @@ -195,6 +195,7 @@ let currentMode = "css"; let sidebarTrigger = null; function openSidebar() { + track("sidebar_open"); // Store trigger element for focus return sidebarTrigger = document.activeElement; @@ -444,6 +445,7 @@ function selectLesson(moduleId, lessonIndex) { } lessonEngine.setLessonByIndex(lessonIndex); + track("lesson_select", { module: moduleId, lesson: lessonIndex }); // Show lesson UI showLessonUI(); @@ -687,6 +689,7 @@ function nextLesson() { const success = lessonEngine.nextLesson(); if (success) { const newState = lessonEngine.getCurrentState(); + track("lesson_nav", { direction: "next", module: newState.module.id, lesson: newState.lessonIndex }); // Update URL updateHash(newState.module.id, newState.lessonIndex); @@ -702,6 +705,7 @@ function prevLesson() { const success = lessonEngine.previousLesson(); if (success) { const newState = lessonEngine.getCurrentState(); + track("lesson_nav", { direction: "prev", module: newState.module.id, lesson: newState.lessonIndex }); // Update URL updateHash(newState.module.id, newState.lessonIndex); @@ -728,6 +732,7 @@ function resetCode() { // Reset editor to initial code for current lesson lessonEngine.reset(); const engineState = lessonEngine.getCurrentState(); + track("reset_code", { module: engineState.module?.id, lesson: engineState.lessonIndex }); if (codeEditor && engineState.lesson) { codeEditor.setValue(engineState.lesson.initialCode || ""); } @@ -853,6 +858,7 @@ function runCode() { // ================= DIALOGS ================= function showHelp() { + track("help_open"); elements.helpDialog.showModal(); } @@ -915,6 +921,7 @@ function handleResetCodeClick() { // ================= SHARE DIALOG ================= function showShareDialog() { + track("share_open"); const engineState = lessonEngine.getCurrentState(); if (engineState.module && engineState.lesson !== null) { const shareUrl = getShareableUrl(engineState.module.id, engineState.lessonIndex); @@ -929,6 +936,7 @@ function closeShareDialog() { } async function copyShareUrl() { + track("share_copy"); try { await navigator.clipboard.writeText(elements.shareUrlInput.value); elements.copyFeedback.hidden = false; @@ -1370,12 +1378,42 @@ const referenceContent = {
box-shadowbox-shadow: 0 4px 8px rgba(0,0,0,0.1);text-shadowtext-shadow: 1px 1px 2px gray;transformtransform: translateY(-2px);transitiontransition: all 0.3s ease;cursorcursor: pointer;| Property | Values | Example |
|---|---|---|
transition | property duration easing | transition: all 0.3s ease; |
transition-property | all, none, specific | transition-property: opacity; |
transition-duration | time (s, ms) | transition-duration: 0.3s; |
transition-timing-function | ease, linear, ease-in-out | transition-timing-function: ease-out; |
animation | name duration timing delay | animation: fade 1s ease-in; |
animation-name | @keyframes name | animation-name: slide; |
animation-duration | time (s, ms) | animation-duration: 2s; |
animation-iteration-count | number, infinite | animation-iteration-count: infinite; |
animation-fill-mode | none, forwards, backwards, both | animation-fill-mode: forwards; |
| Syntax | Description | Example |
|---|---|---|
--name | Define custom property | --primary: steelblue; |
var(--name) | Use custom property | color: var(--primary); |
var(--name, fallback) | With fallback value | color: var(--accent, blue); |
:root { } | Global scope | :root { --gap: 1rem; } |
See also: Flexbox Reference | Grid Reference | Selectors Reference
`, @@ -1737,6 +1775,21 @@ const referenceContent = { +| Element | Purpose | Key Attributes |
|---|---|---|
<details> | Expandable content | open (default expanded) |
<summary> | Details toggle label | First child of details |
<dialog> | Modal/popup dialog | open, use showModal() |
<progress> | Progress bar | value, max |
<meter> | Scalar measurement | value, min, max, low, high |
<datalist> | Input suggestions | id (link via input list attr) |
Learn: HTML Section | Style with: CSS Properties
` };