2 Commits

Author SHA1 Message Date
c560676544 feat: implement #4 — replace answer-revealing validation messages with pedagogical hints
Rewrite ~120 validation error messages across 17 English lesson modules
and their localized variants (ar, de, es, pl, uk) to use concept questions,
property hints, and directional nudges instead of revealing the exact
CSS property-value answers.

Priority modules (flexbox, box-model, colors, positioning) fully rewritten.
All remaining CSS modules updated. Only message strings changed — no
validation logic modifications.
2026-03-28 19:40:28 +01:00
782e87705c docs: add spec, plan, and task breakdown for issue #4
Pedagogical validation message rewrite across 17 English lesson
modules and 5 localized variants (ar, de, es, pl, uk).
2026-03-28 19:21:24 +01:00
58 changed files with 590 additions and 661 deletions

View File

@@ -22,7 +22,7 @@
{
"type": "property_value",
"value": { "property": "color", "expected": "coral" },
"message": "Add <kbd>color: coral;</kbd>"
"message": "Which property controls text color?"
}
]
},
@@ -43,12 +43,12 @@
{
"type": "property_value",
"value": { "property": "background", "expected": "lavender" },
"message": "Add <kbd>background: lavender;</kbd>"
"message": "Check the <kbd>background</kbd> property"
},
{
"type": "property_value",
"value": { "property": "padding", "expected": "1rem" },
"message": "Add <kbd>padding: 1rem;</kbd>"
"message": "The card needs space inside its edges"
}
]
},
@@ -74,7 +74,7 @@
{
"type": "property_value",
"value": { "property": "color", "expected": "steelblue" },
"message": "Set <kbd>color: steelblue</kbd>"
"message": "Which property changes text color?"
}
]
},
@@ -100,7 +100,7 @@
{
"type": "property_value",
"value": { "property": "color", "expected": "coral" },
"message": "Set <kbd>color: coral</kbd>"
"message": "What value gives a warm, reddish-orange color?"
}
]
},
@@ -126,7 +126,7 @@
{
"type": "property_value",
"value": { "property": "background", "expected": "tomato" },
"message": "Set <kbd>background: tomato</kbd>"
"message": "The badge needs a bright red background"
}
]
},
@@ -152,7 +152,7 @@
{
"type": "property_value",
"value": { "property": "background", "expected": "steelblue" },
"message": "Set <kbd>background: steelblue</kbd>"
"message": "Which property sets the button's fill color?"
}
]
},
@@ -178,7 +178,7 @@
{
"type": "property_value",
"value": { "property": "text-decoration", "expected": "none" },
"message": "Set <kbd>text-decoration: none</kbd>"
"message": "Which property controls the underline on links?"
}
]
},
@@ -199,7 +199,7 @@
{
"type": "property_value",
"value": { "property": "color", "expected": "steelblue" },
"message": "Set <kbd>color: steelblue</kbd>"
"message": "Check the <kbd>color</kbd> property"
}
]
},
@@ -225,7 +225,7 @@
{
"type": "property_value",
"value": { "property": "color", "expected": "white" },
"message": "Set <kbd>color: white</kbd>"
"message": "The links need to stand out against the blue background"
}
]
},
@@ -251,7 +251,7 @@
{
"type": "property_value",
"value": { "property": "font-size", "expected": "0.9rem" },
"message": "Set <kbd>font-size: 0.9rem</kbd>"
"message": "Check the <kbd>font-size</kbd> property — the text should be slightly smaller"
}
]
}

View File

@@ -147,7 +147,7 @@
"property": "padding",
"expected": "20px"
},
"message": "Set the padding value to <kbd>20px</kbd>",
"message": "How much breathing room does the content need? Re-read the task for the exact size",
"options": {
"exact": true
}
@@ -181,7 +181,7 @@
"property": "margin-bottom",
"expected": "30px"
},
"message": "Set the margin-bottom value to <kbd>30px</kbd>",
"message": "How much space should separate the title from the content below? Check the task for the amount",
"options": {
"exact": true
}
@@ -212,7 +212,7 @@
{
"type": "regex",
"value": "border:\\s*2px\\s+solid\\s+blue",
"message": "Set the border to <kbd>2px solid blue</kbd>",
"message": "The <kbd>border</kbd> shorthand takes three parts: width, style, and color",
"options": {
"caseSensitive": false
}
@@ -246,7 +246,7 @@
"property": "justify-content",
"expected": "center"
},
"message": "Set <kbd>justify-content</kbd> to <kbd>center</kbd>",
"message": "How do you center items along the main axis?",
"options": {
"exact": true
}
@@ -265,7 +265,7 @@
"property": "align-items",
"expected": "center"
},
"message": "Set <kbd>align-items</kbd> to <kbd>center</kbd>",
"message": "Which property centers items along the cross axis?",
"options": {
"exact": true
}
@@ -327,7 +327,7 @@
{
"type": "regex",
"value": "font-family:\\s*Courier,\\s*monospace",
"message": "Set the font-family to <kbd>Courier, monospace</kbd>",
"message": "A font stack lists preferred fonts first, followed by a generic fallback, separated by commas",
"options": {
"caseSensitive": false
}

View File

@@ -22,7 +22,7 @@
{
"type": "regex",
"value": "^input\\[type=\"text\"\\]\\s*{",
"message": "Use <kbd>input[type=\"text\"] { … }</kbd> as your attribute selector",
"message": "Which attribute selector syntax targets inputs with a specific type? Check the square-bracket notation from the description.",
"options": {
"caseSensitive": true
}
@@ -85,7 +85,7 @@
{
"type": "regex",
"value": "^a\\[href\\^=\"https\"\\]\\s*{",
"message": "Use <kbd>a[href^=\"https\"] { … }</kbd> as your attribute selector to target HTTPS links",
"message": "Which partial-match attribute selector targets values that <em>start with</em> a given string? Combine the element name with that selector.",
"options": {
"caseSensitive": true
}
@@ -145,7 +145,7 @@
{
"type": "regex",
"value": "^\\.main-nav\\s*>\\s*li\\s*{",
"message": "Use <kbd>.main-nav > li { … }</kbd> with the child combinator to target only direct children",
"message": "Which combinator selects only <em>direct</em> children, skipping deeper descendants? Place it between the parent and child selectors.",
"options": {
"caseSensitive": true
}
@@ -203,7 +203,7 @@
{
"type": "regex",
"value": "^nav\\s+a\\s*{",
"message": "Use <kbd>nav a</kbd> with a space between nav and a",
"message": "The descendant combinator is the simplest one — what character separates a parent selector from a descendant selector?",
"options": {
"caseSensitive": true
}
@@ -261,7 +261,7 @@
{
"type": "regex",
"value": "^h2\\s*\\+\\s*p\\s*{",
"message": "Use <kbd>h2 + p</kbd> with the adjacent sibling combinator (+)",
"message": "Which combinator targets the element <em>immediately</em> following a sibling? Place it between the two element selectors.",
"options": {
"caseSensitive": true
}
@@ -319,7 +319,7 @@
{
"type": "regex",
"value": "^h3\\s*~\\s*p\\s*{",
"message": "Use <kbd>h3 ~ p</kbd> with the general sibling combinator (~)",
"message": "Which combinator selects <em>all</em> later siblings, not just the one right next to it? Place it between the two element selectors.",
"options": {
"caseSensitive": true
}
@@ -377,7 +377,7 @@
{
"type": "regex",
"value": "^button:hover\\s*{",
"message": "Use <kbd>button:hover</kbd> to target buttons on hover",
"message": "Which pseudo-class activates when the cursor is over an element? Attach it to the button selector with a colon.",
"options": {
"caseSensitive": true
}
@@ -435,7 +435,7 @@
{
"type": "regex",
"value": "^li:first-child\\s*{",
"message": "Use <kbd>li:first-child</kbd> to target first list items",
"message": "Which pseudo-class selects an element only when it is the <em>first</em> child of its parent? Attach it to the <kbd>li</kbd> selector.",
"options": {
"caseSensitive": true
}

View File

@@ -22,7 +22,7 @@
{
"type": "property_value",
"value": { "property": "padding", "expected": "1rem" },
"message": "Which property adds space between content and the element's edge?"
"message": "Which property adds space between an element's content and its border?"
}
]
},
@@ -43,7 +43,7 @@
{
"type": "regex",
"value": "border-left:\\s*4px\\s+solid\\s+steelblue",
"message": "Use the shorthand that sets a border on just one side",
"message": "Use the <kbd>border-left</kbd> shorthand with width, style, and color values",
"options": { "caseSensitive": false }
}
]
@@ -65,7 +65,7 @@
{
"type": "property_value",
"value": { "property": "margin-bottom", "expected": "1rem" },
"message": "Which property pushes neighboring elements away from the bottom?"
"message": "Which property creates space below an element, pushing neighbors away?"
}
]
},
@@ -86,7 +86,7 @@
{
"type": "property_value",
"value": { "property": "box-sizing", "expected": "border-box" },
"message": "Which sizing mode includes padding and border in the element's width?"
"message": "Which <kbd>box-sizing</kbd> value includes padding and border in the element's total width?"
}
]
},
@@ -107,7 +107,7 @@
{
"type": "regex",
"value": "padding:\\s*8px\\s+1rem",
"message": "Use the two-value shorthand: vertical first, then horizontal",
"message": "Use the <kbd>padding</kbd> shorthand with two values: vertical then horizontal",
"options": { "caseSensitive": false }
}
]
@@ -129,7 +129,7 @@
{
"type": "regex",
"value": "margin:\\s*0\\s+auto",
"message": "Use the shorthand that auto-calculates equal horizontal margins",
"message": "Use <kbd>margin</kbd> with a keyword that auto-calculates equal left and right spacing",
"options": { "caseSensitive": false }
}
]
@@ -151,7 +151,7 @@
{
"type": "property_value",
"value": { "property": "border-radius", "expected": "50%" },
"message": "Which property rounds corners? Think about what percentage makes a circle"
"message": "Which <kbd>border-radius</kbd> percentage creates a perfect circle from a square element?"
}
]
},
@@ -172,18 +172,18 @@
{
"type": "property_value",
"value": { "property": "padding", "expected": "1rem" },
"message": "Add inner spacing to the notification"
"message": "Add inner spacing to the notification card"
},
{
"type": "regex",
"value": "border-left:\\s*4px\\s+solid\\s+coral",
"message": "Add a colored accent on the left edge",
"message": "Add a left border accent using the <kbd>border-left</kbd> shorthand",
"options": { "caseSensitive": false }
},
{
"type": "property_value",
"value": { "property": "border-radius", "expected": "4px" },
"message": "Soften the corners of the notification"
"message": "Round the corners slightly with <kbd>border-radius</kbd>"
}
]
}

View File

@@ -22,7 +22,7 @@
{
"type": "property_value",
"value": { "property": "background-color", "expected": "seashell" },
"message": "Which property fills the area behind the content?"
"message": "Which property sets the fill color behind an element's content area?"
}
]
},
@@ -43,7 +43,7 @@
{
"type": "property_value",
"value": { "property": "color", "expected": "coral" },
"message": "Which property changes the text color?"
"message": "Which CSS property changes the color of text content?"
}
]
},
@@ -64,7 +64,7 @@
{
"type": "property_value",
"value": { "property": "border-color", "expected": "coral" },
"message": "Which property changes just the border's color without redefining the whole border?"
"message": "Which property changes just the color of an existing border?"
}
]
},
@@ -85,7 +85,7 @@
{
"type": "property_value",
"value": { "property": "background-color", "expected": "#ffd700" },
"message": "Use the same background property, but with a hex code this time"
"message": "Set the <kbd>background-color</kbd> using a hex code format"
}
]
}

View File

@@ -142,7 +142,7 @@
{
"type": "contains",
"value": "2px 2px",
"message": "Set offset to <kbd>2px 2px</kbd>"
"message": "How far should the shadow move horizontally and vertically?"
}
]
}

View File

@@ -22,7 +22,7 @@
{
"type": "property_value",
"value": { "property": "max-width", "expected": "40rem" },
"message": "Set <kbd>max-width: 40rem</kbd>"
"message": "Which property caps an element's width? Try a <kbd>rem</kbd> value for readable line length."
}
]
},
@@ -71,7 +71,7 @@
{
"type": "regex",
"value": "width:\\s*calc\\(\\s*100%\\s*-\\s*200px\\s*\\)",
"message": "Set <kbd>width: calc(100% - 200px)</kbd>",
"message": "Use <kbd>calc()</kbd> to subtract the sidebar's fixed width from the full container width.",
"options": { "caseSensitive": false }
}
]
@@ -93,7 +93,7 @@
{
"type": "property_value",
"value": { "property": "min-height", "expected": "100vh" },
"message": "Set <kbd>min-height: 100vh</kbd>"
"message": "Which property ensures a minimum height? Use a viewport unit for full-screen coverage."
}
]
}

View File

@@ -28,7 +28,7 @@
{
"type": "regex",
"value": "transition:\\s*background-color\\s*0\\.3s",
"message": "Set <kbd>transition: background-color 0.3s</kbd>",
"message": "Specify which property to transition and how long it should take.",
"options": { "caseSensitive": false }
}
]
@@ -56,7 +56,7 @@
{
"type": "property_value",
"value": { "property": "transition-timing-function", "expected": "ease-in-out" },
"message": "Set timing to <kbd>ease-in-out</kbd>"
"message": "Which easing keyword starts slow, speeds up, then slows down again?"
}
]
},
@@ -95,7 +95,7 @@
{
"type": "regex",
"value": "animation:.*bounce.*1s.*infinite",
"message": "Apply <kbd>animation: bounce 1s infinite</kbd>",
"message": "Use the <kbd>animation</kbd> shorthand: name, duration, and repeat count.",
"options": { "caseSensitive": false }
}
]
@@ -117,27 +117,27 @@
{
"type": "property_value",
"value": { "property": "animation-name", "expected": "pulse" },
"message": "Set <kbd>animation-name: pulse</kbd>"
"message": "Which property links an element to a named <kbd>@keyframes</kbd> rule?"
},
{
"type": "property_value",
"value": { "property": "animation-duration", "expected": "2s" },
"message": "Set <kbd>animation-duration: 2s</kbd>"
"message": "How long should one full cycle of the animation take?"
},
{
"type": "property_value",
"value": { "property": "animation-delay", "expected": "1s" },
"message": "Set <kbd>animation-delay: 1s</kbd>"
"message": "Which property makes the animation wait before starting?"
},
{
"type": "property_value",
"value": { "property": "animation-iteration-count", "expected": "2" },
"message": "Set <kbd>animation-iteration-count: 2</kbd>"
"message": "Which property controls how many times the animation repeats?"
},
{
"type": "property_value",
"value": { "property": "animation-fill-mode", "expected": "forwards" },
"message": "Set <kbd>animation-fill-mode: forwards</kbd>"
"message": "Which property keeps the element styled in its final keyframe state after the animation ends?"
}
]
}

View File

@@ -18,14 +18,24 @@
"codeSuffix": "}",
"previewContainer": "preview-area",
"validations": [
{ "type": "contains", "value": "display", "message": "Use <kbd>display: flex</kbd>", "options": { "caseSensitive": false } },
{
"type": "contains",
"value": "display",
"message": "Which display mode arranges children in a row or column?",
"options": { "caseSensitive": false }
},
{
"type": "contains",
"value": "justify-content",
"message": "Use <kbd>justify-content: center</kbd>",
"message": "How do you center items along the main axis?",
"options": { "caseSensitive": false }
},
{ "type": "contains", "value": "align-items", "message": "Use <kbd>align-items: center</kbd>", "options": { "caseSensitive": false } }
{
"type": "contains",
"value": "align-items",
"message": "Which property centers items along the cross axis?",
"options": { "caseSensitive": false }
}
]
},
{
@@ -44,13 +54,13 @@
{
"type": "contains",
"value": "flex-wrap: wrap",
"message": "Use <kbd>flex-wrap: wrap</kbd>",
"message": "Which property allows flex items to flow onto multiple lines?",
"options": { "caseSensitive": false }
},
{
"type": "regex",
"value": ".item.*flex:\\s*1\\s+1\\s+100px",
"message": "Set <kbd>flex: 1 1 100px</kbd> on items",
"message": "The <kbd>flex</kbd> shorthand takes grow, shrink, and basis values — what basis size should each item start from?",
"options": { "caseSensitive": false }
}
]
@@ -68,17 +78,22 @@
"codeSuffix": "}",
"previewContainer": "preview-area",
"validations": [
{ "type": "contains", "value": "display: grid", "message": "Use <kbd>display: grid</kbd>", "options": { "caseSensitive": false } },
{
"type": "contains",
"value": "display: grid",
"message": "Which display mode lets you define rows and columns?",
"options": { "caseSensitive": false }
},
{
"type": "contains",
"value": "grid-template-columns",
"message": "Define <kbd>grid-template-columns</kbd>",
"message": "Which property defines the column structure of a grid?",
"options": { "caseSensitive": false }
},
{
"type": "regex",
"value": "grid-template-columns:\\s*repeat\\(3,\\s*1fr\\)\\s*",
"message": "Create three equal columns with <kbd>repeat(3, 1fr)</kbd>",
"message": "The <kbd>repeat()</kbd> function can create equal-width columns — how many do you need, and what unit makes them equal?",
"options": { "caseSensitive": false }
},
{ "type": "contains", "value": "gap", "message": "Use <kbd>gap</kbd> property", "options": { "caseSensitive": false } }
@@ -106,7 +121,7 @@
{
"type": "property_value",
"value": { "property": "grid-column", "expected": "1 / span 2" },
"message": "Span across 2 columns with <kbd>grid-column: 1 / span 2</kbd>",
"message": "Use <kbd>grid-column</kbd> with a start line and a span count \u2014 how many columns should this item stretch across?",
"options": { "caseSensitive": false }
}
]

View File

@@ -22,7 +22,7 @@
{
"type": "regex",
"value": "@media\\s*\\(max-width:\\s*600px\\)",
"message": "Use <kbd>@media (max-width: 600px)</kbd>",
"message": "Start with an <kbd>@media</kbd> rule \u2014 which condition targets screens 600px wide or smaller?",
"options": { "caseSensitive": false }
},
{
@@ -34,7 +34,7 @@
{
"type": "property_value",
"value": { "property": "background", "expected": "lightcoral" },
"message": "Set <kbd>background: lightcoral</kbd>",
"message": "Which property changes the element's background color?",
"options": { "exact": false }
}
]
@@ -53,7 +53,11 @@
"solution": " font-size: 5vw;",
"previewContainer": "preview-area",
"validations": [
{ "type": "property_value", "value": { "property": "font-size", "expected": "5vw" }, "message": "Set <kbd>font-size: 5vw</kbd>" }
{
"type": "property_value",
"value": { "property": "font-size", "expected": "5vw" },
"message": "Which CSS unit scales relative to the viewport width?"
}
]
},
{
@@ -73,18 +77,18 @@
{
"type": "property_value",
"value": { "property": "display", "expected": "grid" },
"message": "Set <kbd>display: grid</kbd>"
"message": "Which display mode lets you define rows and columns?"
},
{
"type": "regex",
"value": "repeat\\(auto-fit,\\s*minmax\\(200px,\\s*1fr\\)\\)",
"message": "Use <kbd>repeat(auto-fit, minmax(200px, 1fr))</kbd>",
"message": "Try <kbd>repeat()</kbd> with <kbd>auto-fit</kbd> and <kbd>minmax()</kbd> — what minimum and maximum sizes create flexible columns?",
"options": { "caseSensitive": false }
},
{
"type": "property_value",
"value": { "property": "gap", "expected": "1rem" },
"message": "Set <kbd>gap: 1rem</kbd>"
"message": "Which property adds space between grid items?"
}
]
},
@@ -105,7 +109,7 @@
{
"type": "regex",
"value": "@media\\s*\\(min-width:\\s*768px\\)",
"message": "Use <kbd>@media (min-width: 768px)</kbd>",
"message": "Which <kbd>@media</kbd> condition applies styles when the viewport is at least 768px wide?",
"options": { "caseSensitive": false }
},
{
@@ -117,7 +121,7 @@
{
"type": "property_value",
"value": { "property": "width", "expected": "250px" },
"message": "Set <kbd>width: 250px</kbd>",
"message": "Which property controls how wide the sidebar should be on larger screens?",
"options": { "exact": false }
}
]

View File

@@ -22,7 +22,7 @@
{
"type": "contains",
"value": "linear-gradient",
"message": "Use <kbd>linear-gradient()</kbd>"
"message": "Which CSS function creates a smooth transition between colors along a straight line?"
},
{
"type": "contains",
@@ -53,7 +53,7 @@
{
"type": "contains",
"value": "to right",
"message": "Add <kbd>to right</kbd> to set the direction"
"message": "Which direction keyword makes a gradient flow horizontally from the left side?"
}
]
},
@@ -74,7 +74,7 @@
{
"type": "contains",
"value": "radial-gradient",
"message": "Use <kbd>radial-gradient()</kbd>"
"message": "Which CSS function creates a gradient that radiates outward from a center point?"
},
{
"type": "contains",

View File

@@ -20,7 +20,7 @@
{
"type": "contains_class",
"value": "bg-blue-500",
"message": "Add the <kbd>bg-blue-500</kbd> class for a blue background."
"message": "Which Tailwind utility sets a blue background color? Think about the <kbd>bg-{color}-{shade}</kbd> pattern."
}
]
},
@@ -38,22 +38,22 @@
{
"type": "contains_class",
"value": "bg-white",
"message": "Add <kbd>bg-white</kbd> to set the background color to white."
"message": "Which Tailwind utility sets a white background? The pattern is <kbd>bg-{color}</kbd>."
},
{
"type": "contains_class",
"value": "p-4",
"message": "Add <kbd>p-4</kbd> to apply 1rem padding on all sides."
"message": "Which Tailwind utility adds 1rem padding on all sides? Remember: each spacing unit is 0.25rem."
},
{
"type": "contains_class",
"value": "rounded",
"message": "Add <kbd>rounded</kbd> to apply border-radius of 0.25rem."
"message": "Which Tailwind utility adds rounded corners? It is one of the simplest utility names."
},
{
"type": "contains_class",
"value": "shadow-sm",
"message": "Add <kbd>shadow-sm</kbd> to apply small drop-shadow."
"message": "Which Tailwind utility adds a small drop-shadow? Look for a <kbd>shadow-</kbd> variant."
}
]
},
@@ -71,17 +71,17 @@
{
"type": "contains_class",
"value": "text-blue-600",
"message": "Add <kbd>text-blue-600</kbd> to make the text blue"
"message": "Which Tailwind utility controls text color? Use the <kbd>text-{color}-{shade}</kbd> pattern with a blue shade."
},
{
"type": "contains_class",
"value": "text-2xl",
"message": "Add <kbd>text-2xl</kbd> to increase the font size to 1.5rem"
"message": "Which Tailwind utility sets the font size to 1.5rem? Check the <kbd>text-{size}</kbd> scale."
},
{
"type": "contains_class",
"value": "font-bold",
"message": "Add <kbd>font-bold</kbd> to make the text bold (font-weight: 700)"
"message": "Which Tailwind utility makes text bold? The <kbd>font-{weight}</kbd> pattern controls font weight."
}
]
},
@@ -99,17 +99,17 @@
{
"type": "contains_class",
"value": "px-6",
"message": "Add <kbd>px-6</kbd> for horizontal padding (1.5rem left and right)"
"message": "Which Tailwind utility adds horizontal padding of 1.5rem? The <kbd>px-</kbd> prefix targets left and right."
},
{
"type": "contains_class",
"value": "py-3",
"message": "Add <kbd>py-3</kbd> for vertical padding (0.75rem top and bottom)"
"message": "Which Tailwind utility adds vertical padding of 0.75rem? The <kbd>py-</kbd> prefix targets top and bottom."
},
{
"type": "contains_class",
"value": "mx-auto",
"message": "Add <kbd>mx-auto</kbd> to center the button horizontally"
"message": "Which Tailwind utility centers an element horizontally using auto margins?"
}
]
},
@@ -127,32 +127,32 @@
{
"type": "contains_class",
"value": "w-full",
"message": "Add <kbd>w-full</kbd> for 100% width on mobile"
"message": "Which Tailwind utility makes an element take up 100% width? This is the base (mobile) style."
},
{
"type": "contains_class",
"value": "md:w-1/2",
"message": "Add <kbd>md:w-1/2</kbd> for 50% width on tablet and up"
"message": "How do you set 50% width at the <kbd>md:</kbd> breakpoint? Tailwind uses fraction notation for widths."
},
{
"type": "contains_class",
"value": "lg:w-1/3",
"message": "Add <kbd>lg:w-1/3</kbd> for 33.33% width on desktop and up"
"message": "How do you set one-third width at the <kbd>lg:</kbd> breakpoint? Use the same fraction pattern."
},
{
"type": "contains_class",
"value": "text-lg",
"message": "Add <kbd>text-lg</kbd> for the base text size"
"message": "Which Tailwind text size utility is one step above the base size? Think about the <kbd>text-{size}</kbd> scale."
},
{
"type": "contains_class",
"value": "md:text-xl",
"message": "Add <kbd>md:text-xl</kbd> for larger text on tablets"
"message": "How do you increase the text size at the <kbd>md:</kbd> breakpoint? Go one step larger."
},
{
"type": "contains_class",
"value": "lg:text-2xl",
"message": "Add <kbd>lg:text-2xl</kbd> for even larger text on desktop"
"message": "How do you set an even larger text size at the <kbd>lg:</kbd> breakpoint? Continue stepping up the scale."
}
]
}

View File

@@ -22,7 +22,7 @@
{
"type": "property_value",
"value": { "property": "filter", "expected": "blur(4px)" },
"message": "Set <kbd>filter: blur(4px)</kbd>"
"message": "Which CSS property applies visual effects like blur? Use the <kbd>blur()</kbd> function with a pixel value."
}
]
},
@@ -48,7 +48,7 @@
{
"type": "contains",
"value": "100%",
"message": "Set to <kbd>100%</kbd> for full grayscale"
"message": "What percentage value removes all color completely?"
}
]
},
@@ -74,7 +74,7 @@
{
"type": "contains",
"value": "120%",
"message": "Set to <kbd>120%</kbd>"
"message": "What percentage makes the element slightly brighter than normal? Normal is 100%."
}
]
},
@@ -100,7 +100,7 @@
{
"type": "contains",
"value": "4px 4px 8px",
"message": "Set shadow offset and blur"
"message": "Set the x-offset, y-offset, and blur radius. The task describes the exact values needed."
}
]
}

View File

@@ -22,7 +22,7 @@
{
"type": "property_value",
"value": { "property": "position", "expected": "relative" },
"message": "Set <kbd>position: relative</kbd>"
"message": "Which position value keeps an element in normal flow but allows offset adjustments?"
}
]
},
@@ -43,7 +43,7 @@
{
"type": "property_value",
"value": { "property": "top", "expected": "-8px" },
"message": "Set <kbd>top: -8px</kbd>"
"message": "Which offset property moves an element upward from its current position?"
}
]
},
@@ -64,7 +64,7 @@
{
"type": "property_value",
"value": { "property": "position", "expected": "absolute" },
"message": "Set <kbd>position: absolute</kbd>"
"message": "Which position value removes an element from normal flow for precise placement?"
}
]
},
@@ -85,12 +85,12 @@
{
"type": "property_value",
"value": { "property": "top", "expected": "8px" },
"message": "Set <kbd>top: 8px</kbd>"
"message": "Which offset property controls the distance from the top of the positioned ancestor?"
},
{
"type": "property_value",
"value": { "property": "right", "expected": "8px" },
"message": "Set <kbd>right: 8px</kbd>"
"message": "Which offset property controls the distance from the right edge?"
}
]
}

View File

@@ -48,7 +48,7 @@
{
"type": "property_value",
"value": { "property": "color", "expected": "coral" },
"message": "Set <kbd>color: coral</kbd>"
"message": "Which CSS property changes the text color of the bullet? Try a warm, pinkish-orange named color."
}
]
},
@@ -95,17 +95,17 @@
{
"type": "property_value",
"value": { "property": "width", "expected": "40px" },
"message": "Set <kbd>width: 40px</kbd>"
"message": "How wide should the decorative line be? Check the task for the pixel value."
},
{
"type": "property_value",
"value": { "property": "height", "expected": "3px" },
"message": "Set <kbd>height: 3px</kbd>"
"message": "Which CSS property controls the thickness of the line? A thin line looks best here."
},
{
"type": "property_value",
"value": { "property": "background", "expected": "steelblue" },
"message": "Set <kbd>background: steelblue</kbd>"
"message": "Which CSS property fills the line with color? Use a steel-toned blue named color."
}
]
}

View File

@@ -22,7 +22,7 @@
{
"type": "property_value",
"value": { "property": "color", "expected": "coral" },
"message": "أضف <kbd>color: coral;</kbd>"
"message": "ما الخاصية التي تتحكم في لون النص؟"
}
]
},
@@ -43,12 +43,12 @@
{
"type": "property_value",
"value": { "property": "background", "expected": "lavender" },
"message": "أضف <kbd>background: lavender;</kbd>"
"message": "تحقق من خاصية <kbd>background</kbd>"
},
{
"type": "property_value",
"value": { "property": "padding", "expected": "1rem" },
"message": "أضف <kbd>padding: 1rem;</kbd>"
"message": "البطاقة تحتاج إلى مساحة داخل حوافها"
}
]
},
@@ -74,7 +74,7 @@
{
"type": "property_value",
"value": { "property": "color", "expected": "steelblue" },
"message": "اضبط <kbd>color: steelblue</kbd>"
"message": "ما الخاصية التي تغيّر لون النص؟"
}
]
},
@@ -100,7 +100,7 @@
{
"type": "property_value",
"value": { "property": "color", "expected": "coral" },
"message": "اضبط <kbd>color: coral</kbd>"
"message": "ما القيمة التي تعطي لوناً دافئاً برتقالياً محمراً؟"
}
]
},
@@ -126,7 +126,7 @@
{
"type": "property_value",
"value": { "property": "background", "expected": "tomato" },
"message": "اضبط <kbd>background: tomato</kbd>"
"message": "الشارة تحتاج إلى خلفية حمراء زاهية"
}
]
},
@@ -152,7 +152,7 @@
{
"type": "property_value",
"value": { "property": "background", "expected": "steelblue" },
"message": "اضبط <kbd>background: steelblue</kbd>"
"message": "ما الخاصية التي تضبط لون تعبئة الزر؟"
}
]
},
@@ -178,7 +178,7 @@
{
"type": "property_value",
"value": { "property": "text-decoration", "expected": "none" },
"message": "اضبط <kbd>text-decoration: none</kbd>"
"message": "ما الخاصية التي تتحكم في الخط أسفل الروابط؟"
}
]
},
@@ -199,7 +199,7 @@
{
"type": "property_value",
"value": { "property": "color", "expected": "steelblue" },
"message": "اضبط <kbd>color: steelblue</kbd>"
"message": "تحقق من خاصية <kbd>color</kbd>"
}
]
},
@@ -225,7 +225,7 @@
{
"type": "property_value",
"value": { "property": "color", "expected": "white" },
"message": "اضبط <kbd>color: white</kbd>"
"message": "الروابط تحتاج إلى أن تبرز على الخلفية الزرقاء"
}
]
},
@@ -251,7 +251,7 @@
{
"type": "property_value",
"value": { "property": "font-size", "expected": "0.9rem" },
"message": "اضبط <kbd>font-size: 0.9rem</kbd>"
"message": "تحقق من خاصية <kbd>font-size</kbd> — النص يجب أن يكون أصغر قليلاً"
}
]
}

View File

@@ -22,7 +22,7 @@
{
"type": "property_value",
"value": { "property": "padding", "expected": "1rem" },
"message": "أي خاصية تضيف مساحة بين المحتوى وحافة العنصر؟"
"message": "ما الخاصية التي تضيف مساحة بين محتوى العنصر وحدوده؟"
}
]
},
@@ -43,7 +43,7 @@
{
"type": "regex",
"value": "border-left:\\s*4px\\s+solid\\s+steelblue",
"message": "استخدم الاختصار الذي يحدد حداً على جانب واحد فقط",
"message": "استخدم اختصار <kbd>border-left</kbd> مع قيم العرض والنمط واللون",
"options": { "caseSensitive": false }
}
]
@@ -65,7 +65,7 @@
{
"type": "property_value",
"value": { "property": "margin-bottom", "expected": "1rem" },
"message": "أي خاصية تدفع العناصر المجاورة بعيداً من الأسفل؟"
"message": "ما الخاصية التي تُنشئ مساحة أسفل العنصر وتدفع الجيران بعيداً؟"
}
]
},
@@ -86,7 +86,7 @@
{
"type": "property_value",
"value": { "property": "box-sizing", "expected": "border-box" },
"message": "أي وضع تحجيم يشمل padding والحدود في عرض العنصر؟"
"message": "ما قيمة <kbd>box-sizing</kbd> التي تشمل الحشو والحدود في العرض الإجمالي للعنصر؟"
}
]
},
@@ -107,7 +107,7 @@
{
"type": "regex",
"value": "padding:\\s*8px\\s+1rem",
"message": "استخدم اختصار القيمتين: العمودي أولاً، ثم الأفقي",
"message": "استخدم اختصار <kbd>padding</kbd> بقيمتين: عمودي ثم أفقي",
"options": { "caseSensitive": false }
}
]
@@ -129,7 +129,7 @@
{
"type": "regex",
"value": "margin:\\s*0\\s+auto",
"message": "استخدم الاختصار الذي يحسب هوامش أفقية متساوية تلقائياً",
"message": "استخدم <kbd>margin</kbd> مع كلمة مفتاحية تحسب تلقائياً مسافات متساوية يميناً ويساراً",
"options": { "caseSensitive": false }
}
]
@@ -151,7 +151,7 @@
{
"type": "property_value",
"value": { "property": "border-radius", "expected": "50%" },
"message": "أي خاصية تدوّر الزوايا؟ فكر في النسبة المئوية التي تصنع دائرة"
"message": "ما نسبة <kbd>border-radius</kbd> التي تُنشئ دائرة كاملة من عنصر مربع؟"
}
]
},
@@ -172,18 +172,18 @@
{
"type": "property_value",
"value": { "property": "padding", "expected": "1rem" },
"message": "أضف مساحة داخلية للإشعار"
"message": "أضف مساحة داخلية لبطاقة الإشعار"
},
{
"type": "regex",
"value": "border-left:\\s*4px\\s+solid\\s+coral",
"message": "أضف لمسة ملونة على الحافة اليسرى",
"message": "أضف لمسة حدود يسارية باستخدام اختصار <kbd>border-left</kbd>",
"options": { "caseSensitive": false }
},
{
"type": "property_value",
"value": { "property": "border-radius", "expected": "4px" },
"message": "نعّم زوايا الإشعار"
"message": "دوّر الزوايا قليلاً باستخدام <kbd>border-radius</kbd>"
}
]
}

View File

@@ -22,7 +22,7 @@
{
"type": "property_value",
"value": { "property": "max-width", "expected": "40rem" },
"message": "اضبط <kbd>max-width: 40rem</kbd>"
"message": "ما الخاصية التي تحدّ من عرض العنصر؟ جرّب قيمة بوحدة <kbd>rem</kbd> لطول سطر مقروء."
}
]
},
@@ -43,13 +43,13 @@
{
"type": "contains",
"value": "--brand",
"message": "عرّف المتغير <kbd>--brand</kbd>",
"message": "عرّف متغير <kbd>--brand</kbd>",
"options": { "caseSensitive": false }
},
{
"type": "contains",
"value": "steelblue",
"message": "اضبط القيمة على <kbd>steelblue</kbd>",
"message": "اضبط القيمة إلى <kbd>steelblue</kbd>",
"options": { "caseSensitive": false }
}
]
@@ -71,7 +71,7 @@
{
"type": "regex",
"value": "width:\\s*calc\\(\\s*100%\\s*-\\s*200px\\s*\\)",
"message": "اضبط <kbd>width: calc(100% - 200px)</kbd>",
"message": "استخدم <kbd>calc()</kbd> لطرح عرض الشريط الجانبي الثابت من عرض الحاوية الكامل.",
"options": { "caseSensitive": false }
}
]
@@ -93,7 +93,7 @@
{
"type": "property_value",
"value": { "property": "min-height", "expected": "100vh" },
"message": "اضبط <kbd>min-height: 100vh</kbd>"
"message": "ما الخاصية التي تضمن حداً أدنى للارتفاع؟ استخدم وحدة viewport لتغطية الشاشة بالكامل."
}
]
}

View File

@@ -28,7 +28,7 @@
{
"type": "regex",
"value": "transition:\\s*background-color\\s*0\\.3s",
"message": "اضبط <kbd>transition: background-color 0.3s</kbd>",
"message": "حدد أي خاصية تريد تحريكها وكم من الوقت يجب أن تستغرق.",
"options": { "caseSensitive": false }
}
]
@@ -56,7 +56,7 @@
{
"type": "property_value",
"value": { "property": "transition-timing-function", "expected": "ease-in-out" },
"message": "اضبط التوقيت على <kbd>ease-in-out</kbd>"
"message": "ما كلمة التسهيل التي تبدأ بطيئة، تتسارع، ثم تبطئ مرة أخرى؟"
}
]
},
@@ -95,7 +95,7 @@
{
"type": "regex",
"value": "animation:.*bounce.*1s.*infinite",
"message": "طبّق <kbd>animation: bounce 1s infinite</kbd>",
"message": "استخدم اختصار <kbd>animation</kbd>: الاسم، المدة، وعدد التكرار.",
"options": { "caseSensitive": false }
}
]
@@ -117,27 +117,27 @@
{
"type": "property_value",
"value": { "property": "animation-name", "expected": "pulse" },
"message": "اضبط <kbd>animation-name: pulse</kbd>"
"message": "ما الخاصية التي تربط العنصر بقاعدة <kbd>@keyframes</kbd> مسماة؟"
},
{
"type": "property_value",
"value": { "property": "animation-duration", "expected": "2s" },
"message": "اضبط <kbd>animation-duration: 2s</kbd>"
"message": "كم يجب أن تستغرق دورة كاملة من الحركة؟"
},
{
"type": "property_value",
"value": { "property": "animation-delay", "expected": "1s" },
"message": "اضبط <kbd>animation-delay: 1s</kbd>"
"message": "ما الخاصية التي تجعل الحركة تنتظر قبل أن تبدأ؟"
},
{
"type": "property_value",
"value": { "property": "animation-iteration-count", "expected": "2" },
"message": "اضبط <kbd>animation-iteration-count: 2</kbd>"
"message": "ما الخاصية التي تتحكم في عدد مرات تكرار الحركة؟"
},
{
"type": "property_value",
"value": { "property": "animation-fill-mode", "expected": "forwards" },
"message": "اضبط <kbd>animation-fill-mode: forwards</kbd>"
"message": "ما الخاصية التي تُبقي العنصر بتنسيق حالته النهائية بعد انتهاء الحركة؟"
}
]
}

View File

@@ -22,7 +22,7 @@
{
"type": "regex",
"value": "@media\\s*\\(max-width:\\s*600px\\)",
"message": "استخدم <kbd>@media (max-width: 600px)</kbd>",
"message": "ابدأ بقاعدة <kbd>@media</kbd> — ما الشرط الذي يستهدف الشاشات بعرض 600px أو أقل؟",
"options": { "caseSensitive": false }
},
{
@@ -34,7 +34,7 @@
{
"type": "property_value",
"value": { "property": "background", "expected": "lightcoral" },
"message": "اضبط <kbd>background: lightcoral</kbd>",
"message": "ما الخاصية التي تغيّر لون خلفية العنصر؟",
"options": { "exact": false }
}
]
@@ -53,7 +53,11 @@
"solution": " font-size: 5vw;",
"previewContainer": "preview-area",
"validations": [
{ "type": "property_value", "value": { "property": "font-size", "expected": "5vw" }, "message": "اضبط <kbd>font-size: 5vw</kbd>" }
{
"type": "property_value",
"value": { "property": "font-size", "expected": "5vw" },
"message": "ما وحدة CSS التي تتناسب مع عرض viewport؟"
}
]
},
{
@@ -73,18 +77,18 @@
{
"type": "property_value",
"value": { "property": "display", "expected": "grid" },
"message": "اضبط <kbd>display: grid</kbd>"
"message": "ما وضع العرض الذي يتيح لك تعريف صفوف وأعمدة؟"
},
{
"type": "regex",
"value": "repeat\\(auto-fit,\\s*minmax\\(200px,\\s*1fr\\)\\)",
"message": "استخدم <kbd>repeat(auto-fit, minmax(200px, 1fr))</kbd>",
"message": "جرّب <kbd>repeat()</kbd> مع <kbd>auto-fit</kbd> و <kbd>minmax()</kbd> — ما الحد الأدنى والأقصى للحجم لإنشاء أعمدة مرنة؟",
"options": { "caseSensitive": false }
},
{
"type": "property_value",
"value": { "property": "gap", "expected": "1rem" },
"message": "اضبط <kbd>gap: 1rem</kbd>"
"message": "ما الخاصية التي تضيف مساحة بين عناصر الشبكة؟"
}
]
},
@@ -105,7 +109,7 @@
{
"type": "regex",
"value": "@media\\s*\\(min-width:\\s*768px\\)",
"message": "استخدم <kbd>@media (min-width: 768px)</kbd>",
"message": "ما شرط <kbd>@media</kbd> الذي يُطبّق الأنماط عندما يكون عرض viewport على الأقل 768px؟",
"options": { "caseSensitive": false }
},
{
@@ -117,7 +121,7 @@
{
"type": "property_value",
"value": { "property": "width", "expected": "250px" },
"message": "اضبط <kbd>width: 250px</kbd>",
"message": "ما الخاصية التي تتحكم في عرض الشريط الجانبي على الشاشات الكبيرة؟",
"options": { "exact": false }
}
]

View File

@@ -22,7 +22,7 @@
{
"type": "property_value",
"value": { "property": "display", "expected": "flex" },
"message": "اضبط <kbd>display: flex</kbd>"
"message": "ما قيمة display التي تحوّل العنصر إلى حاوية صندوق مرن؟"
}
]
},
@@ -43,7 +43,7 @@
{
"type": "property_value",
"value": { "property": "gap", "expected": "1rem" },
"message": "اضبط <kbd>gap: 1rem</kbd>"
"message": "ما الخاصية التي تُنشئ تباعداً بين عناصر flex بدون استخدام الهوامش؟"
}
]
},
@@ -64,7 +64,7 @@
{
"type": "property_value",
"value": { "property": "justify-content", "expected": "space-between" },
"message": "اضبط <kbd>justify-content: space-between</kbd>"
"message": "ما قيمة <kbd>justify-content</kbd> التي تدفع العنصر الأول والأخير إلى الحواف المتقابلة؟"
}
]
},
@@ -85,7 +85,7 @@
{
"type": "property_value",
"value": { "property": "align-items", "expected": "center" },
"message": "اضبط <kbd>align-items: center</kbd>"
"message": "ما الخاصية التي تُحاذي عناصر flex على طول المحور المتقاطع؟"
}
]
},
@@ -106,7 +106,7 @@
{
"type": "property_value",
"value": { "property": "flex-wrap", "expected": "wrap" },
"message": "اضبط <kbd>flex-wrap: wrap</kbd>"
"message": "ما الخاصية التي تسمح لعناصر flex بالتدفق إلى أسطر متعددة؟"
}
]
},
@@ -127,7 +127,7 @@
{
"type": "property_value",
"value": { "property": "flex", "expected": "1" },
"message": "اضبط <kbd>flex: 1</kbd>"
"message": "ما الخاصية التي تجعل عنصر flex ينمو لملء المساحة المتبقية؟"
}
]
}

View File

@@ -22,7 +22,7 @@
{
"type": "property_value",
"value": { "property": "color", "expected": "coral" },
"message": "Füge <kbd>color: coral;</kbd> hinzu"
"message": "Welche Eigenschaft ändert die Textfarbe?"
}
]
},
@@ -43,12 +43,12 @@
{
"type": "property_value",
"value": { "property": "background", "expected": "lavender" },
"message": "Füge <kbd>background: lavender;</kbd> hinzu"
"message": "Welche Eigenschaft steuert die Hintergrundfarbe?"
},
{
"type": "property_value",
"value": { "property": "padding", "expected": "1rem" },
"message": "Füge <kbd>padding: 1rem;</kbd> hinzu"
"message": "Das Element benötigt auch Innenabstand -- überprüfe die <kbd>padding</kbd>-Eigenschaft"
}
]
},
@@ -74,7 +74,7 @@
{
"type": "property_value",
"value": { "property": "color", "expected": "steelblue" },
"message": "Setze <kbd>color: steelblue</kbd>"
"message": "Überprüfe die <kbd>color</kbd>-Eigenschaft -- welcher Farbwert wurde in der Beschreibung genannt?"
}
]
},
@@ -100,7 +100,7 @@
{
"type": "property_value",
"value": { "property": "color", "expected": "coral" },
"message": "Setze <kbd>color: coral</kbd>"
"message": "Überprüfe die <kbd>color</kbd>-Eigenschaft -- welche Farbe sollen die Links haben?"
}
]
},
@@ -126,7 +126,7 @@
{
"type": "property_value",
"value": { "property": "background", "expected": "tomato" },
"message": "Setze <kbd>background: tomato</kbd>"
"message": "Überprüfe die <kbd>background</kbd>-Eigenschaft -- welche Farbe soll das Badge haben?"
}
]
},
@@ -152,7 +152,7 @@
{
"type": "property_value",
"value": { "property": "background", "expected": "steelblue" },
"message": "Setze <kbd>background: steelblue</kbd>"
"message": "Überprüfe die <kbd>background</kbd>-Eigenschaft -- welche Farbe soll der primäre Button haben?"
}
]
},
@@ -178,7 +178,7 @@
{
"type": "property_value",
"value": { "property": "text-decoration", "expected": "none" },
"message": "Setze <kbd>text-decoration: none</kbd>"
"message": "Welche Eigenschaft entfernt die Unterstreichung von Links?"
}
]
},
@@ -199,7 +199,7 @@
{
"type": "property_value",
"value": { "property": "color", "expected": "steelblue" },
"message": "Setze <kbd>color: steelblue</kbd>"
"message": "Welche Eigenschaft ändert die Textfarbe der Überschriften?"
}
]
},
@@ -225,7 +225,7 @@
{
"type": "property_value",
"value": { "property": "color", "expected": "white" },
"message": "Setze <kbd>color: white</kbd>"
"message": "Überprüfe die <kbd>color</kbd>-Eigenschaft -- welche Farbe passt zu einem dunklen Hintergrund?"
}
]
},
@@ -251,7 +251,7 @@
{
"type": "property_value",
"value": { "property": "font-size", "expected": "0.9rem" },
"message": "Setze <kbd>font-size: 0.9rem</kbd>"
"message": "Welche Eigenschaft steuert die Schriftgröße?"
}
]
}

View File

@@ -38,7 +38,7 @@
"property": "background-color",
"expected": "lightblue"
},
"message": "Setze die Hintergrundfarbe auf <kbd>lightblue</kbd>"
"message": "Überprüfe die <kbd>background-color</kbd>-Eigenschaft -- welche Farbe sollen die Text-Eingabefelder haben?"
},
{
"type": "regex",
@@ -56,7 +56,7 @@
"property": "border",
"expected": "2px solid blue"
},
"message": "Setze den Rahmen auf <kbd>2px solid blue</kbd>"
"message": "Das Element benötigt einen Rahmen -- überprüfe die <kbd>border</kbd>-Eigenschaft"
},
{
"type": "regex",
@@ -101,7 +101,7 @@
"property": "color",
"expected": "green"
},
"message": "Setze die Textfarbe auf <kbd>green</kbd>"
"message": "Überprüfe die <kbd>color</kbd>-Eigenschaft -- welche Farbe kennzeichnet sichere Links?"
},
{
"type": "contains",
@@ -114,7 +114,7 @@
"property": "text-decoration",
"expected": "underline"
},
"message": "Setze text-decoration auf <kbd>underline</kbd>, um HTTPS-Links zu unterstreichen"
"message": "Welcher <kbd>text-decoration</kbd>-Wert macht Links visuell hervorgehoben?"
},
{
"type": "regex",
@@ -159,7 +159,7 @@
"property": "background-color",
"expected": "cornflowerblue"
},
"message": "Setze background-color auf <kbd>cornflowerblue</kbd> für das Hauptmenü-Styling"
"message": "Überprüfe die <kbd>background-color</kbd>-Eigenschaft für die Hauptmenüpunkte"
},
{
"type": "contains",
@@ -172,7 +172,7 @@
"property": "color",
"expected": "white"
},
"message": "Setze die Textfarbe auf <kbd>white</kbd> für Kontrast gegen den blauen Hintergrund"
"message": "Welche Textfarbe sorgt für guten Kontrast auf einem blauen Hintergrund?"
},
{
"type": "regex",
@@ -217,7 +217,7 @@
"property": "text-decoration",
"expected": "none"
},
"message": "Setze text-decoration auf <kbd>none</kbd>"
"message": "Welcher <kbd>text-decoration</kbd>-Wert entfernt die Unterstreichung?"
},
{
"type": "contains",
@@ -230,7 +230,7 @@
"property": "color",
"expected": "blue"
},
"message": "Setze color auf <kbd>blue</kbd>"
"message": "Überprüfe die <kbd>color</kbd>-Eigenschaft für die Links"
},
{
"type": "regex",
@@ -275,7 +275,7 @@
"property": "margin-top",
"expected": "0"
},
"message": "Setze margin-top auf <kbd>0</kbd>"
"message": "Welcher Wert bei <kbd>margin-top</kbd> entfernt den oberen Abstand?"
},
{
"type": "contains",
@@ -288,7 +288,7 @@
"property": "font-style",
"expected": "italic"
},
"message": "Setze font-style auf <kbd>italic</kbd>"
"message": "Welcher <kbd>font-style</kbd>-Wert macht den Text kursiv?"
},
{
"type": "regex",
@@ -333,7 +333,7 @@
"property": "color",
"expected": "gray"
},
"message": "Setze color auf <kbd>gray</kbd>"
"message": "Überprüfe die <kbd>color</kbd>-Eigenschaft -- welche Farbe sollen die Absätze haben?"
},
{
"type": "contains",
@@ -346,7 +346,7 @@
"property": "padding-left",
"expected": "20px"
},
"message": "Setze padding-left auf <kbd>20px</kbd>"
"message": "Das Element benötigt eine Einrückung -- überprüfe die <kbd>padding-left</kbd>-Eigenschaft"
},
{
"type": "regex",
@@ -391,7 +391,7 @@
"property": "background-color",
"expected": "darkblue"
},
"message": "Setze background-color auf <kbd>darkblue</kbd>"
"message": "Welche Hintergrundfarbe soll der Button beim Hover haben?"
},
{
"type": "contains",
@@ -404,7 +404,7 @@
"property": "color",
"expected": "white"
},
"message": "Setze color auf <kbd>white</kbd>"
"message": "Welche Textfarbe sorgt für Kontrast auf dunklem Hintergrund?"
},
{
"type": "regex",
@@ -449,7 +449,7 @@
"property": "font-weight",
"expected": "bold"
},
"message": "Setze font-weight auf <kbd>bold</kbd>"
"message": "Welcher <kbd>font-weight</kbd>-Wert macht Text fett?"
},
{
"type": "contains",
@@ -462,7 +462,7 @@
"property": "margin-top",
"expected": "0"
},
"message": "Setze margin-top auf <kbd>0</kbd>"
"message": "Welcher Wert entfernt den oberen Abstand vollständig?"
},
{
"type": "regex",

View File

@@ -22,7 +22,7 @@
{
"type": "property_value",
"value": { "property": "padding", "expected": "1rem" },
"message": "Welche Eigenschaft fügt Abstand zwischen Inhalt und Elementrand hinzu?"
"message": "Welche Eigenschaft steuert den Innenabstand zwischen Inhalt und Rahmen?"
}
]
},
@@ -43,7 +43,7 @@
{
"type": "regex",
"value": "border-left:\\s*4px\\s+solid\\s+steelblue",
"message": "Verwende die Kurzschreibweise, die einen Rahmen auf nur einer Seite setzt",
"message": "Überprüfe die <kbd>border-left</kbd>-Eigenschaft -- welche drei Werte braucht sie?",
"options": { "caseSensitive": false }
}
]
@@ -65,7 +65,7 @@
{
"type": "property_value",
"value": { "property": "margin-bottom", "expected": "1rem" },
"message": "Welche Eigenschaft schiebt benachbarte Elemente nach unten weg?"
"message": "Welche Eigenschaft steuert den Außenabstand nach unten?"
}
]
},
@@ -86,7 +86,7 @@
{
"type": "property_value",
"value": { "property": "box-sizing", "expected": "border-box" },
"message": "Welcher Größenmodus bezieht Padding und Rahmen in die Breite des Elements ein?"
"message": "Welcher <kbd>box-sizing</kbd>-Wert bezieht Padding und Rahmen in die Breite ein?"
}
]
},
@@ -107,7 +107,7 @@
{
"type": "regex",
"value": "padding:\\s*8px\\s+1rem",
"message": "Verwende die Zwei-Werte-Kurzschreibweise: vertikal zuerst, dann horizontal",
"message": "Überprüfe die <kbd>padding</kbd>-Kurzschreibweise -- zwei Werte setzen vertikal und horizontal",
"options": { "caseSensitive": false }
}
]
@@ -129,7 +129,7 @@
{
"type": "regex",
"value": "margin:\\s*0\\s+auto",
"message": "Verwende die Kurzschreibweise, die gleiche horizontale Abstände automatisch berechnet",
"message": "Welche <kbd>margin</kbd>-Kurzschreibweise zentriert ein Block-Element horizontal?",
"options": { "caseSensitive": false }
}
]
@@ -151,7 +151,7 @@
{
"type": "property_value",
"value": { "property": "border-radius", "expected": "50%" },
"message": "Welche Eigenschaft rundet Ecken? Denke daran, welcher Prozentwert einen Kreis ergibt"
"message": "Welcher <kbd>border-radius</kbd>-Wert macht ein quadratisches Element rund?"
}
]
},
@@ -172,18 +172,18 @@
{
"type": "property_value",
"value": { "property": "padding", "expected": "1rem" },
"message": "Füge inneren Abstand zur Benachrichtigung hinzu"
"message": "Das Element benötigt Innenabstand -- überprüfe die <kbd>padding</kbd>-Eigenschaft"
},
{
"type": "regex",
"value": "border-left:\\s*4px\\s+solid\\s+coral",
"message": "Füge einen farbigen Akzent an der linken Kante hinzu",
"message": "Überprüfe die <kbd>border-left</kbd>-Eigenschaft -- sie braucht Breite, Stil und Farbe",
"options": { "caseSensitive": false }
},
{
"type": "property_value",
"value": { "property": "border-radius", "expected": "4px" },
"message": "Runde die Ecken der Benachrichtigung ab"
"message": "Das Element benötigt abgerundete Ecken -- überprüfe die <kbd>border-radius</kbd>-Eigenschaft"
}
]
}

View File

@@ -22,7 +22,7 @@
{
"type": "property_value",
"value": { "property": "max-width", "expected": "40rem" },
"message": "Setze <kbd>max-width: 40rem</kbd>"
"message": "Welche Eigenschaft begrenzt die maximale Breite eines Elements?"
}
]
},
@@ -49,7 +49,7 @@
{
"type": "contains",
"value": "steelblue",
"message": "Setze den Wert auf <kbd>steelblue</kbd>",
"message": "Welche Farbe soll die Variable haben?",
"options": { "caseSensitive": false }
}
]
@@ -71,7 +71,7 @@
{
"type": "regex",
"value": "width:\\s*calc\\(\\s*100%\\s*-\\s*200px\\s*\\)",
"message": "Setze <kbd>width: calc(100% - 200px)</kbd>",
"message": "Überprüfe die <kbd>width</kbd>-Eigenschaft -- wie berechnest du den verbleibenden Platz nach der Sidebar?",
"options": { "caseSensitive": false }
}
]
@@ -93,7 +93,7 @@
{
"type": "property_value",
"value": { "property": "min-height", "expected": "100vh" },
"message": "Setze <kbd>min-height: 100vh</kbd>"
"message": "Welche Eigenschaft setzt die Mindesthöhe? Welche Viewport-Einheit entspricht 100% der Fensterhöhe?"
}
]
}

View File

@@ -28,7 +28,7 @@
{
"type": "regex",
"value": "transition:\\s*background-color\\s*0\\.3s",
"message": "Setze <kbd>transition: background-color 0.3s</kbd>",
"message": "Überprüfe die <kbd>transition</kbd>-Eigenschaft -- welche CSS-Eigenschaft soll sanft übergehen und wie lange?",
"options": { "caseSensitive": false }
}
]
@@ -56,7 +56,7 @@
{
"type": "property_value",
"value": { "property": "transition-timing-function", "expected": "ease-in-out" },
"message": "Setze timing auf <kbd>ease-in-out</kbd>"
"message": "Welche Timing-Funktion startet und endet langsam?"
}
]
},
@@ -117,27 +117,27 @@
{
"type": "property_value",
"value": { "property": "animation-name", "expected": "pulse" },
"message": "Setze <kbd>animation-name: pulse</kbd>"
"message": "Welche Animation soll angewendet werden? Überprüfe den <kbd>@keyframes</kbd>-Namen."
},
{
"type": "property_value",
"value": { "property": "animation-duration", "expected": "2s" },
"message": "Setze <kbd>animation-duration: 2s</kbd>"
"message": "Welche Eigenschaft steuert die Dauer der Animation?"
},
{
"type": "property_value",
"value": { "property": "animation-delay", "expected": "1s" },
"message": "Setze <kbd>animation-delay: 1s</kbd>"
"message": "Welche Eigenschaft verzögert den Start der Animation?"
},
{
"type": "property_value",
"value": { "property": "animation-iteration-count", "expected": "2" },
"message": "Setze <kbd>animation-iteration-count: 2</kbd>"
"message": "Welche Eigenschaft steuert, wie oft die Animation wiederholt wird?"
},
{
"type": "property_value",
"value": { "property": "animation-fill-mode", "expected": "forwards" },
"message": "Setze <kbd>animation-fill-mode: forwards</kbd>"
"message": "Welcher <kbd>animation-fill-mode</kbd>-Wert behält den Endzustand bei?"
}
]
}

View File

@@ -34,7 +34,7 @@
{
"type": "property_value",
"value": { "property": "background", "expected": "lightcoral" },
"message": "Setze <kbd>background: lightcoral</kbd>",
"message": "Überprüfe die <kbd>background</kbd>-Eigenschaft innerhalb der Media Query",
"options": { "exact": false }
}
]
@@ -53,7 +53,11 @@
"solution": " font-size: 5vw;",
"previewContainer": "preview-area",
"validations": [
{ "type": "property_value", "value": { "property": "font-size", "expected": "5vw" }, "message": "Setze <kbd>font-size: 5vw</kbd>" }
{
"type": "property_value",
"value": { "property": "font-size", "expected": "5vw" },
"message": "Welche Eigenschaft steuert die Schriftgröße? Welche Viewport-Einheit skaliert mit der Breite?"
}
]
},
{
@@ -73,7 +77,7 @@
{
"type": "property_value",
"value": { "property": "display", "expected": "grid" },
"message": "Setze <kbd>display: grid</kbd>"
"message": "Welcher Display-Wert aktiviert das CSS-Grid-Layout?"
},
{
"type": "regex",
@@ -84,7 +88,7 @@
{
"type": "property_value",
"value": { "property": "gap", "expected": "1rem" },
"message": "Setze <kbd>gap: 1rem</kbd>"
"message": "Welche Eigenschaft steuert den Abstand zwischen Grid-Zellen?"
}
]
},
@@ -117,7 +121,7 @@
{
"type": "property_value",
"value": { "property": "width", "expected": "250px" },
"message": "Setze <kbd>width: 250px</kbd>",
"message": "Überprüfe die <kbd>width</kbd>-Eigenschaft für die Sidebar",
"options": { "exact": false }
}
]

View File

@@ -20,7 +20,7 @@
{
"type": "contains_class",
"value": "bg-blue-500",
"message": "Füge die <kbd>bg-blue-500</kbd>-Klasse für einen blauen Hintergrund hinzu."
"message": "Welche Tailwind-Klasse setzt eine blaue Hintergrundfarbe? Denke an das <kbd>bg-{farbe}-{abstufung}</kbd>-Muster."
}
]
},
@@ -38,22 +38,22 @@
{
"type": "contains_class",
"value": "bg-white",
"message": "Füge <kbd>bg-white</kbd> hinzu, um die Hintergrundfarbe auf weiß zu setzen."
"message": "Das Element benötigt einen weißen Hintergrund -- welches <kbd>bg-</kbd>-Utility passt?"
},
{
"type": "contains_class",
"value": "p-4",
"message": "Füge <kbd>p-4</kbd> hinzu, um 1rem Padding auf allen Seiten anzuwenden."
"message": "Welches <kbd>p-</kbd>-Utility erzeugt 1rem Padding auf allen Seiten?"
},
{
"type": "contains_class",
"value": "rounded",
"message": "Füge <kbd>rounded</kbd> hinzu, um einen border-radius von 0.25rem anzuwenden."
"message": "Welche Klasse fügt abgerundete Ecken hinzu?"
},
{
"type": "contains_class",
"value": "shadow-sm",
"message": "Füge <kbd>shadow-sm</kbd> hinzu, um einen kleinen Schlagschatten anzuwenden."
"message": "Das Element benötigt einen kleinen Schatten -- welches <kbd>shadow-</kbd>-Utility passt?"
}
]
},
@@ -71,17 +71,17 @@
{
"type": "contains_class",
"value": "text-blue-600",
"message": "Füge <kbd>text-blue-600</kbd> hinzu, um den Text blau zu machen"
"message": "Welches <kbd>text-</kbd>-Utility setzt eine blaue Textfarbe? Denke an das <kbd>text-{farbe}-{abstufung}</kbd>-Muster."
},
{
"type": "contains_class",
"value": "text-2xl",
"message": "Füge <kbd>text-2xl</kbd> hinzu, um die Schriftgröße auf 1.5rem zu erhöhen"
"message": "Welches <kbd>text-</kbd>-Utility setzt die Schriftgröße auf 1.5rem?"
},
{
"type": "contains_class",
"value": "font-bold",
"message": "Füge <kbd>font-bold</kbd> hinzu, um den Text fett zu machen (font-weight: 700)"
"message": "Welches <kbd>font-</kbd>-Utility macht den Text fett?"
}
]
},
@@ -99,17 +99,17 @@
{
"type": "contains_class",
"value": "px-6",
"message": "Füge <kbd>px-6</kbd> für horizontales Padding hinzu (1.5rem links und rechts)"
"message": "Welches <kbd>px-</kbd>-Utility erzeugt 1.5rem horizontales Padding?"
},
{
"type": "contains_class",
"value": "py-3",
"message": "Füge <kbd>py-3</kbd> für vertikales Padding hinzu (0.75rem oben und unten)"
"message": "Welches <kbd>py-</kbd>-Utility erzeugt 0.75rem vertikales Padding?"
},
{
"type": "contains_class",
"value": "mx-auto",
"message": "Füge <kbd>mx-auto</kbd> hinzu, um den Button horizontal zu zentrieren"
"message": "Welches <kbd>mx-</kbd>-Utility zentriert ein Element horizontal?"
}
]
},
@@ -127,32 +127,32 @@
{
"type": "contains_class",
"value": "w-full",
"message": "Füge <kbd>w-full</kbd> für 100% Breite auf Mobil hinzu"
"message": "Welches Breiten-Utility macht das Element auf Mobil 100% breit?"
},
{
"type": "contains_class",
"value": "md:w-1/2",
"message": "Füge <kbd>md:w-1/2</kbd> für 50% Breite auf Tablet und größer hinzu"
"message": "Welches responsive Breiten-Utility setzt 50% ab dem <kbd>md:</kbd>-Breakpoint?"
},
{
"type": "contains_class",
"value": "lg:w-1/3",
"message": "Füge <kbd>lg:w-1/3</kbd> für 33.33% Breite auf Desktop und größer hinzu"
"message": "Welches responsive Breiten-Utility setzt 33.33% ab dem <kbd>lg:</kbd>-Breakpoint?"
},
{
"type": "contains_class",
"value": "text-lg",
"message": "Füge <kbd>text-lg</kbd> für die Basis-Textgröße hinzu"
"message": "Welches <kbd>text-</kbd>-Utility setzt die Basis-Textgröße auf 1.125rem?"
},
{
"type": "contains_class",
"value": "md:text-xl",
"message": "Füge <kbd>md:text-xl</kbd> für größeren Text auf Tablets hinzu"
"message": "Welches responsive Text-Utility setzt eine größere Schrift ab dem <kbd>md:</kbd>-Breakpoint?"
},
{
"type": "contains_class",
"value": "lg:text-2xl",
"message": "Füge <kbd>lg:text-2xl</kbd> für noch größeren Text auf Desktop hinzu"
"message": "Welches responsive Text-Utility setzt die größte Schrift ab dem <kbd>lg:</kbd>-Breakpoint?"
}
]
}

View File

@@ -22,7 +22,7 @@
{
"type": "property_value",
"value": { "property": "display", "expected": "flex" },
"message": "Setze <kbd>display: flex</kbd>"
"message": "Welcher Display-Wert macht ein Element zu einem flexiblen Box-Container?"
}
]
},
@@ -43,7 +43,7 @@
{
"type": "property_value",
"value": { "property": "gap", "expected": "1rem" },
"message": "Setze <kbd>gap: 1rem</kbd>"
"message": "Welche Eigenschaft erzeugt Abstände zwischen Flex-Items ohne Margins?"
}
]
},
@@ -64,7 +64,7 @@
{
"type": "property_value",
"value": { "property": "justify-content", "expected": "space-between" },
"message": "Setze <kbd>justify-content: space-between</kbd>"
"message": "Welcher <kbd>justify-content</kbd>-Wert schiebt das erste und letzte Element an gegenüberliegende Ränder?"
}
]
},
@@ -85,7 +85,7 @@
{
"type": "property_value",
"value": { "property": "align-items", "expected": "center" },
"message": "Setze <kbd>align-items: center</kbd>"
"message": "Welche Eigenschaft richtet Flex-Items entlang der Querachse aus?"
}
]
},
@@ -106,7 +106,7 @@
{
"type": "property_value",
"value": { "property": "flex-wrap", "expected": "wrap" },
"message": "Setze <kbd>flex-wrap: wrap</kbd>"
"message": "Welche Eigenschaft erlaubt Flex-Items, auf mehrere Zeilen umzubrechen?"
}
]
},
@@ -127,7 +127,7 @@
{
"type": "property_value",
"value": { "property": "flex", "expected": "1" },
"message": "Setze <kbd>flex: 1</kbd>"
"message": "Welche Eigenschaft lässt ein Flex-Item wachsen, um den verbleibenden Platz zu füllen?"
}
]
}

View File

@@ -22,7 +22,7 @@
{
"type": "property_value",
"value": { "property": "color", "expected": "coral" },
"message": "Añade <kbd>color: coral;</kbd>"
"message": "¿Qué propiedad controla el color del texto?"
}
]
},
@@ -43,12 +43,12 @@
{
"type": "property_value",
"value": { "property": "background", "expected": "lavender" },
"message": "ade <kbd>background: lavender;</kbd>"
"message": "Revisa la propiedad <kbd>background</kbd>"
},
{
"type": "property_value",
"value": { "property": "padding", "expected": "1rem" },
"message": "Añade <kbd>padding: 1rem;</kbd>"
"message": "La tarjeta necesita espacio dentro de sus bordes"
}
]
},
@@ -74,7 +74,7 @@
{
"type": "property_value",
"value": { "property": "color", "expected": "steelblue" },
"message": "Establece <kbd>color: steelblue</kbd>"
"message": "¿Qué propiedad cambia el color del texto?"
}
]
},
@@ -100,7 +100,7 @@
{
"type": "property_value",
"value": { "property": "color", "expected": "coral" },
"message": "Establece <kbd>color: coral</kbd>"
"message": "¿Qué valor da un color cálido, rojo-anaranjado?"
}
]
},
@@ -126,7 +126,7 @@
{
"type": "property_value",
"value": { "property": "background", "expected": "tomato" },
"message": "Establece <kbd>background: tomato</kbd>"
"message": "El badge necesita un fondo rojo brillante"
}
]
},
@@ -152,7 +152,7 @@
{
"type": "property_value",
"value": { "property": "background", "expected": "steelblue" },
"message": "Establece <kbd>background: steelblue</kbd>"
"message": "¿Qué propiedad establece el color de relleno del botón?"
}
]
},
@@ -178,7 +178,7 @@
{
"type": "property_value",
"value": { "property": "text-decoration", "expected": "none" },
"message": "Establece <kbd>text-decoration: none</kbd>"
"message": "¿Qué propiedad controla el subrayado de los enlaces?"
}
]
},
@@ -199,7 +199,7 @@
{
"type": "property_value",
"value": { "property": "color", "expected": "steelblue" },
"message": "Establece <kbd>color: steelblue</kbd>"
"message": "Revisa la propiedad <kbd>color</kbd>"
}
]
},
@@ -225,7 +225,7 @@
{
"type": "property_value",
"value": { "property": "color", "expected": "white" },
"message": "Establece <kbd>color: white</kbd>"
"message": "Los enlaces necesitan destacar sobre el fondo azul"
}
]
},
@@ -251,7 +251,7 @@
{
"type": "property_value",
"value": { "property": "font-size", "expected": "0.9rem" },
"message": "Establece <kbd>font-size: 0.9rem</kbd>"
"message": "Revisa la propiedad <kbd>font-size</kbd> — el texto debería ser ligeramente más pequeño"
}
]
}

View File

@@ -22,7 +22,7 @@
{
"type": "property_value",
"value": { "property": "padding", "expected": "1rem" },
"message": "¿Qué propiedad añade espacio entre el contenido y el borde del elemento?"
"message": "¿Qué propiedad añade espacio entre el contenido de un elemento y su borde?"
}
]
},
@@ -43,7 +43,7 @@
{
"type": "regex",
"value": "border-left:\\s*4px\\s+solid\\s+steelblue",
"message": "Usa el atajo que define un borde en un solo lado",
"message": "Usa el atajo <kbd>border-left</kbd> con valores de ancho, estilo y color",
"options": { "caseSensitive": false }
}
]
@@ -65,7 +65,7 @@
{
"type": "property_value",
"value": { "property": "margin-bottom", "expected": "1rem" },
"message": "¿Qué propiedad empuja los elementos vecinos hacia abajo?"
"message": "¿Qué propiedad crea espacio debajo de un elemento, separándolo de sus vecinos?"
}
]
},
@@ -86,7 +86,7 @@
{
"type": "property_value",
"value": { "property": "box-sizing", "expected": "border-box" },
"message": "¿Qué modo de tamaño incluye padding y borde en el ancho del elemento?"
"message": "¿Qué valor de <kbd>box-sizing</kbd> incluye padding y borde en el ancho total del elemento?"
}
]
},
@@ -107,7 +107,7 @@
{
"type": "regex",
"value": "padding:\\s*8px\\s+1rem",
"message": "Usa el atajo de dos valores: vertical primero, luego horizontal",
"message": "Usa el atajo <kbd>padding</kbd> con dos valores: vertical y luego horizontal",
"options": { "caseSensitive": false }
}
]
@@ -129,7 +129,7 @@
{
"type": "regex",
"value": "margin:\\s*0\\s+auto",
"message": "Usa el atajo que calcula márgenes horizontales iguales automáticamente",
"message": "Usa <kbd>margin</kbd> con una palabra clave que calcula automáticamente espaciado igual a izquierda y derecha",
"options": { "caseSensitive": false }
}
]
@@ -151,7 +151,7 @@
{
"type": "property_value",
"value": { "property": "border-radius", "expected": "50%" },
"message": "¿Qué propiedad redondea las esquinas? Piensa en qué porcentaje crea un círculo"
"message": "¿Qué porcentaje de <kbd>border-radius</kbd> crea un círculo perfecto a partir de un elemento cuadrado?"
}
]
},
@@ -172,18 +172,18 @@
{
"type": "property_value",
"value": { "property": "padding", "expected": "1rem" },
"message": "Añade espacio interior a la notificación"
"message": "El elemento necesita espacio interior"
},
{
"type": "regex",
"value": "border-left:\\s*4px\\s+solid\\s+coral",
"message": "Añade un acento de color en el borde izquierdo",
"message": "Añade un acento de borde izquierdo usando el atajo <kbd>border-left</kbd>",
"options": { "caseSensitive": false }
},
{
"type": "property_value",
"value": { "property": "border-radius", "expected": "4px" },
"message": "Suaviza las esquinas de la notificación"
"message": "Redondea las esquinas ligeramente con <kbd>border-radius</kbd>"
}
]
}

View File

@@ -22,7 +22,7 @@
{
"type": "property_value",
"value": { "property": "max-width", "expected": "40rem" },
"message": "Establece <kbd>max-width: 40rem</kbd>"
"message": "¿Qué propiedad limita el ancho de un elemento? Prueba un valor en <kbd>rem</kbd> para una longitud de línea legible."
}
]
},
@@ -49,7 +49,7 @@
{
"type": "contains",
"value": "steelblue",
"message": "Establece el valor a <kbd>steelblue</kbd>",
"message": "Asigna el valor <kbd>steelblue</kbd> a la variable",
"options": { "caseSensitive": false }
}
]
@@ -71,7 +71,7 @@
{
"type": "regex",
"value": "width:\\s*calc\\(\\s*100%\\s*-\\s*200px\\s*\\)",
"message": "Establece <kbd>width: calc(100% - 200px)</kbd>",
"message": "Usa <kbd>calc()</kbd> para restar el ancho fijo de la barra lateral del ancho total del contenedor.",
"options": { "caseSensitive": false }
}
]
@@ -93,7 +93,7 @@
{
"type": "property_value",
"value": { "property": "min-height", "expected": "100vh" },
"message": "Establece <kbd>min-height: 100vh</kbd>"
"message": "¿Qué propiedad asegura una altura mínima? Usa una unidad de viewport para cobertura de pantalla completa."
}
]
}

View File

@@ -28,7 +28,7 @@
{
"type": "regex",
"value": "transition:\\s*background-color\\s*0\\.3s",
"message": "Establece <kbd>transition: background-color 0.3s</kbd>",
"message": "Especifica qué propiedad transicionar y cuánto debe durar.",
"options": { "caseSensitive": false }
}
]
@@ -56,7 +56,7 @@
{
"type": "property_value",
"value": { "property": "transition-timing-function", "expected": "ease-in-out" },
"message": "Establece timing a <kbd>ease-in-out</kbd>"
"message": "¿Qué palabra clave de easing empieza lento, acelera, y luego desacelera de nuevo?"
}
]
},
@@ -95,7 +95,7 @@
{
"type": "regex",
"value": "animation:.*bounce.*1s.*infinite",
"message": "Aplica <kbd>animation: bounce 1s infinite</kbd>",
"message": "Usa el atajo <kbd>animation</kbd>: nombre, duración y número de repeticiones.",
"options": { "caseSensitive": false }
}
]
@@ -117,27 +117,27 @@
{
"type": "property_value",
"value": { "property": "animation-name", "expected": "pulse" },
"message": "Establece <kbd>animation-name: pulse</kbd>"
"message": "¿Qué propiedad vincula un elemento a una regla <kbd>@keyframes</kbd> nombrada?"
},
{
"type": "property_value",
"value": { "property": "animation-duration", "expected": "2s" },
"message": "Establece <kbd>animation-duration: 2s</kbd>"
"message": "¿Cuánto debe durar un ciclo completo de la animación?"
},
{
"type": "property_value",
"value": { "property": "animation-delay", "expected": "1s" },
"message": "Establece <kbd>animation-delay: 1s</kbd>"
"message": "¿Qué propiedad hace que la animación espere antes de comenzar?"
},
{
"type": "property_value",
"value": { "property": "animation-iteration-count", "expected": "2" },
"message": "Establece <kbd>animation-iteration-count: 2</kbd>"
"message": "¿Qué propiedad controla cuántas veces se repite la animación?"
},
{
"type": "property_value",
"value": { "property": "animation-fill-mode", "expected": "forwards" },
"message": "Establece <kbd>animation-fill-mode: forwards</kbd>"
"message": "¿Qué propiedad mantiene el elemento con los estilos de su último keyframe después de que termina la animación?"
}
]
}

View File

@@ -22,7 +22,7 @@
{
"type": "regex",
"value": "@media\\s*\\(max-width:\\s*600px\\)",
"message": "Usa <kbd>@media (max-width: 600px)</kbd>",
"message": "Empieza con una regla <kbd>@media</kbd> — ¿qué condición apunta a pantallas de 600px de ancho o menos?",
"options": { "caseSensitive": false }
},
{
@@ -34,7 +34,7 @@
{
"type": "property_value",
"value": { "property": "background", "expected": "lightcoral" },
"message": "Establece <kbd>background: lightcoral</kbd>",
"message": "¿Qué propiedad cambia el color de fondo del elemento?",
"options": { "exact": false }
}
]
@@ -56,7 +56,7 @@
{
"type": "property_value",
"value": { "property": "font-size", "expected": "5vw" },
"message": "Establece <kbd>font-size: 5vw</kbd>"
"message": "¿Qué unidad CSS escala en relación al ancho del viewport?"
}
]
},
@@ -77,18 +77,18 @@
{
"type": "property_value",
"value": { "property": "display", "expected": "grid" },
"message": "Establece <kbd>display: grid</kbd>"
"message": "¿Qué modo de display permite definir filas y columnas?"
},
{
"type": "regex",
"value": "repeat\\(auto-fit,\\s*minmax\\(200px,\\s*1fr\\)\\)",
"message": "Usa <kbd>repeat(auto-fit, minmax(200px, 1fr))</kbd>",
"message": "Prueba <kbd>repeat()</kbd> con <kbd>auto-fit</kbd> y <kbd>minmax()</kbd> — ¿qué tamaños mínimo y máximo crean columnas flexibles?",
"options": { "caseSensitive": false }
},
{
"type": "property_value",
"value": { "property": "gap", "expected": "1rem" },
"message": "Establece <kbd>gap: 1rem</kbd>"
"message": "¿Qué propiedad añade espacio entre los elementos del grid?"
}
]
},
@@ -109,7 +109,7 @@
{
"type": "regex",
"value": "@media\\s*\\(min-width:\\s*768px\\)",
"message": "Usa <kbd>@media (min-width: 768px)</kbd>",
"message": "¿Qué condición <kbd>@media</kbd> aplica estilos cuando el viewport tiene al menos 768px de ancho?",
"options": { "caseSensitive": false }
},
{
@@ -121,7 +121,7 @@
{
"type": "property_value",
"value": { "property": "width", "expected": "250px" },
"message": "Establece <kbd>width: 250px</kbd>",
"message": "¿Qué propiedad controla el ancho de la barra lateral en pantallas más grandes?",
"options": { "exact": false }
}
]

View File

@@ -22,7 +22,7 @@
{
"type": "property_value",
"value": { "property": "display", "expected": "flex" },
"message": "Establece <kbd>display: flex</kbd>"
"message": "¿Qué valor de display convierte un elemento en un contenedor de caja flexible?"
}
]
},
@@ -43,7 +43,7 @@
{
"type": "property_value",
"value": { "property": "gap", "expected": "1rem" },
"message": "Establece <kbd>gap: 1rem</kbd>"
"message": "¿Qué propiedad crea espaciado entre elementos flex sin usar márgenes?"
}
]
},
@@ -64,7 +64,7 @@
{
"type": "property_value",
"value": { "property": "justify-content", "expected": "space-between" },
"message": "Establece <kbd>justify-content: space-between</kbd>"
"message": "¿Qué valor de <kbd>justify-content</kbd> empuja el primer y último elemento a los extremos opuestos?"
}
]
},
@@ -85,7 +85,7 @@
{
"type": "property_value",
"value": { "property": "align-items", "expected": "center" },
"message": "Establece <kbd>align-items: center</kbd>"
"message": "¿Qué propiedad alinea los elementos flex a lo largo del eje transversal?"
}
]
},
@@ -106,7 +106,7 @@
{
"type": "property_value",
"value": { "property": "flex-wrap", "expected": "wrap" },
"message": "Establece <kbd>flex-wrap: wrap</kbd>"
"message": "¿Qué propiedad permite que los elementos flex fluyan a múltiples líneas?"
}
]
},
@@ -127,7 +127,7 @@
{
"type": "property_value",
"value": { "property": "flex", "expected": "1" },
"message": "Establece <kbd>flex: 1</kbd>"
"message": "¿Qué propiedad hace que un elemento flex crezca para llenar el espacio restante?"
}
]
}

View File

@@ -9,7 +9,7 @@
"id": "flexbox-1",
"title": "Container",
"description": "Before flexbox, creating even simple layouts required floats, positioning hacks, or table-based layouts. Flexbox (Flexible Box Layout) revolutionized CSS by providing a one-dimensional layout system designed specifically for distributing space and aligning content.<br><br><strong>How it works:</strong> When you set <kbd>display: flex</kbd> on an element, it becomes a <em>flex container</em>. Its direct children automatically become <em>flex items</em> that flow along a main axis (horizontal by default). This single property transforms stacked block elements into a horizontal row.<br><br><strong>The two axes:</strong><br>• <em>Main axis</em> The primary direction items flow (row = left→right)<br>• <em>Cross axis</em> Perpendicular to main (row = top→bottom)<br><br><pre>.nav {\n display: flex;\n}</pre>",
"task": "The navigation links are stacking vertically. Make them display side by side in a horizontal row.",
"task": "This navigation menu stacks vertically. Add <kbd>display: flex</kbd> to <kbd>.nav</kbd> to arrange the links horizontally.",
"previewHTML": "<nav class=\"nav\"><a href=\"#\">Home</a><a href=\"#\">Products</a><a href=\"#\">About</a><a href=\"#\">Contact</a></nav>",
"previewBaseCSS": "body { font-family: system-ui, sans-serif; margin: 0; } .nav { background: #1a1a2e; padding: 1rem; } .nav a { color: white; text-decoration: none; padding: 8px 1rem; border-radius: 4px; } .nav a:hover { background: rgba(255,255,255,0.1); }",
"sandboxCSS": "",
@@ -22,7 +22,7 @@
{
"type": "property_value",
"value": { "property": "display", "expected": "flex" },
"message": "Try changing the display mode to create a flex container"
"message": "Which display value turns an element into a flexible box container?"
}
]
},
@@ -30,7 +30,7 @@
"id": "flexbox-2",
"title": "Gap",
"description": "The <kbd>gap</kbd> property adds consistent spacing between flex items without needing margins. It only creates space between items, not around the edges.",
"task": "The navigation links are crammed together with no breathing room. Add 1rem of spacing between them.",
"task": "Add <kbd>gap: 1rem</kbd> to space out the navigation links evenly.",
"previewHTML": "<nav class=\"nav\"><a href=\"#\">Home</a><a href=\"#\">Products</a><a href=\"#\">About</a><a href=\"#\">Contact</a></nav>",
"previewBaseCSS": "body { font-family: system-ui, sans-serif; margin: 0; } .nav { background: #1a1a2e; padding: 1rem; display: flex; } .nav a { color: white; text-decoration: none; padding: 8px 1rem; border-radius: 4px; background: rgba(255,255,255,0.1); }",
"sandboxCSS": "",
@@ -43,7 +43,7 @@
{
"type": "property_value",
"value": { "property": "gap", "expected": "1rem" },
"message": "Use the property that adds spacing between flex items"
"message": "Which property creates spacing between flex items without using margins?"
}
]
},
@@ -51,7 +51,7 @@
"id": "flexbox-3",
"title": "Justify Content",
"description": "<kbd>justify-content</kbd> distributes items along the main axis. Common values:<br>• <kbd>flex-start</kbd> pack items at the start<br>• <kbd>flex-end</kbd> pack at the end<br>• <kbd>center</kbd> center items<br>• <kbd>space-between</kbd> equal space between items<br>• <kbd>space-around</kbd> equal space around items",
"task": "The Login button should sit on the far right, with the other links staying on the left. Distribute the space between them.",
"task": "Push the \"Login\" button to the right by setting <kbd>justify-content: space-between</kbd> on the nav.",
"previewHTML": "<nav class=\"nav\"><div class=\"links\"><a href=\"#\">Home</a><a href=\"#\">Products</a><a href=\"#\">About</a></div><a href=\"#\" class=\"login\">Login</a></nav>",
"previewBaseCSS": "body { font-family: system-ui, sans-serif; margin: 0; } .nav { background: #1a1a2e; padding: 1rem; display: flex; } .links { display: flex; gap: 8px; } .nav a { color: white; text-decoration: none; padding: 8px 1rem; border-radius: 4px; } .nav a:hover { background: rgba(255,255,255,0.1); } .login { background: steelblue; }",
"sandboxCSS": "",
@@ -64,7 +64,7 @@
{
"type": "property_value",
"value": { "property": "justify-content", "expected": "space-between" },
"message": "Use the property that distributes items along the main axis"
"message": "Which <kbd>justify-content</kbd> value pushes the first and last items to opposite edges?"
}
]
},
@@ -72,7 +72,7 @@
"id": "flexbox-4",
"title": "Align Items",
"description": "<kbd>align-items</kbd> controls alignment on the cross axis (vertical when flex-direction is row). Values include:<br>• <kbd>stretch</kbd> stretch to fill (default)<br>• <kbd>flex-start</kbd> align to top<br>• <kbd>flex-end</kbd> align to bottom<br>• <kbd>center</kbd> center vertically",
"task": "The logo and nav links sit at different heights. Center them vertically so they line up.",
"task": "The logo and nav links have different heights. Center them vertically with <kbd>align-items: center</kbd>.",
"previewHTML": "<header class=\"header\"><div class=\"logo\">ACME</div><nav><a href=\"#\">Products</a><a href=\"#\">Pricing</a><a href=\"#\">Docs</a></nav></header>",
"previewBaseCSS": "body { font-family: system-ui, sans-serif; margin: 0; } .header { background: white; padding: 1rem 2rem; display: flex; justify-content: space-between; border-bottom: 1px solid #eee; } .logo { font-size: 1.5rem; font-weight: bold; color: steelblue; } nav { display: flex; gap: 1rem; } nav a { color: #333; text-decoration: none; font-size: 0.9rem; }",
"sandboxCSS": "",
@@ -85,7 +85,7 @@
{
"type": "property_value",
"value": { "property": "align-items", "expected": "center" },
"message": "Use the property that controls cross-axis alignment"
"message": "Which property aligns flex items along the cross axis?"
}
]
},
@@ -93,7 +93,7 @@
"id": "flexbox-5",
"title": "Flex Wrap",
"description": "By default, flex items squeeze onto one line. <kbd>flex-wrap: wrap</kbd> allows items to flow onto multiple lines when they run out of space.",
"task": "The cards overflow the container instead of fitting within it. Allow the items to flow onto new rows when they run out of space.",
"task": "These cards overflow the container. Add <kbd>flex-wrap: wrap</kbd> to allow them to wrap to new rows.",
"previewHTML": "<div class=\"cards\"><article class=\"card\">Card 1</article><article class=\"card\">Card 2</article><article class=\"card\">Card 3</article><article class=\"card\">Card 4</article><article class=\"card\">Card 5</article><article class=\"card\">Card 6</article></div>",
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .cards { display: flex; gap: 1rem; } .card { background: white; padding: 2rem; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); min-width: 120px; text-align: center; }",
"sandboxCSS": "",
@@ -106,7 +106,7 @@
{
"type": "property_value",
"value": { "property": "flex-wrap", "expected": "wrap" },
"message": "Use the property that allows flex items to wrap onto new lines"
"message": "Which property allows flex items to flow onto multiple lines?"
}
]
},
@@ -114,7 +114,7 @@
"id": "flexbox-6",
"title": "Flex Grow",
"description": "The <kbd>flex</kbd> property on items controls how they grow and shrink. <kbd>flex: 1</kbd> makes an item grow to fill available space. Multiple items with <kbd>flex: 1</kbd> share space equally.",
"task": "The search input is too narrow. Make it stretch to fill all the remaining space in the toolbar.",
"task": "Make the search input expand to fill available space by setting <kbd>flex: 1</kbd> on <kbd>.search</kbd>.",
"previewHTML": "<div class=\"toolbar\"><input class=\"search\" type=\"text\" placeholder=\"Search...\"><button class=\"btn\">Search</button><button class=\"btn\">Filters</button></div>",
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .toolbar { display: flex; gap: 8px; padding: 1rem; background: #f5f5f5; border-radius: 8px; } .search { padding: 8px 1rem; border: 1px solid #ddd; border-radius: 4px; font-size: 1rem; } .btn { padding: 8px 1rem; background: steelblue; color: white; border: none; border-radius: 4px; cursor: pointer; }",
"sandboxCSS": "",
@@ -125,9 +125,9 @@
"previewContainer": "preview-area",
"validations": [
{
"type": "regex",
"value": "(flex\\s*:\\s*1|flex-grow\\s*:\\s*1)",
"message": "Use the property that makes a flex item grow to fill available space"
"type": "property_value",
"value": { "property": "flex", "expected": "1" },
"message": "Which property makes a flex item grow to fill the remaining space?"
}
]
}

View File

@@ -22,7 +22,7 @@
{
"type": "property_value",
"value": { "property": "display", "expected": "grid" },
"message": "Set <kbd>display: grid</kbd>"
"message": "Which <kbd>display</kbd> value activates the CSS Grid layout system?"
}
]
},
@@ -43,7 +43,7 @@
{
"type": "regex",
"value": "grid-template-columns:\\s*repeat\\(\\s*3\\s*,\\s*1fr\\s*\\)",
"message": "Set <kbd>grid-template-columns: repeat(3, 1fr)</kbd>",
"message": "Which CSS property defines column sizes in a grid? Use <kbd>repeat()</kbd> with the <kbd>fr</kbd> unit for equal columns.",
"options": { "caseSensitive": false }
}
]
@@ -65,7 +65,7 @@
{
"type": "property_value",
"value": { "property": "gap", "expected": "1rem" },
"message": "Set <kbd>gap: 1rem</kbd>"
"message": "Which CSS property adds spacing between grid cells without affecting the outer edges?"
}
]
},
@@ -86,7 +86,7 @@
{
"type": "regex",
"value": "grid-column:\\s*span\\s+2",
"message": "Set <kbd>grid-column: span 2</kbd>",
"message": "Which CSS property makes a grid item stretch across multiple columns? Use the <kbd>span</kbd> keyword.",
"options": { "caseSensitive": false }
}
]
@@ -108,7 +108,7 @@
{
"type": "regex",
"value": "grid-template-columns:\\s*repeat\\(\\s*auto-fit\\s*,\\s*minmax\\(\\s*150px\\s*,\\s*1fr\\s*\\)\\s*\\)",
"message": "Set <kbd>grid-template-columns: repeat(auto-fit, minmax(150px, 1fr))</kbd>",
"message": "Which CSS property creates responsive columns? Combine <kbd>auto-fit</kbd> with <kbd>minmax()</kbd> for flexible sizing.",
"options": { "caseSensitive": false }
}
]

View File

@@ -22,7 +22,7 @@
{
"type": "property_value",
"value": { "property": "color", "expected": "coral" },
"message": "Dodaj <kbd>color: coral;</kbd>"
"message": "Która właściwość kontroluje kolor tekstu?"
}
]
},
@@ -43,12 +43,12 @@
{
"type": "property_value",
"value": { "property": "background", "expected": "lavender" },
"message": "Dodaj <kbd>background: lavender;</kbd>"
"message": "Sprawdź właściwość <kbd>background</kbd> — jaki kolor potrzebuje karta?"
},
{
"type": "property_value",
"value": { "property": "padding", "expected": "1rem" },
"message": "Dodaj <kbd>padding: 1rem;</kbd>"
"message": "Element potrzebuje wewnętrznej przestrzeni — sprawdź właściwość <kbd>padding</kbd>"
}
]
},
@@ -74,7 +74,7 @@
{
"type": "property_value",
"value": { "property": "color", "expected": "steelblue" },
"message": "Ustaw <kbd>color: steelblue</kbd>"
"message": "Która właściwość kontroluje kolor tekstu?"
}
]
},
@@ -100,7 +100,7 @@
{
"type": "property_value",
"value": { "property": "color", "expected": "coral" },
"message": "Ustaw <kbd>color: coral</kbd>"
"message": "Sprawdź właściwość <kbd>color</kbd> — jaki kolor potrzebują linki?"
}
]
},
@@ -126,7 +126,7 @@
{
"type": "property_value",
"value": { "property": "background", "expected": "tomato" },
"message": "Ustaw <kbd>background: tomato</kbd>"
"message": "Sprawdź właściwość <kbd>background</kbd> — jaki kolor potrzebuje badge?"
}
]
},
@@ -152,7 +152,7 @@
{
"type": "property_value",
"value": { "property": "background", "expected": "steelblue" },
"message": "Ustaw <kbd>background: steelblue</kbd>"
"message": "Sprawdź właściwość <kbd>background</kbd> — jaki kolor potrzebuje przycisk?"
}
]
},
@@ -178,7 +178,7 @@
{
"type": "property_value",
"value": { "property": "text-decoration", "expected": "none" },
"message": "Ustaw <kbd>text-decoration: none</kbd>"
"message": "Która właściwość kontroluje podkreślenie linków?"
}
]
},
@@ -199,7 +199,7 @@
{
"type": "property_value",
"value": { "property": "color", "expected": "steelblue" },
"message": "Ustaw <kbd>color: steelblue</kbd>"
"message": "Która właściwość kontroluje kolor tekstu nagłówków?"
}
]
},
@@ -225,7 +225,7 @@
{
"type": "property_value",
"value": { "property": "color", "expected": "white" },
"message": "Ustaw <kbd>color: white</kbd>"
"message": "Sprawdź właściwość <kbd>color</kbd> — jaki kolor potrzebują linki nawigacji?"
}
]
},
@@ -251,7 +251,7 @@
{
"type": "property_value",
"value": { "property": "font-size", "expected": "0.9rem" },
"message": "Ustaw <kbd>font-size: 0.9rem</kbd>"
"message": "Która właściwość kontroluje rozmiar tekstu?"
}
]
}

View File

@@ -22,7 +22,7 @@
{
"type": "property_value",
"value": { "property": "padding", "expected": "1rem" },
"message": "Która właściwość dodaje przestrzeń między treścią a krawędzią elementu?"
"message": "Element potrzebuje wewnętrznej przestrzeni — sprawdź właściwość <kbd>padding</kbd>"
}
]
},
@@ -43,7 +43,7 @@
{
"type": "regex",
"value": "border-left:\\s*4px\\s+solid\\s+steelblue",
"message": "Użyj skrótu, który ustawia ramkę tylko po jednej stronie",
"message": "Sprawdź właściwość <kbd>border-left</kbd> — jakiej szerokości, stylu i koloru potrzebujesz?",
"options": { "caseSensitive": false }
}
]
@@ -65,7 +65,7 @@
{
"type": "property_value",
"value": { "property": "margin-bottom", "expected": "1rem" },
"message": "Która właściwość odpycha sąsiednie elementy w dół?"
"message": "Która właściwość kontroluje przestrzeń pod elementem?"
}
]
},
@@ -86,7 +86,7 @@
{
"type": "property_value",
"value": { "property": "box-sizing", "expected": "border-box" },
"message": "Który tryb rozmiaru uwzględnia padding i ramkę w szerokości elementu?"
"message": "Która wartość <kbd>box-sizing</kbd> włącza padding i ramkę do szerokości?"
}
]
},
@@ -107,7 +107,7 @@
{
"type": "regex",
"value": "padding:\\s*8px\\s+1rem",
"message": "Użyj skrótu dwuwartościowego: najpierw pionowo, potem poziomo",
"message": "Sprawdź skrót <kbd>padding</kbd> — dwie wartości oznaczają pion i poziom",
"options": { "caseSensitive": false }
}
]
@@ -129,7 +129,7 @@
{
"type": "regex",
"value": "margin:\\s*0\\s+auto",
"message": "Użyj skrótu, który automatycznie oblicza równe marginesy poziome",
"message": "Sprawdź skrót <kbd>margin</kbd> — jak automatycznie wycentrować element poziomo?",
"options": { "caseSensitive": false }
}
]
@@ -151,7 +151,7 @@
{
"type": "property_value",
"value": { "property": "border-radius", "expected": "50%" },
"message": "Która właściwość zaokrągla rogi? Pomyśl, jaki procent tworzy koło"
"message": "Która wartość <kbd>border-radius</kbd> tworzy pełne koło?"
}
]
},
@@ -172,18 +172,18 @@
{
"type": "property_value",
"value": { "property": "padding", "expected": "1rem" },
"message": "Dodaj wewnętrzny odstęp do powiadomienia"
"message": "Element potrzebuje wewnętrznej przestrzeni — sprawdź właściwość <kbd>padding</kbd>"
},
{
"type": "regex",
"value": "border-left:\\s*4px\\s+solid\\s+coral",
"message": "Dodaj kolorowy akcent na lewej krawędzi",
"message": "Sprawdź właściwość <kbd>border-left</kbd> — jaki styl akcentu potrzebuje powiadomienie?",
"options": { "caseSensitive": false }
},
{
"type": "property_value",
"value": { "property": "border-radius", "expected": "4px" },
"message": "Wygładź rogi powiadomienia"
"message": "Element potrzebuje zaokrąglonych rogów — sprawdź właściwość <kbd>border-radius</kbd>"
}
]
}

View File

@@ -22,7 +22,7 @@
{
"type": "property_value",
"value": { "property": "max-width", "expected": "40rem" },
"message": "Ustaw <kbd>max-width: 40rem</kbd>"
"message": "Która właściwość ogranicza maksymalną szerokość elementu?"
}
]
},
@@ -49,7 +49,7 @@
{
"type": "contains",
"value": "steelblue",
"message": "Ustaw wartość na <kbd>steelblue</kbd>",
"message": "Jaki kolor powinna mieć zmienna brand?",
"options": { "caseSensitive": false }
}
]
@@ -71,7 +71,7 @@
{
"type": "regex",
"value": "width:\\s*calc\\(\\s*100%\\s*-\\s*200px\\s*\\)",
"message": "Ustaw <kbd>width: calc(100% - 200px)</kbd>",
"message": "Sprawdź funkcję <kbd>calc()</kbd> — jak obliczyć szerokość minus sidebar?",
"options": { "caseSensitive": false }
}
]
@@ -93,7 +93,7 @@
{
"type": "property_value",
"value": { "property": "min-height", "expected": "100vh" },
"message": "Ustaw <kbd>min-height: 100vh</kbd>"
"message": "Która właściwość zapewnia minimalną wysokość na cały viewport?"
}
]
}

View File

@@ -28,7 +28,7 @@
{
"type": "regex",
"value": "transition:\\s*background-color\\s*0\\.3s",
"message": "Ustaw <kbd>transition: background-color 0.3s</kbd>",
"message": "Sprawdź właściwość <kbd>transition</kbd> — jaką właściwość i czas trwania podać?",
"options": { "caseSensitive": false }
}
]
@@ -56,7 +56,7 @@
{
"type": "property_value",
"value": { "property": "transition-timing-function", "expected": "ease-in-out" },
"message": "Ustaw timing na <kbd>ease-in-out</kbd>"
"message": "Która wartość tworzy płynne przyspieszenie i spowolnienie?"
}
]
},
@@ -83,7 +83,7 @@
{
"type": "regex",
"value": "50%.*transform: translateY\\(-20px\\)",
"message": "Przy <kbd>50%</kbd>, użyj <kbd>transform: translateY(-20px)</kbd>",
"message": "W połowie animacji piłka powinna podskoczyć w górę — sprawdź <kbd>transform</kbd>",
"options": { "caseSensitive": false }
},
{
@@ -95,7 +95,7 @@
{
"type": "regex",
"value": "animation:.*bounce.*1s.*infinite",
"message": "Zastosuj <kbd>animation: bounce 1s infinite</kbd>",
"message": "Sprawdź skrót <kbd>animation</kbd> — podaj nazwę, czas trwania i powtarzanie",
"options": { "caseSensitive": false }
}
]
@@ -117,27 +117,27 @@
{
"type": "property_value",
"value": { "property": "animation-name", "expected": "pulse" },
"message": "Ustaw <kbd>animation-name: pulse</kbd>"
"message": "Która właściwość wskazuje nazwę animacji do zastosowania?"
},
{
"type": "property_value",
"value": { "property": "animation-duration", "expected": "2s" },
"message": "Ustaw <kbd>animation-duration: 2s</kbd>"
"message": "Sprawdź właściwość <kbd>animation-duration</kbd> — jak długo trwa jeden cykl?"
},
{
"type": "property_value",
"value": { "property": "animation-delay", "expected": "1s" },
"message": "Ustaw <kbd>animation-delay: 1s</kbd>"
"message": "Sprawdź właściwość <kbd>animation-delay</kbd> — ile czeka przed startem?"
},
{
"type": "property_value",
"value": { "property": "animation-iteration-count", "expected": "2" },
"message": "Ustaw <kbd>animation-iteration-count: 2</kbd>"
"message": "Sprawdź właściwość <kbd>animation-iteration-count</kbd> — ile razy ma się powtórzyć?"
},
{
"type": "property_value",
"value": { "property": "animation-fill-mode", "expected": "forwards" },
"message": "Ustaw <kbd>animation-fill-mode: forwards</kbd>"
"message": "Która wartość <kbd>animation-fill-mode</kbd> zachowuje końcowy stan animacji?"
}
]
}

View File

@@ -34,7 +34,7 @@
{
"type": "property_value",
"value": { "property": "background", "expected": "lightcoral" },
"message": "Ustaw <kbd>background: lightcoral</kbd>",
"message": "Sprawdź właściwość <kbd>background</kbd> — jaki kolor potrzebuje panel na małych ekranach?",
"options": { "exact": false }
}
]
@@ -53,7 +53,11 @@
"solution": " font-size: 5vw;",
"previewContainer": "preview-area",
"validations": [
{ "type": "property_value", "value": { "property": "font-size", "expected": "5vw" }, "message": "Ustaw <kbd>font-size: 5vw</kbd>" }
{
"type": "property_value",
"value": { "property": "font-size", "expected": "5vw" },
"message": "Sprawdź właściwość <kbd>font-size</kbd> — która jednostka skaluje się z viewport?"
}
]
},
{
@@ -73,7 +77,7 @@
{
"type": "property_value",
"value": { "property": "display", "expected": "grid" },
"message": "Ustaw <kbd>display: grid</kbd>"
"message": "Która wartość <kbd>display</kbd> włącza układ siatkowy?"
},
{
"type": "regex",
@@ -84,7 +88,7 @@
{
"type": "property_value",
"value": { "property": "gap", "expected": "1rem" },
"message": "Ustaw <kbd>gap: 1rem</kbd>"
"message": "Sprawdź właściwość <kbd>gap</kbd> — jaki odstęp potrzebują elementy siatki?"
}
]
},
@@ -117,7 +121,7 @@
{
"type": "property_value",
"value": { "property": "width", "expected": "250px" },
"message": "Ustaw <kbd>width: 250px</kbd>",
"message": "Sprawdź właściwość <kbd>width</kbd> — jaką stałą szerokość potrzebuje sidebar?",
"options": { "exact": false }
}
]

View File

@@ -22,7 +22,7 @@
{
"type": "property_value",
"value": { "property": "display", "expected": "flex" },
"message": "Ustaw <kbd>display: flex</kbd>"
"message": "Która właściwość <kbd>display</kbd> tworzy kontener flex?"
}
]
},
@@ -43,7 +43,7 @@
{
"type": "property_value",
"value": { "property": "gap", "expected": "1rem" },
"message": "Ustaw <kbd>gap: 1rem</kbd>"
"message": "Sprawdź właściwość <kbd>gap</kbd> — jaki odstęp potrzebują elementy?"
}
]
},
@@ -64,7 +64,7 @@
{
"type": "property_value",
"value": { "property": "justify-content", "expected": "space-between" },
"message": "Ustaw <kbd>justify-content: space-between</kbd>"
"message": "Która wartość <kbd>justify-content</kbd> rozdziela elementy na końce?"
}
]
},
@@ -85,7 +85,7 @@
{
"type": "property_value",
"value": { "property": "align-items", "expected": "center" },
"message": "Ustaw <kbd>align-items: center</kbd>"
"message": "Która właściwość kontroluje wyrównanie na osi poprzecznej?"
}
]
},
@@ -106,7 +106,7 @@
{
"type": "property_value",
"value": { "property": "flex-wrap", "expected": "wrap" },
"message": "Ustaw <kbd>flex-wrap: wrap</kbd>"
"message": "Sprawdź właściwość <kbd>flex-wrap</kbd> — jak pozwolić elementom przenosić się na nowe linie?"
}
]
},
@@ -127,7 +127,7 @@
{
"type": "property_value",
"value": { "property": "flex", "expected": "1" },
"message": "Ustaw <kbd>flex: 1</kbd>"
"message": "Sprawdź właściwość <kbd>flex</kbd> — jak sprawić, by element wypełnił dostępną przestrzeń?"
}
]
}

View File

@@ -22,7 +22,7 @@
{
"type": "property_value",
"value": { "property": "color", "expected": "coral" },
"message": "Додайте <kbd>color: coral;</kbd>"
"message": "Яка властивість керує кольором тексту?"
}
]
},
@@ -43,12 +43,12 @@
{
"type": "property_value",
"value": { "property": "background", "expected": "lavender" },
"message": "Додайте <kbd>background: lavender;</kbd>"
"message": "Перевірте властивість <kbd>background</kbd>"
},
{
"type": "property_value",
"value": { "property": "padding", "expected": "1rem" },
"message": "Додайте <kbd>padding: 1rem;</kbd>"
"message": "Картка потребує простору всередині її меж"
}
]
},
@@ -74,7 +74,7 @@
{
"type": "property_value",
"value": { "property": "color", "expected": "steelblue" },
"message": "Встановіть <kbd>color: steelblue</kbd>"
"message": "Яка властивість змінює колір тексту?"
}
]
},
@@ -100,7 +100,7 @@
{
"type": "property_value",
"value": { "property": "color", "expected": "coral" },
"message": "Встановіть <kbd>color: coral</kbd>"
"message": "Яке значення дає теплий червонувато-оранжевий колір?"
}
]
},
@@ -126,7 +126,7 @@
{
"type": "property_value",
"value": { "property": "background", "expected": "tomato" },
"message": "Встановіть <kbd>background: tomato</kbd>"
"message": "Значку потрібен яскравий червоний фон"
}
]
},
@@ -152,7 +152,7 @@
{
"type": "property_value",
"value": { "property": "background", "expected": "steelblue" },
"message": "Встановіть <kbd>background: steelblue</kbd>"
"message": "Яка властивість встановлює колір заливки кнопки?"
}
]
},
@@ -178,7 +178,7 @@
{
"type": "property_value",
"value": { "property": "text-decoration", "expected": "none" },
"message": "Встановіть <kbd>text-decoration: none</kbd>"
"message": "Яка властивість керує підкресленням посилань?"
}
]
},
@@ -199,7 +199,7 @@
{
"type": "property_value",
"value": { "property": "color", "expected": "steelblue" },
"message": "Встановіть <kbd>color: steelblue</kbd>"
"message": "Перевірте властивість <kbd>color</kbd>"
}
]
},
@@ -225,7 +225,7 @@
{
"type": "property_value",
"value": { "property": "color", "expected": "white" },
"message": "Встановіть <kbd>color: white</kbd>"
"message": "Посилання мають виділятися на синьому фоні"
}
]
},
@@ -251,7 +251,7 @@
{
"type": "property_value",
"value": { "property": "font-size", "expected": "0.9rem" },
"message": "Встановіть <kbd>font-size: 0.9rem</kbd>"
"message": "Перевірте властивість <kbd>font-size</kbd> — текст має бути трохи меншим"
}
]
}

View File

@@ -22,7 +22,7 @@
{
"type": "property_value",
"value": { "property": "padding", "expected": "1rem" },
"message": "Яка властивість додає простір між контентом і краєм елемента?"
"message": "Яка властивість додає простір між вмістом елемента та його межею?"
}
]
},
@@ -43,7 +43,7 @@
{
"type": "regex",
"value": "border-left:\\s*4px\\s+solid\\s+steelblue",
"message": "Використайте скорочення, яке встановлює межу лише з одного боку",
"message": "Використайте скорочення <kbd>border-left</kbd> зі значеннями ширини, стилю та кольору",
"options": { "caseSensitive": false }
}
]
@@ -65,7 +65,7 @@
{
"type": "property_value",
"value": { "property": "margin-bottom", "expected": "1rem" },
"message": "Яка властивість відштовхує сусідні елементи знизу?"
"message": "Яка властивість створює простір знизу елемента, відштовхуючи сусідів?"
}
]
},
@@ -86,7 +86,7 @@
{
"type": "property_value",
"value": { "property": "box-sizing", "expected": "border-box" },
"message": "Який режим розміру включає padding і межу в ширину елемента?"
"message": "Яке значення <kbd>box-sizing</kbd> включає padding та межу в загальну ширину елемента?"
}
]
},
@@ -107,7 +107,7 @@
{
"type": "regex",
"value": "padding:\\s*8px\\s+1rem",
"message": "Використайте скорочення з двома значеннями: спочатку вертикальне, потім горизонтальне",
"message": "Використайте скорочення <kbd>padding</kbd> з двома значеннями: вертикальне та горизонтальне",
"options": { "caseSensitive": false }
}
]
@@ -129,7 +129,7 @@
{
"type": "regex",
"value": "margin:\\s*0\\s+auto",
"message": "Використайте скорочення, яке автоматично розраховує рівні горизонтальні поля",
"message": "Використайте <kbd>margin</kbd> з ключовим словом, яке автоматично обчислює рівні ліві та праві відступи",
"options": { "caseSensitive": false }
}
]
@@ -151,7 +151,7 @@
{
"type": "property_value",
"value": { "property": "border-radius", "expected": "50%" },
"message": "Яка властивість заокруглює кути? Подумайте, який відсоток створює коло"
"message": "Який відсоток <kbd>border-radius</kbd> створює ідеальне коло з квадратного елемента?"
}
]
},
@@ -172,18 +172,18 @@
{
"type": "property_value",
"value": { "property": "padding", "expected": "1rem" },
"message": "Додайте внутрішній відступ до сповіщення"
"message": "Додайте внутрішній відступ до картки сповіщення"
},
{
"type": "regex",
"value": "border-left:\\s*4px\\s+solid\\s+coral",
"message": "Додайте кольоровий акцент на лівому краю",
"message": "Додайте лівий акцент за допомогою скорочення <kbd>border-left</kbd>",
"options": { "caseSensitive": false }
},
{
"type": "property_value",
"value": { "property": "border-radius", "expected": "4px" },
"message": "Згладьте кути сповіщення"
"message": "Злегка заокругліть кути за допомогою <kbd>border-radius</kbd>"
}
]
}

View File

@@ -22,7 +22,7 @@
{
"type": "property_value",
"value": { "property": "max-width", "expected": "40rem" },
"message": "Встановіть <kbd>max-width: 40rem</kbd>"
"message": "Яка властивість обмежує ширину елемента? Спробуйте значення в <kbd>rem</kbd> для комфортної довжини рядка."
}
]
},
@@ -49,7 +49,7 @@
{
"type": "contains",
"value": "steelblue",
"message": "Встановіть значення <kbd>steelblue</kbd>",
"message": "Встановіть значення на <kbd>steelblue</kbd>",
"options": { "caseSensitive": false }
}
]
@@ -71,7 +71,7 @@
{
"type": "regex",
"value": "width:\\s*calc\\(\\s*100%\\s*-\\s*200px\\s*\\)",
"message": "Встановіть <kbd>width: calc(100% - 200px)</kbd>",
"message": "Використайте <kbd>calc()</kbd>, щоб відняти фіксовану ширину сайдбару від повної ширини контейнера.",
"options": { "caseSensitive": false }
}
]
@@ -93,7 +93,7 @@
{
"type": "property_value",
"value": { "property": "min-height", "expected": "100vh" },
"message": "Встановіть <kbd>min-height: 100vh</kbd>"
"message": "Яка властивість забезпечує мінімальну висоту? Використайте одиницю viewport для повноекранного покриття."
}
]
}

View File

@@ -28,7 +28,7 @@
{
"type": "regex",
"value": "transition:\\s*background-color\\s*0\\.3s",
"message": "Встановіть <kbd>transition: background-color 0.3s</kbd>",
"message": "Вкажіть, яку властивість анімувати та скільки це має тривати.",
"options": { "caseSensitive": false }
}
]
@@ -56,7 +56,7 @@
{
"type": "property_value",
"value": { "property": "transition-timing-function", "expected": "ease-in-out" },
"message": "Встановіть timing на <kbd>ease-in-out</kbd>"
"message": "Яке ключове слово пом'якшення починається повільно, прискорюється, а потім знову сповільнюється?"
}
]
},
@@ -95,7 +95,7 @@
{
"type": "regex",
"value": "animation:.*bounce.*1s.*infinite",
"message": "Застосуйте <kbd>animation: bounce 1s infinite</kbd>",
"message": "Використайте скорочення <kbd>animation</kbd>: назва, тривалість та кількість повторень.",
"options": { "caseSensitive": false }
}
]
@@ -117,27 +117,27 @@
{
"type": "property_value",
"value": { "property": "animation-name", "expected": "pulse" },
"message": "Встановіть <kbd>animation-name: pulse</kbd>"
"message": "Яка властивість пов'язує елемент з іменованим правилом <kbd>@keyframes</kbd>?"
},
{
"type": "property_value",
"value": { "property": "animation-duration", "expected": "2s" },
"message": "Встановіть <kbd>animation-duration: 2s</kbd>"
"message": "Скільки має тривати один повний цикл анімації?"
},
{
"type": "property_value",
"value": { "property": "animation-delay", "expected": "1s" },
"message": "Встановіть <kbd>animation-delay: 1s</kbd>"
"message": "Яка властивість змушує анімацію зачекати перед початком?"
},
{
"type": "property_value",
"value": { "property": "animation-iteration-count", "expected": "2" },
"message": "Встановіть <kbd>animation-iteration-count: 2</kbd>"
"message": "Яка властивість контролює кількість повторень анімації?"
},
{
"type": "property_value",
"value": { "property": "animation-fill-mode", "expected": "forwards" },
"message": "Встановіть <kbd>animation-fill-mode: forwards</kbd>"
"message": "Яка властивість зберігає стиль елемента в його фінальному стані keyframe після завершення анімації?"
}
]
}

View File

@@ -22,7 +22,7 @@
{
"type": "regex",
"value": "@media\\s*\\(max-width:\\s*600px\\)",
"message": "Використайте <kbd>@media (max-width: 600px)</kbd>",
"message": "Почніть з правила <kbd>@media</kbd> — яка умова націлюється на екрани шириною 600px або менше?",
"options": { "caseSensitive": false }
},
{
@@ -34,7 +34,7 @@
{
"type": "property_value",
"value": { "property": "background", "expected": "lightcoral" },
"message": "Встановіть <kbd>background: lightcoral</kbd>",
"message": "Яка властивість змінює колір фону елемента?",
"options": { "exact": false }
}
]
@@ -56,7 +56,7 @@
{
"type": "property_value",
"value": { "property": "font-size", "expected": "5vw" },
"message": "Встановіть <kbd>font-size: 5vw</kbd>"
"message": "Яка одиниця CSS масштабується відносно ширини viewport?"
}
]
},
@@ -77,18 +77,18 @@
{
"type": "property_value",
"value": { "property": "display", "expected": "grid" },
"message": "Встановіть <kbd>display: grid</kbd>"
"message": "Який режим display дозволяє визначати рядки та колонки?"
},
{
"type": "regex",
"value": "repeat\\(auto-fit,\\s*minmax\\(200px,\\s*1fr\\)\\)",
"message": "Використайте <kbd>repeat(auto-fit, minmax(200px, 1fr))</kbd>",
"message": "Спробуйте <kbd>repeat()</kbd> з <kbd>auto-fit</kbd> та <kbd>minmax()</kbd> — які мінімальний та максимальний розміри створять гнучкі колонки?",
"options": { "caseSensitive": false }
},
{
"type": "property_value",
"value": { "property": "gap", "expected": "1rem" },
"message": "Встановіть <kbd>gap: 1rem</kbd>"
"message": "Яка властивість додає простір між елементами grid?"
}
]
},
@@ -109,7 +109,7 @@
{
"type": "regex",
"value": "@media\\s*\\(min-width:\\s*768px\\)",
"message": "Використайте <kbd>@media (min-width: 768px)</kbd>",
"message": "Яка умова <kbd>@media</kbd> застосовує стилі, коли viewport має ширину щонайменше 768px?",
"options": { "caseSensitive": false }
},
{
@@ -121,7 +121,7 @@
{
"type": "property_value",
"value": { "property": "width", "expected": "250px" },
"message": "Встановіть <kbd>width: 250px</kbd>",
"message": "Яка властивість контролює ширину сайдбару на великих екранах?",
"options": { "exact": false }
}
]

View File

@@ -22,7 +22,7 @@
{
"type": "property_value",
"value": { "property": "display", "expected": "flex" },
"message": "Встановіть <kbd>display: flex</kbd>"
"message": "Яке значення display перетворює елемент на гнучкий контейнер?"
}
]
},
@@ -43,7 +43,7 @@
{
"type": "property_value",
"value": { "property": "gap", "expected": "1rem" },
"message": "Встановіть <kbd>gap: 1rem</kbd>"
"message": "Яка властивість створює відстань між flex-елементами без використання margin?"
}
]
},
@@ -64,7 +64,7 @@
{
"type": "property_value",
"value": { "property": "justify-content", "expected": "space-between" },
"message": "Встановіть <kbd>justify-content: space-between</kbd>"
"message": "Яке значення <kbd>justify-content</kbd> розміщує перший та останній елементи на протилежних краях?"
}
]
},
@@ -85,7 +85,7 @@
{
"type": "property_value",
"value": { "property": "align-items", "expected": "center" },
"message": "Встановіть <kbd>align-items: center</kbd>"
"message": "Яка властивість вирівнює flex-елементи вздовж поперечної осі?"
}
]
},
@@ -106,7 +106,7 @@
{
"type": "property_value",
"value": { "property": "flex-wrap", "expected": "wrap" },
"message": "Встановіть <kbd>flex-wrap: wrap</kbd>"
"message": "Яка властивість дозволяє flex-елементам переходити на кілька рядків?"
}
]
},
@@ -127,7 +127,7 @@
{
"type": "property_value",
"value": { "property": "flex", "expected": "1" },
"message": "Встановіть <kbd>flex: 1</kbd>"
"message": "Яка властивість змушує flex-елемент зростати, щоб заповнити залишковий простір?"
}
]
}

View File

@@ -1,76 +0,0 @@
# Implementation Plan
## Objective
Rewrite all 6 flexbox lesson task descriptions to describe the desired visual outcome instead of giving the exact CSS declaration. Update validation messages to hint without revealing answers, and accept alternative valid solutions where applicable.
## Approach
This is a content-only change to a single JSON file (`lessons/flexbox.json`). Each lesson needs three edits:
1. **Task text**: Replace copy-pasteable CSS declarations with outcome-oriented descriptions
2. **Validation messages**: Replace answer-revealing messages with pedagogical hints
3. **Validations array**: Add alternative accepted solutions where multiple CSS approaches achieve the same visual result
The lesson `description` fields (which teach concepts with code examples) remain unchanged — they are the learning material, not the exercise prompt.
## File Mapping
| File | Action | Description |
|------|--------|-------------|
| `lessons/flexbox.json` | modify | Rewrite `task` and validation `message` fields for all 6 lessons; add alternative validations for flexbox-6 |
No new files need to be created. No validator code changes needed — the existing `property_value` and `regex` validation types already support everything required.
## Detailed Changes Per Lesson
### flexbox-1 (Container)
- **Task**: Describe that nav links stack vertically and should display side by side
- **Validation msg**: Hint at display property for flex layout
- **Alt solutions**: None — `display: flex` is the only correct answer (inline-flex changes block behavior)
### flexbox-2 (Gap)
- **Task**: Describe that links are crammed together and need 1rem of spacing between them
- **Validation msg**: Hint at the gap property
- **Alt solutions**: None — `gap: 1rem` is the specific expected value
### flexbox-3 (Justify Content)
- **Task**: Describe that Login button should be pushed to the far right, with nav links on the left
- **Validation msg**: Hint at main-axis distribution property
- **Alt solutions**: None — `justify-content: space-between` is the only property that works when targeting `.nav`
### flexbox-4 (Align Items)
- **Task**: Describe the visual misalignment and ask for vertical centering
- **Validation msg**: Hint at cross-axis alignment property
- **Alt solutions**: None — `align-items: center` is the correct answer
### flexbox-5 (Flex Wrap)
- **Task**: Describe cards overflowing and needing to flow onto new rows
- **Validation msg**: Hint at wrapping property
- **Alt solutions**: None — `flex-wrap: wrap` is the only answer
### flexbox-6 (Flex Grow)
- **Task**: Describe that the search input should stretch to fill remaining space
- **Validation msg**: Hint at flex growth property
- **Alt solutions**: Accept both `flex: 1` and `flex-grow: 1` via regex validation
## Architecture Decisions
1. **No validator code changes**: The existing `regex` validation type can handle alternative solutions for flexbox-6. No need to add a new validation type.
2. **Keep values in tasks where needed**: Some tasks mention target values like "1rem" since the validator checks exact values and students need to know the amount. The key change is removing the *property name* from the task.
3. **Solution field unchanged**: The `solution` field is used for the "show solution" feature and should remain as the canonical answer.
4. **codePrefix unchanged**: The existing codePrefix already shows the selector context (e.g., `.nav {`), which is enough guidance for students.
## Risks
| Risk | Likelihood | Mitigation |
|------|-----------|------------|
| Tasks become too vague for beginners | Low | Descriptions still teach the property; tasks describe specific visual outcomes |
| Alternative regex validation too permissive | Low | Regex will be specific to `flex:\s*1` and `flex-grow:\s*1` patterns |
| Validation messages too cryptic | Low | Messages will hint at the property category without giving the exact declaration |
## Testing Strategy
1. **Run existing test suite**: `npm run test` — all tests should pass since no code or module structure changes
2. **Manual verification**: Validate that each rewritten task accurately describes the visual outcome shown in the preview
3. **JSON schema validation**: Ensure `lessons/flexbox.json` still conforms to the module schema

View File

@@ -1,35 +0,0 @@
# fix: remove answers from flexbox task descriptions (copy-paste score 95%)
**Issue**: [libretech/code-crispies#3](https://git.librete.ch/libretech/code-crispies/issues/3)
**State**: open
**Author**: libretech
**Labels**: none
**Complexity**: simple
## Issue Body
Pedagogy audit: All 6 flexbox exercises give the exact CSS declaration in the task text. Students type without understanding. Rewrite tasks to describe the DESIRED OUTCOME instead of the exact code. Example: 'Add display: flex' → 'The navigation links stack vertically. Make them display side by side.' Accept multiple valid solutions in validations.
## Current State
All 6 lessons in `lessons/flexbox.json` have task descriptions that include the exact CSS declaration students need to type:
| Lesson | Current Task (gives away answer) |
|--------|----------------------------------|
| flexbox-1 | "Add `display: flex` to `.nav`" |
| flexbox-2 | "Add `gap: 1rem` to space out..." |
| flexbox-3 | "setting `justify-content: space-between` on the nav" |
| flexbox-4 | "Center them vertically with `align-items: center`" |
| flexbox-5 | "Add `flex-wrap: wrap` to allow them to wrap" |
| flexbox-6 | "setting `flex: 1` on `.search`" |
Validation error messages also give away answers (e.g., "Set `display: flex`").
## Acceptance Criteria
1. All 6 flexbox task descriptions rewritten to describe the desired visual outcome, not the exact CSS code
2. Students cannot copy-paste from the task into the editor to pass
3. Validation error messages updated to provide hints without revealing the exact declaration
4. Where applicable, validations accept multiple valid CSS solutions (e.g., `flex: 1` and `flex-grow: 1`)
5. Existing tests continue to pass
6. Lesson descriptions (which teach the concepts) remain unchanged

View File

@@ -1,13 +0,0 @@
# Tasks
## Phase 1: Core Content Changes
- [X] Task 1.1: Rewrite task text for all 6 flexbox lessons to describe visual outcomes [P]
- [X] Task 1.2: Rewrite validation error messages to hint without revealing answers [P]
## Phase 2: Alternative Validations
- [X] Task 2.1: Add regex validation for flexbox-6 to accept both `flex: 1` and `flex-grow: 1`
## Phase 3: Validation
- [X] Task 3.1: Run existing test suite to confirm no regressions
- [X] Task 3.2: Verify flexbox.json still conforms to module schema
- [X] Task 3.3: Run lesson format check (`npm run format.lessons`)

View File

@@ -0,0 +1,87 @@
# Implementation Plan
## 1. Objective
Rewrite all answer-revealing validation error messages across lesson JSON files to use pedagogical hints (concept questions, property-name nudges, directional guidance) instead of literal CSS solutions. This eliminates the fail-then-copy anti-pattern and promotes genuine learning.
## 2. Approach
**Phase-based, content-first strategy:**
1. Define a message style guide with 3 hint categories:
- **Concept question:** "Which property adds space inside an element?" (for property discovery)
- **Property hint:** "Check the `padding` property" (when the property is known but value is wrong)
- **Directional nudge:** "The items need to wrap to the next line" (for layout concepts)
2. Rewrite English priority modules first (flexbox, box-model, colors, positioning) — these are 100% answer-revealing and form the template for all other rewrites.
3. Rewrite remaining English modules, reusing the same hint patterns established in step 2.
4. Update localized variants with equivalent pedagogical messages in each target language (ar, de, es, pl, uk), translating the English hints while preserving natural phrasing in each language.
5. Run `npm run format.lessons` to ensure consistent formatting, then run tests.
## 3. File Mapping
### Files to modify (message field only, no validation logic changes):
**English priority (create → N/A, modify → 4, delete → N/A):**
- `lessons/flexbox.json` — modify 6 messages
- `lessons/01-box-model.json` — modify 10 messages
- `lessons/03-colors.json` — modify 4 messages
- `lessons/12-positioning.json` — modify 5 messages
**English remaining (modify → 13):**
- `lessons/00-basics.json` — modify 4 messages
- `lessons/00-basic-selectors.json` — modify 15 messages
- `lessons/01-advanced-selectors.json` — modify 8 messages
- `lessons/04-typography.json` — modify 1 message
- `lessons/05-units-variables.json` — modify 3 messages
- `lessons/06-transitions-animations.json` — modify 8 messages
- `lessons/07-layouts.json` — modify 8 messages
- `lessons/08-responsive.json` — modify 8 messages
- `lessons/09-gradients.json` — modify 3 messages
- `lessons/10-tailwind-basics.json` — modify 16 messages
- `lessons/11-filters.json` — modify 4 messages
- `lessons/13-pseudo-elements.json` — modify 4 messages
- `lessons/grid.json` — modify 5 messages
**Localized variants (modify):**
- `lessons/ar/flexbox.json`, `lessons/ar/01-box-model.json`, + other ar/ modules with answer-revealing messages
- `lessons/de/flexbox.json`, `lessons/de/01-box-model.json`, + other de/ modules
- `lessons/es/flexbox.json`, `lessons/es/01-box-model.json`, + other es/ modules
- `lessons/pl/flexbox.json`, `lessons/pl/01-box-model.json`, + other pl/ modules
- `lessons/uk/flexbox.json`, `lessons/uk/01-box-model.json`, + other uk/ modules
**No new files or deleted files.**
## 4. Architecture Decisions
1. **Message-only changes:** Only the `"message"` string within validation objects is modified. The `type`, `value`, and `options` fields remain untouched. This preserves all validation logic.
2. **No code changes to validator.js:** The validator reads the `message` field as a passthrough string for display. No runtime changes needed.
3. **Hint style per validation type:**
- `property_value` validations → concept question or property hint (since the property and value are tested programmatically, the message should teach the concept, not repeat the answer)
- `regex` validations → directional nudge describing the expected pattern conceptually
- `contains` / `contains_class` validations → concept question about what to include
4. **Localization approach:** Each localized message should be a natural translation of the English pedagogical hint, not a word-for-word translation. The hint category (question, nudge, property hint) should match the English version.
5. **Preserve `<kbd>` tags selectively:** `<kbd>` tags may still be used for property names (e.g., "Check the `<kbd>padding</kbd>` property") but never for complete property-value pairs that reveal the answer.
## 5. Risks
| Risk | Likelihood | Mitigation |
|------|-----------|------------|
| Pedagogical hints are too vague, frustrating learners | Medium | Each hint should name the relevant CSS property or concept — just not the exact value. The task description already provides context. |
| Localized translations lose pedagogical intent | Medium | Use consistent hint categories across languages. Review each language for natural phrasing. |
| Existing tests assert on specific message text | Low | Check test files for hardcoded message assertions before changing. Adjust tests if needed. |
| Formatting inconsistency after bulk edits | Low | Run `npm run format.lessons` after all changes. |
## 6. Testing Strategy
1. **Existing test suite:** Run `npm run test` to verify no regressions. The validator tests should pass since validation logic is unchanged.
2. **Grep audit:** After changes, grep all lesson files for remaining "Set <kbd>" patterns to confirm none were missed.
3. **JSON validity:** Ensure all modified JSON files parse correctly (the format.lessons command will catch syntax errors).
4. **Manual spot-check:** Verify a few lessons in the dev server to confirm messages display correctly in the UI.

View File

@@ -0,0 +1,50 @@
# fix: validation error messages reveal the solution instead of guiding learning
**Issue:** [#4](https://git.librete.ch/libretech/code-crispies/issues/4)
**Repository:** libretech/code-crispies
**Author:** libretech
**State:** open
**Labels:** none
## Issue Body
Pedagogy audit: 88% of exercises reveal the answer in error messages, creating a fail-then-copy loop. Change validation messages from 'Set padding: 1rem' to 'Which property adds space between content and the element edge?' This applies across all modules — start with flexbox, box-model, and colors (the 3 worst offenders).
## Acceptance Criteria
1. Validation error messages in **flexbox**, **box-model**, and **colors** modules must no longer reveal the exact CSS property-value answer
2. Replacement messages should use pedagogical hints: concept questions, property-name hints, or directional guidance — never the literal solution
3. All remaining English lesson modules with answer-revealing messages must also be rewritten
4. Localized variants (ar/, de/, es/, pl/, uk/) of affected modules must be updated with equivalent pedagogical messages in each language
5. Existing validations (type, value, options) must remain unchanged — only the `"message"` field is modified
6. All existing tests must continue to pass
## Scope
### English priority modules (100% answer-revealing):
- `lessons/flexbox.json` — 6 messages
- `lessons/01-box-model.json` — 10 messages
- `lessons/03-colors.json` — 4 messages
- `lessons/12-positioning.json` — 5 messages
### English remaining modules (partial answer-revealing):
- `lessons/00-basics.json` — 4 of 26
- `lessons/00-basic-selectors.json` — 15 of 18
- `lessons/01-advanced-selectors.json` — 8 of 49
- `lessons/04-typography.json` — 1 of 9
- `lessons/05-units-variables.json` — 3 of 5
- `lessons/06-transitions-animations.json` — 8 of 13
- `lessons/07-layouts.json` — 8 of 11
- `lessons/08-responsive.json` — 8 of 10
- `lessons/09-gradients.json` — 3 of 7
- `lessons/10-tailwind-basics.json` — 16 of 17
- `lessons/11-filters.json` — 4 of 7
- `lessons/13-pseudo-elements.json` — 4 of 8
- `lessons/grid.json` — 5 of 9
### Localized variants (each language directory):
- `lessons/ar/` — Arabic
- `lessons/de/` — German
- `lessons/es/` — Spanish
- `lessons/pl/` — Polish
- `lessons/uk/` — Ukrainian

View File

@@ -0,0 +1,39 @@
# Tasks
## Phase 1: Preparation
- [X] Task 1.1: Audit existing tests for hardcoded validation message assertions; note any that need updating
- [X] Task 1.2: Read each priority English module and draft replacement messages using the hint style guide (concept question / property hint / directional nudge)
## Phase 2: English Priority Modules (100% answer-revealing)
- [X] Task 2.1: Rewrite validation messages in `lessons/flexbox.json` (6 messages) [P]
- [X] Task 2.2: Rewrite validation messages in `lessons/01-box-model.json` (10 messages) [P]
- [X] Task 2.3: Rewrite validation messages in `lessons/03-colors.json` (4 messages) [P]
- [X] Task 2.4: Rewrite validation messages in `lessons/12-positioning.json` (5 messages) [P]
## Phase 3: English Remaining Modules
- [X] Task 3.1: Rewrite messages in `lessons/00-basic-selectors.json` (15 messages) [P]
- [X] Task 3.2: Rewrite messages in `lessons/00-basics.json` (4 messages) [P]
- [X] Task 3.3: Rewrite messages in `lessons/01-advanced-selectors.json` (8 messages) [P]
- [X] Task 3.4: Rewrite messages in `lessons/04-typography.json` (1 message) [P]
- [X] Task 3.5: Rewrite messages in `lessons/05-units-variables.json` (3 messages) [P]
- [X] Task 3.6: Rewrite messages in `lessons/06-transitions-animations.json` (8 messages) [P]
- [X] Task 3.7: Rewrite messages in `lessons/07-layouts.json` (8 messages) [P]
- [X] Task 3.8: Rewrite messages in `lessons/08-responsive.json` (8 messages) [P]
- [X] Task 3.9: Rewrite messages in `lessons/09-gradients.json` (3 messages) [P]
- [X] Task 3.10: Rewrite messages in `lessons/10-tailwind-basics.json` (16 messages) [P]
- [X] Task 3.11: Rewrite messages in `lessons/11-filters.json` (4 messages) [P]
- [X] Task 3.12: Rewrite messages in `lessons/13-pseudo-elements.json` (4 messages) [P]
- [X] Task 3.13: Rewrite messages in `lessons/grid.json` (5 messages) [P]
## Phase 4: Localized Variants
- [X] Task 4.1: Update Arabic (ar/) localized modules with pedagogical messages [P]
- [X] Task 4.2: Update German (de/) localized modules with pedagogical messages [P]
- [X] Task 4.3: Update Spanish (es/) localized modules with pedagogical messages [P]
- [X] Task 4.4: Update Polish (pl/) localized modules with pedagogical messages [P]
- [X] Task 4.5: Update Ukrainian (uk/) localized modules with pedagogical messages [P]
## Phase 5: Validation & Polish
- [X] Task 5.1: Run `npm run format.lessons` to ensure JSON formatting consistency
- [X] Task 5.2: Run `npm run test` and fix any test failures related to message text assertions
- [X] Task 5.3: Grep audit — verify no "Set <kbd>" answer-revealing patterns remain in any lesson file
- [X] Task 5.4: Spot-check a few lessons via `npm start` to confirm messages render correctly in the UI

View File

@@ -1,77 +0,0 @@
# Implementation Plan
## Objective
Rewrite validation error messages in the box-model and colors lesson modules (and their localizations) so they guide learners toward the answer instead of revealing it. This breaks the "fail-then-copy" loop identified in the pedagogy audit.
## Approach
1. Rewrite each validation `message` field in the English box-model and colors JSON files using question/hint phrasing that describes the *concept* without stating the exact property-value pair
2. Use the flexbox module's existing messages as the style guide
3. Apply equivalent translations to all 5 localized box-model files (ar, de, es, pl, uk)
4. Run the format-lessons script and tests to verify nothing breaks
5. Commit as a docs/content fix (`fix:` conventional commit)
## File Mapping
### Files to Modify
| File | Action | Changes |
|------|--------|---------|
| `lessons/01-box-model.json` | modify | Rewrite 11 validation messages |
| `lessons/03-colors.json` | modify | Rewrite 4 validation messages |
| `lessons/ar/01-box-model.json` | modify | Translate 11 new guiding messages to Arabic |
| `lessons/de/01-box-model.json` | modify | Translate 11 new guiding messages to German |
| `lessons/es/01-box-model.json` | modify | Translate 11 new guiding messages to Spanish |
| `lessons/pl/01-box-model.json` | modify | Translate 11 new guiding messages to Polish |
| `lessons/uk/01-box-model.json` | modify | Translate 11 new guiding messages to Ukrainian |
### Files NOT Changed
- `lessons/flexbox.json` — already uses guiding messages
- All localized flexbox files — already correct
- No colors localizations exist
## Architecture Decisions
1. **Message style**: Use the same imperative hint style as flexbox ("Use the property that...", "Try the property that...") rather than pure questions. This is consistent with the existing codebase and gives just enough direction without revealing the answer.
2. **No `<kbd>` tags in new messages**: The current answer-revealing messages use `<kbd>` to format exact code. The new guiding messages should avoid `<kbd>` since they won't contain code literals — they describe concepts.
3. **Preserve validation logic**: Only the `message` field changes. The `type`, `value`, `options`, and all other fields remain untouched.
4. **Localization approach**: Translate the English guiding messages into each target language, maintaining the same hint/question style. Keep CSS property names untranslated (they are code).
## Message Mapping (English)
| Lesson | Current Message | New Message |
|--------|----------------|-------------|
| box-model-1 | Set `padding: 1rem` | Which property adds space between content and the element's edge? |
| box-model-2 | Set `border-left: 4px solid steelblue` | Use the shorthand that sets a border on just one side |
| box-model-3 | Set `margin-bottom: 1rem` | Which property pushes neighboring elements away from the bottom? |
| box-model-4 | Set `box-sizing: border-box` | Which sizing mode includes padding and border in the element's width? |
| box-model-5 | Set `padding: 8px 1rem` | Use the two-value shorthand: vertical first, then horizontal |
| box-model-6 | Set `margin: 0 auto` | Use the shorthand that auto-calculates equal horizontal margins |
| box-model-7 | Set `border-radius: 50%` | Which property rounds corners? Think about what percentage makes a circle |
| box-model-8 v1 | Set `padding: 1rem` | Add inner spacing to the notification |
| box-model-8 v2 | Set `border-left: 4px solid coral` | Add a colored accent on the left edge |
| box-model-8 v3 | Set `border-radius: 4px` | Soften the corners of the notification |
| colors-1 | Set `background-color: seashell` | Which property fills the area behind the content? |
| colors-2 | Set `color: coral` | Which property changes the text color? |
| colors-3 | Set `border-color: coral` | Which property changes just the border's color without redefining the whole border? |
| colors-4 | Set `background-color: #ffd700` | Use the same background property, but with a hex code this time |
## Risks
| Risk | Likelihood | Mitigation |
|------|-----------|------------|
| Translation quality for 5 languages | Medium | Use consistent patterns; CSS property names stay in English; keep messages short |
| Messages too vague, frustrating learners | Low | Each message still hints at the concept/direction; task descriptions already contain the answer for early lessons |
| Schema validation failure | Very Low | Only `message` string changes; no structural changes |
## Testing Strategy
1. **Automated**: Run `npm run test` — existing unit tests validate the validator logic, not message content, so they should pass unchanged
2. **Automated**: Run `npm run format.lessons` — ensures JSON formatting is correct
3. **Manual verification**: Spot-check that each new message conceptually matches its lesson without revealing the answer
4. **Schema validation**: JSON files reference the schema; any structural errors would be caught by the editor/tooling

View File

@@ -1,57 +0,0 @@
# fix: validation error messages reveal the solution instead of guiding learning
**Issue:** [#4](https://git.librete.ch/libretech/code-crispies/issues/4)
**Repository:** libretech/code-crispies
**Author:** libretech
**State:** open
**Labels:** none
## Issue Body
Pedagogy audit: 88% of exercises reveal the answer in error messages, creating a fail-then-copy loop. Change validation messages from 'Set padding: 1rem' to 'Which property adds space between content and the element edge?' This applies across all modules — start with flexbox, box-model, and colors (the 3 worst offenders).
## Scope
The three priority modules:
1. **Flexbox** (`lessons/flexbox.json`) — already uses guiding messages (0 messages need changes)
2. **Box Model** (`lessons/01-box-model.json`) — 11 validation messages reveal exact answers
3. **Colors** (`lessons/03-colors.json`) — 4 validation messages reveal exact answers
Localized versions that need corresponding updates:
- `lessons/ar/01-box-model.json`
- `lessons/de/01-box-model.json`
- `lessons/es/01-box-model.json`
- `lessons/pl/01-box-model.json`
- `lessons/uk/01-box-model.json`
No localized versions exist for colors.
## Acceptance Criteria
- [ ] All validation messages in box-model module guide the learner instead of revealing the answer
- [ ] All validation messages in colors module guide the learner instead of revealing the answer
- [ ] Messages use question or hint phrasing (e.g., "Which property..." or "Try the property that...")
- [ ] Messages never include the exact property-value pair that solves the exercise
- [ ] All 5 localized box-model files receive equivalent translated guiding messages
- [ ] Existing tests continue to pass (message content is not tested, only validation logic)
- [ ] Lesson JSON files remain valid against the module schema
## Current vs Desired Pattern
**Current (answer-revealing):**
```
"message": "Set <kbd>padding: 1rem</kbd>"
```
**Desired (guiding):**
```
"message": "Which property adds space between the content and the element's edge?"
```
## Prior Art
The flexbox module already follows the desired pattern. Its messages serve as the style reference:
- "Try changing the display mode to create a flex container"
- "Use the property that adds spacing between flex items"
- "Use the property that distributes items along the main axis"

View File

@@ -1,20 +0,0 @@
# Tasks
## Phase 1: English Lesson Files
- [X] Task 1.1: Rewrite 11 validation messages in `lessons/01-box-model.json`
- [X] Task 1.2: Rewrite 4 validation messages in `lessons/03-colors.json`
## Phase 2: Localized Box-Model Files
- [X] Task 2.1: Update validation messages in `lessons/ar/01-box-model.json` (Arabic) [P]
- [X] Task 2.2: Update validation messages in `lessons/de/01-box-model.json` (German) [P]
- [X] Task 2.3: Update validation messages in `lessons/es/01-box-model.json` (Spanish) [P]
- [X] Task 2.4: Update validation messages in `lessons/pl/01-box-model.json` (Polish) [P]
- [X] Task 2.5: Update validation messages in `lessons/uk/01-box-model.json` (Ukrainian) [P]
## Phase 3: Validation
- [X] Task 3.1: Run `npm run format.lessons` to normalize JSON formatting
- [X] Task 3.2: Run `npm run test` to verify no regressions
- [X] Task 3.3: Spot-check that no message reveals the exact answer
## Phase 4: Commit
- [X] Task 4.1: Commit all changes with conventional commit message