From f6f37a6b2494ca8afdfe19ecaf494c20ae151f23 Mon Sep 17 00:00:00 2001 From: Michael Czechowski Date: Sun, 29 Mar 2026 23:24:32 +0200 Subject: [PATCH 1/2] docs: add spec, plan, and tasks for filters task wording fix (#12) --- specs/012-filters-tasks/plan.md | 65 ++++++++++++++++++++++++++++++++ specs/012-filters-tasks/spec.md | 30 +++++++++++++++ specs/012-filters-tasks/tasks.md | 12 ++++++ 3 files changed, 107 insertions(+) create mode 100644 specs/012-filters-tasks/plan.md create mode 100644 specs/012-filters-tasks/spec.md create mode 100644 specs/012-filters-tasks/tasks.md diff --git a/specs/012-filters-tasks/plan.md b/specs/012-filters-tasks/plan.md new file mode 100644 index 0000000..d878e58 --- /dev/null +++ b/specs/012-filters-tasks/plan.md @@ -0,0 +1,65 @@ +# Implementation Plan + +## Objective + +Rewrite the 4 CSS Filters lessons in `lessons/11-filters.json` so tasks describe visual outcomes instead of giving exact CSS code, and validations accept multiple valid answers. + +## Approach + +Follow the same pattern established in issue #9 (colors/box-model rewrite): +1. Rewrite each `task` field to describe the desired visual effect +2. Replace exact-match validations (`property_value`, `contains`) with `regex` validations that accept a range of values +3. Update validation `message` fields to give pedagogical hints without revealing answers +4. Keep `description`, `previewHTML`, `previewBaseCSS`, `codePrefix`, `codeSuffix`, and `solution` unchanged + +## File Mapping + +| File | Action | Description | +|------|--------|-------------| +| `lessons/11-filters.json` | modify | Rewrite tasks and validations for all 4 lessons | + +## Lesson-by-Lesson Plan + +### filters-1: Blur Filter +- **Current task:** "Blur the background image using `filter: blur(4px)`." +- **New task:** Describe frosted-glass effect, mention blur radius range (2px-8px) +- **Validation:** `regex` matching `filter:\s*blur\((2|3|4|5|6|7|8)px\)` — accepts 2px through 8px +- **Message:** Hint about the `filter` property and `blur()` function + +### filters-2: Grayscale Filter +- **Current task:** "Make the image grayscale with `filter: grayscale(100%)`." +- **New task:** Describe removing all color to create a black-and-white effect +- **Validation:** `regex` matching `filter:\s*grayscale\(100%\)` — grayscale only makes sense at 100% for "fully desaturated" +- **Message:** Hint about which filter function removes color + +### filters-3: Brightness Filter +- **Current task:** "Brighten the card with `filter: brightness(120%)`." +- **New task:** Describe making the card appear brighter/more vivid, accept range 110%-150% +- **Validation:** `regex` matching `filter:\s*brightness\(1[1-5]0%\)` — accepts 110% through 150% +- **Message:** Hint about the brightness function and values above 100% + +### filters-4: Drop Shadow +- **Current task:** "Add a drop shadow with `filter: drop-shadow(4px 4px 8px gray)`." +- **New task:** Describe adding a soft shadow behind the star to give it depth +- **Validation:** `regex` matching `filter:\s*drop-shadow\(` with reasonable offset/blur values +- **Message:** Hint about the drop-shadow filter function + +## Architecture Decisions + +- Use `regex` validation type (not `property_value`) to allow multiple acceptable values, consistent with the colors/box-model rewrite +- Use `options: { "caseSensitive": false }` on all regex validations for consistency +- Keep solution fields unchanged as reference answers + +## Risks + +| Risk | Mitigation | +|------|------------| +| Regex too permissive | Test edge cases; only accept pedagogically reasonable values | +| Regex too restrictive | Allow generous ranges; err on the side of accepting creative answers | +| Breaking existing progress | localStorage keys are based on lesson IDs which are unchanged | + +## Testing Strategy + +- Run existing test suite (`npm test`) to verify no regressions +- Validate JSON file against schema +- Manual review of regex patterns for correctness diff --git a/specs/012-filters-tasks/spec.md b/specs/012-filters-tasks/spec.md new file mode 100644 index 0000000..1d60250 --- /dev/null +++ b/specs/012-filters-tasks/spec.md @@ -0,0 +1,30 @@ +# fix: rewrite CSS Filters tasks to describe visual outcomes instead of exact code + +**Issue:** [#12](https://git.librete.ch/public/code-crispies/issues/12) +**Repository:** public/code-crispies +**Author:** libretech +**State:** open +**Labels:** none + +## Problem + +Pedagogy audit: the Filters module has a 100% copy-paste score. Every exercise gives the exact CSS declaration in the task text, so students can complete lessons without understanding the concepts. + +## Requirements + +- Rewrite ONLY the filters module (`lessons/11-filters.json`) task descriptions to describe the desired visual effect instead of the exact code +- Accept multiple valid values in validations (e.g., accept blur values between 2px and 8px instead of only 4px) +- Do NOT change any other module + +## Example + +- **Before:** "Add `filter: blur(4px)`" +- **After:** "Blur the background image to create a frosted-glass effect. Use a blur radius between 2px and 8px." + +## Acceptance Criteria + +1. All 4 filter lesson tasks describe visual outcomes, not exact CSS +2. Validations accept a range of valid values using regex patterns +3. Validation messages provide pedagogical hints without revealing answers +4. No changes to any file outside `lessons/11-filters.json` +5. Existing tests continue to pass diff --git a/specs/012-filters-tasks/tasks.md b/specs/012-filters-tasks/tasks.md new file mode 100644 index 0000000..89b67da --- /dev/null +++ b/specs/012-filters-tasks/tasks.md @@ -0,0 +1,12 @@ +# Tasks + +## Phase 1: Core Implementation +- [ ] Task 1.1: Rewrite filters-1 (Blur) task and validations [P] +- [ ] Task 1.2: Rewrite filters-2 (Grayscale) task and validations [P] +- [ ] Task 1.3: Rewrite filters-3 (Brightness) task and validations [P] +- [ ] Task 1.4: Rewrite filters-4 (Drop Shadow) task and validations [P] + +## Phase 2: Validation +- [ ] Task 2.1: Validate JSON against schema +- [ ] Task 2.2: Run existing test suite +- [ ] Task 2.3: Format lesson file with Prettier From 8bd407899a88416a3d1239251d615f653ecfd06e Mon Sep 17 00:00:00 2001 From: Michael Czechowski Date: Sun, 29 Mar 2026 23:27:43 +0200 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20implement=20#12=20=E2=80=94=20rewrit?= =?UTF-8?q?e=20CSS=20Filters=20tasks=20to=20describe=20visual=20outcomes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lessons/11-filters.json | 51 +++++++++++++------------------- specs/012-filters-tasks/tasks.md | 14 ++++----- 2 files changed, 27 insertions(+), 38 deletions(-) diff --git a/lessons/11-filters.json b/lessons/11-filters.json index 11fdd7b..a6d24b2 100644 --- a/lessons/11-filters.json +++ b/lessons/11-filters.json @@ -9,7 +9,7 @@ "id": "filters-1", "title": "Blur Filter", "description": "The filter property applies visual effects to elements. The blur() function creates a Gaussian blur effect.

filter: blur(4px);

Higher values create more blur. This is great for backgrounds or creating depth.", - "task": "Blur the background image using filter: blur(4px).", + "task": "Blur the background image to create a frosted-glass effect. Use a blur radius between 2px and 8px.", "previewHTML": "

Welcome

", "previewBaseCSS": "body { font-family: system-ui, sans-serif; margin: 0; height: 200px; position: relative; overflow: hidden; } .bg { position: absolute; inset: 0; background: linear-gradient(45deg, coral, gold, steelblue); } .content { position: relative; z-index: 1; display: flex; align-items: center; justify-content: center; height: 100%; } .content h2 { color: white; text-shadow: 0 2px 8px rgba(0,0,0,0.3); margin: 0; }", "sandboxCSS": "", @@ -20,9 +20,10 @@ "previewContainer": "preview-area", "validations": [ { - "type": "property_value", - "value": { "property": "filter", "expected": "blur(4px)" }, - "message": "Set filter: blur(4px)" + "type": "regex", + "value": "filter:\\s*blur\\((2|3|4|5|6|7|8)px\\)", + "message": "Use the filter property with the blur() function. Try a value between 2px and 8px", + "options": { "caseSensitive": false } } ] }, @@ -30,7 +31,7 @@ "id": "filters-2", "title": "Grayscale Filter", "description": "The grayscale() function removes color from an element. Use values from 0% (full color) to 100% (fully grayscale).

filter: grayscale(100%);

Great for hover effects or disabled states.", - "task": "Make the image grayscale with filter: grayscale(100%).", + "task": "Remove all color from the image to create a black-and-white effect.", "previewHTML": "
", "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .photo { width: 200px; height: 150px; background: linear-gradient(135deg, coral 0%, gold 50%, steelblue 100%); border-radius: 8px; }", "sandboxCSS": "", @@ -41,14 +42,10 @@ "previewContainer": "preview-area", "validations": [ { - "type": "contains", - "value": "grayscale", - "message": "Use grayscale() filter" - }, - { - "type": "contains", - "value": "100%", - "message": "Set to 100% for full grayscale" + "type": "regex", + "value": "filter:\\s*grayscale\\(100%\\)", + "message": "Which filter function removes color from an element? Set it to full strength", + "options": { "caseSensitive": false } } ] }, @@ -56,7 +53,7 @@ "id": "filters-3", "title": "Brightness Filter", "description": "The brightness() function adjusts how bright an element appears. Values below 100% darken, above 100% brighten.

filter: brightness(150%);
", - "task": "Brighten the card with filter: brightness(120%).", + "task": "Make the card appear brighter and more vivid. Use a brightness value between 110% and 150%.", "previewHTML": "
Featured
", "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #1a1a2e; } .card { padding: 2rem; background: linear-gradient(135deg, #4a4a6a, #2a2a4a); border-radius: 12px; text-align: center; } .card span { color: gold; font-weight: 600; text-transform: uppercase; letter-spacing: 2px; }", "sandboxCSS": "", @@ -67,14 +64,10 @@ "previewContainer": "preview-area", "validations": [ { - "type": "contains", - "value": "brightness", - "message": "Use brightness() filter" - }, - { - "type": "contains", - "value": "120%", - "message": "Set to 120%" + "type": "regex", + "value": "filter:\\s*brightness\\(1[1-5]0%\\)", + "message": "Use the filter property with the brightness() function. Values above 100% make things brighter", + "options": { "caseSensitive": false } } ] }, @@ -82,7 +75,7 @@ "id": "filters-4", "title": "Drop Shadow", "description": "The drop-shadow() filter creates a shadow that follows the shape of the element, including transparency. Unlike box-shadow, it works on images with transparent backgrounds.

filter: drop-shadow(2px 4px 6px black);
", - "task": "Add a drop shadow with filter: drop-shadow(4px 4px 8px gray).", + "task": "Add a soft shadow behind the star to give it depth. Use the drop-shadow filter with offset, blur, and a color.", "previewHTML": "
", "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 2rem; display: flex; justify-content: center; } .icon { font-size: 4rem; color: gold; }", "sandboxCSS": "", @@ -93,14 +86,10 @@ "previewContainer": "preview-area", "validations": [ { - "type": "contains", - "value": "drop-shadow", - "message": "Use drop-shadow() filter" - }, - { - "type": "contains", - "value": "4px 4px 8px", - "message": "Set shadow offset and blur" + "type": "regex", + "value": "filter:\\s*drop-shadow\\(\\d+px\\s+\\d+px\\s+\\d+px\\s+\\w+\\)", + "message": "Use the filter property with drop-shadow(). It needs horizontal offset, vertical offset, blur radius, and a color", + "options": { "caseSensitive": false } } ] } diff --git a/specs/012-filters-tasks/tasks.md b/specs/012-filters-tasks/tasks.md index 89b67da..fbd6854 100644 --- a/specs/012-filters-tasks/tasks.md +++ b/specs/012-filters-tasks/tasks.md @@ -1,12 +1,12 @@ # Tasks ## Phase 1: Core Implementation -- [ ] Task 1.1: Rewrite filters-1 (Blur) task and validations [P] -- [ ] Task 1.2: Rewrite filters-2 (Grayscale) task and validations [P] -- [ ] Task 1.3: Rewrite filters-3 (Brightness) task and validations [P] -- [ ] Task 1.4: Rewrite filters-4 (Drop Shadow) task and validations [P] +- [X] Task 1.1: Rewrite filters-1 (Blur) task and validations [P] +- [X] Task 1.2: Rewrite filters-2 (Grayscale) task and validations [P] +- [X] Task 1.3: Rewrite filters-3 (Brightness) task and validations [P] +- [X] Task 1.4: Rewrite filters-4 (Drop Shadow) task and validations [P] ## Phase 2: Validation -- [ ] Task 2.1: Validate JSON against schema -- [ ] Task 2.2: Run existing test suite -- [ ] Task 2.3: Format lesson file with Prettier +- [X] Task 2.1: Validate JSON against schema +- [X] Task 2.2: Run existing test suite +- [X] Task 2.3: Format lesson file with Prettier