From 3a5e5220440e8bb9762d0e35cdf8b29a1d7ba1c7 Mon Sep 17 00:00:00 2001 From: Krishna Jha Date: Mon, 18 May 2026 15:53:42 +0530 Subject: [PATCH 1/2] Fix: add Read more toggle to expand truncated project descriptions --- static/script.js | 24 ++++++++++++++++++++++-- static/style.css | 16 ++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/static/script.js b/static/script.js index d4de2211..d6e8447e 100644 --- a/static/script.js +++ b/static/script.js @@ -541,11 +541,31 @@ if (isIndexPage) { title.textContent = project.title; // Description (truncated for visual consistency) + // Description with Read More toggle var desc = document.createElement("p"); desc.className = "project-card-desc"; - // Cut description to 120 chars so all cards stay the same height - desc.textContent = truncate(project.description, 120); + var shortText = truncate(project.description, 120); + var fullText = project.description; + var isExpanded = false; + + desc.textContent = shortText; + + // Only add Read More button if description is actually truncated + if (fullText.length > 120) { + var readMoreBtn = document.createElement("button"); + readMoreBtn.className = "read-more-btn"; + readMoreBtn.textContent = "Read more"; + + readMoreBtn.addEventListener("click", function () { + isExpanded = !isExpanded; + desc.textContent = isExpanded ? fullText : shortText; + readMoreBtn.textContent = isExpanded ? "Read less" : "Read more"; + desc.appendChild(readMoreBtn); // re-append button since textContent clears it + }); + + desc.appendChild(readMoreBtn); + } // Tags row var tagsRow = document.createElement("div"); tagsRow.className = "project-card-tags"; diff --git a/static/style.css b/static/style.css index 3b11ca9e..4f9b4271 100644 --- a/static/style.css +++ b/static/style.css @@ -2011,4 +2011,20 @@ select:focus { #scroll-top-btn.visible { display: flex; +} + +.read-more-btn { + background: none; + border: none; + color: #6366f1; + font-size: 0.85rem; + font-weight: 600; + cursor: pointer; + padding: 0; + margin-left: 4px; + text-decoration: underline; +} + +.read-more-btn:hover { + color: #4f46e5; } \ No newline at end of file From ad3305d793bb1ebfc6d2450bfe5b4e248b59af23 Mon Sep 17 00:00:00 2001 From: Krishna Jha Date: Tue, 26 May 2026 12:14:29 +0530 Subject: [PATCH 2/2] Fix: separate desc text and button elements, add aria-expanded support --- static/script.js | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/static/script.js b/static/script.js index b791b5cd..ca9a23e0 100644 --- a/static/script.js +++ b/static/script.js @@ -654,47 +654,52 @@ if (clearFiltersBtn) { title.className = "project-card-title"; title.textContent = project.title; - // Description (truncated for visual consistency) - // Description with Read More toggle + // Description wrapper — keeps text and button as separate child elements + // so we never use textContent (which would wipe out child nodes like the button) var desc = document.createElement("p"); desc.className = "project-card-desc"; + // Separate span for the description text so we can update it + // without touching the toggle button + var descText = document.createElement("span"); + descText.className = "project-card-desc-text"; + var shortText = truncate(project.description, 120); - var fullText = project.description; + var fullText = project.description; var isExpanded = false; - desc.textContent = shortText; + descText.textContent = shortText; + desc.appendChild(descText); // Only add Read More button if description is actually truncated if (fullText.length > 120) { var readMoreBtn = document.createElement("button"); readMoreBtn.className = "read-more-btn"; readMoreBtn.textContent = "Read more"; + // aria-expanded tells screen readers whether the content is expanded or not + readMoreBtn.setAttribute("aria-expanded", "false"); readMoreBtn.addEventListener("click", function () { isExpanded = !isExpanded; - desc.textContent = isExpanded ? fullText : shortText; + // Update only the text span — button stays in the DOM untouched + descText.textContent = isExpanded ? fullText : shortText; readMoreBtn.textContent = isExpanded ? "Read less" : "Read more"; - desc.appendChild(readMoreBtn); // re-append button since textContent clears it + readMoreBtn.setAttribute("aria-expanded", isExpanded ? "true" : "false"); }); desc.appendChild(readMoreBtn); } + // Tags row var tagsRow = document.createElement("div"); tagsRow.className = "project-card-tags"; - // Show all project skills as tags so users can see the full match (project.skills || []).forEach(function (skill) { tagsRow.appendChild(createTag(skill, "skill")); }); - // Level tag (colour-coded via CSS class) - // Lowercase so it matches the CSS class names like "level beginner", "level advanced" var levelClass = "level " + (project.level || "").toLowerCase(); tagsRow.appendChild(createTag(project.level, levelClass)); - - // Time tag tagsRow.appendChild(createTag("Time: " + project.time, "time")); // Footer with view-details link @@ -704,7 +709,7 @@ if (clearFiltersBtn) { var link = document.createElement("a"); link.className = "btn-details"; link.textContent = "View Full Project"; - link.href = "/project/" + project.id; //each project has a unique id + link.href = "/project/" + project.id; footer.appendChild(link); @@ -984,4 +989,4 @@ function scrollToTop() { if (scrollTopBtn) { window.addEventListener('scroll', handleScroll); scrollTopBtn.addEventListener('click', scrollToTop); -} + }